LoongArch: Do not relax pcalau12i+ld.d when there is overflow
[binutils-gdb.git] / gprofng / gp-display-html / gp-display-html.in
blob8290160a71d2c743ad65f61e598687ca283f6867
1 #!/usr/bin/env perl
2 # Copyright (C) 2021-2023 Free Software Foundation, Inc.
3 # Contributed by Oracle.
5 # This file is part of GNU Binutils.
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3, or (at your option)
10 # any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, 51 Franklin Street - Fifth Floor, Boston,
20 # MA 02110-1301, USA.
22 use strict;
23 use warnings;
25 # Disable before release
26 # use Perl::Critic;
28 use bigint;
29 use List::Util qw (max);
30 use Cwd qw (abs_path cwd);
31 use File::Basename;
32 use File::stat;
33 use feature qw (state);
34 use POSIX;
35 use Getopt::Long qw (Configure);
37 #------------------------------------------------------------------------------
38 # Check as early as possible if the version of Perl used is supported.
39 #------------------------------------------------------------------------------
40 INIT
42 my $perl_minimal_version_supported = version->parse ("5.10.0")->normal;
43 my $perl_current_version = version->parse ("$]")->normal;
45 if ($perl_current_version lt $perl_minimal_version_supported)
47 my $msg;
49 $msg = "Error: minimum Perl release required: ";
50 $msg .= $perl_minimal_version_supported;
51 $msg .= " current: ";
52 $msg .= $perl_current_version;
53 $msg .= "\n";
55 print $msg;
57 exit (1);
59 } #-- End of INIT
61 #------------------------------------------------------------------------------
62 # Poor man's version of a boolean.
63 #------------------------------------------------------------------------------
64 my $TRUE = 1;
65 my $FALSE = 0;
67 #------------------------------------------------------------------------------
68 # The total number of functions to be processed.
69 #------------------------------------------------------------------------------
70 my $g_total_function_count = 0;
72 #------------------------------------------------------------------------------
73 # Used to ensure correct alignment of columns.
74 #------------------------------------------------------------------------------
75 my $g_max_length_first_metric;
77 #------------------------------------------------------------------------------
78 # This variable contains the path used to execute $GP_DISPAY_TEXT.
79 #------------------------------------------------------------------------------
80 my $g_path_to_tools;
82 #------------------------------------------------------------------------------
83 # Code debugging flag.
84 #------------------------------------------------------------------------------
85 my $g_test_code = $FALSE;
87 #------------------------------------------------------------------------------
88 # GPROFNG commands and files used.
89 #------------------------------------------------------------------------------
90 my $GP_DISPLAY_TEXT = "gprofng-display-text";
92 my $g_gp_output_file = $GP_DISPLAY_TEXT.".stdout.log";
93 my $g_gp_error_logfile = $GP_DISPLAY_TEXT.".stderr.log";
95 #------------------------------------------------------------------------------
96 # Global variables.
97 #------------------------------------------------------------------------------
98 my $g_addressing_mode = "64 bit";
100 #------------------------------------------------------------------------------
101 # The global regex section.
103 # First step towards consolidating all regexes.
104 #------------------------------------------------------------------------------
105 my $g_less_than_regex = '<';
106 my $g_html_less_than_regex = '&lt;';
107 my $g_endbr_inst_regex = 'endbr[32|64]';
108 my $g_rm_surrounding_spaces_regex = '^\s+|\s+$';
110 #------------------------------------------------------------------------------
111 # For consistency, use a global variable.
112 #------------------------------------------------------------------------------
113 my $g_html_new_line = "<br>";
115 #------------------------------------------------------------------------------
116 # These are the regex's used.
117 #------------------------------------------------------------------------------
118 #------------------------------------------------------------------------------
119 # Disassembly analysis
120 #------------------------------------------------------------------------------
121 my $g_branch_regex = '\.*([0-9a-fA-F]*):\s+(j).*\s*0x([0-9a-fA-F]+)';
122 my $g_endbr_regex = '\.*([0-9a-fA-F]*):\s+(endbr[32|64])';
123 my $g_function_call_v2_regex =
124 '(.*)\s+([0-9a-fA-F]*):\s+(call)\s*0x([0-9a-fA-F]+)\s*';
126 my $g_first_metric;
128 my $binutils_version;
129 my $driver_cmd;
130 my $tool_name;
131 my $version_info;
133 my %g_mapped_cmds = ();
135 #------------------------------------------------------------------------------
136 # Variables dealing with warnings and errors. Since a message may span
137 # multiple lines (for readability reasons), the number of entries in the
138 # array may not reflect the total number of messages. This is why we use
139 # separate variables for the counts.
140 #------------------------------------------------------------------------------
141 my @g_error_msgs = ();
142 my @g_warning_msgs = ();
143 my $g_total_error_count = 0;
144 #------------------------------------------------------------------------------
145 # This count is used in the html_create_warnings_page HTML page to show how
146 # many warning messages there are. Warnings are printed through gp_message(),
147 # but since one warning may span multiple lines, we update a separate counter
148 # that contains the total number of warning messages issued so far.
149 #------------------------------------------------------------------------------
150 my $g_total_warning_count = 0;
151 my $g_options_printed = $FALSE;
152 my $g_abort_msg = "cannot recover from the error(s)";
154 #------------------------------------------------------------------------------
155 # Contains the names that have already been tagged. This is a global
156 # structure because otherwise the code would get much more complicated.
157 #------------------------------------------------------------------------------
158 my %g_tagged_names = ();
160 #------------------------------------------------------------------------------
161 # TBD Remove the use of these structures. No longer used.
162 #------------------------------------------------------------------------------
163 my %g_function_tag_id = ();
164 my $g_context = 5; # Defines the range of scan
166 my $g_default_setting_lang = "en-US.UTF-8";
167 my %g_exp_dir_meta_data;
169 my $g_html_credits_line;
171 my $g_warn_keyword = "[Warning]";
172 my $g_error_keyword = "[Error]";
174 my %g_function_occurrences = ();
175 my %g_map_function_to_index = ();
176 my %g_multi_count_function = ();
177 my %g_function_view_all = ();
178 my @g_full_function_view_table = ();
180 my @g_html_experiment_stats = ();
182 #------------------------------------------------------------------------------
183 # These structures contain the information printed in the function views.
184 #------------------------------------------------------------------------------
185 my $g_header_lines;
187 my @g_html_function_name = ();
189 #------------------------------------------------------------------------------
190 # TBD: This variable may not be needed and replaced by tp_value
191 my $thresh = 0;
192 #------------------------------------------------------------------------------
194 #------------------------------------------------------------------------------
195 # Define the driver command, tool name and version number.
196 #------------------------------------------------------------------------------
197 $driver_cmd = "gprofng display html";
198 $tool_name = "gprofng-display-html";
199 #$binutils_version = "2.38.50";
200 $binutils_version = "2.43.0";
201 $version_info = $tool_name . " GNU binutils version " . $binutils_version;
203 #------------------------------------------------------------------------------
205 #------------------------------------------------------------------------------
206 #------------------------------------------------------------------------------
207 # Define several key data structures.
208 #------------------------------------------------------------------------------
209 #------------------------------------------------------------------------------
211 #------------------------------------------------------------------------------
212 # This table has the settings of the variables the user may set.
213 #------------------------------------------------------------------------------
214 my %g_user_settings =
216 verbose => { option => "--verbose",
217 no_of_arguments => 1,
218 data_type => "onoff",
219 current_value => "off", defined => $FALSE},
221 debug => { option => "--debug",
222 no_of_arguments => 1,
223 data_type => "size",
224 current_value => "off", defined => $FALSE},
226 warnings => { option => "--warnings",
227 no_of_arguments => 1,
228 data_type => "onoff" ,
229 current_value => "off", defined => $FALSE},
231 nowarnings => { option => "--nowarnings",
232 no_of_arguments => 1,
233 data_type => "onoff",
234 current_value => "off", defined => $FALSE},
236 quiet => { option => "--quiet",
237 no_of_arguments => 1,
238 data_type => "onoff",
239 current_value => "off", defined => $FALSE},
241 output => { option => "-o",
242 no_of_arguments => 1,
243 data_type => "path",
244 current_value => undef, defined => $FALSE},
246 overwrite => { option => "-O",
247 no_of_arguments => 1,
248 data_type => "path",
249 current_value => undef, defined => $FALSE},
251 calltree => { option => "-ct",
252 no_of_arguments => 1,
253 data_type => "onoff",
254 current_value => "off", defined => $FALSE},
256 func_limit => { option => "-fl",
257 no_of_arguments => 1,
258 data_type => "pinteger",
259 current_value => 500, defined => $FALSE},
261 highlight_percentage => { option => "--highlight-percentage",
262 no_of_arguments => 1,
263 data_type => "pfloat",
264 current_value => 90.0, defined => $FALSE},
266 hp => { option => "-hp",
267 no_of_arguments => 1,
268 data_type => "pfloat",
269 current_value => 90.0, defined => $FALSE},
271 threshold_percentage => { option => "-tp",
272 no_of_arguments => 1,
273 data_type => "pfloat",
274 current_value => 100.0, defined => $FALSE},
276 default_metrics => { option => "-dm",
277 no_of_arguments => 1,
278 data_type => "onoff",
279 current_value => "off", defined => $FALSE},
281 ignore_metrics => { option => "-im",
282 no_of_arguments => 1,
283 data_type => "metric_names",
284 current_value => undef, defined => $FALSE},
287 #------------------------------------------------------------------------------
288 # Convenience. These map the on/off value to $TRUE/$FALSE to make the code
289 # easier to read. For example: "if ($g_verbose)" as opposed to the following:
290 # "if ($verbose_setting eq "on").
291 #------------------------------------------------------------------------------
292 my $g_verbose = $FALSE;
293 my $g_debug = $FALSE;
294 my $g_warnings = $TRUE;
295 my $g_quiet = $FALSE;
297 #------------------------------------------------------------------------------
298 # Since ARGV is modified when parsing the options, a clean copy is used to
299 # print the original ARGV values in case of a warning, or error.
300 #------------------------------------------------------------------------------
301 my @CopyOfARGV = ();
303 my %g_debug_size =
305 "on" => $FALSE,
306 "s" => $FALSE,
307 "m" => $FALSE,
308 "l" => $FALSE,
309 "xl" => $FALSE,
312 my %local_system_config =
314 kernel_name => "undefined",
315 nodename => "undefined",
316 kernel_release => "undefined",
317 kernel_version => "undefined",
318 machine => "undefined",
319 processor => "undefined",
320 hardware_platform => "undefined",
321 operating_system => "undefined",
322 hostname_current => "undefined",
325 #------------------------------------------------------------------------------
326 # Note that we use single quotes here, because regular expressions wreak
327 # havoc otherwise.
328 #------------------------------------------------------------------------------
330 my %g_arch_specific_settings =
332 arch_supported => $FALSE,
333 arch => 'undefined',
334 regex => 'undefined',
335 subexp => 'undefined',
336 linksubexp => 'undefined',
339 my %g_locale_settings = (
340 LANG => "en_US.UTF-8",
341 decimal_separator => "\\.",
342 covert_to_dot => $FALSE
345 #------------------------------------------------------------------------------
346 # See this page for a nice overview with the colors:
347 # https://www.w3schools.com/colors/colors_groups.asp
348 #------------------------------------------------------------------------------
350 my %g_html_color_scheme = (
351 "control_flow" => "Brown",
352 "target_function_name" => "Red",
353 "non_target_function_name" => "BlueViolet",
354 "background_color_hot" => "PeachPuff",
355 "background_color_lukewarm" => "LemonChiffon",
356 "link_outside_range" => "Crimson",
357 "error_message" => "LightPink",
358 "background_color_page" => "White",
359 # "background_color_page" => "LightGray",
360 "background_selected_sort" => "LightSlateGray",
361 "index" => "Lavender",
364 #------------------------------------------------------------------------------
365 # These are the base names for the HTML files that are generated.
366 #------------------------------------------------------------------------------
367 my %g_html_base_file_name = (
368 "caller_callee" => "caller-callee",
369 "disassembly" => "dis",
370 "experiment_info" => "experiment-info",
371 "function_view" => "function-view-sorted",
372 "index" => "index",
373 "source" => "src",
374 "warnings" => "warnings",
377 #------------------------------------------------------------------------------
378 # Introducing main() is cosmetic, but helps with the scoping of variables.
379 #------------------------------------------------------------------------------
380 main ();
382 exit (0);
384 #------------------------------------------------------------------------------
385 # This is the driver part of the program.
386 #------------------------------------------------------------------------------
387 sub main
389 my $subr_name = get_my_name ();
391 @CopyOfARGV = @ARGV;
393 #------------------------------------------------------------------------------
394 # The name of the configuration file.
395 #------------------------------------------------------------------------------
396 my $rc_file_name = ".gp-display-html.rc";
398 #------------------------------------------------------------------------------
399 # OS commands executed and search paths.
401 # TBD: check if elfdump should be here too (most likely not though)
402 #------------------------------------------------------------------------------
403 my @selected_os_cmds = qw (rm cat hostname locale which printenv uname
404 readelf mkdir);
406 my @search_paths_os_cmds = qw (
407 /usr/bin
408 /bin
409 /usr/local/bin
410 /usr/local/sbin
411 /usr/sbin
412 /sbin
415 #------------------------------------------------------------------------------
416 # TBD: Eliminate these.
417 #------------------------------------------------------------------------------
418 my $ARCHIVES_MAP_NAME;
419 my $ARCHIVES_MAP_VADDR;
421 #------------------------------------------------------------------------------
422 # Local structures (hashes and arrays).
423 #------------------------------------------------------------------------------
424 my @exp_dir_list = ();
425 my @metrics_data;
427 my %function_address_info = ();
428 my $function_address_info_ref;
430 my @function_info = ();
431 my $function_info_ref;
433 my %function_address_and_index = ();
434 my $function_address_and_index_ref;
436 my %addressobjtextm = ();
437 my $addressobjtextm_ref;
439 my %addressobj_index = ();
440 my $addressobj_index_ref;
442 my %LINUX_vDSO = ();
443 my $LINUX_vDSO_ref;
445 my %function_view_structure = ();
446 my $function_view_structure_ref;
448 my %elf_rats = ();
449 my $elf_rats_ref;
451 #------------------------------------------------------------------------------
452 # Local variables.
453 #------------------------------------------------------------------------------
454 my $abs_path_outputdir;
455 my $archive_dir_not_empty;
456 my $base_va_executable;
457 my $executable_name;
458 my $found_exp_dir;
459 my $ignore_value;
460 my $msg;
461 my $number_of_metrics;
462 my $va_executable_in_hex;
464 my $failed_command_mappings;
466 my $script_pc_metrics;
467 my $dir_check_errors;
468 my $consistency_errors;
469 my $outputdir;
470 my $return_code;
472 my $decimal_separator;
473 my $convert_to_dot;
474 my $architecture_supported;
475 my $elf_arch;
476 my $elf_support;
477 my $home_dir;
478 my $elf_loadobjects_found;
480 my $rc_file_paths_ref;
481 my @rc_file_paths = ();
482 my $rc_file_errors = 0;
484 my @sort_fields = ();
485 my $summary_metrics;
486 my $call_metrics;
487 my $user_metrics;
488 my $system_metrics;
489 my $wall_metrics;
490 my $detail_metrics;
491 my $detail_metrics_system;
493 my $html_test;
494 my @experiment_data;
495 my $exp_info_file;
496 my $exp_info_ref;
497 my @exp_info;
499 my $pretty_dir_list;
501 my %metric_value = ();
502 my %metric_description = ();
503 my %metric_description_reversed = ();
504 my %metric_found = ();
505 my %ignored_metrics = ();
507 my $metric_value_ref;
508 my $metric_description_ref;
509 my $metric_found_ref;
510 my $ignored_metrics_ref;
512 my @table_execution_stats = ();
513 my $table_execution_stats_ref;
515 my $html_first_metric_file_ref;
516 my $html_first_metric_file;
518 my $arch;
519 my $subexp;
520 my $linksubexp;
522 my $setting_for_LANG;
523 my $time_percentage_multiplier;
524 my $process_all_functions;
526 my $selected_archive;
528 #------------------------------------------------------------------------------
529 # If no options are given, print the help info and exit.
530 #------------------------------------------------------------------------------
531 if ($#ARGV == -1)
533 $ignore_value = print_help_info ();
534 return (0);
537 #------------------------------------------------------------------------------
538 # This part is like a preamble. Before we continue we need to figure out some
539 # things that are needed later on.
540 #------------------------------------------------------------------------------
542 #------------------------------------------------------------------------------
543 # Store the absolute path of the command executed.
544 #------------------------------------------------------------------------------
545 my $location_gp_command = $0;
547 #------------------------------------------------------------------------------
548 # Get the ball rolling. Parse and interpret the options. Some first checks
549 # are performed.
551 # Instead of bailing out on the first user error, we capture all warnings and
552 # errors. The warnings, if any, will be printed once the command line has
553 # been parsed and verified. Execution continues.
555 # Any error(s) accumulated in this phase will be printed after the command
556 # line has been parsed and verified. Execution is then terminated.
558 # In the remainder, any error encountered will immediately terminate the
559 # execution because we can't guarantee the remaining code will work up to
560 # some point.
561 #------------------------------------------------------------------------------
562 my ($found_exp_dir_ref, $exp_dir_list_ref) = parse_and_check_user_options ();
564 $found_exp_dir = ${ $found_exp_dir_ref };
566 if ($found_exp_dir)
568 @exp_dir_list = @{ $exp_dir_list_ref };
570 else
572 $msg = "the list with experiments is either missing, or incorrect";
573 gp_message ("debug", $subr_name, $msg);
576 #------------------------------------------------------------------------------
577 # The final settings for verbose, debug, warnings and quiet are known and the
578 # gp_message() subroutine is aware of these.
579 #------------------------------------------------------------------------------
580 $msg = "parsing of the user options completed";
581 gp_message ("verbose", $subr_name, $msg);
583 #------------------------------------------------------------------------------
584 # The user options have been taken in. Check for validity and consistency.
585 #------------------------------------------------------------------------------
586 $msg = "process user options";
587 gp_message ("verbose", $subr_name, $msg);
589 ($ignored_metrics_ref, $outputdir,
590 $time_percentage_multiplier, $process_all_functions, $exp_dir_list_ref) =
591 process_user_options (\@exp_dir_list);
593 @exp_dir_list = @{ $exp_dir_list_ref };
594 %ignored_metrics = %{$ignored_metrics_ref};
596 #------------------------------------------------------------------------------
597 # The next subroutine is executed early to ensure the OS commands we need are
598 # available.
600 # This subroutine stores the commands and the full path names as an
601 # associative array called "g_mapped_cmds". The command is the key and the
602 # value is the full path. For example: ("uname", /usr/bin/uname).
603 #------------------------------------------------------------------------------
604 gp_message ("debug", $subr_name, "verify the OS commands");
605 $failed_command_mappings = check_and_define_cmds (\@selected_os_cmds,
606 \@search_paths_os_cmds);
608 if ($failed_command_mappings == 0)
610 $msg = "successfully verified the OS commands";
611 gp_message ("debug", $subr_name, $msg);
614 #------------------------------------------------------------------------------
615 #------------------------------------------------------------------------------
616 # Time to check if any warnings and/or errors have been generated.
617 #------------------------------------------------------------------------------
618 #------------------------------------------------------------------------------
620 #------------------------------------------------------------------------------
621 # We have completed all the upfront checks. Print any warnings and errors.
622 # If there are already any errors, execution is terminated. As execution
623 # continues, errors may occur and they are typically fatal.
624 #------------------------------------------------------------------------------
625 if ($g_debug)
627 $msg = "internal settings after option processing";
628 $ignore_value = print_table_user_settings ("diag", $msg);
631 #------------------------------------------------------------------------------
632 # Terminate execution in case fatal errors have occurred.
633 #------------------------------------------------------------------------------
634 if ( $g_total_error_count > 0)
636 my $msg = "the current values for the user controllable settings";
637 print_user_settings ("debug", $msg);
639 gp_message ("abort", $subr_name, $g_abort_msg);
641 else
643 my $msg = "after parsing the user options, the final values are";
644 print_user_settings ("debug", $msg);
647 #------------------------------------------------------------------------------
648 # If no option is given for the output directory, pick a default. Otherwise,
649 # if the output directory exists, wipe it clean in case the -O option is used.
650 # If not, raise an error because the -o option does not overwrite an existing
651 # directory.
652 # Also in case of other errors, the execution is terminated.
653 #------------------------------------------------------------------------------
654 $outputdir = set_up_output_directory ();
655 $abs_path_outputdir = Cwd::cwd () . "/" . $outputdir;
657 $msg = "the output directory is $outputdir";
658 gp_message ("debug", $subr_name, $msg);
660 #------------------------------------------------------------------------------
661 # Get the home directory and the locations for the configuration file on the
662 # current system.
663 #------------------------------------------------------------------------------
664 ($home_dir, $rc_file_paths_ref) = get_home_dir_and_rc_path ($rc_file_name);
666 @rc_file_paths = @{ $rc_file_paths_ref };
668 $msg = "the home directory is $home_dir";
669 gp_message ("debug", $subr_name, $msg);
671 #------------------------------------------------------------------------------
672 # TBD: de-activated until this feature has been fully implemented.
673 #------------------------------------------------------------------------------
674 ## $msg = "the search path for the rc file is @rc_file_paths";
675 ## gp_message ("debug", $subr_name, $msg);
676 ## $pretty_dir_list = build_pretty_dir_list (\@rc_file_paths);
678 #------------------------------------------------------------------------------
679 # Get the ball rolling. Parse and interpret the configuration file (if any)
680 # and the command line options.
682 # Note that the verbose, debug, and quiet options can be set in this file.
683 # It is a deliberate choice to ignore these for now. The assumption is that
684 # the user will not be happy if we ignore the command line settings for a
685 # while.
686 #------------------------------------------------------------------------------
687 $msg = "processing of the rc file has been disabled for now";
688 gp_message ("debugXL", $subr_name, $msg);
690 # Temporarily disabled
691 # print_table_user_settings ("debugXL", "before function process_rc_file");
692 # $rc_file_errors = process_rc_file ($rc_file_name, $rc_file_paths_ref);
693 # if ($rc_file_errors != 0)
695 # $message = "fatal errors in file $rc_file_name encountered";
696 # gp_message ("debugXL", $subr_name, $message);
698 # print_table_user_settings ("debugXL", "after function process_rc_file");
700 #------------------------------------------------------------------------------
701 # Print a list with the experiment directory names
702 #------------------------------------------------------------------------------
703 $pretty_dir_list = build_pretty_dir_list (\@exp_dir_list);
705 my $plural = ($#exp_dir_list > 0) ? "directories are" : "directory is";
707 $msg = "the experiment " . $plural . ":";
708 gp_message ("verbose", $subr_name, $msg);
709 gp_message ("verbose", $subr_name, $pretty_dir_list);
711 #------------------------------------------------------------------------------
712 # Set up the first entry with the meta data for the experiments. This field
713 # contains the absolute paths to the experiment directories.
714 #------------------------------------------------------------------------------
715 for my $exp_dir (@exp_dir_list)
717 my ($filename, $directory_path, $ignore_suffix) = fileparse ($exp_dir);
718 gp_message ("debug", $subr_name, "exp_dir = $exp_dir");
719 gp_message ("debug", $subr_name, "filename = $filename");
720 gp_message ("debug", $subr_name, "directory_path = $directory_path");
721 $g_exp_dir_meta_data{$filename}{"directory_path"} = $directory_path;
724 #------------------------------------------------------------------------------
725 # TBD:
726 # This subroutine may be overkill. See what is really needed here and remove
727 # everything else.
729 # Upon return, one directory has been selected to be used in the remainder.
730 # This is not always the correct thing to do, but is the same as the original
731 # code. In due time this should be addressed though.
732 #------------------------------------------------------------------------------
733 ($archive_dir_not_empty, $selected_archive, $elf_rats_ref) =
734 check_validity_exp_dirs (\@exp_dir_list);
736 %elf_rats = %{$elf_rats_ref};
738 $msg = "the experiment directories have been verified and are valid";
739 gp_message ("verbose", $subr_name, $msg);
741 #------------------------------------------------------------------------------
742 # Now that we know the map.xml file(s) are present, we can scan these and get
743 # the required information. This includes setting the base virtual address.
744 #------------------------------------------------------------------------------
745 $ignore_value = determine_base_virtual_address ($exp_dir_list_ref);
747 #------------------------------------------------------------------------------
748 # Check whether the experiment directories are consistent.
749 #------------------------------------------------------------------------------
750 ($consistency_errors, $executable_name) =
751 verify_consistency_experiments ($exp_dir_list_ref);
753 if ($consistency_errors == 0)
755 $msg = "the experiment directories are consistent";
756 gp_message ("verbose", $subr_name, $msg);
758 else
760 $msg = "the number of consistency errors detected: $consistency_errors";
761 gp_message ("abort", $subr_name, $msg);
764 #------------------------------------------------------------------------------
765 # The directories are consistent. We can now set the base virtual address of
766 # the executable.
767 #------------------------------------------------------------------------------
768 $base_va_executable =
769 $g_exp_dir_meta_data{$selected_archive}{"va_base_in_hex"};
771 $msg = "executable_name = " . $executable_name;
772 gp_message ("debug", $subr_name, $msg);
773 $msg = "selected_archive = " . $selected_archive;
774 gp_message ("debug", $subr_name, $msg);
775 $msg = "base_va_executable = " . $base_va_executable;
776 gp_message ("debug", $subr_name, $msg);
778 #------------------------------------------------------------------------------
779 # The $GP_DISPLAY_TEXT tool is critical and has to be available in order to
780 # proceed.
781 # This subroutine only returns a value if the tool can be found.
782 #------------------------------------------------------------------------------
783 $g_path_to_tools = ${ check_availability_tool (\$location_gp_command)};
785 $GP_DISPLAY_TEXT = $g_path_to_tools . $GP_DISPLAY_TEXT;
787 $msg = "updated GP_DISPLAY_TEXT = $GP_DISPLAY_TEXT";
788 gp_message ("debug", $subr_name, $msg);
790 #------------------------------------------------------------------------------
791 # Check if $GP_DISPLAY_TEXT is executable for user, group, and other.
792 # If not, print a warning only, since this may not be fatal but could
793 # potentially lead to issues later on.
794 #------------------------------------------------------------------------------
795 if (not is_file_executable ($GP_DISPLAY_TEXT))
797 $msg = "file $GP_DISPLAY_TEXT is not executable for user, group, and";
798 $msg .= " other";
799 gp_message ("warning", $subr_name, $msg);
802 #------------------------------------------------------------------------------
803 # Find out what the decimal separator is, as set by the user.
804 #------------------------------------------------------------------------------
805 ($return_code, $decimal_separator, $convert_to_dot) =
806 determine_decimal_separator ();
808 if ($return_code == 0)
810 $msg = "decimal separator is $decimal_separator";
811 $msg .= " (conversion to dot is ";
812 $msg .= ($convert_to_dot == $TRUE ? "enabled" : "disabled") . ")";
813 gp_message ("debugXL", $subr_name, $msg);
815 else
817 $msg = "the decimal separator cannot be determined -";
818 $msg .= " set to $decimal_separator";
819 gp_message ("warning", $subr_name, $msg);
822 #------------------------------------------------------------------------------
823 # Collect and store the system information.
824 #------------------------------------------------------------------------------
825 $msg = "collect system information and adapt settings";
826 gp_message ("verbose", $subr_name, $msg);
828 $return_code = get_system_config_info ();
830 #------------------------------------------------------------------------------
831 # The 3 variables below are used in the remainder.
833 # The output from "uname -p" is recommended to be used for the ISA.
834 #------------------------------------------------------------------------------
835 my $hostname_current = $local_system_config{hostname_current};
836 my $arch_uname_s = $local_system_config{kernel_name};
837 my $arch_uname = $local_system_config{processor};
839 gp_message ("debug", $subr_name, "set hostname_current = $hostname_current");
840 gp_message ("debug", $subr_name, "set arch_uname_s = $arch_uname_s");
841 gp_message ("debug", $subr_name, "set arch_uname = $arch_uname");
843 #------------------------------------------------------------------------------
844 # This function also sets the values in "g_arch_specific_settings". This
845 # includes several definitions of regular expressions.
846 #------------------------------------------------------------------------------
847 ($architecture_supported, $elf_arch, $elf_support) =
848 set_system_specific_variables ($arch_uname, $arch_uname_s);
850 $msg = "architecture_supported = $architecture_supported";
851 gp_message ("debug", $subr_name, $msg);
852 $msg = "elf_arch = $elf_arch";
853 gp_message ("debug", $subr_name, $msg);
854 $msg = "elf_support = ".($elf_arch ? "TRUE" : "FALSE");
855 gp_message ("debug", $subr_name, $msg);
857 for my $feature (sort keys %g_arch_specific_settings)
859 $msg = "g_arch_specific_settings{$feature} = ";
860 $msg .= $g_arch_specific_settings{$feature};
861 gp_message ("debug", $subr_name, $msg);
864 $arch = $g_arch_specific_settings{"arch"};
865 $subexp = $g_arch_specific_settings{"subexp"};
866 $linksubexp = $g_arch_specific_settings{"linksubexp"};
868 $g_locale_settings{"LANG"} = get_LANG_setting ();
870 $msg = "after get_LANG_setting: LANG = $g_locale_settings{'LANG'}";
871 gp_message ("debugXL", $subr_name, $msg);
873 #------------------------------------------------------------------------------
874 # Temporarily reset selected settings since these are not yet implemented.
875 #------------------------------------------------------------------------------
876 $ignore_value = reset_selected_settings ();
878 #------------------------------------------------------------------------------
879 # TBD: Revisit. Is this really necessary?
880 #------------------------------------------------------------------------------
882 ($executable_name, $va_executable_in_hex) =
883 check_loadobjects_are_elf ($selected_archive);
884 $elf_loadobjects_found = $TRUE;
886 # TBD: Hack and those ARCHIVES_ names can be eliminated
887 $ARCHIVES_MAP_NAME = $executable_name;
888 $ARCHIVES_MAP_VADDR = $va_executable_in_hex;
890 $msg = "hack ARCHIVES_MAP_NAME = $ARCHIVES_MAP_NAME";
891 gp_message ("debugXL", $subr_name, $msg);
892 $msg = "hack ARCHIVES_MAP_VADDR = $ARCHIVES_MAP_VADDR";
893 gp_message ("debugXL", $subr_name, $msg);
895 $msg = "after call to check_loadobjects_are_elf forced";
896 $msg .= " elf_loadobjects_found = $elf_loadobjects_found";
897 gp_message ("debugXL", $subr_name, $msg);
899 $g_html_credits_line = ${ create_html_credits () };
901 $msg = "g_html_credits_line = $g_html_credits_line";
902 gp_message ("debugXL", $subr_name, $msg);
904 #------------------------------------------------------------------------------
905 # Add a "/" to simplify the construction of path names in the remainder.
907 # TBD: Push this into a subroutine(s).
908 #------------------------------------------------------------------------------
909 $outputdir = append_forward_slash ($outputdir);
911 $msg = "prepared outputdir = ". $outputdir;
912 gp_message ("debug", $subr_name, $msg);
914 #------------------------------------------------------------------------------
915 #------------------------------------------------------------------------------
916 # ******* TBD: e.system not available on Linux!!
917 #------------------------------------------------------------------------------
918 #------------------------------------------------------------------------------
920 ## my $summary_metrics = 'e.totalcpu';
921 $detail_metrics = 'e.totalcpu';
922 $detail_metrics_system = 'e.totalcpu:e.system';
923 $call_metrics = 'a.totalcpu';
925 $msg = "set detail_metrics_system = " . $detail_metrics_system;
926 gp_message ("debug", $subr_name, $msg);
927 $msg = "set detail_metrics = " . $detail_metrics;
928 gp_message ("debug", $subr_name, $msg);
929 $msg = "set call_metrics = " . $call_metrics;
930 gp_message ("debug", $subr_name, $msg);
932 my $cmd_options;
933 my $metrics_cmd;
935 my $outfile1 = $outputdir ."metrics";
936 my $outfile2 = $outputdir . "metrictotals";
937 my $gp_error_file = $outputdir . $g_gp_error_logfile;
939 #------------------------------------------------------------------------------
940 # Execute the $GP_DISPLAY_TEXT tool with the appropriate options. The goal is
941 # to get all the output in files $outfile1 and $outfile2. These are then
942 # parsed.
943 #------------------------------------------------------------------------------
944 $msg = "gather the metrics data from the experiments";
945 gp_message ("verbose", $subr_name, $msg);
947 $return_code = get_metrics_data (\@exp_dir_list, $outputdir, $outfile1,
948 $outfile2, $gp_error_file);
950 if ($return_code != 0)
952 gp_message ("abort", $subr_name, "execution terminated");
955 #------------------------------------------------------------------------------
956 # TBD: Test this code
957 #------------------------------------------------------------------------------
958 $msg = "unable to open metric value data file $outfile1 for reading:";
959 open (METRICS, "<", $outfile1)
960 or die ($subr_name . " - " . $msg . " " . $!);
962 $msg = "opened file $outfile1 for reading";
963 gp_message ("debug", $subr_name, "opened file $outfile1 for reading");
965 chomp (@metrics_data = <METRICS>);
966 close (METRICS);
968 for my $i (keys @metrics_data)
970 $msg = "metrics_data[$i] = " . $metrics_data[$i];
971 gp_message ("debugXL", $subr_name, $msg);
974 #------------------------------------------------------------------------------
975 # Process the generated metrics data.
976 #------------------------------------------------------------------------------
977 if ($g_user_settings{"default_metrics"}{"current_value"} eq "off")
979 #------------------------------------------------------------------------------
980 # The metrics will be derived from the experiments.
981 #------------------------------------------------------------------------------
983 gp_message ("verbose", $subr_name, "Process the metrics data");
985 ($metric_value_ref, $metric_description_ref, $metric_found_ref,
986 $user_metrics, $system_metrics, $wall_metrics,
987 $summary_metrics, $detail_metrics, $detail_metrics_system, $call_metrics
988 ) = process_metrics_data ($outfile1, $outfile2, \%ignored_metrics);
990 %metric_value = %{ $metric_value_ref };
991 %metric_description = %{ $metric_description_ref };
992 %metric_found = %{ $metric_found_ref };
993 %metric_description_reversed = reverse %metric_description;
995 $msg = "after the call to process_metrics_data";
996 gp_message ("debugXL", $subr_name, $msg);
998 for my $metric (sort keys %metric_value)
1000 $msg = "metric_value{$metric} = " . $metric_value{$metric};
1001 gp_message ("debugXL", $subr_name, $msg);
1003 for my $metric (sort keys %metric_description)
1005 $msg = "metric_description{$metric} =";
1006 $msg .= " " . $metric_description{$metric};
1007 gp_message ("debugXL", $subr_name, $msg);
1009 gp_message ("debugXL", $subr_name, "user_metrics = $user_metrics");
1010 gp_message ("debugXL", $subr_name, "system_metrics = $system_metrics");
1011 gp_message ("debugXL", $subr_name, "wall_metrics = $wall_metrics");
1013 else
1015 #------------------------------------------------------------------------------
1016 # A default set of metrics will be used.
1018 # TBD: These should be OS dependent.
1019 #------------------------------------------------------------------------------
1020 $msg = "select the set of default metrics";
1021 gp_message ("verbose", $subr_name, $msg);
1023 ($metric_description_ref, $metric_found_ref, $summary_metrics,
1024 $detail_metrics, $detail_metrics_system, $call_metrics
1025 ) = set_default_metrics ($outfile1, \%ignored_metrics);
1028 %metric_description = %{ $metric_description_ref };
1029 %metric_found = %{ $metric_found_ref };
1030 %metric_description_reversed = reverse %metric_description;
1032 $msg = "after the call to set_default_metrics";
1033 gp_message ("debug", $subr_name, $msg);
1037 $number_of_metrics = split (":", $summary_metrics);
1039 $msg = "summary_metrics = " . $summary_metrics;
1040 gp_message ("debugM", $subr_name, $msg);
1041 $msg = "detail_metrics = " . $detail_metrics;
1042 gp_message ("debugM", $subr_name, $msg);
1043 $msg = "detail_metrics_system = " . $detail_metrics_system;
1044 gp_message ("debugM", $subr_name, $msg);
1045 $msg = "call_metrics = " . $call_metrics;
1046 gp_message ("debugM", $subr_name, $msg);
1047 $msg = "number_of_metrics = " . $number_of_metrics;
1048 gp_message ("debugM", $subr_name, $msg);
1050 #------------------------------------------------------------------------------
1051 # TBD Find a way to better handle this situation:
1052 #------------------------------------------------------------------------------
1053 for my $im (keys %metric_found)
1055 $msg = "metric_found{$im} = " . $metric_found{$im};
1056 gp_message ("debugXL", $subr_name, $msg);
1058 for my $im (keys %ignored_metrics)
1060 if (not exists ($metric_found{$im}))
1062 $msg = "user requested ignored metric (-im) $im does not exist in";
1063 $msg .= " collected metrics";
1064 gp_message ("debugXL", $subr_name, $msg);
1068 #------------------------------------------------------------------------------
1069 # Get the information on the experiments.
1070 #------------------------------------------------------------------------------
1071 $msg = "generate the experiment information";
1072 gp_message ("verbose", $subr_name, $msg);
1074 my $experiment_data_ref = get_experiment_info (\$outputdir, \@exp_dir_list);
1075 @experiment_data = @{ $experiment_data_ref };
1077 for my $i (sort keys @experiment_data)
1079 my $msg = "i = $i " . $experiment_data[$i]{"exp_id"} . " => " .
1080 $experiment_data[$i]{"exp_name_full"};
1081 gp_message ("debugM", $subr_name, $msg);
1084 $experiment_data_ref = process_experiment_info ($experiment_data_ref);
1085 @experiment_data = @{ $experiment_data_ref };
1087 for my $i (sort keys @experiment_data)
1089 for my $fields (sort keys %{ $experiment_data[$i] })
1091 my $msg = "i = $i experiment_data[$i]{$fields} = " .
1092 $experiment_data[$i]{$fields};
1093 gp_message ("debugXL", $subr_name, $msg);
1097 @g_html_experiment_stats = @{ create_exp_info (\@exp_dir_list,
1098 \@experiment_data) };
1100 $table_execution_stats_ref = html_generate_exp_summary (\$outputdir,
1101 \@experiment_data);
1102 @table_execution_stats = @{ $table_execution_stats_ref };
1104 #------------------------------------------------------------------------------
1105 # Get the function overview.
1106 #------------------------------------------------------------------------------
1107 $msg = "generate the list with functions executed";
1108 gp_message ("verbose", $subr_name, $msg);
1110 my ($outfile, $sort_fields_ref) =
1111 get_hot_functions (\@exp_dir_list, $summary_metrics, $outputdir);
1113 @sort_fields = @{$sort_fields_ref};
1115 #------------------------------------------------------------------------------
1116 # Parse the output from the fsummary command and store the relevant data for
1117 # all the functions listed there.
1118 #------------------------------------------------------------------------------
1119 $msg = "analyze and store the relevant function information";
1120 gp_message ("verbose", $subr_name, $msg);
1122 ($function_info_ref, $function_address_and_index_ref, $addressobjtextm_ref,
1123 $LINUX_vDSO_ref, $function_view_structure_ref) =
1124 get_function_info ($outfile);
1126 @function_info = @{ $function_info_ref };
1127 %function_address_and_index = %{ $function_address_and_index_ref };
1128 %addressobjtextm = %{ $addressobjtextm_ref };
1129 %LINUX_vDSO = %{ $LINUX_vDSO_ref };
1130 %function_view_structure = %{ $function_view_structure_ref };
1132 $msg = "found " . $g_total_function_count . " functions to process";
1133 gp_message ("verbose", $subr_name, $msg);
1135 for my $keys (0 .. $#function_info)
1137 for my $fields (keys %{$function_info[$keys]})
1139 $msg = "$keys $fields $function_info[$keys]{$fields}";
1140 gp_message ("debugXL", $subr_name, $msg);
1144 for my $i (keys %addressobjtextm)
1146 $msg = "addressobjtextm{$i} = " . $addressobjtextm{$i};
1147 gp_message ("debugXL", $subr_name, $msg);
1150 $msg = "generate the files with function overviews and the";
1151 $msg .= " callers-callees information";
1152 gp_message ("verbose", $subr_name, $msg);
1154 $script_pc_metrics = generate_function_level_info (\@exp_dir_list,
1155 $call_metrics,
1156 $summary_metrics,
1157 $outputdir,
1158 $sort_fields_ref);
1160 $msg = "preprocess the files with the function level information";
1161 gp_message ("verbose", $subr_name, $msg);
1163 $ignore_value = preprocess_function_files (
1164 $metric_description_ref,
1165 $script_pc_metrics,
1166 $outputdir,
1167 \@sort_fields);
1169 $msg = "for each function, generate a set of files";
1170 gp_message ("verbose", $subr_name, $msg);
1172 ($function_info_ref, $function_address_info_ref, $addressobj_index_ref) =
1173 process_function_files (\@exp_dir_list,
1174 $executable_name,
1175 $time_percentage_multiplier,
1176 $summary_metrics,
1177 $process_all_functions,
1178 $elf_loadobjects_found,
1179 $outputdir,
1180 \@sort_fields,
1181 \@function_info,
1182 \%function_address_and_index,
1183 \%LINUX_vDSO,
1184 \%metric_description,
1185 $elf_arch,
1186 $base_va_executable,
1187 $ARCHIVES_MAP_NAME,
1188 $ARCHIVES_MAP_VADDR,
1189 \%elf_rats);
1191 @function_info = @{ $function_info_ref };
1192 %function_address_info = %{ $function_address_info_ref };
1193 %addressobj_index = %{ $addressobj_index_ref };
1195 #------------------------------------------------------------------------------
1196 # Parse the disassembly information and generate the html files.
1197 #------------------------------------------------------------------------------
1198 $msg = "parse the disassembly files and generate the html files";
1199 gp_message ("verbose", $subr_name, $msg);
1201 $ignore_value = parse_dis_files (\$number_of_metrics,
1202 \@function_info,
1203 \%function_address_and_index,
1204 \$outputdir,
1205 \%addressobj_index);
1207 #------------------------------------------------------------------------------
1208 # Parse the source information and generate the html files.
1209 #------------------------------------------------------------------------------
1210 $msg = "parse the source files and generate the html files";
1211 gp_message ("verbose", $subr_name, $msg);
1213 parse_source_files (\$number_of_metrics, \@function_info, \$outputdir);
1215 #------------------------------------------------------------------------------
1216 # Parse the caller-callee information and generate the html files.
1217 #------------------------------------------------------------------------------
1218 $msg = "process the caller-callee information and generate the html file";
1219 gp_message ("verbose", $subr_name, $msg);
1221 #------------------------------------------------------------------------------
1222 # Generate the caller-callee information.
1223 #------------------------------------------------------------------------------
1224 $ignore_value = generate_caller_callee (\$number_of_metrics,
1225 \@function_info,
1226 \%function_view_structure,
1227 \%function_address_info,
1228 \%addressobjtextm,
1229 \$outputdir);
1231 #------------------------------------------------------------------------------
1232 # Parse the calltree information and generate the html files.
1233 #------------------------------------------------------------------------------
1234 if ($g_user_settings{"calltree"}{"current_value"} eq "on")
1236 $msg = "process the call tree information and generate the html file";
1237 gp_message ("verbose", $subr_name, $msg);
1239 $ignore_value = process_calltree (\@function_info,
1240 \%function_address_info,
1241 \%addressobjtextm,
1242 $outputdir);
1245 #------------------------------------------------------------------------------
1246 # Process the metric values.
1247 #------------------------------------------------------------------------------
1248 $msg = "generate the html file with the metrics information";
1249 gp_message ("verbose", $subr_name, $msg);
1251 $ignore_value = process_metrics ($outputdir,
1252 \@sort_fields,
1253 \%metric_description,
1254 \%ignored_metrics);
1256 #------------------------------------------------------------------------------
1257 # Generate the function view html files.
1258 #------------------------------------------------------------------------------
1259 $msg = "generate the function view html files";
1260 gp_message ("verbose", $subr_name, $msg);
1262 $html_first_metric_file_ref = generate_function_view (
1263 \$outputdir,
1264 \$summary_metrics,
1265 \$number_of_metrics,
1266 \@function_info,
1267 \%function_view_structure,
1268 \%function_address_info,
1269 \@sort_fields,
1270 \@exp_dir_list,
1271 \%addressobjtextm);
1273 $html_first_metric_file = ${ $html_first_metric_file_ref };
1275 $msg = "html_first_metric_file = " . $html_first_metric_file;
1276 gp_message ("debugXL", $subr_name, $msg);
1278 $html_test = ${ generate_home_link ("left") };
1279 $msg = "html_test = " . $html_test;
1280 gp_message ("debugXL", $subr_name, $msg);
1282 #------------------------------------------------------------------------------
1283 # Unconditionnaly generate the page with the warnings.
1284 #------------------------------------------------------------------------------
1285 $ignore_value = html_create_warnings_page (\$outputdir);
1287 #------------------------------------------------------------------------------
1288 # Generate the index.html file.
1289 #------------------------------------------------------------------------------
1290 $msg = "generate the index.html file";
1291 gp_message ("verbose", $subr_name, $msg);
1293 $ignore_value = html_generate_index (\$outputdir,
1294 \$html_first_metric_file,
1295 \$summary_metrics,
1296 \$number_of_metrics,
1297 \@function_info,
1298 \%function_address_info,
1299 \@sort_fields,
1300 \@exp_dir_list,
1301 \%addressobjtextm,
1302 \%metric_description_reversed,
1303 \@table_execution_stats);
1305 #------------------------------------------------------------------------------
1306 # We're done. In debug mode, print the meta data for the experiment
1307 # directories.
1308 #------------------------------------------------------------------------------
1309 $ignore_value = print_meta_data_experiments ("debug");
1311 #------------------------------------------------------------------------------
1312 # Before the execution completes, print the warning(s) on the screen.
1314 # Note that this assumes that no additional warnings have been created since
1315 # the call to html_create_warnings_page. Otherwise there will be a discrepancy
1316 # between what is printed on the screen and shown in the warnings.html page.
1317 #------------------------------------------------------------------------------
1318 if (($g_total_warning_count > 0) and ($g_warnings))
1320 $ignore_value = print_warnings_buffer ();
1321 @g_warning_msgs = ();
1324 #------------------------------------------------------------------------------
1325 # This is not supposed to happen, but in case there are any fatal errors that
1326 # have not caused the execution to terminate, print them here.
1327 #------------------------------------------------------------------------------
1328 if (@g_error_msgs)
1330 $ignore_value = print_errors_buffer (\$g_error_keyword);
1333 #------------------------------------------------------------------------------
1334 # One line message to show where the results can be found.
1335 #------------------------------------------------------------------------------
1336 my $results_file = $abs_path_outputdir . "/index.html";
1337 my $prologue_text = "Processing completed - view file $results_file" .
1338 " in a browser";
1339 gp_message ("diag", $subr_name, $prologue_text);
1341 return (0);
1343 } #-- End of subroutine main
1345 #------------------------------------------------------------------------------
1346 # If it is not present, add a "/" to the name of the argument. This is
1347 # intended to be used for the name of the output directory and makes it
1348 # easier to construct pathnames.
1349 #------------------------------------------------------------------------------
1350 sub append_forward_slash
1352 my $subr_name = get_my_name ();
1354 my ($input_string) = @_;
1356 my $length_of_string = length ($input_string);
1357 my $return_string = $input_string;
1359 if (rindex ($input_string, "/") != $length_of_string-1)
1361 $return_string .= "/";
1364 return ($return_string);
1366 } #-- End of subroutine append_forward_slash
1368 #------------------------------------------------------------------------------
1369 # Return a string with a comma separated list of directory names.
1370 #------------------------------------------------------------------------------
1371 sub build_pretty_dir_list
1373 my $subr_name = get_my_name ();
1375 my ($dir_list_ref) = @_;
1377 my @dir_list = @{ $dir_list_ref};
1379 my $pretty_dir_list = join ("\n", @dir_list);
1381 return ($pretty_dir_list);
1383 } #-- End of subroutine build_pretty_dir_list
1385 #------------------------------------------------------------------------------
1386 # Calculate the target address in hex by adding the instruction to the
1387 # instruction address.
1388 #------------------------------------------------------------------------------
1389 sub calculate_target_hex_address
1391 my $subr_name = get_my_name ();
1393 my ($instruction_address, $instruction_offset) = @_;
1395 my $dec_branch_target;
1396 my $d1;
1397 my $d2;
1398 my $first_char;
1399 my $length_of_string;
1400 my $mask;
1401 my $msg;
1402 my $number_of_fields;
1403 my $raw_hex_branch_target;
1404 my $result;
1406 if ($g_addressing_mode eq "64 bit")
1408 $mask = "0xffffffffffffffff";
1409 $number_of_fields = 16;
1411 else
1413 $msg = "g_addressing_mode = $g_addressing_mode not supported";
1414 gp_message ("abort", $subr_name, $msg);
1417 $length_of_string = length ($instruction_offset);
1418 $first_char = lcfirst (substr ($instruction_offset,0,1));
1419 $d1 = bigint::hex ($instruction_offset);
1420 $d2 = bigint::hex ($mask);
1421 # if ($first_char eq "f")
1422 if (($first_char =~ /[89a-f]/) and ($length_of_string == $number_of_fields))
1424 #------------------------------------------------------------------------------
1425 # The offset is negative. Convert to decimal and perform the subtrraction.
1426 #------------------------------------------------------------------------------
1427 #------------------------------------------------------------------------------
1428 # XOR the decimal representation and add 1 to the result.
1429 #------------------------------------------------------------------------------
1430 $result = ($d1 ^ $d2) + 1;
1431 $dec_branch_target = bigint::hex ($instruction_address) - $result;
1433 else
1435 $result = $d1;
1436 $dec_branch_target = bigint::hex ($instruction_address) + $result;
1438 #------------------------------------------------------------------------------
1439 # Convert to hexadecimal.
1440 #------------------------------------------------------------------------------
1441 $raw_hex_branch_target = sprintf ("%x", $dec_branch_target);
1443 return ($raw_hex_branch_target);
1445 } #-- End of subroutine calculate_target_hex_address
1447 #------------------------------------------------------------------------------
1448 # Sets the absolute path to all commands in array @cmds.
1450 # First, it is checked if the command is in the search path, built-in, or an
1451 # alias. If this is not the case, search for it in a couple of locations.
1453 # If this all fails, warning messages are printed, but this is not a hard
1454 # error. Yet. Most likely, things will go bad later on.
1456 # The commands and their respective paths are stored in hash "g_mapped_cmds".
1457 #------------------------------------------------------------------------------
1458 sub check_and_define_cmds
1460 my $subr_name = get_my_name ();
1462 my ($cmds_ref, $search_path_ref) = @_;
1464 #------------------------------------------------------------------------------
1465 # Dereference the array addressess first and then store the contents.
1466 #------------------------------------------------------------------------------
1467 my @cmds = @{$cmds_ref};
1468 my @search_path = @{$search_path_ref};
1470 my @the_fields = ();
1472 my $cmd;
1473 my $cmd_found;
1474 my $error_code;
1475 my $failed_cmd;
1476 my $failed_cmds;
1477 my $found_match;
1478 my $mapped;
1479 my $msg;
1480 my $no_of_failed_mappings;
1481 my $no_of_fields;
1482 my $output_cmd;
1483 my $target_cmd;
1484 my $failed_mapping = $FALSE;
1485 my $full_path_cmd;
1487 gp_message ("debugXL", $subr_name, "\@cmds = @cmds");
1488 gp_message ("debugXL", $subr_name, "\@search_path = @search_path");
1490 #------------------------------------------------------------------------------
1491 # Search for the command and record the absolute path. In case no such path
1492 # can be found, the entry in $g_mapped_cmds is assigned a special value that
1493 # will be checked for in the next block.
1494 #------------------------------------------------------------------------------
1495 for $cmd (@cmds)
1497 $target_cmd = "(command -v $cmd; echo \$\?)";
1499 $msg = "check target_cmd = " . $target_cmd;
1500 gp_message ("debug", $subr_name, $msg);
1502 ($error_code, $output_cmd) = execute_system_cmd ($target_cmd);
1504 if ($error_code != 0)
1505 #------------------------------------------------------------------------------
1506 # This is unlikely to happen, since it means the command executed failed.
1507 #------------------------------------------------------------------------------
1509 $msg = "error executing this command: " . $target_cmd;
1510 gp_message ("warning", $subr_name, $msg);
1511 $msg = "execution continues, but may fail later on";
1512 gp_message ("warning", $subr_name, $msg);
1514 $g_total_warning_count++;
1516 else
1517 #------------------------------------------------------------------------------
1518 # So far, all is well, but is the target command available?
1519 #------------------------------------------------------------------------------
1521 #------------------------------------------------------------------------------
1522 # The output from the $target_cmd command should contain 2 lines in case the
1523 # command has been found. The first line shows the command with the full
1524 # path, while the second line has the exit code.
1526 # If the exit code is not zero, the command has not been found.
1527 #------------------------------------------------------------------------------
1529 #------------------------------------------------------------------------------
1530 # Split the output at the \n character and check the number of lines as
1531 # well as the return code.
1532 #------------------------------------------------------------------------------
1533 @the_fields = split ("\n", $output_cmd);
1534 $no_of_fields = scalar (@the_fields);
1535 $cmd_found = ($the_fields[$no_of_fields-1] == 0 ? $TRUE : $FALSE);
1537 #------------------------------------------------------------------------------
1538 # This is unexpected. Throw an assertion error and bail out.
1539 #------------------------------------------------------------------------------
1540 if ($no_of_fields > 2)
1542 gp_message ("error", $subr_name, "output from $target_cmd:");
1543 gp_message ("error", $subr_name, $output_cmd);
1545 $msg = "the output from $target_cmd has more than 2 lines";
1546 gp_message ("assertion", $subr_name, $msg);
1549 if ($cmd_found)
1551 $full_path_cmd = $the_fields[0];
1552 #------------------------------------------------------------------------------
1553 # The command is in the search path. Store the full path to the command.
1554 #------------------------------------------------------------------------------
1555 $msg = "the $cmd command is in the search path";
1556 gp_message ("debug", $subr_name, $msg);
1558 $g_mapped_cmds{$cmd} = $full_path_cmd;
1560 else
1561 #------------------------------------------------------------------------------
1562 # A best effort to locate the command elsewhere. If found, store the command
1563 # with the absolute path included. Otherwise print a warning, but continue.
1564 #------------------------------------------------------------------------------
1566 $msg = "the $cmd command is not in the search path";
1567 $msg .= " - start a best effort search to find it";
1568 gp_message ("debug", $subr_name, $msg);
1570 $found_match = $FALSE;
1571 for my $path (@search_path)
1573 $target_cmd = $path . "/" . $cmd;
1574 if (-x $target_cmd)
1576 $msg = "found the command in $path";
1577 gp_message ("debug", $subr_name, $msg);
1579 $found_match = $TRUE;
1580 $g_mapped_cmds{$cmd} = $target_cmd;
1581 last;
1583 else
1585 $msg = "failure to find the $cmd command in $path";
1586 gp_message ("debug", $subr_name, $msg);
1590 if (not $found_match)
1592 $g_mapped_cmds{$cmd} = "road to nowhere";
1593 $failed_mapping = $TRUE;
1599 #------------------------------------------------------------------------------
1600 # Scan the results stored in $g_mapped_cmds and flag errors.
1601 #------------------------------------------------------------------------------
1602 $no_of_failed_mappings = 0;
1603 $failed_cmds = "";
1605 #------------------------------------------------------------------------------
1606 # Print a warning message before showing the results, that at least one search
1607 # has failed.
1608 #------------------------------------------------------------------------------
1609 if ($failed_mapping)
1611 $msg = "<br>" . "failure in the verification of the OS commands:";
1612 gp_message ("warning", $subr_name, $msg);
1615 while ( ($cmd, $mapped) = each %g_mapped_cmds)
1617 if ($mapped eq "road to nowhere")
1619 $msg = "cannot find a path for command $cmd";
1620 gp_message ("warning", $subr_name, $msg);
1621 gp_message ("debug", $subr_name, $msg);
1623 $no_of_failed_mappings++;
1624 $failed_cmds .= $cmd;
1625 $g_mapped_cmds{$cmd} = $cmd;
1627 else
1629 $msg = "path for the $cmd command is $mapped";
1630 gp_message ("debug", $subr_name, $msg);
1633 if ($no_of_failed_mappings != 0)
1635 my $plural_1 = ($no_of_failed_mappings > 1) ? "failures" : "failure";
1636 my $plural_2 = ($no_of_failed_mappings > 1) ? "commands" : "command";
1638 $msg = "encountered $no_of_failed_mappings $plural_1 to locate";
1639 $msg .= " selected " . $plural_2;
1640 gp_message ("warning", $subr_name, $msg);
1641 gp_message ("debug", $subr_name, $msg);
1643 $msg = "execution continues, but may fail later on";
1644 gp_message ("warning", $subr_name, $msg);
1645 gp_message ("debug", $subr_name, $msg);
1647 $g_total_warning_count++;
1650 return ($no_of_failed_mappings);
1652 } #-- End of subroutine check_and_define_cmds
1654 #------------------------------------------------------------------------------
1655 # Look for a branch instruction, or the special endbr32/endbr64 instruction
1656 # that is also considered to be a branch target. Note that the latter is x86
1657 # specific.
1658 #------------------------------------------------------------------------------
1659 sub check_and_proc_dis_branches
1661 my $subr_name = get_my_name ();
1663 my ($input_line_ref, $line_no_ref, $branch_target_ref,
1664 $extended_branch_target_ref, $branch_target_no_ref_ref) = @_;
1666 my $input_line = ${ $input_line_ref };
1667 my $line_no = ${ $line_no_ref };
1668 my %branch_target = %{ $branch_target_ref };
1669 my %extended_branch_target = %{ $extended_branch_target_ref };
1670 my %branch_target_no_ref = %{ $branch_target_no_ref_ref };
1672 my $found_it = $TRUE;
1673 my $hex_branch_target;
1674 my $instruction_address;
1675 my $instruction_offset;
1676 my $msg;
1677 my $raw_hex_branch_target;
1679 if ( ($input_line =~ /$g_branch_regex/)
1680 or ($input_line =~ /$g_endbr_regex/))
1682 if (defined ($3))
1684 $msg = "found a branch or endbr instruction: " .
1685 "\$1 = $1 \$2 = $2 \$3 = $3";
1687 else
1689 $msg = "found a branch or endbr instruction: " .
1690 "\$1 = $1 \$2 = $2";
1692 gp_message ("debugXL", $subr_name, $msg);
1694 if (defined ($1))
1696 #------------------------------------------------------------------------------
1697 # Found a qualifying instruction
1698 #------------------------------------------------------------------------------
1699 $instruction_address = $1;
1700 if (defined ($3))
1702 #------------------------------------------------------------------------------
1703 # This must be the branch target and needs to be converted and processed.
1704 #------------------------------------------------------------------------------
1705 $instruction_offset = $3;
1706 $raw_hex_branch_target = calculate_target_hex_address (
1707 $instruction_address,
1708 $instruction_offset);
1710 $hex_branch_target = "0x" . $raw_hex_branch_target;
1711 $branch_target{$hex_branch_target} = 1;
1712 $extended_branch_target{$instruction_address} =
1713 $raw_hex_branch_target;
1715 if (defined ($2) and (not defined ($3)))
1717 #------------------------------------------------------------------------------
1718 # Unlike a branch, the endbr32/endbr64 instructions do not have a second field.
1719 #------------------------------------------------------------------------------
1720 my $instruction_name = $2;
1721 if ($instruction_name =~ /$g_endbr_inst_regex/)
1723 my $msg = "found endbr: $instruction_name " .
1724 $instruction_address;
1725 gp_message ("debugXL", $subr_name, $msg);
1726 $raw_hex_branch_target = $instruction_address;
1728 $hex_branch_target = "0x" . $raw_hex_branch_target;
1729 $branch_target_no_ref{$instruction_address} = 1;
1733 else
1735 #------------------------------------------------------------------------------
1736 # TBD: Perhaps this should be an assertion or alike.
1737 #------------------------------------------------------------------------------
1738 $branch_target{"0x0000"} = $FALSE;
1739 $msg = "cannot determine branch target";
1740 gp_message ("debug", $subr_name, $msg);
1743 else
1745 $found_it = $FALSE;
1748 return (\$found_it, \%branch_target, \%extended_branch_target,
1749 \%branch_target_no_ref);
1751 } #-- End of subroutine check_and_proc_dis_branches
1753 #------------------------------------------------------------------------------
1754 # Check an input line from the disassembly file to include a function call.
1755 # If it does, process the line and return the branch target results.
1756 #------------------------------------------------------------------------------
1757 sub check_and_proc_dis_func_call
1759 my $subr_name = get_my_name ();
1761 my ($input_line_ref, $line_no_ref, $branch_target_ref,
1762 $extended_branch_target_ref) = @_;
1764 my $input_line = ${ $input_line_ref };
1765 my $line_no = ${ $line_no_ref };
1766 my %branch_target = %{ $branch_target_ref };
1767 my %extended_branch_target = %{ $extended_branch_target_ref };
1769 my $found_it = $TRUE;
1770 my $hex_branch_target;
1771 my $instruction_address;
1772 my $instruction_offset;
1773 my $msg;
1774 my $raw_hex_branch_target;
1776 if ( $input_line =~ /$g_function_call_v2_regex/ )
1778 $msg = "found a function call - line[$line_no] = $input_line";
1779 gp_message ("debugXL", $subr_name, $msg);
1780 if (not defined ($2))
1782 $msg = "line[$line_no] " .
1783 "an instruction address is expected, but not found";
1784 gp_message ("assertion", $subr_name, $msg);
1786 else
1788 $instruction_address = $2;
1790 $msg = "instruction_address = $instruction_address";
1791 gp_message ("debugXL", $subr_name, $msg);
1793 if (not defined ($4))
1795 $msg = "line[$line_no] " .
1796 "an address offset is expected, but not found";
1797 gp_message ("assertion", $subr_name, $msg);
1799 else
1801 $instruction_offset = $4;
1802 if ($instruction_offset =~ /[0-9a-fA-F]+/)
1804 $msg = "calculate branch target: " .
1805 "instruction_address = $instruction_address";
1806 gp_message ("debugXL", $subr_name, $msg);
1807 $msg = "calculate branch target: " .
1808 "instruction_offset = $instruction_offset";
1809 gp_message ("debugXL", $subr_name, $msg);
1811 #------------------------------------------------------------------------------
1812 # The instruction offset needs to be converted and added to the instruction
1813 # address.
1814 #------------------------------------------------------------------------------
1815 $raw_hex_branch_target = calculate_target_hex_address (
1816 $instruction_address,
1817 $instruction_offset);
1818 $hex_branch_target = "0x" . $raw_hex_branch_target;
1820 $msg = "calculated hex_branch_target = " .
1821 $hex_branch_target;
1822 gp_message ("debugXL", $subr_name, $msg);
1824 $branch_target{$hex_branch_target} = 1;
1825 $extended_branch_target{$instruction_address} =
1826 $raw_hex_branch_target;
1828 $msg = "set branch_target{$hex_branch_target} to 1";
1829 gp_message ("debugXL", $subr_name, $msg);
1830 $msg = "added extended_branch_target{$instruction_address}";
1831 $msg .= " = $extended_branch_target{$instruction_address}";
1832 gp_message ("debugXL", $subr_name, $msg);
1834 else
1836 $msg = "line[$line_no] unknown address format";
1837 gp_message ("assertion", $subr_name, $msg);
1842 else
1844 $found_it = $FALSE;
1847 return (\$found_it, \%branch_target, \%extended_branch_target);
1849 } #-- End of subroutine check_and_proc_dis_func_call
1851 #------------------------------------------------------------------------------
1852 # Check if the value for the user option given is valid.
1854 # In case the value is valid, the g_user_settings table is updated with the
1855 # (new) value.
1857 # Otherwise an error message is pushed into the g_error_msgs buffer.
1859 # The return value is TRUE/FALSE.
1860 #------------------------------------------------------------------------------
1861 sub check_and_set_user_option
1863 my $subr_name = get_my_name ();
1865 my ($internal_opt_name, $value) = @_;
1867 my $msg;
1868 my $valid;
1869 my $option_value_missing;
1871 my $option = $g_user_settings{$internal_opt_name}{"option"};
1872 my $data_type = $g_user_settings{$internal_opt_name}{"data_type"};
1873 my $no_of_args = $g_user_settings{$internal_opt_name}{"no_of_arguments"};
1875 if (($no_of_args >= 1) and
1876 ((not defined ($value)) or (length ($value) == 0)))
1877 #------------------------------------------------------------------------------
1878 # If there was no value given, but it is required, flag an error.
1879 # There could also be a value, but it might be the empty string.
1881 # Note that that there are currently no options with multiple values. Should
1882 # these be introduced, the current check may need to be refined.
1883 #------------------------------------------------------------------------------
1885 $valid = $FALSE;
1886 $option_value_missing = $TRUE;
1888 elsif ($no_of_args >= 1)
1890 $option_value_missing = $FALSE;
1891 #------------------------------------------------------------------------------
1892 # There is an input value. Check if it is valid and if so, store it.
1894 # Note that we allow the options to be case insensitive.
1895 #------------------------------------------------------------------------------
1896 $valid = verify_if_input_is_valid ($value, $data_type);
1898 if ($valid)
1900 if (($data_type eq "onoff") or ($data_type eq "size"))
1902 $g_user_settings{$internal_opt_name}{"current_value"} =
1903 lc ($value);
1905 else
1907 $g_user_settings{$internal_opt_name}{"current_value"} = $value;
1909 $g_user_settings{$internal_opt_name}{"defined"} = $TRUE;
1913 return (\$valid, \$option_value_missing);
1915 } #-- End of subroutine check_and_set_user_option
1917 #------------------------------------------------------------------------------
1918 # Check for the $GP_DISPLAY_TEXT tool to be available. This is a critical tool
1919 # needed to provide the information. If it can not be found, execution is
1920 # terminated.
1922 # We first search for this tool in the current execution directory. If it
1923 # cannot be found there, use $PATH to try to locate it.
1924 #------------------------------------------------------------------------------
1925 sub check_availability_tool
1927 my $subr_name = get_my_name ();
1929 my ($location_gp_command_ref) = @_;
1931 my $error_code;
1932 my $error_occurred;
1933 my $gp_path;
1934 my $msg;
1935 my $output_which_gp_display_text;
1936 my $return_value;
1937 my $target_cmd;
1939 #------------------------------------------------------------------------------
1940 # Get the path to gprofng-display-text.
1941 #------------------------------------------------------------------------------
1942 my ($error_occurred_ref, $gp_path_ref, $return_value_ref) =
1943 find_path_to_gp_display_text ($location_gp_command_ref);
1945 $error_occurred = ${ $error_occurred_ref};
1946 $gp_path = ${ $gp_path_ref };
1947 $return_value = ${ $return_value_ref};
1949 $msg = "error_occurred = $error_occurred return_value = $return_value";
1950 gp_message ("debugXL", $subr_name, $msg);
1952 if (not $error_occurred)
1953 #------------------------------------------------------------------------------
1954 # All is well and gprofng-display-text has been located.
1955 #------------------------------------------------------------------------------
1957 $g_path_to_tools = $return_value;
1959 $msg = "located $GP_DISPLAY_TEXT in the execution directory";
1960 gp_message ("debug", $subr_name, $msg);
1961 $msg = "g_path_to_tools = $g_path_to_tools";
1962 gp_message ("debug", $subr_name, $msg);
1964 else
1965 #------------------------------------------------------------------------------
1966 # Something went wrong, but perhaps we can still continue. Try to find
1967 # $GP_DISPLAY_TEXT through the search path.
1968 #------------------------------------------------------------------------------
1970 $msg = $g_html_new_line;
1971 $msg .= "could not find $GP_DISPLAY_TEXT in directory $gp_path :";
1972 $msg .= " $return_value";
1973 gp_message ("warning", $subr_name, $msg);
1975 #------------------------------------------------------------------------------
1976 # Check if we can find $GP_DISPLAY_TEXT in the search path.
1977 #------------------------------------------------------------------------------
1978 $msg = "check for $GP_DISPLAY_TEXT to be in the search path";
1979 gp_message ("debug", $subr_name, $msg);
1981 gp_message ("warning", $subr_name, $msg);
1982 $g_total_warning_count++;
1984 $target_cmd = $g_mapped_cmds{"which"} . " $GP_DISPLAY_TEXT 2>&1";
1986 ($error_code, $output_which_gp_display_text) =
1987 execute_system_cmd ($target_cmd);
1989 if ($error_code == 0)
1991 my ($gp_file_name, $gp_path, $suffix_not_used) =
1992 fileparse ($output_which_gp_display_text);
1993 $g_path_to_tools = $gp_path;
1995 $msg = "located $GP_DISPLAY_TEXT in $g_path_to_tools";
1996 gp_message ("warning", $subr_name, $msg);
1997 $msg = "this is the version that will be used";
1998 gp_message ("warning", $subr_name, $msg);
2000 $msg = "the $GP_DISPLAY_TEXT tool is in the search path";
2001 gp_message ("debug", $subr_name, $msg);
2002 $msg = "g_path_to_tools = $g_path_to_tools";
2003 gp_message ("debug", $subr_name, $msg);
2005 else
2007 $msg = "failure to find $GP_DISPLAY_TEXT in the search path";
2008 gp_message ("error", $subr_name, $msg);
2010 $g_total_error_count++;
2012 gp_message ("abort", $subr_name, $g_abort_msg);
2016 return (\$g_path_to_tools);
2018 } #-- End of subroutine check_availability_tool
2020 #------------------------------------------------------------------------------
2021 # This function determines whether load objects are in ELF format.
2023 # Compared to the original code, any input value other than 2 or 3 is rejected
2024 # upfront. This not only reduces the nesting level, but also eliminates a
2025 # possible bug.
2027 # Also, by isolating the tests for the input files, another nesting level could
2028 # be eliminated, further simplifying this still too complex code.
2029 #------------------------------------------------------------------------------
2030 sub check_loadobjects_are_elf
2032 my $subr_name = get_my_name ();
2034 my ($selected_archive) = @_;
2036 my $event_kind_map_regex;
2037 $event_kind_map_regex = '^<event kind="map"\s.*vaddr=';
2038 $event_kind_map_regex .= '"0x([0-9a-fA-F]+)"\s+.*foffset=';
2039 $event_kind_map_regex .= '"\+*0x([0-9a-fA-F]+)"\s.*modes=';
2040 $event_kind_map_regex .= '"0x([0-9]+)"\s.*name="(.*)".*>$';
2042 my $hostname_current = $local_system_config{"hostname_current"};
2043 my $arch = $local_system_config{"processor"};
2044 my $arch_uname_s = $local_system_config{"kernel_name"};
2046 my $extracted_information;
2048 my $elf_magic_number;
2050 my $executable_name;
2051 my $va_executable_in_hex;
2053 my $arch_exp;
2054 my $hostname_exp;
2055 my $os_exp;
2056 my $os_exp_full;
2058 my $archives_file;
2059 my $rc_b;
2060 my $file;
2061 my $line;
2062 my $msg;
2063 my $name;
2064 my $name_path;
2065 my $foffset;
2066 my $vaddr;
2067 my $modes;
2069 my $path_to_map_file;
2070 my $path_to_log_file;
2072 #------------------------------------------------------------------------------
2073 # TBD: Parameterize and should be the first experiment directory from the list.
2074 #------------------------------------------------------------------------------
2075 $path_to_log_file =
2076 $g_exp_dir_meta_data{$selected_archive}{"directory_path"};
2077 $path_to_log_file .= $selected_archive;
2078 $path_to_log_file .= "/log.xml";
2080 gp_message ("debug", $subr_name, "hostname_current = $hostname_current");
2081 gp_message ("debug", $subr_name, "arch = $arch");
2082 gp_message ("debug", $subr_name, "arch_uname_s = $arch_uname_s");
2084 #------------------------------------------------------------------------------
2085 # TBD
2087 # This check can probably be removed since the presence of the log.xml file is
2088 # checked for in an earlier phase.
2089 #------------------------------------------------------------------------------
2090 $msg = " - unable to open file $path_to_log_file for reading:";
2091 open (LOG_XML, "<", $path_to_log_file)
2092 or die ($subr_name . $msg . " " . $!);
2094 $msg = "opened file $path_to_log_file for reading";
2095 gp_message ("debug", $subr_name, $msg);
2097 while (<LOG_XML>)
2099 $line = $_;
2100 chomp ($line);
2101 gp_message ("debugM", $subr_name, "read line: $line");
2102 #------------------------------------------------------------------------------
2103 # Search for the first line starting with "<system". Bail out if found and
2104 # parsed. These are two examples:
2105 # <system hostname="ruud-vm" arch="x86_64" \
2106 # os="Linux 4.14.35-2025.400.8.el7uek.x86_64" pagesz="4096" npages="30871514">
2107 #------------------------------------------------------------------------------
2108 if ($line =~ /^\s*<system\s+/)
2110 $msg = "selected the following line from the log.xml file:";
2111 gp_message ("debugM", $subr_name, $msg);
2112 gp_message ("debugM", $subr_name, "$line");
2113 if ($line =~ /.*\s+hostname="([^"]+)/)
2115 $hostname_exp = $1;
2116 $msg = "extracted hostname_exp = " . $hostname_exp;
2117 gp_message ("debugM", $subr_name, $msg);
2119 if ($line =~ /.*\s+arch="([^"]+)/)
2121 $arch_exp = $1;
2122 $msg = "extracted arch_exp = " . $arch_exp;
2123 gp_message ("debugM", $subr_name, $msg);
2125 if ($line =~ /.*\s+os="([^"]+)/)
2127 $os_exp_full = $1;
2128 #------------------------------------------------------------------------------
2129 # Capture the first word only.
2130 #------------------------------------------------------------------------------
2131 if ($os_exp_full =~ /([^\s]+)/)
2133 $os_exp = $1;
2135 $msg = "extracted os_exp = " . $os_exp;
2136 gp_message ("debugM", $subr_name, $msg);
2138 last;
2140 } #-- End of while loop
2142 close (LOG_XML);
2144 #------------------------------------------------------------------------------
2145 # If the current system is identical to the system used in the experiment,
2146 # we can return early. Otherwise we need to dig deeper.
2148 # TBD: How about the other experiment directories?! This needs to be fixed.
2149 #------------------------------------------------------------------------------
2151 gp_message ("debug", $subr_name, "completed while loop");
2152 gp_message ("debug", $subr_name, "hostname_exp = $hostname_exp");
2153 gp_message ("debug", $subr_name, "arch_exp = $arch_exp");
2154 gp_message ("debug", $subr_name, "os_exp = $os_exp");
2156 #TBD: THIS DOES NOT CHECK IF ELF IS FOUND!
2158 if (($hostname_current eq $hostname_exp) and
2159 ($arch eq $arch_exp) and
2160 ($arch_uname_s eq $os_exp))
2162 $msg = "early return: the hostname, architecture and OS match";
2163 $msg .= " the current system";
2164 gp_message ("debug", $subr_name, $msg);
2165 $msg = "FAKE THIS IS NOT THE CASE AND CONTINUE";
2166 gp_message ("debug", $subr_name, $msg);
2167 # FAKE return ($TRUE);
2170 if (not $g_exp_dir_meta_data{$selected_archive}{"archive_is_empty"})
2172 $msg = "selected_archive = " . $selected_archive;
2173 gp_message ("debug", $subr_name, $msg);
2174 for my $i (sort keys
2175 %{$g_exp_dir_meta_data{$selected_archive}{"archive_files"}})
2177 $msg = "stored loadobject " . $i . " ";
2178 $msg .= $g_exp_dir_meta_data{$selected_archive}{"archive_files"}{$i};
2179 gp_message ("debug", $subr_name, $msg);
2183 #------------------------------------------------------------------------------
2184 # Check if the selected experiment directory has archived files in ELF format.
2185 # If not, use the information in map.xml to get the name of the executable
2186 # and the virtual address.
2187 #------------------------------------------------------------------------------
2189 if ($g_exp_dir_meta_data{$selected_archive}{"archive_in_elf_format"})
2191 $msg = "the files in directory $selected_archive/archives are in";
2192 $msg .= " ELF format";
2193 gp_message ("debugM", $subr_name, $msg);
2194 $msg = "IGNORE THIS AND USE MAP.XML";
2195 gp_message ("debugM", $subr_name, $msg);
2196 ## return ($TRUE);
2199 $msg = "the files in directory $selected_archive/archives are not in";
2200 $msg .= " ELF format";
2201 gp_message ("debug", $subr_name, $msg);
2203 $path_to_map_file =
2204 $g_exp_dir_meta_data{$selected_archive}{"directory_path"};
2205 $path_to_map_file .= $selected_archive;
2206 $path_to_map_file .= "/map.xml";
2208 $msg = " - unable to open file $path_to_map_file for reading:";
2209 open (MAP_XML, "<", $path_to_map_file)
2210 or die ($subr_name . $msg . " " . $!);
2211 $msg = "opened file $path_to_map_file for reading";
2212 gp_message ("debug", $subr_name, $msg);
2214 #------------------------------------------------------------------------------
2215 # Scan the map.xml file. We need to find the name of the executable with the
2216 # mode set to 0x005. For this entry we have to capture the virtual address.
2217 #------------------------------------------------------------------------------
2218 $extracted_information = $FALSE;
2219 while (<MAP_XML>)
2221 $line = $_;
2222 chomp ($line);
2223 gp_message ("debugM", $subr_name, "MAP_XML read line = $line");
2224 #------------------------------------------------------------------------------
2225 # Replaces this way too long line:
2226 # if ($line =~ /^<event kind="map"\s.*vaddr="0x([0-9a-fA-F]+)"\s+.
2227 # *foffset="\+*0x([0-9a-fA-F]+)"\s.*modes="0x([0-9]+)"\s.*
2228 # name="(.*)".*>$/)
2229 #------------------------------------------------------------------------------
2230 if ($line =~ /$event_kind_map_regex/)
2232 gp_message ("debugM", $subr_name, "target line = $line");
2233 $vaddr = $1;
2234 $foffset = $2;
2235 $modes = $3;
2236 $name_path = $4;
2237 $name = get_basename ($name_path);
2238 $msg = "extracted vaddr = $vaddr foffset = $foffset";
2239 $msg .= " modes = $modes";
2240 gp_message ("debugM", $subr_name, $msg);
2241 $msg = "extracted name_path = $name_path name = $name";
2242 gp_message ("debugM", $subr_name, $msg);
2243 # $error_extracting_information = $TRUE;
2244 $executable_name = $name;
2245 my $result_VA = bigint::hex ($vaddr) - bigint::hex ($foffset);
2246 my $hex_VA = sprintf ("0x%016x", $result_VA);
2247 $va_executable_in_hex = $hex_VA;
2249 $msg = "set executable_name = " . $executable_name;
2250 gp_message ("debugM", $subr_name, $msg);
2251 $msg = "set va_executable_in_hex = " . $va_executable_in_hex;
2252 gp_message ("debugM", $subr_name, $msg);
2253 $msg = "result_VA = " . $result_VA;
2254 gp_message ("debugM", $subr_name, $msg);
2255 $msg = "hex_VA = " . $hex_VA;
2256 gp_message ("debugM", $subr_name, $msg);
2258 if ($modes eq "005")
2260 $extracted_information = $TRUE;
2261 last;
2266 close (MAP_XML);
2268 if (not $extracted_information)
2270 $msg = "cannot find the necessary information in";
2271 $msg .= " the $path_to_map_file file";
2272 gp_message ("assertion", $subr_name, $msg);
2275 ## $executable_name = $ARCHIVES_MAP_NAME;
2276 ## $va_executable_in_hex = $ARCHIVES_MAP_VADDR;
2278 return ($executable_name, $va_executable_in_hex);
2280 } #-- End of subroutine check_loadobjects_are_elf
2282 #------------------------------------------------------------------------------
2283 # Compare the current metric values against the maximum values. Mark the line
2284 # if a value is within the percentage defined by $hp_value.
2285 #------------------------------------------------------------------------------
2286 sub check_metric_values
2288 my $subr_name = get_my_name ();
2290 my ($metric_values, $max_metric_values_ref) = @_;
2292 my @max_metric_values = @{ $max_metric_values_ref };
2294 my @current_metrics = ();
2295 my $colour_coded_line;
2296 my $current_value;
2297 my $hp_value = $g_user_settings{"highlight_percentage"}{"current_value"};
2298 my $max_value;
2299 my $msg;
2300 my $relative_distance;
2302 @current_metrics = split (" ", $metric_values);
2303 $colour_coded_line = $FALSE;
2305 for my $metric (0 .. $#current_metrics)
2307 $current_value = $current_metrics[$metric];
2308 if (exists ($max_metric_values[$metric]))
2310 $max_value = $max_metric_values[$metric];
2312 $msg = "metric = $metric current_value = $current_value";
2313 $msg .= " max_value = $max_value";
2314 gp_message ("debugXL", $subr_name, $msg);
2316 if ( ($max_value > 0) and ($current_value > 0) and
2317 ($current_value != $max_value) )
2319 # TBD: abs needed?
2320 $msg = "metric = $metric current_value = $current_value";
2321 $msg .= " max_value = $max_value";
2322 gp_message ("debugXL", $subr_name, $msg);
2324 $relative_distance = 1.00 - abs (
2325 ($max_value - $current_value)/$max_value );
2327 $msg = "relative_distance = $relative_distance";
2328 gp_message ("debugXL", $subr_name, $msg);
2330 if ($relative_distance >= $hp_value/100.0)
2332 $msg = "metric $metric is within the relative_distance";
2333 gp_message ("debugXL", $subr_name, $msg);
2335 $colour_coded_line = $TRUE;
2336 last;
2340 } #-- End of loop over metrics
2342 return (\$colour_coded_line);
2344 } #-- End of subroutine check_metric_values
2346 #------------------------------------------------------------------------------
2347 # Check if the system is supported.
2348 #------------------------------------------------------------------------------
2349 sub check_support_for_processor
2351 my $subr_name = get_my_name ();
2353 my ($machine_ref) = @_;
2355 my $machine = ${ $machine_ref };
2356 my $is_supported;
2358 if ($machine eq "x86_64")
2360 $is_supported = $TRUE;
2362 else
2364 $is_supported = $FALSE;
2367 return (\$is_supported);
2369 } #-- End of subroutine check_support_for_processor
2371 #------------------------------------------------------------------------------
2372 # Check the command line options for the occurrence of experiments and make
2373 # sure that this list is contigious. No other names are allowed in this list.
2375 # Terminate execution in case of an error. Otherwise remove the experiment
2376 # names for ARGV (to make the subsequent parsing easier), and return an array
2377 # with the experiment names.
2379 # The following patterns are supposed to be detected:
2381 # <expdir_1> some other word(s) <expdir_2>
2382 # <expdir> some other word(s)
2383 #------------------------------------------------------------------------------
2384 sub check_the_experiment_list
2386 my $subr_name = get_my_name ();
2388 #------------------------------------------------------------------------------
2389 # The name of an experiment directory can contain any non-whitespace
2390 # character(s), but has to end with .er, or optionally .er/. Multiple
2391 # forward slashes are allowed.
2392 #------------------------------------------------------------------------------
2393 my $exp_dir_regex = '^(\S+)(\.er)\/*$';
2394 my $forward_slash_regex = '\/*$';
2396 my $current_value;
2397 my @exp_dir_list = ();
2398 my $found_experiment = $FALSE;
2399 my $found_non_exp = $FALSE;
2400 my $msg;
2401 my $name_non_exp_dir = "";
2402 my $no_of_experiments = 0;
2403 my $no_of_invalid_dirs = 0;
2404 my $opt_remainder;
2405 my $valid = $TRUE;
2407 for my $i (keys @ARGV)
2409 $current_value = $ARGV[$i];
2410 if ($current_value =~ /$exp_dir_regex/)
2411 #------------------------------------------------------------------------------
2412 # The current value is an experiment. Remove any trailing forward slashes,
2413 # Increment the count, push the value into the array and set the
2414 # found_experiment flag to TRUE.
2415 #------------------------------------------------------------------------------
2417 $no_of_experiments += 1;
2419 $current_value =~ s/$forward_slash_regex//;
2420 push (@exp_dir_list, $current_value);
2422 if (not $found_experiment)
2423 #------------------------------------------------------------------------------
2424 # Start checking for the next field(s).
2425 #------------------------------------------------------------------------------
2427 $found_experiment = $TRUE;
2429 #------------------------------------------------------------------------------
2430 # We had found non-experiment names and now see another experiment. Time to
2431 # bail out of the loop.
2432 #------------------------------------------------------------------------------
2433 if ($found_non_exp)
2435 last;
2438 else
2440 if ($found_experiment)
2441 #------------------------------------------------------------------------------
2442 # The current value is not an experiment, but the value of found_experiment
2443 # indicates at least one experiment has been seen already. This means that
2444 # the list of experiment names is not contiguous and that is a fatal error.
2445 #------------------------------------------------------------------------------
2447 $name_non_exp_dir .= $current_value . " ";
2448 $found_non_exp = $TRUE;
2454 #------------------------------------------------------------------------------
2455 #------------------------------------------------------------------------------
2456 # Error handling.
2457 #------------------------------------------------------------------------------
2458 #------------------------------------------------------------------------------
2460 if ($found_non_exp)
2461 #------------------------------------------------------------------------------
2462 # The experiment list is not contiguous.
2463 #------------------------------------------------------------------------------
2465 $valid = $FALSE;
2466 $msg = "the list with the experiments is not contiguous:";
2467 gp_message ("error", $subr_name, $msg);
2469 $msg = "\"" . $name_non_exp_dir. "\"". " is not an experiment, but" .
2470 " appears in a list where experiments are expected";
2471 gp_message ("error", $subr_name, $msg);
2473 $g_total_error_count++;
2476 if ($no_of_experiments == 0)
2477 #------------------------------------------------------------------------------
2478 # The experiment list is empty.
2479 #------------------------------------------------------------------------------
2481 $valid = $FALSE;
2482 $msg = "the experiment list is missing from the options";
2483 gp_message ("error", $subr_name, $msg);
2485 $g_total_error_count++;
2488 if (not $valid)
2489 #------------------------------------------------------------------------------
2490 # If an error has occurred, print the error(s) and terminate execution.
2491 #------------------------------------------------------------------------------
2493 gp_message ("abort", $subr_name, $g_abort_msg);
2496 #------------------------------------------------------------------------------
2497 # We now have a list with experiments, but we still need to verify whether they
2498 # exist, and if so, are these valid experiments?
2499 #------------------------------------------------------------------------------
2500 for my $exp_dir (@exp_dir_list)
2502 $msg = "checking experiment directory $exp_dir";
2503 gp_message ("debug", $subr_name, $msg);
2505 if (-d $exp_dir)
2507 $msg = "directory $exp_dir found";
2508 gp_message ("debug", $subr_name, $msg);
2509 #------------------------------------------------------------------------------
2510 # Files log.xml and map.xml have to be there.
2511 #------------------------------------------------------------------------------
2512 if ((-e $exp_dir."/log.xml") and (-e $exp_dir."/map.xml"))
2514 $msg = "directory $exp_dir appears to be a valid experiment";
2515 $msg .= " directory";
2516 gp_message ("debug", $subr_name, $msg);
2518 else
2520 $no_of_invalid_dirs++;
2521 $msg = "file " . $exp_dir . "/log.xml and/or " . $exp_dir;
2522 $msg .= "/map.xml missing";
2523 gp_message ("debug", $subr_name, $msg);
2525 $msg = "directory " . get_basename($exp_dir) . " does not";
2526 $msg .= " appear to be a valid experiment directory";
2527 gp_message ("error", $subr_name, $msg);
2529 $g_total_error_count++;
2532 else
2534 $no_of_invalid_dirs++;
2535 $msg = "directory " . get_basename($exp_dir) . " does not exist";
2536 gp_message ("error", $subr_name, $msg);
2538 $g_total_error_count++;
2542 if ($no_of_invalid_dirs > 0)
2543 #------------------------------------------------------------------------------
2544 # This is a fatal error, but for now, we can continue to check for more errors.
2545 # Even if none more are found, execution is terminated before the data is
2546 # generated and processed. In this way we can catch as many errors as
2547 # possible.
2548 #------------------------------------------------------------------------------
2550 my $plural_or_single = ($no_of_invalid_dirs == 1) ?
2551 "one experiment is" : $no_of_invalid_dirs . " experiments are";
2553 $msg = $plural_or_single . " not valid";
2554 ## gp_message ("abort", $subr_name, $msg);
2556 ## $g_total_error_count++;
2559 #------------------------------------------------------------------------------
2560 # Remove the experiments from ARGV and return the array with the experiment
2561 # names. Note that these may, or may not be valid, but if invalid, execution
2562 # terminates before they are used.
2563 #------------------------------------------------------------------------------
2564 for my $i (1 .. $no_of_experiments)
2566 my $poppy = pop (@ARGV);
2568 $msg = "popped $poppy from ARGV";
2569 gp_message ("debug", $subr_name, $msg);
2571 $msg = "ARGV after update = " . join (" ", @ARGV);
2572 gp_message ("debug", $subr_name, $msg);
2575 return (\@exp_dir_list);
2577 } #-- End of subroutine check_the_experiment_list
2579 #------------------------------------------------------------------------------
2580 # Perform multiple checks on the experiment directories.
2582 # TBD: It needs to be investigated whether all of this is really neccesary.
2583 #------------------------------------------------------------------------------
2584 sub check_validity_exp_dirs
2586 my $subr_name = get_my_name ();
2588 my ($exp_dir_list_ref) = @_;
2590 my @exp_dir_list = @{ $exp_dir_list_ref };
2592 my %elf_rats = ();
2594 my $dir_not_found = $FALSE;
2595 my $missing_dirs = 0;
2596 my $invalid_dirs = 0;
2598 my $archive_dir_not_empty;
2599 my $archives_dir;
2600 my $archives_file;
2601 my $count_exp_dir_not_elf;
2602 my $elf_magic_number;
2603 my $first_line;
2604 my $msg;
2606 my $first_time;
2607 my $filename;
2609 my $comment;
2611 my $selected_archive_has_elf_format;
2613 my $selected_archive;
2614 my $archive_dir_selected;
2615 my $no_of_files_in_selected_archive;
2617 #------------------------------------------------------------------------------
2618 # Initialize ELF status to FALSE.
2619 #------------------------------------------------------------------------------
2620 ## for my $exp_dir (@exp_dir_list)
2621 for my $exp_dir (keys %g_exp_dir_meta_data)
2623 $g_exp_dir_meta_data{$exp_dir}{"elf_format"} = $FALSE;
2624 $g_exp_dir_meta_data{$exp_dir}{"archive_in_elf_format"} = $FALSE;
2626 #------------------------------------------------------------------------------
2627 # Check if the load objects are in ELF format.
2628 #------------------------------------------------------------------------------
2629 for my $exp_dir (keys %g_exp_dir_meta_data)
2631 $archives_dir = $g_exp_dir_meta_data{$exp_dir}{"directory_path"};
2632 $archives_dir .= $exp_dir . "/archives";
2633 $archive_dir_not_empty = $FALSE;
2634 $first_time = $TRUE;
2635 $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"} = $TRUE;
2636 $g_exp_dir_meta_data{$exp_dir}{"no_of_files_in_archive"} = 0;
2638 $msg = "g_exp_dir_meta_data{$exp_dir}{'archive_is_empty'} = ";
2639 $msg .= $g_exp_dir_meta_data{$exp_dir}{'archive_is_empty'};
2640 gp_message ("debug", $subr_name, $msg);
2642 $msg = "checking $archives_dir";
2643 gp_message ("debug", $subr_name, $msg);
2645 while (glob ("$archives_dir/*"))
2647 $filename = get_basename ($_);
2649 $msg = "processing file: $filename";
2650 gp_message ("debug", $subr_name, $msg);
2652 $g_exp_dir_meta_data{$exp_dir}{"archive_files"}{$filename} = $TRUE;
2653 $g_exp_dir_meta_data{$exp_dir}{"no_of_files_in_archive"}++;
2655 $archive_dir_not_empty = $TRUE;
2656 #------------------------------------------------------------------------------
2657 # Replaces the ELF_RATS part in elf_phdr.
2659 # Challenge: splittable_mrg.c_I0txnOW_Wn5
2661 # TBD: Store this for each relevant experiment directory.
2662 #------------------------------------------------------------------------------
2663 my $last_dot = rindex ($filename,".");
2664 my $underscore_before_dot = $TRUE;
2665 my $first_underscore = -1;
2667 $msg = "last_dot = $last_dot";
2668 gp_message ("debugXL", $subr_name, $msg);
2670 while ($underscore_before_dot)
2672 $first_underscore = index ($filename, "_", $first_underscore+1);
2673 if ($last_dot < $first_underscore)
2675 $underscore_before_dot = $FALSE;
2678 my $original_name = substr ($filename, 0, $first_underscore);
2679 $msg = "stripped archive name: " . $original_name;
2680 gp_message ("debug", $subr_name, $msg);
2681 if (not exists ($elf_rats{$original_name}))
2683 $elf_rats{$original_name} = [$filename, $exp_dir];
2685 #------------------------------------------------------------------------------
2686 # We only need to detect the presence of an object once.
2687 #------------------------------------------------------------------------------
2688 if ($first_time)
2690 $first_time = $FALSE;
2691 $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"} = $FALSE;
2692 $msg = "g_exp_dir_meta_data{$exp_dir}{'archive_is_empty'} = ";
2693 $msg .= $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"};
2695 gp_message ("debugXL", $subr_name, $msg);
2698 } #-- End of loop over experiment directories
2700 for my $exp_dir (sort keys %g_exp_dir_meta_data)
2702 my $empty = $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"};
2703 $msg = "archive directory " . $exp_dir . "/archives is";
2704 $msg .= " " . ($empty ? "empty" : "not empty");
2705 gp_message ("debug", $subr_name, $msg);
2708 #------------------------------------------------------------------------------
2709 # Verify that all relevant files in the archive directories are in ELF format.
2710 #------------------------------------------------------------------------------
2711 for my $exp_dir (sort keys %g_exp_dir_meta_data)
2713 $g_exp_dir_meta_data{$exp_dir}{"archive_in_elf_format"} = $FALSE;
2714 if (not $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"})
2716 $archives_dir = $g_exp_dir_meta_data{$exp_dir}{"directory_path"};
2717 $archives_dir .= $exp_dir . "/archives";
2718 $msg = "exp_dir = " . $exp_dir . " archives_dir = " . $archives_dir;
2719 gp_message ("debug", $subr_name, $msg);
2720 #------------------------------------------------------------------------------
2721 # Check if any of the loadobjects is of type ELF. Bail out on the first one
2722 # found. The assumption is that all other loadobjects must be of type ELF too
2723 # then.
2724 #------------------------------------------------------------------------------
2725 for my $aname (sort keys
2726 %{$g_exp_dir_meta_data{$exp_dir}{"archive_files"}})
2728 $filename = $g_exp_dir_meta_data{$exp_dir}{"directory_path"};
2729 $filename .= $exp_dir . "/archives/" . $aname;
2730 $msg = " - unable to open file $filename for reading:";
2731 open (ARCF,"<", $filename)
2732 or die ($subr_name . $msg . " " . $!);
2734 $first_line = <ARCF>;
2735 close (ARCF);
2737 #------------------------------------------------------------------------------
2738 # The first 4 hex fields in the header of an ELF file are: 7F 45 4c 46 (7FELF).
2740 # See also https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
2741 #------------------------------------------------------------------------------
2742 # if ($first_line =~ /^\177ELF.*/)
2744 $elf_magic_number = unpack ('H8', $first_line);
2745 if ($elf_magic_number eq "7f454c46")
2747 $g_exp_dir_meta_data{$exp_dir}{"archive_in_elf_format"} =
2748 $TRUE;
2749 $g_exp_dir_meta_data{$exp_dir}{"elf_format"} = $TRUE;
2750 last;
2756 for my $exp_dir (sort keys %g_exp_dir_meta_data)
2758 $msg = "the loadobjects in the archive in $exp_dir are";
2759 $msg .= ($g_exp_dir_meta_data{$exp_dir}{"archive_in_elf_format"}) ?
2760 " in" : " not in";
2761 $msg .= " ELF format";
2762 gp_message ("debug", $subr_name, $msg);
2764 for my $exp_dir (sort keys %g_exp_dir_meta_data)
2766 if ($g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"})
2768 $msg = "there are no archived files in " . $exp_dir;
2769 gp_message ("debug", $subr_name, $msg);
2773 #------------------------------------------------------------------------------
2774 # If there are archived files and they are not in ELF format, a debug message
2775 # is issued.
2777 # TBD: Bail out?
2778 #------------------------------------------------------------------------------
2779 $count_exp_dir_not_elf = 0;
2780 for my $exp_dir (sort keys %g_exp_dir_meta_data)
2782 if (not $g_exp_dir_meta_data{$exp_dir}{"archive_in_elf_format"})
2784 $count_exp_dir_not_elf++;
2787 if ($count_exp_dir_not_elf != 0)
2789 $msg = "there are $count_exp_dir_not_elf experiments with non-ELF";
2790 $msg .= " load objects";
2791 gp_message ("debug", $subr_name, $msg);
2794 #------------------------------------------------------------------------------
2795 # Select the experiment directory that is used for the files in the archive.
2796 # By default, a directory with archived files is used, but in case this does
2797 # not exist, a directory without archived files is selected. Obviously this
2798 # needs to be dealt with later on.
2799 #------------------------------------------------------------------------------
2801 #------------------------------------------------------------------------------
2802 # Try the experiments with archived files first.
2803 #------------------------------------------------------------------------------
2804 $archive_dir_not_empty = $FALSE;
2805 $archive_dir_selected = $FALSE;
2806 ## for my $exp_dir (sort @exp_dir_list)
2807 for my $exp_dir (sort keys %g_exp_dir_meta_data)
2809 $msg = "exp_dir = " . $exp_dir;
2810 gp_message ("debugXL", $subr_name, $msg);
2811 $msg = "g_exp_dir_meta_data{$exp_dir}{'archive_is_empty'}";
2812 $msg .= " = " . $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"};
2813 gp_message ("debugXL", $subr_name, $msg);
2815 if (not $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"})
2817 $selected_archive = $exp_dir;
2818 $archive_dir_not_empty = $TRUE;
2819 $archive_dir_selected = $TRUE;
2820 $selected_archive_has_elf_format =
2821 ($g_exp_dir_meta_data{$exp_dir}{"archive_in_elf_format"}) ?
2822 $TRUE : $FALSE;
2823 last;
2826 if (not $archive_dir_selected)
2827 #------------------------------------------------------------------------------
2828 # None are found and pick the first one without archived files.
2829 #------------------------------------------------------------------------------
2831 for my $exp_dir (sort keys %g_exp_dir_meta_data)
2833 if ($g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"})
2835 $selected_archive = $exp_dir;
2836 $archive_dir_not_empty = $FALSE;
2837 $archive_dir_selected = $TRUE;
2838 $selected_archive_has_elf_format = $FALSE;
2839 last;
2844 $msg = "experiment $selected_archive has been selected for";
2845 $msg .= " archive analysis";
2846 gp_message ("debug", $subr_name, $msg);
2847 $msg = "this archive is";
2848 $msg .= $archive_dir_not_empty ? " not empty" : " empty";
2849 gp_message ("debug", $subr_name, $msg);
2850 $msg = "this archive is";
2851 $msg .= $selected_archive_has_elf_format ? " in" : " not in";
2852 $msg .= " ELF format";
2853 gp_message ("debug", $subr_name, $msg);
2854 #------------------------------------------------------------------------------
2855 # Get the size of the hash that contains the archived files.
2856 #------------------------------------------------------------------------------
2857 ## $NO_OF_FILES_IN_ARCHIVE = scalar (keys %ARCHIVES_FILES);
2859 $no_of_files_in_selected_archive =
2860 $g_exp_dir_meta_data{$selected_archive}{"no_of_files_in_archive"};
2862 $msg = "number of files in archive $selected_archive is";
2863 $msg .= " " . $no_of_files_in_selected_archive;
2864 gp_message ("debug", $subr_name, $msg);
2866 for my $exp_dir (sort keys %g_exp_dir_meta_data)
2868 my $is_empty = $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"};
2869 $msg = "archive directory $exp_dir/archives is";
2870 $msg .= $is_empty ? " empty" : " not empty";
2871 gp_message ("debug", $subr_name, $msg);
2873 for my $exp_dir (sort keys %g_exp_dir_meta_data)
2875 if (not $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"})
2877 for my $object (sort keys
2878 %{$g_exp_dir_meta_data{$exp_dir}{"archive_files"}})
2880 $msg = $exp_dir . " " . $object . " ";
2881 $msg .=
2882 $g_exp_dir_meta_data{$exp_dir}{'archive_files'}{$object};
2883 gp_message ("debug", $subr_name, $msg);
2888 return ($archive_dir_not_empty, $selected_archive, \%elf_rats);
2890 } #-- End of subroutine check_validity_exp_dirs
2892 #------------------------------------------------------------------------------
2893 # Color the string and optionally mark it boldface.
2895 # For supported colors, see:
2896 # https://www.w3schools.com/colors/colors_names.asp
2897 #------------------------------------------------------------------------------
2898 sub color_string
2900 my $subr_name = get_my_name ();
2902 my ($input_string, $boldface, $color) = @_;
2904 my $colored_string;
2906 $colored_string = "<font color='" . $color . "'>";
2908 if ($boldface)
2910 $colored_string .= "<b>";
2913 $colored_string .= $input_string;
2915 if ($boldface)
2917 $colored_string .= "</b>";
2919 $colored_string .= "</font>";
2921 return ($colored_string);
2923 } #-- End of subroutine color_string
2925 #------------------------------------------------------------------------------
2926 # Generate the array with the info on the experiment(s).
2927 #------------------------------------------------------------------------------
2928 sub create_exp_info
2930 my $subr_name = get_my_name ();
2932 my ($experiment_dir_list_ref, $experiment_data_ref) = @_;
2934 my @experiment_dir_list = @{ $experiment_dir_list_ref };
2935 my @experiment_data = @{ $experiment_data_ref };
2937 my @experiment_stats_html = ();
2938 my $experiment_stats_line;
2939 my $msg;
2940 my $plural;
2942 $plural = ($#experiment_dir_list > 0) ? "s:" : ":";
2944 $experiment_stats_line = "<h3>\n";
2945 $experiment_stats_line .= "Full pathnames to the input experiment";
2946 $experiment_stats_line .= $plural . "\n";
2947 $experiment_stats_line .= "</h3>\n";
2948 $experiment_stats_line .= "<pre>\n";
2950 for my $i (0 .. $#experiment_dir_list)
2952 $experiment_stats_line .= $experiment_dir_list[$i] . " (" ;
2953 $experiment_stats_line .= $experiment_data[$i]{"start_date"} . ")\n";
2955 $experiment_stats_line .= "</pre>\n";
2957 push (@experiment_stats_html, $experiment_stats_line);
2959 $msg = "experiment_stats_line = " . $experiment_stats_line;
2960 gp_message ("debugXL", $subr_name, $msg);
2962 return (\@experiment_stats_html);
2964 } #-- End of subroutine create_exp_info
2966 #------------------------------------------------------------------------------
2967 # Trivial function to generate a tag. This has been made a function to ensure
2968 # consistency creating tags and also make it easier to change them.
2969 #------------------------------------------------------------------------------
2970 sub create_function_tag
2972 my $subr_name = get_my_name ();
2974 my ($tag_id) = @_;
2976 my $function_tag = "function_tag_" . $tag_id;
2978 return ($function_tag);
2980 } #-- End of subroutine create_function_tag
2982 #------------------------------------------------------------------------------
2983 # Generate and return a string with the credits. Note that this also ends
2984 # the HTML formatting controls.
2985 #------------------------------------------------------------------------------
2986 sub create_html_credits
2988 my $subr_name = get_my_name ();
2990 my $msg;
2991 my $the_date;
2993 my @months = qw (January February March April May June July
2994 August September October November December);
2996 my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
2997 localtime ();
2999 $year += 1900;
3001 $the_date = $months[$mon] . " " . $mday . ", " . $year;
3003 $msg = "<i>\n";
3004 $msg .= "Output generated by the $driver_cmd command ";
3005 $msg .= "on $the_date ";
3006 $msg .= "(GNU binutils version " . $binutils_version . ")";
3007 $msg .= "\n";
3008 $msg .= "</i>";
3010 gp_message ("debug", $subr_name, "the date = $the_date");
3012 return (\$msg);
3014 } #-- End of subroutine create_html_credits
3016 #------------------------------------------------------------------------------
3017 # Generate a string that contains all the necessary HTML header information,
3018 # plus a title.
3020 # See also https://www.w3schools.com for the details on the features used.
3021 #------------------------------------------------------------------------------
3022 sub create_html_header
3024 my $subr_name = get_my_name ();
3026 my ($title_ref) = @_;
3028 my $title = ${ $title_ref };
3030 my $LANG = $g_locale_settings{"LANG"};
3031 my $background_color = $g_html_color_scheme{"background_color_page"};
3033 my $html_header;
3035 $html_header = "<!DOCTYPE html public \"-//w3c//dtd html 3.2//en\">\n";
3036 $html_header .= "<html lang=\"$LANG\">\n";
3037 $html_header .= "<head>\n";
3038 $html_header .= "<meta http-equiv=\"content-type\"";
3039 $html_header .= " content=\"text/html; charset=iso-8859-1\">\n";
3040 $html_header .= "<title>" . $title . "</title>\n";
3041 $html_header .= "</head>\n";
3042 $html_header .= "<body lang=\"$LANG\" bgcolor=". $background_color . ">\n";
3043 $html_header .= "<style>\n";
3044 $html_header .= "div.left {\n";
3045 $html_header .= "text-align: left;\n";
3046 $html_header .= "}\n";
3047 $html_header .= "div.right {\n";
3048 $html_header .= "text-align: right;\n";
3049 $html_header .= "}\n";
3050 $html_header .= "div.center {\n";
3051 $html_header .= "text-align: center;\n";
3052 $html_header .= "}\n";
3053 $html_header .= "div.justify {\n";
3054 $html_header .= "text-align: justify;\n";
3055 $html_header .= "}\n";
3056 $html_header .= "</style>";
3058 return (\$html_header);
3060 } #-- End of subroutine create_html_header
3062 #------------------------------------------------------------------------------
3063 # Create a complete table.
3064 #------------------------------------------------------------------------------
3065 sub create_table
3067 my $subr_name = get_my_name ();
3069 my ($experiment_data_ref, $table_definition_ref) = @_;
3071 my @experiment_data = @{ $experiment_data_ref };
3072 my @table_definition = @{ $table_definition_ref };
3074 my @html_exp_table_data = ();
3075 my $html_header_line;
3076 my $html_table_line;
3077 my $html_end_table;
3079 $html_header_line = ${ create_table_header_exp (\@experiment_data) };
3081 push (@html_exp_table_data, $html_header_line);
3083 for my $i (sort keys @table_definition)
3085 $html_table_line = ${
3086 create_table_entry_exp (\$table_definition[$i]{"name"},
3087 \$table_definition[$i]{"key"},
3088 \@experiment_data) };
3089 push (@html_exp_table_data, $html_table_line);
3091 my $msg = "i = $i html_table_line = $html_table_line";
3092 gp_message ("debugXL", $subr_name, $msg);
3095 $html_end_table = "</table>\n";
3096 push (@html_exp_table_data, $html_end_table);
3098 return (\@html_exp_table_data);
3100 } #-- End of subroutine create_table
3102 #------------------------------------------------------------------------------
3103 # Create one row for the table with experiment info.
3104 #------------------------------------------------------------------------------
3105 sub create_table_entry_exp
3107 my $subr_name = get_my_name ();
3109 my ($entry_name_ref, $key_ref, $experiment_data_ref) = @_;
3111 my $entry_name = ${ $entry_name_ref };
3112 my $key = ${ $key_ref };
3113 my @experiment_data = @{ $experiment_data_ref };
3115 my $html_line;
3116 my $msg;
3118 $msg = "entry_name = $entry_name key = $key";
3119 gp_message ("debugXL", $subr_name, $msg);
3121 ## $html_line = "<tr><div class=\"left\"><td><b>&nbsp; ";
3122 $html_line = "<tr><div class=\"right\"><td><b>&nbsp; ";
3123 $html_line .= $entry_name;
3124 $html_line .= " &nbsp;</b></td>";
3125 for my $i (sort keys @experiment_data)
3127 if (exists ($experiment_data[$i]{$key}))
3129 $html_line .= "<td>&nbsp; " . $experiment_data[$i]{$key};
3130 $html_line .= " &nbsp;</td>";
3132 else
3134 $msg = "experiment_data[$i]{$key} does not exist";
3135 ## gp_message ("assertion", $subr_name, $msg);
3136 # TBD: warning or error?
3137 gp_message ("warning", $subr_name, $msg);
3140 $html_line .= "</div></tr>\n";
3142 gp_message ("debugXL", $subr_name, "return html_line = $html_line");
3144 return (\$html_line);
3146 } #-- End of subroutine create_table_entry_exp
3148 #------------------------------------------------------------------------------
3149 # Create the table header for the experiment info.
3150 #------------------------------------------------------------------------------
3151 sub create_table_header_exp
3153 my $subr_name = get_my_name ();
3155 my ($experiment_data_ref) = @_;
3157 my @experiment_data = @{ $experiment_data_ref };
3158 my $html_header_line;
3159 my $msg;
3161 $html_header_line = "<style>\n";
3162 $html_header_line .= "table, th, td {\n";
3163 $html_header_line .= "border: 1px solid black;\n";
3164 $html_header_line .= "border-collapse: collapse;\n";
3165 $html_header_line .= "}\n";
3166 $html_header_line .= "</style>\n";
3167 $html_header_line .= "</pre>\n";
3168 $html_header_line .= "<table>\n";
3169 $html_header_line .= "<tr><div class=\"center\"><th></th>";
3171 for my $i (sort keys @experiment_data)
3173 $html_header_line .= "<th>&nbsp; Experiment ID ";
3174 $html_header_line .= $experiment_data[$i]{"exp_id"} . "&nbsp;</th>";
3176 $html_header_line .= "</div></tr>\n";
3178 $msg = "html_header_line = " . $html_header_line;
3179 gp_message ("debugXL", $subr_name, $msg);
3181 return (\$html_header_line);
3183 } #-- End of subroutine create_table_header_exp
3185 #------------------------------------------------------------------------------
3186 # Handle where the output should go. If needed, a directory is created where
3187 # the results will go.
3188 #------------------------------------------------------------------------------
3189 sub define_the_output_directory
3191 my $subr_name = get_my_name ();
3193 my ($define_new_output_dir, $overwrite_output_dir) = @_;
3195 my $msg;
3196 my $outputdir;
3198 #------------------------------------------------------------------------------
3199 # If neither -o or -O are set, find the next number to be used in the name for
3200 # the default output directory.
3201 #------------------------------------------------------------------------------
3202 if ((not $define_new_output_dir) and (not $overwrite_output_dir))
3204 my $dir_id = 1;
3205 while (-d "er.".$dir_id.".html")
3206 { $dir_id++; }
3207 $outputdir = "er.".$dir_id.".html";
3210 if (-d $outputdir)
3212 #------------------------------------------------------------------------------
3213 # The -o option is used, but the directory already exists.
3214 #------------------------------------------------------------------------------
3215 if ($define_new_output_dir)
3217 $msg = "directory $outputdir already exists";
3218 gp_message ("error", $subr_name, $msg);
3219 $g_total_error_count++;
3221 $msg = "use the -O/--overwrite option to overwrite an existing";
3222 $msg .= " directory";
3223 gp_message ("abort", $subr_name, $msg);
3225 #------------------------------------------------------------------------------
3226 # This is a bit risky, so we proceed with caution. The output directory exists,
3227 # but it is okay to overwrite it. It is removed here and created again below.
3228 #------------------------------------------------------------------------------
3229 elsif ($overwrite_output_dir)
3231 my $target_cmd = $g_mapped_cmds{"rm"};
3232 my $rm_output = qx ($target_cmd -rf $outputdir);
3233 my $error_code = ${^CHILD_ERROR_NATIVE};
3234 if ($error_code != 0)
3236 gp_message ("error", $subr_name, $rm_output);
3237 $msg = "fatal error when trying to remove " . $outputdir;
3238 gp_message ("abort", $subr_name, $msg);
3240 else
3242 $msg = "directory $outputdir has been removed";
3243 gp_message ("debug", $subr_name, $msg);
3247 #------------------------------------------------------------------------------
3248 # When we get here, the fatal scenarios have been cleared and the name for
3249 # $outputdir is known. Time to create it.
3250 #------------------------------------------------------------------------------
3251 if (mkdir ($outputdir, 0777))
3253 $msg = "created output directory " . $outputdir;
3254 gp_message ("debug", $subr_name, $msg);
3256 else
3258 $msg = "a fatal problem occurred when creating directory " . $outputdir;
3259 gp_message ("abort", $subr_name, $msg);
3262 return ($outputdir);
3264 } #-- End of subroutine define_the_output_directory
3266 #------------------------------------------------------------------------------
3267 # Return the virtual address for the load object.
3269 # Note that at this point, $elf_arch is known to be supported.
3271 # TBD: Duplications?
3272 #------------------------------------------------------------------------------
3273 sub determine_base_va_address
3275 my $subr_name = get_my_name ();
3277 my ($executable_name, $base_va_executable, $loadobj, $routine) = @_;
3279 my $msg;
3280 my $name_loadobject;
3281 my $base_va_address;
3283 $msg = "base_va_executable = " . $base_va_executable;
3284 gp_message ("debugXL", $subr_name, $msg);
3285 $msg = "loadobj = " . $loadobj;
3286 gp_message ("debugXL", $subr_name, $msg);
3287 $msg = "routine = " . $routine;
3288 gp_message ("debugXL", $subr_name, $msg);
3290 #------------------------------------------------------------------------------
3291 # Strip the pathname from the load object name.
3292 #------------------------------------------------------------------------------
3293 $name_loadobject = get_basename ($loadobj);
3295 #------------------------------------------------------------------------------
3296 # If the load object is the executable, return the base address determined
3297 # earlier. Otherwise return 0x0. Note that I am not sure if this is always
3298 # the right thing to do, but for .so files it seems to work out fine.
3299 #------------------------------------------------------------------------------
3300 if ($name_loadobject eq $executable_name)
3302 $base_va_address = $base_va_executable;
3304 else
3306 $base_va_address = "0x0";
3309 my $decimal_address = bigint::hex ($base_va_address);
3311 $msg = "return base_va_address = $base_va_address";
3312 $msg .= " (decimal: $decimal_address)";
3313 gp_message ("debugXL", $subr_name, $msg);
3315 return ($base_va_address);
3317 } #-- End of subroutine determine_base_va_address
3319 #------------------------------------------------------------------------------
3320 # Now that we know the map.xml file(s) are present, we can scan these and get
3321 # the required information.
3322 #------------------------------------------------------------------------------
3323 sub determine_base_virtual_address
3325 my $subr_name = get_my_name ();
3327 my ($exp_dir_list_ref) = @_;
3329 my @exp_dir_list = @{ $exp_dir_list_ref };
3331 my $executable_name;
3332 my $full_path_exec;
3333 my $msg;
3334 my $path_to_map_file;
3335 my $va_executable_in_hex;
3337 for my $exp_dir (keys %g_exp_dir_meta_data)
3339 $path_to_map_file = $g_exp_dir_meta_data{$exp_dir}{"directory_path"};
3340 $path_to_map_file .= $exp_dir;
3341 $path_to_map_file .= "/map.xml";
3343 ($full_path_exec, $executable_name, $va_executable_in_hex) =
3344 extract_info_from_map_xml ($path_to_map_file);
3346 $g_exp_dir_meta_data{$exp_dir}{"full_path_exec"} = $full_path_exec;
3347 $g_exp_dir_meta_data{$exp_dir}{"exec_name"} = $executable_name;
3348 $g_exp_dir_meta_data{$exp_dir}{"va_base_in_hex"} = $va_executable_in_hex;
3350 $msg = "exp_dir = " . $exp_dir;
3351 gp_message ("debug", $subr_name, $msg);
3352 $msg = "full_path_exece = " . $full_path_exec;
3353 gp_message ("debug", $subr_name, $msg);
3354 $msg = "executable_name = " . $executable_name;
3355 gp_message ("debug", $subr_name, $msg);
3356 $msg = "va_executable_in_hex = " . $va_executable_in_hex;
3357 gp_message ("debug", $subr_name, $msg);
3360 return (0);
3362 } #-- End of subroutine determine_base_virtual_address
3364 #------------------------------------------------------------------------------
3365 # Determine whether the decimal separator is a point or a comma.
3366 #------------------------------------------------------------------------------
3367 sub determine_decimal_separator
3369 my $subr_name = get_my_name ();
3371 my $cmd_output;
3372 my $convert_to_dot;
3373 my $decimal_separator;
3374 my $error_code;
3375 my $field;
3376 my $ignore_count;
3377 my @locale_info = ();
3378 my $msg;
3379 my $target_cmd;
3380 my $target_found;
3382 my $default_decimal_separator = "\\.";
3384 $target_cmd = $g_mapped_cmds{locale} . " -k LC_NUMERIC";
3385 ($error_code, $cmd_output) = execute_system_cmd ($target_cmd);
3387 if ($error_code != 0)
3388 #------------------------------------------------------------------------------
3389 # This is unlikely to happen, but you never know. To reduce the nesting level,
3390 # return right here in case of an error.
3391 #------------------------------------------------------------------------------
3393 $msg = "failure to execute the command " . $target_cmd;
3394 gp_message ("error", $subr_name, $msg);
3396 $g_total_error_count++;
3398 $convert_to_dot = $TRUE;
3400 return ($error_code, $default_decimal_separator, $convert_to_dot);
3403 #------------------------------------------------------------------------------
3404 #------------------------------------------------------------------------------
3405 # Scan the locale info and search for the target line of the form
3406 # decimal_point="<target>" where <target> is either a dot, or a comma.
3407 #------------------------------------------------------------------------------
3408 #------------------------------------------------------------------------------
3410 #------------------------------------------------------------------------------
3411 # Split the output into the different lines and scan for the line we need.
3412 #------------------------------------------------------------------------------
3413 @locale_info = split ("\n", $cmd_output);
3414 $target_found = $FALSE;
3415 for my $line (@locale_info)
3417 chomp ($line);
3418 $msg = "line from locale_info = " . $line;
3419 gp_message ("debug", $subr_name, $msg);
3421 if ($line =~ /decimal_point=/)
3424 #------------------------------------------------------------------------------
3425 # Found the target line. Split this line to get the value field.
3426 #------------------------------------------------------------------------------
3427 my @split_line = split ("=", $line);
3429 #------------------------------------------------------------------------------
3430 # There should be 2 fields. If not, something went wrong.
3431 #------------------------------------------------------------------------------
3432 if (scalar @split_line != 2)
3434 # if (scalar @split_line == 2) {
3435 # $target_found = $FALSE;
3436 #------------------------------------------------------------------------------
3437 # Remove the newline before printing the variables.
3438 #------------------------------------------------------------------------------
3439 $ignore_count = chomp ($line);
3440 $ignore_count = chomp (@split_line);
3442 $msg = "line $line matches the search, but the decimal";
3443 $msg .= " separator has the wrong format";
3444 gp_message ("warning", $subr_name, $msg);
3445 $msg = "the splitted line is [@split_line] and does not";
3446 $msg .= " contain 2 fields";
3447 gp_message ("warning", $subr_name, $msg);
3448 $msg = "the default decimal separator will be used";
3449 gp_message ("warning", $subr_name, $msg);
3451 $g_total_warning_count++;
3453 else
3455 #------------------------------------------------------------------------------
3456 # We know there are 2 fields and the second one has the decimal point.
3457 #------------------------------------------------------------------------------
3458 $msg = "split_line[1] = " . $split_line[1];
3459 gp_message ("debug", $subr_name, $msg);
3461 chomp ($split_line[1]);
3462 $field = $split_line[1];
3464 if (length ($field) != 3)
3465 #------------------------------------------------------------------------------
3466 # The field still includes the quotes. Check if the string has length 3, which
3467 # should be the case, but if not, we flag an error. The error code is set such
3468 # that the callee will know a problem has occurred.
3469 #------------------------------------------------------------------------------
3471 $msg = "unexpected output from the $target_cmd command:";
3472 $msg .= " " . $field;
3473 gp_message ("error", $subr_name, $msg);
3475 $g_total_error_count++;
3477 $error_code = 1;
3478 last;
3481 $msg = "field = ->$field<-";
3482 gp_message ("debug", $subr_name, $msg);
3484 if (($field eq "\".\"") or ($field eq "\",\""))
3485 #------------------------------------------------------------------------------
3486 # Found the separator. Capture the character between the quotes.
3487 #------------------------------------------------------------------------------
3489 $target_found = $TRUE;
3490 $decimal_separator = substr ($field,1,1);
3491 $msg = "decimal_separator = $decimal_separator--end";
3492 $msg .= " skip remainder of loop";
3493 gp_message ("debug", $subr_name, $msg);
3494 last;
3499 if (not $target_found)
3501 $decimal_separator = $default_decimal_separator;
3502 $msg = "cannot determine the decimal separator";
3503 $msg .= " - use the default " . $decimal_separator;
3504 gp_message ("warning", $subr_name, $msg);
3506 $g_total_warning_count++;
3509 if ($decimal_separator ne ".")
3511 $convert_to_dot = $TRUE;
3513 else
3515 $convert_to_dot = $FALSE;
3518 $decimal_separator = "\\".$decimal_separator;
3519 $g_locale_settings{"decimal_separator"} = $decimal_separator;
3520 $g_locale_settings{"convert_to_dot"} = $convert_to_dot;
3522 return ($error_code, $decimal_separator, $convert_to_dot);
3524 } #-- End of subroutine determine_decimal_separator
3526 #------------------------------------------------------------------------------
3527 # TBD
3528 #------------------------------------------------------------------------------
3529 sub dump_function_info
3531 my $subr_name = get_my_name ();
3533 my ($function_info_ref, $name) = @_;
3535 my %function_info = %{$function_info_ref};
3536 my $kip;
3537 my $msg;
3539 $msg = "function_info for " . $name;
3540 gp_message ("debug", $subr_name, $msg);
3542 $kip = 0;
3543 for my $farray ($function_info{$name})
3545 for my $elm (@{$farray})
3547 $msg = $kip . ": routine = " . ${$elm}{"routine"};
3548 gp_message ("debug", $subr_name, $msg);
3549 for my $key (sort keys %{$elm})
3551 if ($key eq "routine")
3553 next;
3555 $msg = $kip . ": $key = " . ${$elm}{$key};
3556 gp_message ("debug", $subr_name, $msg);
3558 $kip++;
3562 return (0);
3564 } #-- End of subroutine dump_function_info
3566 #------------------------------------------------------------------------------
3567 # TBD
3568 #------------------------------------------------------------------------------
3569 sub elf_phdr
3571 my $subr_name = get_my_name ();
3573 my ($elf_loadobjects_found, $elf_arch, $loadobj, $routine,
3574 $ARCHIVES_MAP_NAME, $ARCHIVES_MAP_VADDR, $elf_rats_ref) = @_;
3576 my %elf_rats = %{$elf_rats_ref};
3578 my $msg;
3579 my $return_value;
3581 #------------------------------------------------------------------------------
3582 # TBD. Quick check. Can be moved up the call tree.
3583 #------------------------------------------------------------------------------
3584 if ( $elf_arch ne "Linux" )
3586 $msg = $elf_arch . " is not a supported OS";
3587 gp_message ("error", $subr_name, $msg);
3588 $g_total_error_count++;
3589 gp_message ("abort", $subr_name, $g_abort_msg);
3592 #------------------------------------------------------------------------------
3593 # TBD: This should not be in a loop over $loadobj and only use the executable.
3594 #------------------------------------------------------------------------------
3596 #------------------------------------------------------------------------------
3597 # TBD: $routine is not really used in these subroutines. Is this a bug?
3598 #------------------------------------------------------------------------------
3599 if ($elf_loadobjects_found)
3601 gp_message ("debugXL", $subr_name, "calling elf_phdr_usual");
3602 $return_value = elf_phdr_usual ($elf_arch,
3603 $loadobj,
3604 $routine,
3605 \%elf_rats);
3607 else
3609 gp_message ("debugXL", $subr_name, "calling elf_phdr_sometimes");
3610 $return_value = elf_phdr_sometimes ($elf_arch,
3611 $loadobj,
3612 $routine,
3613 $ARCHIVES_MAP_NAME,
3614 $ARCHIVES_MAP_VADDR);
3617 gp_message ("debug", $subr_name, "the return value = $return_value");
3619 if (not $return_value)
3621 $msg = "need to handle a return value of FALSE";
3622 gp_message ("error", $subr_name, $msg);
3623 $g_total_error_count++;
3624 gp_message ("abort", $subr_name, $g_abort_msg);
3627 return ($return_value);
3629 } #-- End of subroutine elf_phdr
3631 #------------------------------------------------------------------------------
3632 # Return the virtual address for the load object.
3633 #------------------------------------------------------------------------------
3634 sub elf_phdr_sometimes
3636 my $subr_name = get_my_name ();
3638 my ($elf_arch, $loadobj, $routine, $ARCHIVES_MAP_NAME,
3639 $ARCHIVES_MAP_VADDR) = @_;
3641 my $arch_uname_s = $local_system_config{"kernel_name"};
3642 my $arch_uname = $local_system_config{"processor"};
3643 my $arch = $g_arch_specific_settings{"arch"};
3645 gp_message ("debug", $subr_name, "arch_uname_s = $arch_uname_s");
3646 gp_message ("debug", $subr_name, "arch_uname = $arch_uname");
3647 gp_message ("debug", $subr_name, "arch = $arch");
3649 my $cmd_output;
3650 my $command_string;
3651 my $error_code;
3652 my $msg;
3653 my $target_cmd;
3655 my $line;
3656 my $blo;
3658 my $elf_offset;
3659 my $i;
3660 my @foo;
3661 my $foo;
3662 my $foo1;
3663 my $p_vaddr;
3664 my $rc;
3665 my $archives_file;
3666 my $loadobj_SAVE;
3667 my $Offset;
3668 my $VirtAddr;
3669 my $PhysAddr;
3670 my $FileSiz;
3671 my $MemSiz;
3672 my $Flg;
3673 my $Align;
3675 if ($ARCHIVES_MAP_NAME eq $blo)
3677 return ($ARCHIVES_MAP_VADDR);
3679 else
3681 return ($FALSE);
3684 if ($arch_uname_s ne $elf_arch)
3686 #------------------------------------------------------------------------------
3687 # We are masquerading between systems, must leave
3688 #------------------------------------------------------------------------------
3689 $msg = "masquerading arch_uname_s->$arch_uname_s elf_arch->$elf_arch";
3690 gp_message ("debug", $subr_name, $msg);
3691 return ($FALSE);
3694 if ($loadobj eq "DYNAMIC_FUNCTIONS")
3695 #------------------------------------------------------------------------------
3696 # Linux vDSO, leave for now
3697 #------------------------------------------------------------------------------
3699 return ($FALSE);
3702 # TBD: STILL NEEDED??!!
3704 $loadobj_SAVE = $loadobj;
3706 $blo = get_basename ($loadobj);
3707 gp_message ("debug", $subr_name, "loadobj = $loadobj");
3708 gp_message ("debug", $subr_name, "blo = $blo");
3709 gp_message ("debug", $subr_name, "ARCHIVES_MAP_NAME = $ARCHIVES_MAP_NAME");
3710 gp_message ("debug", $subr_name, "ARCHIVES_MAP_VADDR = $ARCHIVES_MAP_VADDR");
3711 if ($ARCHIVES_MAP_NAME eq $blo)
3713 return ($ARCHIVES_MAP_VADDR);
3715 else
3717 return ($FALSE);
3720 } #-- End of subroutine elf_phdr_sometimes
3722 #------------------------------------------------------------------------------
3723 # Return the virtual address for the load object.
3725 # Note that at this point, $elf_arch is known to be supported.
3726 #------------------------------------------------------------------------------
3727 sub elf_phdr_usual
3729 my $subr_name = get_my_name ();
3731 my ($elf_arch, $loadobj, $routine, $elf_rats_ref) = @_;
3733 my %elf_rats = %{$elf_rats_ref};
3735 my $load_long_regex;
3736 $load_long_regex = '^\s+LOAD\s+(\S+)\s+(\S+)\s+(\S+)';
3737 $load_long_regex .= '\s+(\S+)\s+(\S+)\s+(R\sE)\s+(\S+)$';
3738 my $load_short_regex = '^\s+LOAD\s+(\S+)\s+(\S+)\s+(\S+)$';
3739 my $re_regex = '^\s+(\S+)\s+(\S+)\s+(R\sE)\s+(\S+)$';
3741 my $return_code;
3742 my $cmd_output;
3743 my $target_cmd;
3744 my $command_string;
3745 my $error_code;
3746 my $error_code1;
3747 my $error_code2;
3748 my $msg;
3750 my ($elf_offset, $loadobjARC);
3751 my ($i, @foo, $foo, $foo1, $p_vaddr, $rc);
3752 my ($Offset, $VirtAddr, $PhysAddr, $FileSiz, $MemSiz, $Flg, $Align);
3754 my $arch_uname_s = $local_system_config{"kernel_name"};
3756 $msg = "elf_arch = $elf_arch loadobj = $loadobj routine = $routine";
3757 gp_message ("debug", $subr_name, $msg);
3759 my ($base, $ignore_value, $ignore_too) = fileparse ($loadobj);
3761 $msg = "base = $base " . basename ($loadobj);
3762 gp_message ("debug", $subr_name, $msg);
3764 if ($elf_arch eq "Linux")
3766 if ($arch_uname_s ne $elf_arch)
3768 #------------------------------------------------------------------------------
3769 # We are masquerading between systems, must leave.
3770 # Maybe we could use ELF_RATS
3771 #------------------------------------------------------------------------------
3772 $msg = "masquerading arch_uname_s->" . $arch_uname_s;
3773 $msg .= " elf_arch->" . $elf_arch;
3774 gp_message ("debug", $subr_name, $msg);
3776 return ($FALSE);
3778 if ($loadobj eq "DYNAMIC_FUNCTIONS")
3780 #------------------------------------------------------------------------------
3781 # Linux vDSO, leave for now
3782 #------------------------------------------------------------------------------
3783 gp_message ("debug", $subr_name, "early return: loadobj = $loadobj");
3784 return ($FALSE);
3787 $target_cmd = $g_mapped_cmds{"readelf"};
3788 $command_string = $target_cmd . " -l " . $loadobj . " 2>/dev/null";
3790 ($error_code1, $cmd_output) = execute_system_cmd ($command_string);
3792 $msg = "executed command_string = " . $command_string;
3793 gp_message ("debug", $subr_name, $msg);
3794 $msg = "cmd_output = " . $cmd_output;
3795 gp_message ("debug", $subr_name, $msg);
3797 if ($error_code1 != 0)
3799 gp_message ("debug", $subr_name, "call failure for $command_string");
3800 #------------------------------------------------------------------------------
3801 # e.g. $loadobj->/usr/lib64/libc-2.17.so
3802 #------------------------------------------------------------------------------
3803 $loadobjARC = get_basename ($loadobj);
3804 gp_message ("debug", $subr_name, "seek elf_rats for $loadobjARC");
3806 if (exists ($elf_rats{$loadobjARC}))
3808 my $elfoid;
3809 $elfoid = $elf_rats{$loadobjARC}[1] . "/archives/";
3810 $elfoid .= $elf_rats{$loadobjARC}[0];
3811 $target_cmd = $g_mapped_cmds{"readelf"};
3812 $command_string = $target_cmd . "-l " . $elfoid . " 2>/dev/null";
3813 ($error_code2, $cmd_output) =
3814 execute_system_cmd ($command_string);
3816 if ($error_code2 != 0)
3818 $msg = "call failure for " . $command_string;
3819 gp_message ("error", $subr_name, $msg);
3820 $g_total_error_count++;
3821 gp_message ("abort", $subr_name, $g_abort_msg);
3823 else
3825 $msg = "executed command_string = " . $command_string;
3826 gp_message ("debug", $subr_name, $msg);
3827 $msg = "cmd_output = " . $cmd_output;
3828 gp_message ("debug", $subr_name, $msg);
3831 else
3833 $msg = "elf_rats{$loadobjARC} does not exist";
3834 gp_message ("assertion", $subr_name, $msg);
3837 #------------------------------------------------------------------------------
3838 # Example output of "readelf -l" on Linux:
3840 # Elf file type is EXEC (Executable file)
3841 # Entry point 0x4023a0
3842 # There are 11 program headers, starting at offset 64
3844 # Program Headers:
3845 # Type Offset VirtAddr PhysAddr
3846 # FileSiz MemSiz Flags Align
3847 # PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
3848 # 0x0000000000000268 0x0000000000000268 R 8
3849 # INTERP 0x00000000000002a8 0x00000000004002a8 0x00000000004002a8
3850 # 0x000000000000001c 0x000000000000001c R 1
3851 # [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
3852 # LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
3853 # 0x0000000000001310 0x0000000000001310 R 1000
3854 # LOAD 0x0000000000002000 0x0000000000402000 0x0000000000402000
3855 # 0x0000000000006515 0x0000000000006515 R E 1000
3856 # LOAD 0x0000000000009000 0x0000000000409000 0x0000000000409000
3857 # 0x000000000006f5a8 0x000000000006f5a8 R 1000
3858 # LOAD 0x0000000000078dc8 0x0000000000479dc8 0x0000000000479dc8
3859 # 0x000000000000047c 0x0000000000000f80 RW 1000
3860 # DYNAMIC 0x0000000000078dd8 0x0000000000479dd8 0x0000000000479dd8
3861 # 0x0000000000000220 0x0000000000000220 RW 8
3862 # NOTE 0x00000000000002c4 0x00000000004002c4 0x00000000004002c4
3863 # 0x0000000000000044 0x0000000000000044 R 4
3864 # GNU_EH_FRAME 0x00000000000777f4 0x00000000004777f4 0x00000000004777f4
3865 # 0x000000000000020c 0x000000000000020c R 4
3866 # GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
3867 # 0x0000000000000000 0x0000000000000000 RW 10
3868 # GNU_RELRO 0x0000000000078dc8 0x0000000000479dc8 0x0000000000479dc8
3869 # 0x0000000000000238 0x0000000000000238 R 1
3871 # Section to Segment mapping:
3872 # Segment Sections...
3873 # 00
3874 # 01 .interp
3875 # 02 .interp .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym
3876 # .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
3877 # 03 .init .plt .text .fini
3878 # 04 .rodata .eh_frame_hdr .eh_frame
3879 # 05 .init_array .fini_array .dynamic .got .got.plt .data .bss
3880 # 06 .dynamic
3881 # 07 .note.gnu.build-id .note.ABI-tag
3882 # 08 .eh_frame_hdr
3883 # 09
3884 # 10 .init_array .fini_array .dynamic .got
3885 #------------------------------------------------------------------------------
3887 #------------------------------------------------------------------------------
3888 # Analyze the ELF information and try to find the virtual address.
3890 # Note that the information printed as part of LOAD needs to have "R E" in it.
3891 # In the example output above, the return value would be "0x0000000000402000".
3893 # We also need to distinguish two cases. It could be that the output is on
3894 # a single line, or spread over two lines:
3896 # Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
3897 # LOAD 0x000000 0x08048000 0x08048000 0x61b4ae 0x61b4ae R E 0x1000
3898 # or 2 lines
3899 # LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
3900 # 0x0000000000001010 0x0000000000001010 R E 200000
3901 #------------------------------------------------------------------------------
3902 @foo = split ("\n",$cmd_output);
3903 for $i (0 .. $#foo)
3905 $foo = $foo[$i];
3906 chomp ($foo);
3907 if ($foo =~ /$load_long_regex/)
3909 $Offset = $1;
3910 $VirtAddr = $2;
3911 $PhysAddr = $3;
3912 $FileSiz = $4;
3913 $MemSiz = $5;
3914 $Flg = $6;
3915 $Align = $7;
3917 $elf_offset = $VirtAddr;
3918 $msg = "single line version elf_offset = " . $elf_offset;
3919 gp_message ("debug", $subr_name, $msg);
3920 return ($elf_offset);
3922 elsif ($foo =~ /$load_short_regex/)
3924 #------------------------------------------------------------------------------
3925 # is it a two line version?
3926 #------------------------------------------------------------------------------
3927 $Offset = $1;
3928 $VirtAddr = $2; # maybe
3929 $PhysAddr = $3;
3930 if ($i != $#foo)
3932 $foo1 = $foo[$i + 1];
3933 chomp ($foo1);
3934 if ($foo1 =~ /$re_regex/)
3936 $FileSiz = $1;
3937 $MemSiz = $2;
3938 $Flg = $3;
3939 $Align = $4;
3940 $elf_offset = $VirtAddr;
3941 $msg = "two line version elf_offset = " . $elf_offset;
3942 gp_message ("debug", $subr_name, $msg);
3943 return ($elf_offset);
3950 } #-- End of subroutine elf_phdr_usual
3952 #------------------------------------------------------------------------------
3953 # Execute a system command. In case of an error, a non-zero error code is
3954 # returned. It is upon the caller to decide what to do next.
3955 #------------------------------------------------------------------------------
3956 sub execute_system_cmd
3958 my $subr_name = get_my_name ();
3960 my ($target_cmd) = @_;
3962 my $cmd_output;
3963 my $error_code;
3964 my $msg;
3966 chomp ($target_cmd);
3968 $cmd_output = qx ($target_cmd);
3969 $error_code = ${^CHILD_ERROR_NATIVE};
3971 if ($error_code != 0)
3973 chomp ($cmd_output);
3974 $msg = "failure executing command " . $target_cmd;
3975 gp_message ("error", $subr_name, $msg);
3976 $msg = "error code = " . $error_code;
3977 gp_message ("error", $subr_name, $msg);
3978 $msg = "cmd_output = " . $cmd_output;
3980 gp_message ("error", $subr_name, $msg);
3981 $g_total_error_count++;
3983 else
3985 $msg = "executed command " . $target_cmd;
3986 gp_message ("debugXL", $subr_name, $msg);
3989 return ($error_code, $cmd_output);
3991 } #-- End of subroutine execute_system_cmd
3993 #------------------------------------------------------------------------------
3994 # Scan the input file, which should be a gprofng generated map.xml file, and
3995 # extract the relevant information.
3996 #------------------------------------------------------------------------------
3997 sub extract_info_from_map_xml
3999 my $subr_name = get_my_name ();
4001 my ($input_map_xml_file) = @_;
4003 my $map_xml_regex;
4004 $map_xml_regex = '<event kind="map"\s.*';
4005 $map_xml_regex .= 'vaddr="0x([0-9a-fA-F]+)"\s+.*';
4006 $map_xml_regex .= 'foffset="\+*0x([0-9a-fA-F]+)"\s.*';
4007 $map_xml_regex .= 'modes="0x([0-9]+)"\s.*';
4008 $map_xml_regex .= 'name="(.*)".*>$';
4010 my $extracted_information;
4011 my $input_line;
4012 my $vaddr;
4013 my $foffset;
4014 my $msg;
4015 my $modes;
4016 my $name_path;
4017 my $name;
4019 my $full_path_exec;
4020 my $executable_name;
4021 my $result_VA;
4022 my $va_executable_in_hex;
4024 $msg = " - unable to open file $input_map_xml_file for reading:";
4025 open (MAP_XML, "<", $input_map_xml_file)
4026 or die ($subr_name . $msg . " " . $!);
4028 $msg = "opened file $input_map_xml_file for reading";
4029 gp_message ("debug", $subr_name, $msg);
4031 #------------------------------------------------------------------------------
4032 # Scan the file. We need to find the name of the executable with the mode set
4033 # to 0x005. For this entry we have to capture the name, the mode, the virtual
4034 # address and the offset.
4035 #------------------------------------------------------------------------------
4036 $extracted_information = $FALSE;
4037 while (<MAP_XML>)
4039 $input_line = $_;
4040 chomp ($input_line);
4042 $msg = "read input_line = $input_line";
4043 gp_message ("debug", $subr_name, $msg);
4045 if ($input_line =~ /^$map_xml_regex/)
4047 $msg = "target line = $input_line";
4048 gp_message ("debug", $subr_name, $msg);
4050 $vaddr = $1;
4051 $foffset = $2;
4052 $modes = $3;
4053 $name_path = $4;
4054 $name = get_basename ($name_path);
4056 $msg = "extracted vaddr = $vaddr foffset = $foffset";
4057 $msg .= " modes = $modes";
4058 gp_message ("debug", $subr_name, $msg);
4060 $msg = "extracted name_path = $name_path name = $name";
4061 gp_message ("debug", $subr_name, $msg);
4063 #------------------------------------------------------------------------------
4064 # The base virtual address is calculated as vaddr-foffset. Although Perl
4065 # handles arithmetic in hex, we take the safe way here. Maybe overkill, but
4066 # I prefer to be safe than sorry in cases like this.
4067 #------------------------------------------------------------------------------
4068 $full_path_exec = $name_path;
4069 $executable_name = $name;
4070 $result_VA = bigint::hex ($vaddr) - bigint::hex ($foffset);
4071 $va_executable_in_hex = sprintf ("0x%016x", $result_VA);
4073 ## $ARCHIVES_MAP_NAME = $name;
4074 ## $ARCHIVES_MAP_VADDR = $va_executable_in_hex;
4076 $msg = "result_VA = $result_VA";
4077 gp_message ("debug", $subr_name, $msg);
4079 $msg = "va_executable_in_hex = $va_executable_in_hex";
4080 gp_message ("debug", $subr_name, $msg);
4082 #------------------------------------------------------------------------------
4083 # Stop reading when we found the correct entry.
4084 #------------------------------------------------------------------------------
4085 if ($modes eq "005")
4087 $extracted_information = $TRUE;
4088 last;
4091 } #-- End of while-loop
4093 if (not $extracted_information)
4095 $msg = "cannot find the necessary information in file";
4096 $msg .= " " . $input_map_xml_file;
4097 gp_message ("assertion", $subr_name, $msg);
4100 $msg = "full_path_exec = $full_path_exec";
4101 gp_message ("debug", $subr_name, $msg);
4102 $msg = "executable_name = $executable_name";
4103 gp_message ("debug", $subr_name, $msg);
4104 $msg = "va_executable_in_hex = $va_executable_in_hex";
4105 gp_message ("debug", $subr_name, $msg);
4107 return ($full_path_exec, $executable_name, $va_executable_in_hex);
4109 } #-- End of subroutine extract_info_from_map_xml
4111 #------------------------------------------------------------------------------
4112 # This routine analyzes the metric line and extracts the metric details.
4113 # Example input: Exclusive Total CPU Time: e.%totalcpu
4114 #------------------------------------------------------------------------------
4115 sub extract_metric_specifics
4117 my $subr_name = get_my_name ();
4119 my ($metric_line) = @_;
4121 my $metric_description;
4122 my $metric_flavor;
4123 my $metric_visibility;
4124 my $metric_name;
4125 my $metric_spec;
4126 my $msg;
4128 # Ruud if (($metric =~ /\s*(.*):\s+(\S)((\.\S+)|(\+\S+))/) && !($metric =~/^Current/)){
4129 if (($metric_line =~ /\s*(.+):\s+([ei])([\.\+%]+)(\S*)/) and !($metric_line =~/^Current/))
4131 $msg = "input line = " . $metric_line;
4132 gp_message ("debug", $subr_name, $msg);
4134 $metric_description = $1;
4135 $metric_flavor = $2;
4136 $metric_visibility = $3;
4137 $metric_name = $4;
4139 #------------------------------------------------------------------------------
4140 # Although we have captured the metric visibility, the original code removes
4141 # this from the name. Since the structure is more complicated, the code is
4142 # more tedious as well. With our new approach we just leave the visibility
4143 # out.
4144 #------------------------------------------------------------------------------
4145 # $metric_spec = $metric_flavor.$metric_visibility.$metric_name;
4147 $metric_spec = $metric_flavor . "." . $metric_name;
4149 #------------------------------------------------------------------------------
4150 # From the original code:
4152 # On x64 systems there are metrics which contain ~ (for example
4153 # DC_access~umask=0 . When er_print lists them, they come out
4154 # as DC_access%7e%umask=0 (see 6530691). Untill 6530691 is
4155 # fixed, we need this. Later we may need something else, or
4156 # things may just work.
4157 #------------------------------------------------------------------------------
4158 # $metric_spec=~s/\%7e\%/,/;
4159 # # remove % metric
4160 # print "DB: before \$metric_spec = $metric_spec\n";
4162 #------------------------------------------------------------------------------
4163 # TBD: I don't know why the "%" symbol is removed.
4164 #------------------------------------------------------------------------------
4165 # $metric_spec =~ s/\%//;
4166 # print "DB: after \$metric_spec = $metric_spec\n";
4168 $msg = "on return: metric_spec = " . $metric_spec;
4169 gp_message ("debugM", $subr_name, $msg);
4170 $msg = "on return: metric_flavor = " . $metric_flavor;
4171 gp_message ("debugM", $subr_name, $msg);
4172 $msg = "on return: metric_visibility = " . $metric_visibility;
4173 gp_message ("debugM", $subr_name, $msg);
4174 $msg = "on return: metric_name = " . $metric_name;
4175 gp_message ("debugM", $subr_name, $msg);
4176 $msg = "on return: metric_description = " . $metric_description;
4177 gp_message ("debugM", $subr_name, $msg);
4179 return ($metric_spec, $metric_flavor, $metric_visibility,
4180 $metric_name, $metric_description);
4182 else
4184 return ("skipped", "void");
4187 } #-- End of subroutine extract_metric_specifics
4189 #------------------------------------------------------------------------------
4190 # Extract the option value(s) from the input array. In case the number of
4191 # values execeeds the specified limit, warning messages are printed.
4193 # In case the option value is valid, g_user_settings is updated with this
4194 # value and a value of TRUE is returned. Otherwise the return value is FALSE.
4196 # Note that not in all invocations of this subroutine, gp_message() is
4197 # operational. Only after the debug settings have been finalized, the
4198 # messages are printed.
4200 # This subroutine also generates warnings about multiple occurrences
4201 # and the validity of the values.
4202 #------------------------------------------------------------------------------
4203 sub extract_option_value
4205 my $subr_name = get_my_name ();
4207 my ($option_dir_ref, $max_occurrences_ref, $internal_option_name_ref,
4208 $option_name_ref) = @_;
4210 my @option_dir = @{ $option_dir_ref };
4211 my $max_occurrences = ${ $max_occurrences_ref };
4212 my $internal_option_name = ${ $internal_option_name_ref };
4213 my $option_name = ${ $option_name_ref };
4215 my $deprecated_option_used;
4216 my $excess_occurrences;
4217 my $msg;
4218 my $no_of_occurrences;
4219 my $no_of_warnings = 0;
4220 my $option_value = "not set yet";
4221 my $option_value_missing;
4222 my $option_value_missing_ref;
4223 my $reset_blank_value;
4224 my $special_treatment = $FALSE;
4225 my $valid = $FALSE;
4226 my $valid_ref;
4228 if (@option_dir)
4230 $no_of_occurrences = scalar (@option_dir);
4232 $msg = "option_name = $option_name";
4233 gp_message ("debug", $subr_name, $msg);
4234 $msg = "internal_option_name = $internal_option_name";
4235 gp_message ("debug", $subr_name, $msg);
4236 $msg = "no_of_occurrences = $no_of_occurrences";
4237 gp_message ("debug", $subr_name, $msg);
4239 $excess_occurrences = ($no_of_occurrences > $max_occurrences) ?
4240 $TRUE : $FALSE;
4242 #------------------------------------------------------------------------------
4243 # This is not supposed to happen, but just to be sure, there is a check.
4244 #------------------------------------------------------------------------------
4245 if ($no_of_occurrences < 1)
4247 $msg = "the number of fields is $no_of_occurrences";
4248 $msg .= " - should at least be 1";
4249 gp_message ("assertion", $subr_name, $msg);
4252 #------------------------------------------------------------------------------
4253 # For backward compatibility, we support the legacy "on" and "off" values for
4254 # certain options.
4256 # We also support the debug option without value. In case no value is given,
4257 # it is set to "on".
4259 # Note that regardless of the value(s) in ARGV, internally we use the on/off
4260 # setting.
4261 #------------------------------------------------------------------------------
4262 if (($g_user_settings{$internal_option_name}{"data_type"} eq "onoff") or
4263 ($internal_option_name eq "debug"))
4265 $msg = "enable special treatment of the option";
4266 gp_message ("debug", $subr_name, $msg);
4268 $special_treatment = $TRUE;
4271 #------------------------------------------------------------------------------
4272 # Issue a warning if the same option occcurs more often than what is supported.
4273 #------------------------------------------------------------------------------
4274 if ($excess_occurrences)
4276 $msg = "multiple occurrences of the " . $option_name .
4277 " option found:";
4279 gp_message ("debugM", $subr_name, $msg);
4281 gp_message ("warning", $subr_name, $g_html_new_line . $msg);
4284 #------------------------------------------------------------------------------
4285 # Main loop over all the occurrences of the options. This is a rather simple
4286 # approach since only the last value seen will be accepted.
4288 # To assist the user with troubleshooting, the values that are ignored will be
4289 # checked for validity and a marker to this extent will be printed.
4291 # NOTE:
4292 # If an option may have multiple meaningful occurrences, this part needs to be
4293 # revisited.
4294 #------------------------------------------------------------------------------
4295 $deprecated_option_used = $FALSE;
4296 for my $key (keys @option_dir)
4298 $option_value = $option_dir[$key];
4299 $reset_blank_value = $FALSE;
4301 #------------------------------------------------------------------------------
4302 # For the "onoff" options, convert a blank value to "on".
4303 #------------------------------------------------------------------------------
4304 if (($option_value eq "on") or ($option_value eq "off"))
4306 if (($option_name eq "--verbose") or ($option_name eq "--quiet"))
4308 $deprecated_option_used = $TRUE;
4312 #------------------------------------------------------------------------------
4313 # For the "onoff" options, convert a blank value to "on".
4314 #------------------------------------------------------------------------------
4315 if ($special_treatment and ($option_value eq ""))
4317 $option_value = "on";
4318 $reset_blank_value = $TRUE;
4320 $msg = "reset option value for $option_name from blank";
4321 $msg .= " to \"on\"";
4322 gp_message ("debug", $subr_name, $msg);
4325 #------------------------------------------------------------------------------
4326 # Check for the option value to be valid. It may also happen that an option
4327 # does not have a value, while it should have one.
4328 #------------------------------------------------------------------------------
4329 ($valid_ref, $option_value_missing_ref) = check_and_set_user_option (
4330 $internal_option_name,
4331 $option_value);
4333 $valid = ${ $valid_ref };
4334 $option_value_missing = ${ $option_value_missing_ref };
4336 $msg = "option_value = $option_value";
4337 gp_message ("debug", $subr_name, $msg);
4338 $msg = "after check_and_set_user_option: valid = $valid";
4339 $msg .= " option_value_missing = $option_value_missing";
4340 gp_message ("debug", $subr_name, $msg);
4342 #------------------------------------------------------------------------------
4343 # Generate warning messages, but if an option value is missing, it will also
4344 # be considered to be a fatal error.
4345 #------------------------------------------------------------------------------
4346 if ($excess_occurrences)
4348 if ($option_value_missing)
4350 $msg = "$option_name option - missing a value";
4352 else
4354 #------------------------------------------------------------------------------
4355 # A little trick to avoid user confusion. Although we have set the internal
4356 # value to "on", the user did not set this and so we print "" instead.
4357 #------------------------------------------------------------------------------
4358 if ($reset_blank_value)
4360 $msg = "$option_name option - value = \"\"";
4362 else
4364 $msg = "$option_name option - value = $option_value";
4366 $msg .= ($valid) ? " (valid value)" : " (invalid value)";
4369 gp_message ("debug", $subr_name, $msg);
4370 gp_message ("warning", $subr_name, $msg);
4373 #------------------------------------------------------------------------------
4374 # Check for the last occurrence of the option to be valid. If it is not, it
4375 # is a fatal error.
4376 #------------------------------------------------------------------------------
4377 if ((not $valid) && ($key == $no_of_occurrences-1))
4379 if ($option_value_missing)
4381 $msg = "the $option_name option requires a value";
4383 else
4385 $msg = "the value of $option_value for the $option_name";
4386 $msg .= " option is invalid";
4388 gp_message ("debug", $subr_name, $g_error_keyword . $msg);
4390 gp_message ("error", $subr_name, $msg);
4392 $g_total_error_count++;
4396 #------------------------------------------------------------------------------
4397 # Issue a warning if the same option occcurs more often than what is supported
4398 # and warn the user that all but the last value will be ignored.
4399 #------------------------------------------------------------------------------
4400 if ($excess_occurrences)
4402 $msg = "all values but the last one shown above are ignored";
4404 gp_message ("debugM", $subr_name, $msg);
4405 gp_message ("warning", $subr_name, $msg);
4407 $g_total_warning_count++;
4411 #------------------------------------------------------------------------------
4412 # Issue a warning if the old on/off syntax is used still.
4413 #------------------------------------------------------------------------------
4414 if ($deprecated_option_used)
4416 $msg = "<br>";
4417 $msg .= "the on/off syntax for option $option_name has been";
4418 $msg .= " deprecated";
4419 gp_message ("warning", $subr_name, $msg);
4421 $msg = "this option acts like a switch now";
4422 gp_message ("warning", $subr_name, $msg);
4424 $msg = "support for the old syntax may be terminated";
4425 $msg .= " in a future update";
4426 gp_message ("warning", $subr_name, $msg);
4428 $msg = "please check the gprofng-display-html man page";
4429 $msg .= " for more details";
4430 gp_message ("warning", $subr_name, $msg);
4431 $g_total_warning_count++;
4434 return (\$valid);
4436 } #-- End of subroutine extract_option_value
4438 #------------------------------------------------------------------------------
4439 # TBD
4440 #------------------------------------------------------------------------------
4441 sub extract_source_line_number
4443 my $subr_name = get_my_name ();
4445 my ($src_times_regex, $function_regex, $number_of_metrics, $input_line) = @_;
4447 #------------------------------------------------------------------------------
4448 # The regex section.
4449 #------------------------------------------------------------------------------
4450 my $find_dot_regex = '\.';
4452 my @fields_in_line = ();
4453 my $hot_line;
4454 my $line_id;
4456 #------------------------------------------------------------------------------
4457 # To extract the source line number, we need to distinguish whether this is
4458 # a line with, or without metrics.
4459 #------------------------------------------------------------------------------
4460 @fields_in_line = split (" ", $input_line);
4461 if ( $input_line =~ /$src_times_regex/ )
4463 $hot_line = $1;
4464 if ($hot_line eq "##")
4465 #------------------------------------------------------------------------------
4466 # The line id comes after the "##" symbol and the metrics.
4467 #------------------------------------------------------------------------------
4469 $line_id = $fields_in_line[$number_of_metrics+1];
4471 else
4472 #------------------------------------------------------------------------------
4473 # The line id comes after the metrics.
4474 #------------------------------------------------------------------------------
4476 $line_id = $fields_in_line[$number_of_metrics];
4479 elsif ($input_line =~ /$function_regex/)
4481 $line_id = "func";
4483 else
4484 #------------------------------------------------------------------------------
4485 # The line id is the first non-blank element.
4486 #------------------------------------------------------------------------------
4488 $line_id = $fields_in_line[0];
4490 #------------------------------------------------------------------------------
4491 # Remove the trailing dot.
4492 #------------------------------------------------------------------------------
4493 $line_id =~ s/$find_dot_regex//;
4495 return ($line_id);
4497 } #-- End of subroutine extract_source_line_number
4499 #------------------------------------------------------------------------------
4500 # Finalize the settings for the special options verbose, debug, warnings and
4501 # quiet.
4502 #------------------------------------------------------------------------------
4503 sub finalize_special_options
4505 my $subr_name = get_my_name ();
4507 my $msg;
4509 #------------------------------------------------------------------------------
4510 # If quiet mode has been enabled, disable verbose, warnings and debug.
4511 #------------------------------------------------------------------------------
4512 if ($g_quiet)
4514 $g_user_settings{"verbose"}{"current_value"} = "off";
4515 $g_user_settings{"nowarnings"}{"current_value"} = "on";
4516 $g_user_settings{"warnings"}{"current_value"} = "off";
4517 $g_user_settings{"debug"}{"current_value"} = "off";
4518 $g_debug = $FALSE;
4519 $g_verbose = $FALSE;
4520 $g_warnings = $FALSE;
4521 my $debug_off = "off";
4522 my $ignore_value = set_debug_size (\$debug_off);
4524 else
4526 #------------------------------------------------------------------------------
4527 # Disable output buffering if verbose, debug, and/or warnings are enabled.
4528 #------------------------------------------------------------------------------
4529 if ($g_verbose or $g_debug or $g_warnings)
4531 STDOUT->autoflush (1);
4533 $msg = "enabled autoflush for STDOUT";
4534 gp_message ("debug", $subr_name, $msg);
4536 #------------------------------------------------------------------------------
4537 # If verbose and/or debug have been enabled, print a message.
4538 #------------------------------------------------------------------------------
4539 ## gp_message ("verbose", $subr_name, "verbose mode has been enabled");
4540 ## gp_message ("debug", $subr_name, "debug " . $g_debug_size_value . " mode has been enabled");
4543 return (0);
4545 } #-- End of subroutine finalize_special_options
4547 #------------------------------------------------------------------------------
4548 # For a give routine name and address, find the index into the
4549 # function_info array
4550 #------------------------------------------------------------------------------
4551 sub find_index_in_function_info
4553 my $subr_name = get_my_name ();
4555 my ($routine_ref, $current_address_ref, $function_info_ref) = @_;
4557 my $routine = ${ $routine_ref };
4558 my $current_address = ${ $current_address_ref };
4559 my @function_info = @{ $function_info_ref };
4561 my $addr_offset;
4562 my $ref_index;
4564 gp_message ("debugXL", $subr_name, "find index for routine = $routine and current_address = $current_address");
4565 if (exists ($g_multi_count_function{$routine}))
4568 # TBD: Scan all of the function_info list. Or beter: add index to g_multi_count_function!!
4570 gp_message ("debugXL", $subr_name, "$routine: occurrences = $g_function_occurrences{$routine}");
4571 for my $ref (keys @{ $g_map_function_to_index{$routine} })
4573 $ref_index = $g_map_function_to_index{$routine}[$ref];
4575 gp_message ("debugXL", $subr_name, "$routine: retrieving duplicate entry at ref_index = $ref_index");
4576 gp_message ("debugXL", $subr_name, "$routine: function_info[$ref_index]{'alt_name'} = $function_info[$ref_index]{'alt_name'}");
4578 $addr_offset = $function_info[$ref_index]{"addressobjtext"};
4579 gp_message ("debugXL", $subr_name, "$routine: addr_offset = $addr_offset");
4581 $addr_offset =~ s/^@\d+://;
4582 gp_message ("debugXL", $subr_name, "$routine: addr_offset = $addr_offset");
4583 if ($addr_offset eq $current_address)
4585 last;
4589 else
4591 #------------------------------------------------------------------------------
4592 # There is only a single occurrence and it is straightforward to get the index.
4593 #------------------------------------------------------------------------------
4594 if (exists ($g_map_function_to_index{$routine}))
4596 $ref_index = $g_map_function_to_index{$routine}[0];
4598 else
4600 my $msg = "index for $routine cannot be determined";
4601 gp_message ("assertion", $subr_name, $msg);
4605 gp_message ("debugXL", $subr_name, "routine = $routine current_address = $current_address ref_index = $ref_index");
4607 return (\$ref_index);
4609 } #-- End of subroutine find_index_in_function_info
4611 #------------------------------------------------------------------------------
4612 # TBD
4613 #------------------------------------------------------------------------------
4614 sub find_keyword_in_string
4616 my $subr_name = get_my_name ();
4618 my ($target_string_ref, $target_keyword_ref) = @_;
4620 my $target_string = ${ $target_string_ref };
4621 my $target_keyword = ${ $target_keyword_ref };
4622 my $foundit = $FALSE;
4624 my @index_values = ();
4626 my $ret_val = 0;
4627 my $offset = 0;
4628 gp_message ("debugXL", $subr_name, "target_string = $target_string");
4629 $ret_val = index ($target_string, $target_keyword, $offset);
4630 gp_message ("debugXL", $subr_name, "ret_val = $ret_val");
4632 if ($ret_val != -1)
4634 $foundit = $TRUE;
4635 while ($ret_val != -1)
4637 push (@index_values, $ret_val);
4638 $offset = $ret_val + 1;
4639 gp_message ("debugXL", $subr_name, "ret_val = $ret_val offset = $offset");
4640 $ret_val = index ($target_string, $target_keyword, $offset);
4642 for my $i (keys @index_values)
4644 gp_message ("debugXL", $subr_name, "index_values[$i] = $index_values[$i]");
4647 else
4649 gp_message ("debugXL", $subr_name, "target keyword $target_keyword not found");
4652 return (\$foundit, \@index_values);
4654 } #-- End of subroutine find_keyword_in_string
4656 #------------------------------------------------------------------------------
4657 # Retrieve the absolute path that was used to execute the command. This path
4658 # is used to execute gprofng-display-text later on.
4659 #------------------------------------------------------------------------------
4660 sub find_path_to_gp_display_text
4662 my $subr_name = get_my_name ();
4664 my ($full_command_ref) = @_;
4666 my $full_command = ${ $full_command_ref };
4668 my $error_occurred = $TRUE;
4669 my $return_value;
4671 #------------------------------------------------------------------------------
4672 # Get the path name.
4673 #------------------------------------------------------------------------------
4674 my ($gp_file_name, $gp_path, $suffix_not_used) = fileparse ($full_command);
4676 gp_message ("debug", $subr_name, "full_command = $full_command");
4677 gp_message ("debug", $subr_name, "gp_path = $gp_path");
4679 my $gp_display_text_instance = $gp_path . $GP_DISPLAY_TEXT;
4681 #------------------------------------------------------------------------------
4682 # Check if $GP_DISPLAY_TEXT exists, is not empty, and executable.
4683 #------------------------------------------------------------------------------
4684 if (not -e $gp_display_text_instance)
4686 $return_value = "file not found";
4688 else
4690 if (is_file_empty ($gp_display_text_instance))
4692 $return_value = "file is empty";
4694 else
4696 #------------------------------------------------------------------------------
4697 # All is well. Capture the path.
4698 #------------------------------------------------------------------------------
4699 $error_occurred = $FALSE;
4700 $return_value = $gp_path;
4704 return (\$error_occurred, \$gp_path, \$return_value);
4706 } #-- End of subroutine find_path_to_gp_display_text
4708 #------------------------------------------------------------------------------
4709 # Scan the command line to see if the specified option is present.
4711 # Two types of options are supported: options without a value (e.g. --help) or
4712 # those that are set to "on" or "off".
4714 # In this phase, we only need to check if a value is valid. If it is, we have
4715 # to enable the corresponding global setting. If the value is not valid, we
4716 # ignore it, since it will be caught later and a warning message is issued.
4717 #------------------------------------------------------------------------------
4718 sub find_target_option
4720 my $subr_name = get_my_name ();
4722 my ($command_line_ref, $option_requires_value, $target_option) = @_;
4724 my @command_line = @{ $command_line_ref };
4725 my $option_value = undef;
4726 my $found_option = $FALSE;
4728 my ($command_line_string) = join (" ", @command_line);
4730 ## if ($command_line_string =~ /\s*($target_option)\s*(on|off)*\s*/)
4731 #------------------------------------------------------------------------------
4732 # This does not make any assumptions on the values we are looking for.
4733 #------------------------------------------------------------------------------
4734 if ($command_line_string =~ /\s*\-\-($target_option)\s*(\w*)\s*/)
4736 if (defined ($1))
4737 #------------------------------------------------------------------------------
4738 # We have found the option we are looking for.
4739 #------------------------------------------------------------------------------
4741 $found_option = $TRUE;
4742 if ($option_requires_value and defined ($2))
4743 #------------------------------------------------------------------------------
4744 # There is a value and it is passed on to the caller.
4745 #------------------------------------------------------------------------------
4747 $option_value = $2;
4752 return ($found_option, $option_value);
4754 } #-- End of subroutine find_target_option
4756 #------------------------------------------------------------------------------
4757 # Find the occurrences of non-space characters in a string and return their
4758 # start and end index values(s).
4759 #------------------------------------------------------------------------------
4760 sub find_words_in_line
4762 my $subr_name = get_my_name ();
4764 my ($input_line_ref) = @_;
4766 my $input_line = ${ $input_line_ref };
4768 my $finished = $TRUE;
4770 my $space = 0;
4771 my $space_position = 0;
4772 my $start_word;
4773 my $end_word;
4775 my @word_delimiters = ();
4777 gp_message ("debugXL", $subr_name, "input_line = $input_line");
4779 $finished = $FALSE;
4780 while (not $finished)
4782 $space = index ($input_line, " ", $space_position);
4784 my $txt = "string search space_position = $space_position ";
4785 $txt .= "space = $space";
4786 gp_message ("debugXL", $subr_name, $txt);
4788 if ($space != -1)
4790 if ($space > $space_position)
4792 $start_word = $space_position;
4793 $end_word = $space - 1;
4794 $space_position = $space;
4795 my $keyword = substr ($input_line, $start_word, $end_word - $start_word + 1);
4796 gp_message ("debugXL", $subr_name, "string search start_word = $start_word end_word = $end_word space_position = $space_position $keyword");
4797 push (@word_delimiters, [$start_word, $end_word]);
4799 elsif ( ($space == $space_position) and ($space < length ($input_line) - 1))
4801 $space = $space + 1;
4802 $space_position = $space;
4804 else
4806 print "DONE\n";
4807 $finished = $TRUE;
4808 gp_message ("debugXL", $subr_name, "completed - finished = $finished");
4811 else
4813 $finished = $TRUE;
4814 $start_word = $space_position;
4815 $end_word = length ($input_line) - 1;
4816 my $keyword = substr ($input_line, $start_word, $end_word - $start_word + 1);
4817 push (@word_delimiters, [$start_word, $end_word]);
4818 if ($keyword =~ /\s+/)
4820 my $txt = "end search spaces only";
4821 gp_message ("debugXL", $subr_name, $txt);
4823 else
4825 my $txt = "end search start_word = $start_word ";
4826 $txt .= "end_word = $end_word ";
4827 $txt .= "space_position = $space_position -->$keyword<--";
4828 gp_message ("debugXL", $subr_name, $txt);
4834 for my $i (keys @word_delimiters)
4836 gp_message ("debugXL", $subr_name, "i = $i $word_delimiters[$i][0] $word_delimiters[$i][1]");
4839 return (\@word_delimiters);
4841 } #-- End of subroutine find_words_in_line
4843 #------------------------------------------------------------------------------
4844 # TBD
4845 #------------------------------------------------------------------------------
4846 sub function_info
4848 my $subr_name = get_my_name ();
4850 my ($outputdir, $FUNC_FILE, $metric, $LINUX_vDSO_ref) = @_;
4852 my %LINUX_vDSO = %{ $LINUX_vDSO_ref };
4854 my $index_val;
4855 my $address_decimal;
4856 my $full_address_field;
4858 my $FUNC_FILE_NO_PC;
4859 my $off_with_the_PC;
4861 my $blanks;
4862 my $lblanks;
4863 my $lvdso_key;
4864 my $line_regex;
4866 my %functions_per_metric_indexes = ();
4867 my %functions_per_metric_first_index = ();
4868 my @order;
4870 my ($line,$line_n,$value);
4871 my ($df_flag,$n,$u);
4872 my ($metric_value,$PC_Address,$routine);
4873 my ($is_calls,$metric_ok,$name_regex,$pc_len);
4874 my ($segment,$offset,$offy,$spaces,$rest,$not_printed,$vdso_key);
4876 #------------------------------------------------------------------------------
4877 # If the directory name does not end with a "/", add it.
4878 #------------------------------------------------------------------------------
4879 my $length_of_string = length ($outputdir);
4881 if (rindex ($outputdir, "/") != $length_of_string-1)
4883 $outputdir .= "/";
4886 gp_message ("debug", $subr_name, "on input FUNC_FILE = $FUNC_FILE metric = $metric");
4888 $is_calls = $FALSE;
4889 $metric_ok = $TRUE;
4890 $off_with_the_PC = rindex ($FUNC_FILE, "-PC");
4891 $FUNC_FILE_NO_PC = substr ($FUNC_FILE, 0, $off_with_the_PC);
4893 if ($FUNC_FILE_NO_PC eq $outputdir."calls.sort.func")
4895 $FUNC_FILE_NO_PC = $outputdir."calls";
4896 $is_calls = $TRUE;
4897 $metric_ok = $FALSE;
4899 elsif ($FUNC_FILE_NO_PC eq $outputdir."calltree.sort.func")
4901 $FUNC_FILE_NO_PC = $outputdir."calltree";
4902 $metric_ok = $FALSE;
4904 elsif ($FUNC_FILE_NO_PC eq $outputdir."functions.sort.func")
4906 $FUNC_FILE_NO_PC = $outputdir."functions.func";
4907 $metric_ok = $FALSE;
4909 gp_message ("debugM", $subr_name, "set FUNC_FILE_NO_PC = $FUNC_FILE_NO_PC");
4911 open (FUNC_FILE, "<", $FUNC_FILE)
4912 or die ("Not able to open file $FUNC_FILE for reading - '$!'");
4913 gp_message ("debug", $subr_name, "opened file FUNC_FILE = $FUNC_FILE for reading");
4915 open (FUNC_FILE_NO_PC, ">", $FUNC_FILE_NO_PC)
4916 or die ("Not able to open file $FUNC_FILE_NO_PC for writing - '$!'");
4917 gp_message ("debug", $subr_name, "opened file FUNC_FILE_NO_PC = $FUNC_FILE_NO_PC for writing");
4919 open (FUNC_FILE_REGEXP, "<", "$FUNC_FILE.name-regex")
4920 or die ("Not able to open file $FUNC_FILE.name-regex for reading - '$!'");
4921 gp_message ("debug", $subr_name, "opened file FUNC_FILE_REGEXP = $FUNC_FILE.name-regex for reading");
4923 $name_regex = <FUNC_FILE_REGEXP>;
4924 chomp ($name_regex);
4925 close (FUNC_FILE_REGEXP);
4927 gp_message ("debugXL", $subr_name, "name_regex = $name_regex");
4929 $n = 0;
4930 $u = 0;
4931 $pc_len = 0;
4933 #------------------------------------------------------------------------------
4934 # Note that the double \\ is needed here. The regex used will not have these.
4935 #------------------------------------------------------------------------------
4936 if ($is_calls)
4938 #------------------------------------------------------------------------------
4939 # TBD
4940 # I do not see the "*" in my test output, but no harm to leave the code in.
4942 # er_print * before PC for calls ! 101315
4943 #------------------------------------------------------------------------------
4944 $line_regex = "^(\\s*)(\\**)(\\S+)(:)(\\S+)(\\s+)(.*)";
4946 else
4948 $line_regex = "^(\\s*)(\\S+)(:)(\\S+)(\\s+)(.*)";
4950 gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." line_regex->$line_regex<-");
4951 gp_message ("debugXL", $subr_name, "read FUNC_FILE = $FUNC_FILE");
4953 $line_n = 0;
4954 $index_val = 0;
4955 while (<FUNC_FILE>)
4957 $line = $_;
4958 chomp ($line);
4959 $line =~ s/ -- no functions found//;
4961 gp_message ("debug", $subr_name, "FUNC_FILE: input line = $line");
4963 $line_n++;
4964 if ($line =~ /$line_regex/) # field 2|3 needs to be \S in case of -ve sign
4966 #------------------------------------------------------------------------------
4967 # A typical target line looks like this:
4968 # 11:0x001492e0 6976.900 <additional_timings> _lwp_start
4969 #------------------------------------------------------------------------------
4970 gp_message ("debugXL", $subr_name, "select = $line");
4971 if ($is_calls)
4973 $segment = $3;
4974 $offset = $5;
4975 $spaces = $6;
4976 $rest = $7;
4977 $PC_Address = $segment.$4.$offset; # PC Addr.
4978 gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$3 = $3");
4979 gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$5 = $5");
4980 gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$6 = $6");
4981 gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$7 = $7");
4983 else
4985 $segment = $2;
4986 $offset = $4;
4987 $spaces = $5;
4988 $rest = $6;
4989 $PC_Address = $segment.$3.$offset; # PC Addr.
4990 gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$2 = $2");
4991 gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$4 = $4");
4992 gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$5 = $5");
4993 gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$6 = $6");
4995 if ($segment == -1)
4997 #------------------------------------------------------------------------------
4998 # presume vDSO field overflow - er_print used an inadequate format
4999 # or the fsummary (MASTER) had the wrong format for -1?
5000 # rats - get ahead of ourselves - should not be a field abuttal so
5001 #------------------------------------------------------------------------------
5002 if ($line =~ /$name_regex/)
5004 if ($metric_ok)
5006 $metric_value = $1; # whatever
5007 $routine = $2;
5009 else
5011 $routine = $1;
5013 if ($is_calls)
5015 if (substr ($routine,0,1) eq "*")
5017 $routine = substr ($routine,1);
5020 for $vdso_key (keys %LINUX_vDSO)
5022 if ($routine eq $LINUX_vDSO{$vdso_key})
5024 #------------------------------------------------------------------------------
5025 # presume no duplicates - at least can check offset
5026 #------------------------------------------------------------------------------
5027 if ($vdso_key =~ /(\d+):(\S+)/)
5028 #------------------------------------------------------------------------------
5029 # no -ve segments allowed and not expected
5030 #------------------------------------------------------------------------------
5032 if ($2 eq $offset)
5034 #------------------------------------------------------------------------------
5035 # the real segment
5036 #------------------------------------------------------------------------------
5037 $segment = $1;
5038 gp_message ("debugXL", $subr_name, "rescued segment for $PC_Address($routine)->$segment:$offset $FUNC_FILE");
5039 $PC_Address = $segment.":".$offset; # PC Addr.
5040 gp_message ("debugXL", $subr_name, "vdso line ->$line");
5041 $line = $PC_Address.(' ' x (length ($spaces)-2)).$rest;
5042 gp_message ("debugXL", $subr_name, "becomes ->$line");
5043 last;
5049 else
5051 gp_message ("debug", $subr_name, "name_regex failure for file $FUNC_FILE");
5055 #------------------------------------------------------------------------------
5056 # a rotten exception for Linux vDSO
5057 # With a BIG "PC Address" like 32767:0x841fecd0, the functions.sort.func_PC file
5058 # can have lines like
5059 #->32767:0x841fecd0161.553 527182898954 131.936 100003 __vdso_gettimeofday<-
5060 #->32767:0x153ff810 42.460 0 0 __vdso_gettimeofday<-
5061 #->-1:0xff600000 99.040 0 0 [vsyscall]<-
5062 # (Real PC Address: 4294967295:0xff600000)
5063 #-> 4294967295:0xff600000 99.040 0 0 [vsyscall]<-
5064 #-> 9:0x00000020 49.310 0 0 <static>@0x7fff153ff600 ([vdso])<-
5065 # Rats!
5066 # $LINUX_vDSO{substr($order[$i]{"addressobjtext"},1)} = $order[$i]{"routine"};
5067 #------------------------------------------------------------------------------
5069 $not_printed = $TRUE;
5070 for $vdso_key (keys %LINUX_vDSO)
5072 if ($line =~ /^(\s*)($vdso_key)(.*)$/)
5074 $blanks = 1;
5075 $rest = 3;
5076 $lblanks = length ($blanks);
5077 $lvdso_key = length ($vdso_key);
5078 $PC_Address = $vdso_key; # PC Addr.
5079 $offy = ($lblanks+$lvdso_key < $pc_len) ? $pc_len : $lblanks+$lvdso_key;
5080 gp_message ("debugXL", $subr_name, "offy = $offy for ->$line<-");
5081 if ($pc_len)
5083 print FUNC_FILE_NO_PC substr ($line,$offy)."\n";
5084 $not_printed = $FALSE;
5086 else
5088 die ("sod1a");
5090 gp_message ("debugXL", $subr_name, "vdso line ->$line");
5091 if (substr ($line,$lblanks+$lvdso_key,1) eq " ")
5093 #------------------------------------------------------------------------------
5094 # O.K. no field abuttal
5095 #------------------------------------------------------------------------------
5096 gp_message ("debugXL", $subr_name, "vdso no field abuttal line ->$line");
5098 else
5100 gp_message ("debugXL", $subr_name, "vdso field abuttal line ->$line");
5101 $line = $blanks.$vdso_key." ".$rest;
5103 gp_message ("debugXL", $subr_name, "becomes ->$line");
5104 last;
5107 if ($not_printed)
5109 if ($pc_len)
5111 print FUNC_FILE_NO_PC substr ($line,$pc_len)."\n";
5113 else
5115 die ("sod1b");
5117 $not_printed = $FALSE;
5120 else
5122 if (!$pc_len)
5124 if ($line =~ /(^\s*PC Addr.\s+)(\S+)/)
5126 $pc_len = length ($1); # say 15
5127 print FUNC_FILE_NO_PC substr ($line,$pc_len)."\n";
5129 else
5131 print FUNC_FILE_NO_PC "$line\n";
5134 else
5136 if ($pc_len)
5138 my $strlen = length ($line);
5139 if ($strlen > 0 )
5141 print FUNC_FILE_NO_PC substr ($line,$pc_len)."\n";
5143 else
5145 print FUNC_FILE_NO_PC "\n";
5148 else
5150 die ("sod2");
5153 next;
5155 $routine = "";
5156 if ($line =~ /$name_regex/)
5158 if ($metric_ok)
5160 $metric_value = $1; # whatever
5161 $routine = $2;
5163 else
5165 $routine = $1;
5169 if ($is_calls)
5171 if (substr ($routine,0,1) eq "*")
5173 $routine = substr ($routine,1);
5176 if (length ($routine))
5178 $order[$index_val]{"routine"} = $routine;
5179 if ($metric_ok)
5181 $order[$index_val]{"metric_value"} = $metric_value;
5183 $order[$index_val]{"PC Address"} = $PC_Address;
5184 $df_flag = 0;
5185 if (not exists ($functions_per_metric_indexes{$routine}))
5187 $functions_per_metric_indexes{$routine} = [$index_val];
5189 else
5191 push (@{$functions_per_metric_indexes{$routine}},$index_val); # add $RI to list
5193 gp_message ("debugXL", $subr_name, "updated functions_per_metric_indexes $routine [$index_val] line = $line");
5194 if ($PC_Address =~ /\s*(\S+):(\S+)/)
5196 my ($segment,$offset);
5197 $segment = $1;
5198 $offset = $2;
5199 $address_decimal = bigint::hex ($offset); # decimal
5200 ## $full_address_field = '@'.$segment.":".$offset; # e.g. @2:0x0003f280
5201 $full_address_field = $segment.":".$offset; # e.g. @2:0x0003f280
5202 $order[$index_val]{"addressobj"} = $address_decimal;
5203 $order[$index_val]{"addressobjtext"} = $full_address_field;
5205 #------------------------------------------------------------------------------
5206 # Check uniqueness
5207 #------------------------------------------------------------------------------
5208 if (not exists ($functions_per_metric_first_index{$routine}{$PC_Address}))
5210 $functions_per_metric_first_index{$routine}{$PC_Address} = $index_val;
5211 $u++; #$RI
5213 else
5215 if (!($metric eq "calls" || $metric eq "calltree"))
5217 gp_message ("debug", $subr_name, "file $FUNC_FILE: function $routine already has a PC Address");
5221 $index_val++;
5222 gp_message ("debugXL", $subr_name, "updated index_val = $index_val");
5223 $n++;
5224 next;
5226 else
5228 if ($n && length ($line))
5230 my $msg = "unexpected line format in functions file $FUNC_FILE line->$line<-";
5231 gp_message ("assertion", $subr_name, $msg);
5235 close (FUNC_FILE);
5236 close (FUNC_FILE_NO_PC);
5238 for my $i (sort keys %functions_per_metric_indexes)
5240 my $values = "";
5241 for my $fields (sort keys @{ $functions_per_metric_indexes{$i} })
5243 $values .= "$functions_per_metric_indexes{$i}[$fields] ";
5245 gp_message ("debugXL", $subr_name, "on return: functions_per_metric_indexes{$i} = $values");
5248 return (\@order, \%functions_per_metric_first_index, \%functions_per_metric_indexes);
5250 } #-- End of subroutine function_info
5252 #------------------------------------------------------------------------------
5253 # Generate a html header.
5254 #------------------------------------------------------------------------------
5255 sub generate_a_header
5257 my $subr_name = get_my_name ();
5259 my ($page_text_ref, $size_text_ref, $position_text_ref) = @_;
5261 my $page_text = ${ $page_text_ref };
5262 my $size_text = ${ $size_text_ref };
5263 my $position_text = ${ $position_text_ref };
5264 my $html_header;
5266 $html_header = "<div class=\"" . $position_text . "\">\n";
5267 $html_header .= "<". $size_text . ">\n";
5268 $html_header .= $page_text . "\n";
5269 $html_header .= "</". $size_text . ">\n";
5270 $html_header .= "</div>";
5272 gp_message ("debugXL", $subr_name, "on exit page_title = $html_header");
5274 return (\$html_header);
5276 } #-- End of subroutine generate_a_header
5278 #------------------------------------------------------------------------------
5279 # Generate the caller-callee information.
5280 #------------------------------------------------------------------------------
5281 sub generate_caller_callee
5283 my $subr_name = get_my_name ();
5285 my ($number_of_metrics_ref, $function_info_ref, $function_view_structure_ref,
5286 $function_address_info_ref, $addressobjtextm_ref,
5287 $input_string_ref) = @_;
5289 my $number_of_metrics = ${ $number_of_metrics_ref };
5290 my @function_info = @{ $function_info_ref };
5291 my %function_view_structure = %{ $function_view_structure_ref };
5292 my %function_address_info = %{ $function_address_info_ref };
5293 my %addressobjtextm = %{ $addressobjtextm_ref };
5294 my $input_string = ${ $input_string_ref };
5296 my @caller_callee_data = ();
5297 my $caller_callee_data_ref;
5298 my $outfile;
5299 my $input_line;
5301 my $fullname;
5302 my $separator = "cuthere";
5304 my @address_field = ();
5305 my @fields = ();
5306 my @function_names = ();
5307 my @marker = ();
5308 my @metric_values = ();
5309 my @word_index_values = ();
5310 my @header_lines = ();
5312 my $all_metrics;
5313 my $elements_in_name;
5314 my $full_hex_address;
5315 my $hex_address;
5316 my $msg;
5318 my $remainder2;
5320 my $file_title;
5321 my $page_title;
5322 my $size_text;
5323 my $position_text;
5324 my @html_metric_sort_header = ();
5325 my $html_header;
5326 my $html_title_header;
5327 my $html_home;
5328 my $html_acknowledgement;
5329 my $html_end;
5330 my $html_line;
5332 my $marker_target_function;
5333 my $max_metrics_length = 0;
5334 my $metrics_length;
5335 my $modified_line;
5336 my $name_regex;
5337 my $no_of_fields;
5338 my $routine;
5339 my $routine_length;
5340 my $string_length;
5341 my $top_header;
5342 my $total_header_lines;
5343 my $word_index_values_ref;
5344 my $infile;
5346 my $outputdir = append_forward_slash ($input_string);
5347 my $LANG = $g_locale_settings{"LANG"};
5348 my $decimal_separator = $g_locale_settings{"decimal_separator"};
5350 gp_message ("debug", $subr_name, "decimal_separator = $decimal_separator");
5351 gp_message ("debug", $subr_name, "outputdir = $outputdir");
5353 $infile = $outputdir . "caller-callee-PC2";
5354 $outfile = $outputdir . $g_html_base_file_name{"caller_callee"} . ".html";
5356 gp_message ("debug", $subr_name, "infile = $infile outfile = $outfile");
5358 open (CALLER_CALLEE_IN, "<", $infile)
5359 or die ("unable to open caller file $infile for reading - '$!'");
5360 gp_message ("debug", $subr_name, "opened file $infile for reading");
5362 open (CALLER_CALLEE_OUT, ">", $outfile)
5363 or die ("unable to open $outfile for writing - '$!'");
5364 gp_message ("debug", $subr_name, "opened file $outfile for writing");
5366 $msg = "building caller-callee file " . $outfile;
5367 gp_message ("debug", $subr_name, $msg);
5368 gp_message ("verbose", $subr_name, $msg);
5370 #------------------------------------------------------------------------------
5371 # Generate some of the structures used in the HTML output.
5372 #------------------------------------------------------------------------------
5373 $file_title = "Caller-callee overview";
5374 $html_header = ${ create_html_header (\$file_title) };
5375 $html_home = ${ generate_home_link ("right") };
5377 $page_title = "Caller Callee View";
5378 $size_text = "h2";
5379 $position_text = "center";
5380 $html_title_header = ${ generate_a_header (\$page_title,
5381 \$size_text,
5382 \$position_text) };
5384 #------------------------------------------------------------------------------
5385 # Read all of the file into an array with the name caller_callee_data.
5386 #------------------------------------------------------------------------------
5387 chomp (@caller_callee_data = <CALLER_CALLEE_IN>);
5389 #------------------------------------------------------------------------------
5390 # Remove a legacy redundant string, if any.
5391 #------------------------------------------------------------------------------
5392 @caller_callee_data = @{ remove_redundant_string (\@caller_callee_data)};
5394 #------------------------------------------------------------------------------
5395 # Typical structure of the input file:
5397 # Current metrics: address:name:e.totalcpu:e.cycles:e+insts:e+llm
5398 # Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu )
5399 # Functions sorted by metric: Exclusive Total CPU Time
5400 # Callers and callees sorted by metric: Attributed Total CPU Time
5402 # PC Addr. Name Attr. Attr. CPU Attr. Attr.
5403 # Total Cycles Instructions Last-Level
5404 # CPU sec. sec. Executed Cache Misses
5405 # 1:0x00000000 *<Total> 3.502 4.005 15396819700 24024250
5406 # 7:0x00008070 start_thread 3.342 3.865 14500538981 23824045
5407 # 6:0x000233a0 __libc_start_main 0.160 0.140 896280719 200205
5409 # PC Addr. Name Attr. Attr. CPU Attr. Attr.
5410 # Total Cycles Instructions Last-Level
5411 # CPU sec. sec. Executed Cache Misses
5412 # 2:0x000021f9 driver_mxv 3.342 3.865 14500538981 23824045
5413 # 2:0x000021ae *mxv_core 3.342 3.865 14500538981 23824045
5414 #------------------------------------------------------------------------------
5416 #------------------------------------------------------------------------------
5417 # Scan the input file. The first lines are assumed to be part of the header,
5418 # so we store those. The diagnostic lines that echo some settings are also
5419 # stored, but currently not used.
5420 #------------------------------------------------------------------------------
5421 my $scan_header = $FALSE;
5422 my $scan_caller_callee_data = $FALSE;
5423 my $data_function_block = "";
5424 my @function_blocks = ();
5425 my $first = $TRUE;
5426 my @html_caller_callee = ();
5427 my @top_level_header = ();
5429 #------------------------------------------------------------------------------
5430 # The regexes.
5431 #------------------------------------------------------------------------------
5432 my $empty_line_regex = '^\s*$';
5433 my $line_of_interest_regex = '\s*(\d+:0x[a-fA-F0-9]+)\s+(\**)(.*)';
5434 my $get_hex_address_regex = '(\d+):0x(\S+)';
5435 my $get_metric_field_regex = ')\s+([\s\d' . $decimal_separator . ']*)';
5436 my $header_name_regex = '(.*\.)(\s+)(Name)\s+(.*)';
5437 my $sorted_by_regex = 'sorted by metric:';
5438 my $current_regex = '^Current';
5439 my $get_addr_offset_regex = '^@\d+:';
5441 #------------------------------------------------------------------------------
5442 # Get the length of the first metric field across all lines. This value is
5443 # used to pad the first metric with spaces and get the alignment right.
5445 # Scan the input data and find the line(s) with metric values. A complication
5446 # is that a function name may consists of more than one field.
5448 # Note. This part could be used to parse the other elements of the input file,
5449 # but that makes the loop very complicated. Instead, we re-scan the data
5450 # below and process each block separately.
5452 # Since this data is all in memory and relatively small, the performance should
5453 # not suffer much, but it does improve the readability of the code.
5454 #------------------------------------------------------------------------------
5455 $g_max_length_first_metric = 0;
5457 my @hex_addresses = ();
5458 my @metrics_array = ();
5459 my @length_first_metric = ();
5460 my @special_marker = ();
5461 my @the_function_name = ();
5462 my @the_metrics = ();
5464 my $find_hex_address_regex = '\s*(\d+:0x[a-fA-F0-9]+)\s+(.*)';
5465 my $find_metric_values_regex = '\)\s+\[.*\]\s+(\d+';
5466 $find_metric_values_regex .= '[\.\d\ ]*)|\)\s+(\d+[\.\d\ ]*)';
5467 my $find_marker_regex = '(^\*).*';
5469 my @html_block_prologue;
5470 my @html_code_function_block;
5471 my $marker;
5472 my $list_with_metrics;
5473 my $reduced_line;
5475 $msg = "loop over the caller-callee data - number of lines = ";
5476 $msg .= ($#caller_callee_data + 1);
5477 gp_message ("debugXL", $subr_name, $msg);
5479 for (my $line = 0; $line <= $#caller_callee_data; $line++)
5481 $input_line = $caller_callee_data[$line];
5482 $reduced_line = $input_line;
5484 $msg = "line = " . $line . " input_line = " . $input_line;
5485 gp_message ("debugXL", $subr_name, $msg);
5487 if ($input_line =~ /$find_hex_address_regex/)
5488 #------------------------------------------------------------------------------
5489 # This is an input line of interest.
5490 #------------------------------------------------------------------------------
5492 my ($hex_address_ref, $marker_ref, $reduced_line_ref,
5493 $list_with_metrics_ref) =
5494 split_function_data_line (\$input_line);
5496 $hex_address = ${ $hex_address_ref };
5497 $marker = ${ $marker_ref };
5498 $reduced_line = ${ $reduced_line_ref };
5499 $list_with_metrics = ${ $list_with_metrics_ref };
5501 $msg = "RESULT full_hex_address = " . $hex_address;
5502 $msg .= " -- metric values = " . $list_with_metrics;
5503 $msg .= " -- marker = " . $marker;
5504 $msg .= " -- function name = " . $reduced_line;
5505 gp_message ("debugXL", $subr_name, $msg);
5507 #------------------------------------------------------------------------------
5508 # Store the address and marker.
5509 #------------------------------------------------------------------------------
5510 push (@the_function_name, $reduced_line);
5511 push (@hex_addresses, $hex_address);
5512 if ($marker eq "*")
5514 push (@special_marker, "*");
5516 else
5518 push (@special_marker, "X");
5520 #------------------------------------------------------------------------------
5521 # Processing of the metrics.
5522 #------------------------------------------------------------------------------
5523 @metrics_array = split (" ", $list_with_metrics);
5525 #------------------------------------------------------------------------------
5526 # If the first metric is 0. (or 0, depending on the locale), the calculation
5527 # of the length needs to be adjusted, because 0. is really 0.000.
5529 # While we could easily add 3 to the length, we assign a symbolic value to the
5530 # first metric (ZZZ) and then compute the length. This makes things clearer.
5531 # I hope ;-)
5532 #------------------------------------------------------------------------------
5533 my $first_metric = $metrics_array[0];
5534 $msg = "first metric found = " . $first_metric;
5535 gp_message ("debugXL", $subr_name, $msg);
5536 if ($first_metric =~ /^0$decimal_separator$/)
5538 $first_metric = "0.ZZZ";
5539 $msg = "fixed up $first_metric";
5540 gp_message ("debugXL", $subr_name, $msg);
5542 $g_max_length_first_metric = max ($g_max_length_first_metric,
5543 length ($first_metric));
5545 $msg = "first_metric = $first_metric " .
5546 "g_max_length_first_metric = $g_max_length_first_metric";
5547 gp_message ("debugXL", $subr_name, $msg);
5548 push (@length_first_metric, length ($first_metric));
5549 push (@the_metrics, $list_with_metrics);
5553 $msg = "the following function names have been found";
5554 gp_message ("debugM", $subr_name, $msg);
5555 for my $i (0 .. $#the_function_name)
5557 $msg = "the_function_name{" . $i . "] = " . $the_function_name[$i];
5558 gp_message ("debugM", $subr_name, $msg);
5561 $msg = "final: g_max_length_first_metric = " . $g_max_length_first_metric;
5562 gp_message ("debugM", $subr_name, $msg);
5563 $msg = "\$#hex_addresses = " . $#hex_addresses;
5564 gp_message ("debugM", $subr_name, $msg);
5566 #------------------------------------------------------------------------------
5567 # Main loop over the input data.
5568 #------------------------------------------------------------------------------
5569 my $index_start = 0; # 1
5570 my $index_end = -1; # 0
5571 for (my $line = 0; $line <= $#caller_callee_data; $line++)
5573 $input_line = $caller_callee_data[$line];
5575 if ($input_line =~ /$header_name_regex/)
5577 $scan_header = $TRUE;
5578 $msg = "line = " . $line . " encountered start of the header";
5579 $msg .= " scan_header = " . $scan_header . " first = " . $first;
5580 gp_message ("debugXL", $subr_name, $msg);
5582 elsif (($input_line =~ /$sorted_by_regex/) or
5583 ($input_line =~ /$current_regex/))
5585 $msg = "line = " . $line . " captured top level header: " .
5586 "input_line = " . $input_line;
5587 gp_message ("debugXL", $subr_name, $msg);
5589 push (@top_level_header, $input_line);
5591 elsif ($input_line =~ /$line_of_interest_regex/)
5593 $index_end++;
5594 $scan_header = $FALSE;
5595 $scan_caller_callee_data = $TRUE;
5596 $data_function_block .= $separator . $input_line;
5598 $msg = "line = $line updated index_end = $index_end";
5599 gp_message ("debugXL", $subr_name, $msg);
5600 $msg = "line = $line input_line = " . $input_line;
5601 gp_message ("debugXL", $subr_name, $msg);
5602 $msg = "line = $line data_function_block = " . $data_function_block;
5603 gp_message ("debugXL", $subr_name, $msg);
5605 elsif (($input_line =~ /$empty_line_regex/) and
5606 ($scan_caller_callee_data))
5608 #------------------------------------------------------------------------------
5609 # An empty line is interpreted as the end of the current block and we process
5610 # this, including the generation of the html code for this block.
5611 #------------------------------------------------------------------------------
5612 $first = $FALSE;
5613 $scan_caller_callee_data = $FALSE;
5615 $msg = "new block";
5616 gp_message ("debugXL", $subr_name, $msg);
5617 $msg = "line = " . $line . " index_start = " . $index_start;
5618 gp_message ("debugXL", $subr_name, $msg);
5619 $msg = "line = " . $line . " index_end = " . $index_end;
5620 gp_message ("debugXL", $subr_name, $msg);
5622 $msg = "line = " . $line . " data_function_block = ";
5623 $msg .= $data_function_block;
5624 gp_message ("debugXL", $subr_name, $msg);
5626 push (@function_blocks, $data_function_block);
5628 ## $msg = " generating the html blocks (";
5629 ## $msg .= $index_start . " - " . $index_end .")";
5630 ## gp_message ("verbose", $subr_name, $msg);
5632 my ($html_block_prologue_ref, $html_code_function_block_ref) =
5633 generate_html_function_blocks (
5634 \$index_start,
5635 \$index_end,
5636 \@hex_addresses,
5637 \@the_metrics,
5638 \@length_first_metric,
5639 \@special_marker,
5640 \@the_function_name,
5641 \$separator,
5642 $number_of_metrics_ref,
5643 \$data_function_block,
5644 $function_info_ref,
5645 $function_view_structure_ref);
5647 @html_block_prologue = @{ $html_block_prologue_ref };
5648 @html_code_function_block = @{ $html_code_function_block_ref };
5650 for my $lines (0 .. $#html_code_function_block)
5652 $msg = "final html_code_function_block[" . $lines . "] = " .
5653 $html_code_function_block[$lines];
5654 gp_message ("debugXL", $subr_name, $msg);
5657 $data_function_block = "";
5659 push (@html_caller_callee, @html_block_prologue);
5660 push (@html_caller_callee, @header_lines);
5661 push (@html_caller_callee, @html_code_function_block);
5663 $index_start = $index_end + 1;
5664 $index_end = $index_start - 1;
5665 $msg = "line = " . $line . " reset index_start = " . $index_start;
5666 gp_message ("debugXL", $subr_name, $msg);
5667 $msg = "line = " . $line . " reset index_end = " . $index_end;
5668 gp_message ("debugXL", $subr_name, $msg);
5671 #------------------------------------------------------------------------------
5672 # Only capture the first header. They are all identical.
5673 #------------------------------------------------------------------------------
5674 if ($scan_header and $first)
5676 if (defined ($4))
5678 #------------------------------------------------------------------------------
5679 # This group is only defined for the first line of the header.
5680 #------------------------------------------------------------------------------
5681 gp_message ("debugXL", $subr_name, "header1 = $4");
5682 gp_message ("debugXL", $subr_name, "extra = $3 spaces=x$2x");
5683 my $newline = "<b>" . $4 . "</b>";
5684 push (@header_lines, $newline);
5686 elsif ($input_line =~ /\s*(.*)/)
5688 #------------------------------------------------------------------------------
5689 # Capture the subsequent header lines.
5690 #------------------------------------------------------------------------------
5691 gp_message ("debugXL", $subr_name, "headern = $1");
5692 my $newline = "<b>" . $1 . "</b>";
5693 push (@header_lines, $newline);
5699 for my $i (0 .. $#header_lines)
5701 gp_message ("debugXL", $subr_name, "header_lines[$i] = $header_lines[$i]");
5703 for my $i (0 .. $#function_blocks)
5705 gp_message ("debugXL", $subr_name, "function_blocks[$i] = $function_blocks[$i]");
5708 my $number_of_blocks = $#function_blocks + 1;
5709 gp_message ("debugXL", $subr_name, "There are " . $number_of_blocks . " function blocks:");
5711 for my $i (0 .. $#function_blocks)
5713 #------------------------------------------------------------------------------
5714 # The split produces an empty first field and is why we skip the first field.
5715 #------------------------------------------------------------------------------
5716 ## my @entries = split ("cuthere", $function_blocks[$i]);
5717 my @entries = split ($separator, $function_blocks[$i]);
5718 for my $k (1 .. $#entries)
5720 my $msg = "entries[" . $k . "] = ". $entries[$k];
5721 gp_message ("debugXL", $subr_name, $k . $msg);
5725 #------------------------------------------------------------------------------
5726 # Parse and process the individual function blocks.
5727 #------------------------------------------------------------------------------
5728 $msg = "Parse and process function blocks - total blocks = ";
5729 $msg .= $#function_blocks + 1;
5730 gp_message ("verbose", $subr_name, $msg);
5732 for my $i (0 .. $#function_blocks)
5734 $msg = "process function block " . $i;
5735 gp_message ("debugXL", $subr_name, $msg);
5737 $msg = "function_blocks[" . $i . "] = ". $function_blocks[$i];
5738 gp_message ("debugXL", $subr_name, $msg);
5739 #------------------------------------------------------------------------------
5740 # This split produces an empty first field. This is why we skip this in the
5741 # loop below.
5742 #------------------------------------------------------------------------------
5743 my @entries = split ($separator, $function_blocks[$i]);
5745 #------------------------------------------------------------------------------
5746 # An example of the content of array @entries:
5747 # <empty line>
5748 # 6:0x0003ad20 drand48 0.100 0.084 768240570 0
5749 # 6:0x0003af50 *erand48_r 0.080 0.084 768240570 0
5750 # 6:0x0003b160 __drand48_iterate 0.020 0. 0 0
5751 #------------------------------------------------------------------------------
5752 for my $k (1 .. $#entries)
5754 my $input_line = $entries[$k];
5756 $msg = "input_line = entries[" . $k . "] = ". $entries[$k];
5757 gp_message ("debugXL", $subr_name, $msg);
5759 my ($hex_address_ref, $marker_ref, $reduced_line_ref,
5760 $list_with_metrics_ref) =
5761 split_function_data_line (\$input_line);
5763 $full_hex_address = ${ $hex_address_ref };
5764 $marker_target_function = ${ $marker_ref };
5765 $routine = ${ $reduced_line_ref };
5766 $all_metrics = ${ $list_with_metrics_ref };
5768 $msg = "RESULT full_hex_address = " . $full_hex_address;
5769 $msg .= " -- metric values = " . $all_metrics;
5770 $msg .= " -- marker = " . $marker_target_function;
5771 $msg .= " -- function name = " . $routine;
5772 gp_message ("debugXL", $subr_name, $msg);
5774 $metrics_length = length ($all_metrics);
5775 $max_metrics_length = max ($max_metrics_length, $metrics_length);
5777 if ($full_hex_address =~ /(\d+):0x(\S+)/)
5779 $hex_address = "0x" . $2;
5781 push (@marker, $marker_target_function);
5783 push (@address_field, $hex_address);
5784 push (@address_field, $full_hex_address);
5785 $msg = "pushed " . $full_hex_address;
5786 $msg .= " to array address_field";
5787 gp_message ("debugXL", $subr_name, $msg);
5789 $modified_line = $all_metrics . " " . $routine;
5790 gp_message ("debugXL", $subr_name, "xxxxxxx = $modified_line");
5792 push (@metric_values, $all_metrics);
5793 $msg = "pushed " . $all_metrics . " to array metric_values";
5794 gp_message ("debugXL", $subr_name, $msg);
5796 push (@function_names, $routine);
5797 $msg = "pushed " . $routine . " to array function_names";
5798 gp_message ("debugXL", $subr_name, $msg);
5801 $total_header_lines = $#header_lines + 1;
5802 $msg = "total_header_lines = " . $total_header_lines;
5803 gp_message ("debugXL", $subr_name, $msg);
5805 gp_message ("debugXL", $subr_name, "Final output");
5806 for my $i (keys @header_lines)
5808 gp_message ("debugXL", $subr_name, "$header_lines[$i]");
5810 for my $i (0 .. $#function_names)
5812 $msg = $metric_values[$i] . " " . $marker[$i];
5813 $msg .= $function_names[$i] . " (" . $address_field[$i] . ")";
5814 gp_message ("debugXL", $subr_name, $msg);
5816 #------------------------------------------------------------------------------
5817 # Check if this function has multiple occurrences.
5818 # TBD: Replace by the function call for this.
5819 #------------------------------------------------------------------------------
5820 $msg = "check for multiple occurrences - function_names = ";
5821 $msg .= ($#function_names + 1);
5822 gp_message ("debugXL", $subr_name, $msg);
5824 for my $i (0 .. $#function_names)
5826 my $current_address = $address_field[$i];
5827 my $found_a_match;
5828 my $ref_index;
5829 my $alt_name;
5830 my $addr_offset;
5832 $routine = $function_names[$i];
5833 $alt_name = $routine;
5834 gp_message ("debugXL", $subr_name, "checking for routine = $routine");
5835 if (exists ($g_multi_count_function{$routine}))
5837 #------------------------------------------------------------------------------
5838 # TBD: Scan all of the function_info list. Or beter: add index to
5839 # g_multi_count_function.
5840 #------------------------------------------------------------------------------
5842 $found_a_match = $FALSE;
5844 $msg = $routine . ": occurrences = ";
5845 $msg .= $g_function_occurrences{$routine};
5846 gp_message ("debugXL", $subr_name, $msg);
5848 for my $ref (keys @{ $g_map_function_to_index{$routine} })
5850 $ref_index = $g_map_function_to_index{$routine}[$ref];
5852 $msg = $routine . ": retrieving duplicate entry at ";
5853 $msg .= "ref_index = " . $ref_index;
5854 gp_message ("debugXL", $subr_name, $msg);
5855 $msg = $routine . ": function_info[" . $ref_index;
5856 $msg .= "]{alt_name} = ";
5857 $msg .= $function_info[$ref_index]{'alt_name'};
5858 gp_message ("debugXL", $subr_name, $msg);
5860 $addr_offset = $function_info[$ref_index]{"addressobjtext"};
5861 $msg = $routine . ": addr_offset = " . $addr_offset;
5862 gp_message ("debugXL", $subr_name, $msg);
5864 $addr_offset =~ s/$get_addr_offset_regex//;
5865 $msg = $routine . ": addr_offset = " . $addr_offset;
5866 gp_message ("debugXL", $subr_name, $msg);
5868 if ($addr_offset eq $current_address)
5870 $found_a_match = $TRUE;
5871 last;
5874 $msg = $function_info[$ref_index]{'alt_name'};
5875 $msg .= " is the actual function for i = " . $i . " ";
5876 $msg .= $found_a_match;
5877 gp_message ("debugXL", $subr_name, $msg);
5879 $alt_name = $function_info[$ref_index]{'alt_name'};
5881 gp_message ("debugXL", $subr_name, "alt_name = $alt_name");
5883 $msg = "completed the check for multiple occurrences";
5884 gp_message ("debugXL", $subr_name, $msg);
5886 #------------------------------------------------------------------------------
5887 # Figure out the column width. Since the columns in the header may include
5888 # spaces, we use the first line with metrics for this.
5889 #------------------------------------------------------------------------------
5890 my $top_header = $metric_values[0];
5891 my $word_index_values_ref = find_words_in_line (\$top_header);
5892 my @word_index_values = @{ $word_index_values_ref };
5894 # $i = 0 0 4
5895 # $i = 1 10 14
5896 # $i = 2 21 31
5897 # $i = 3 35 42
5898 for my $i (keys @word_index_values)
5900 $msg = "i = " . $i . " " . $word_index_values[$i][0] . " ";
5901 $msg .= $word_index_values[$i][1];
5902 gp_message ("debugXL", $subr_name, $msg);
5905 #------------------------------------------------------------------------------
5906 # Empty the buffers before processing the next block with data.
5907 #------------------------------------------------------------------------------
5908 @function_names = ();
5909 @metric_values = ();
5910 @address_field = ();
5911 @marker = ();
5913 $msg = "erased contents of arrays function_names, metric_values, ";
5914 $msg .= "address_field, and marker";
5915 gp_message ("debugXL", $subr_name, $msg);
5919 push (@html_metric_sort_header, "<i>");
5920 for my $i (0 .. $#top_level_header)
5922 $html_line = $top_level_header[$i] . "<br>";
5923 push (@html_metric_sort_header, $html_line);
5925 push (@html_metric_sort_header, "</i>");
5927 print CALLER_CALLEE_OUT $html_header;
5928 print CALLER_CALLEE_OUT $html_home;
5929 print CALLER_CALLEE_OUT $html_title_header;
5930 print CALLER_CALLEE_OUT "$_" for @g_html_experiment_stats;
5931 ## print CALLER_CALLEE_OUT "<br>\n";
5932 ## print CALLER_CALLEE_OUT "$_\n" for @html_metric_sort_header;
5933 print CALLER_CALLEE_OUT "<pre>\n";
5934 print CALLER_CALLEE_OUT "$_\n" for @html_caller_callee;
5935 print CALLER_CALLEE_OUT "</pre>\n";
5937 #------------------------------------------------------------------------------
5938 # Get the acknowledgement, return to main link, and final html statements.
5939 #------------------------------------------------------------------------------
5940 $html_home = ${ generate_home_link ("left") };
5941 $html_acknowledgement = ${ create_html_credits () };
5942 $html_end = ${ terminate_html_document () };
5944 print CALLER_CALLEE_OUT $html_home;
5945 print CALLER_CALLEE_OUT "<br>\n";
5946 print CALLER_CALLEE_OUT $html_acknowledgement;
5947 print CALLER_CALLEE_OUT $html_end;
5949 close (CALLER_CALLEE_OUT);
5951 $msg = "the caller-callee information has been generated";
5952 gp_message ("verbose", $subr_name, $msg);
5954 return (0);
5956 } #-- End of subroutine generate_caller_callee
5958 #------------------------------------------------------------------------------
5959 # Generate the html version of the disassembly file.
5961 # Note to self (TBD)
5962 # https://community.intel.com/t5/Intel-oneAPI-AI-Analytics/bd-p/ai-analytics-toolkit
5963 #------------------------------------------------------------------------------
5964 sub generate_dis_html
5966 my $subr_name = get_my_name ();
5968 my ($target_function_ref, $number_of_metrics_ref, $function_info_ref,
5969 $function_address_and_index_ref, $outputdir_ref, $func_ref,
5970 $source_line_ref, $metric_ref, $addressobj_index_ref) = @_;
5972 my $target_function = ${ $target_function_ref };
5973 my $number_of_metrics = ${ $number_of_metrics_ref };
5974 my @function_info = @{ $function_info_ref };
5975 my %function_address_and_index = %{ $function_address_and_index_ref };
5976 my $outputdir = ${ $outputdir_ref };
5977 my $func = ${ $func_ref };
5978 my @source_line = @{ $source_line_ref };
5979 my @metric = @{ $metric_ref };
5980 my %addressobj_index = %{ $addressobj_index_ref };
5982 my $dec_instruction_start;
5983 my $dec_instruction_end;
5984 my $hex_instruction_start;
5985 my $hex_instruction_end;
5987 my @colour_line = ();
5988 my $hot_line;
5989 my $metric_values;
5990 my $src_line;
5991 my $dec_instr_address;
5992 my $instruction;
5993 my $operands;
5995 my $html_new_line = "<br>";
5996 my $add_new_line_before;
5997 my $add_new_line_after;
5998 my $address_key;
5999 my $boldface;
6000 my $file;
6001 my $filename = $func;
6002 my $func_name;
6003 my $orig_hex_instr_address;
6004 my $hex_instr_address;
6005 my $index_string;
6006 my $input_metric;
6007 my $linenumber;
6008 my $name;
6009 my $last_address;
6010 my $last_address_in_hex;
6012 my $file_title;
6013 my $html_header;
6014 my $html_home;
6015 my $html_end;
6017 my $branch_regex = $g_arch_specific_settings{"regex"};
6018 my $convert_to_dot = $g_locale_settings{"convert_to_dot"};
6019 my $decimal_separator = $g_locale_settings{"decimal_separator"};
6020 my $hp_value = $g_user_settings{"highlight_percentage"}{"current_value"};
6021 my $linksubexp = $g_arch_specific_settings{"linksubexp"};
6022 my $subexp = $g_arch_specific_settings{"subexp"};
6024 my $file_is_empty;
6026 my %branch_target = ();
6027 my %branch_target_no_ref = ();
6028 my @disassembly_file = ();
6029 my %extended_branch_target = ();
6030 my %inverse_branch_target = ();
6031 my @metrics = ();
6032 my @modified_html = ();
6034 my $branch_target_ref;
6035 my $extended_branch_target_ref;
6036 my $branch_target_no_ref_ref;
6038 my $branch_address;
6039 my $dec_branch_address;
6040 my $found_it;
6041 my $found_it_ref;
6042 my $func_name_in_dis_file;
6043 my $hex_branch_target;
6044 my $instruction_address;
6045 my $instruction_offset;
6046 my $link;
6047 my $modified_line;
6048 my $raw_hex_branch_target;
6049 my $src_line_ref;
6050 my $threshold_line;
6051 my $html_dis_out = $func . ".html";
6053 #------------------------------------------------------------------------------
6054 # The regex section.
6055 #------------------------------------------------------------------------------
6056 my $call_regex = '.*([0-9a-fA-F]*):\s+(call)\s*0x([0-9a-fA-F]+)';
6057 my $line_of_interest_regex = '^#*\s+([\d' . $decimal_separator . '\s+]+)\[\s*(\d+|\?)\]';
6058 my $white_space_regex = '\s+';
6059 my $first_integer_regex = '^\d+$';
6060 my $integer_regex = '\d+';
6061 my $qmark_regex = '\?';
6062 my $src_regex = '(\s*)(\d+)\.(.*)';
6063 my $function_regex = '^(\s*)<Function:\s(.*)>';
6064 my $end_src_header_regex = "(^\\s+)(\\d+)\\.\\s+(.*)";
6065 my $end_dis_header_regex = "(^\\s+)(<Function: )(.*)>";
6066 my $control_flow_1_regex = 'j[a-z]+';
6067 my $control_flow_2_regex = 'call';
6068 my $control_flow_3_regex = 'ret';
6070 ## my $function_call_regex2 = '(.*)\s+([0-9a-fA-F]*):\s+(call)\s*0x([0-9a-fA-F]+)\s*';
6071 ## my $endbr_regex = '\.*([0-9a-fA-F]*):\s+(endbr[32|64])';
6072 #------------------------------------------------------------------------------
6073 # Dynamic. Computed below.
6075 # TBD: Try to move these up.
6076 #------------------------------------------------------------------------------
6077 my $dis_regex;
6078 my $metric_regex;
6080 gp_message ("debug", $subr_name, "g_branch_regex = $g_branch_regex");
6081 gp_message ("debug", $subr_name, "call_regex = $call_regex");
6082 gp_message ("debug", $subr_name, "g_function_call_v2_regex = $g_function_call_v2_regex");
6084 my $the_title = set_title ($function_info_ref, $func, "disassembly");
6086 gp_message ("debug", $subr_name, "the_title = $the_title");
6088 $file_title = $the_title;
6089 $html_header = ${ create_html_header (\$file_title) };
6090 $html_home = ${ generate_home_link ("right") };
6092 push (@modified_html, $html_header);
6093 push (@modified_html, $html_home);
6094 push (@modified_html, "<pre>");
6096 #------------------------------------------------------------------------------
6097 # Open the input and output files.
6098 #------------------------------------------------------------------------------
6099 open (INPUT_DISASSEMBLY, "<", $filename)
6100 or die ("$subr_name - unable to open disassembly file $filename for reading: '$!'");
6101 gp_message ("debug", $subr_name , "opened file $filename for reading");
6103 open (HTML_OUTPUT, ">", $html_dis_out)
6104 or die ("$subr_name - unable to open file $html_dis_out for writing: '$!'");
6105 gp_message ("debug", $subr_name , "opened file $html_dis_out for writing");
6107 #------------------------------------------------------------------------------
6108 # Check if the file is empty
6109 #------------------------------------------------------------------------------
6110 $file_is_empty = is_file_empty ($filename);
6111 if ($file_is_empty)
6114 #------------------------------------------------------------------------------
6115 # The input file is empty. Write a message in the html file and exit.
6116 #------------------------------------------------------------------------------
6117 gp_message ("debug", $subr_name ,"file $filename is empty");
6119 my $comment = "No disassembly generated by $tool_name - file $filename is empty";
6120 my $gp_error_file = $outputdir . "gp-listings.err";
6122 my $html_empty_file_ref = html_text_empty_file (\$comment, \$gp_error_file);
6123 my @html_empty_file = @{ $html_empty_file_ref };
6125 print HTML_OUTPUT "$_\n" for @html_empty_file;
6127 close (HTML_OUTPUT);
6129 return (\@source_line);
6131 else
6134 #------------------------------------------------------------------------------
6135 # Read the file into memory.
6136 #------------------------------------------------------------------------------
6137 chomp (@disassembly_file = <INPUT_DISASSEMBLY>);
6138 gp_message ("debug", $subr_name ,"read file $filename into memory");
6141 my $max_length_first_metric = 0;
6142 my $src_line_no;
6144 #------------------------------------------------------------------------------
6145 # First scan through the assembly listing.
6146 #------------------------------------------------------------------------------
6147 for (my $line_no=0; $line_no <= $#disassembly_file; $line_no++)
6149 my $input_line = $disassembly_file[$line_no];
6150 gp_message ("debugXL", $subr_name, "[line $line_no] $input_line");
6152 if ($input_line =~ /$line_of_interest_regex/)
6155 #------------------------------------------------------------------------------
6156 # Found a matching line. Examples are:
6157 # 0.370 [37] 4021d1: addsd %xmm0,%xmm1
6158 # ## 1.001 [36] 4021d5: add $0x1,%rax
6159 #------------------------------------------------------------------------------
6160 gp_message ("debugXL", $subr_name, "selected line \$1 = $1 \$2 = $2");
6162 if (defined ($2) and defined($1))
6164 @metrics = split (/$white_space_regex/ ,$1);
6165 $src_line_no = $2;
6167 else
6169 my $msg = "$input_line has an unexpected format";
6170 gp_message ("assertion", $subr_name, $msg);
6173 #------------------------------------------------------------------------------
6174 # Compute the maximum length of the first metric and pad the field from the
6175 # left later on. The fractional part is ignored.
6176 #------------------------------------------------------------------------------
6177 my $first_metric = $metrics[0];
6178 my $new_length;
6179 if ($first_metric =~ /$first_integer_regex/)
6181 $new_length = length ($first_metric);
6183 else
6185 my @fields = split (/$decimal_separator/, $first_metric);
6186 $new_length = length ($fields[0]);
6188 $max_length_first_metric = max ($max_length_first_metric, $new_length);
6189 my $msg;
6190 $msg = "first_metric = $first_metric " .
6191 "max_length_first_metric = $max_length_first_metric";
6192 gp_message ("debugXL", $subr_name, $msg);
6194 if ($src_line_no !~ /$qmark_regex/)
6195 #------------------------------------------------------------------------------
6196 # The source code line number is known and is stored.
6197 #------------------------------------------------------------------------------
6199 $source_line[$line_no] = $src_line_no;
6200 my $msg;
6201 $msg = "found an instruction with a source line ref:";
6202 $msg .= " source_line[$line_no] = $source_line[$line_no]";
6203 gp_message ("debugXL", $subr_name, $msg);
6206 #------------------------------------------------------------------------------
6207 # Check for function calls. If found, get the address offset from $4 and
6208 # compute the target address.
6209 #------------------------------------------------------------------------------
6210 ($found_it_ref, $branch_target_ref, $extended_branch_target_ref) =
6211 check_and_proc_dis_func_call (
6212 \$input_line,
6213 \$line_no,
6214 \%branch_target,
6215 \%extended_branch_target);
6216 $found_it = ${ $found_it_ref };
6218 if ($found_it)
6220 %branch_target = %{ $branch_target_ref };
6221 %extended_branch_target = %{ $extended_branch_target_ref };
6224 #------------------------------------------------------------------------------
6225 # Look for a branch instruction, or the special endbr32/endbr64 instruction
6226 # that is also considered to be a branch target. Note that the latter is x86
6227 # specific.
6228 #------------------------------------------------------------------------------
6229 ($found_it_ref, $branch_target_ref, $extended_branch_target_ref,
6230 $branch_target_no_ref_ref) = check_and_proc_dis_branches (
6231 \$input_line,
6232 \$line_no,
6233 \%branch_target,
6234 \%extended_branch_target,
6235 \%branch_target_no_ref);
6236 $found_it = ${ $found_it_ref };
6238 if ($found_it)
6240 %branch_target = %{ $branch_target_ref };
6241 %extended_branch_target = %{ $extended_branch_target_ref };
6242 %branch_target_no_ref = %{ $branch_target_no_ref_ref };
6245 } #-- End of loop over line_no
6247 %inverse_branch_target = reverse (%extended_branch_target);
6249 gp_message ("debug", $subr_name, "generated inverse of branch target structure");
6250 gp_message ("debug", $subr_name, "completed parsing file $filename");
6252 for my $key (sort keys %branch_target)
6254 gp_message ("debug", $subr_name, "branch_target{$key} = $branch_target{$key}");
6256 for my $key (sort keys %extended_branch_target)
6258 gp_message ("debug", $subr_name, "extended_branch_target{$key} = $extended_branch_target{$key}");
6260 for my $key (sort keys %inverse_branch_target)
6262 gp_message ("debug", $subr_name, "inverse_branch_target{$key} = $inverse_branch_target{$key}");
6264 for my $key (sort keys %branch_target_no_ref)
6266 gp_message ("debug", $subr_name, "branch_target_no_ref{$key} = $branch_target_no_ref{$key}");
6267 $inverse_branch_target{$key} = $key;
6269 for my $key (sort keys %inverse_branch_target)
6271 gp_message ("debug", $subr_name, "inverse_branch_target{$key} = $inverse_branch_target{$key}");
6274 #------------------------------------------------------------------------------
6275 # Process the disassembly.
6276 #------------------------------------------------------------------------------
6278 #------------------------------------------------------------------------------
6279 # Dynamically generate the regexes.
6280 #------------------------------------------------------------------------------
6281 $metric_regex = '';
6282 for my $metric_used (1 .. $number_of_metrics)
6284 $metric_regex .= '(\d+' . $decimal_separator . '*\d*)\s+';
6287 $dis_regex = '^(#{2}|\s{2})\s+';
6288 $dis_regex .= '(.*)';
6289 ## $dis_regex .= '\[\s*([0-9?]+)\]\s+([0-9a-fA-F]+):\s+([a-z0-9]+)\s+(.*)';
6290 $dis_regex .= '\[\s*([0-9?]+)\]\s+([0-9a-fA-F]+):\s+([a-z0-9]+)(.*)';
6292 gp_message ("debugXL", $subr_name, "metric_regex = $metric_regex");
6293 gp_message ("debugXL", $subr_name, "dis_regex = $dis_regex");
6294 gp_message ("debugXL", $subr_name, "src_regex = $src_regex");
6295 gp_message ("debugXL", $subr_name, "contents of lines array");
6297 #------------------------------------------------------------------------------
6298 # Identify the header lines. Make the minimal assumptions.
6300 # In both cases, the first line after the header has whitespace. This is
6301 # followed by:
6303 # - A source line file has "<line_no>."
6304 # - A dissasembly file has "<Function:"
6306 # These are the characteristics we use below.
6307 #------------------------------------------------------------------------------
6308 for (my $line_no=0; $line_no <= $#disassembly_file; $line_no++)
6310 my $input_line = $disassembly_file[$line_no];
6311 gp_message ("debugXL", $subr_name, "[line $line_no] $input_line");
6313 if ($input_line =~ /$end_src_header_regex/)
6315 gp_message ("debugXL", $subr_name, "header time is over - hit source line\n");
6316 gp_message ("debugXL", $subr_name, "$1 $2 $3\n");
6317 last;
6319 if ($input_line =~ /$end_dis_header_regex/)
6321 gp_message ("debugXL", $subr_name, "header time is over - hit disassembly line\n");
6322 last;
6324 push (@modified_html, "<i>" . $input_line . "</i>");
6326 my $line_index = scalar (@modified_html);
6327 gp_message ("debugXL", $subr_name, "final line_index = $line_index");
6329 for (my $line_no=0; $line_no <= $line_index-1; $line_no++)
6331 my $msg = " modified_html[$line_no] = $modified_html[$line_no]";
6332 gp_message ("debugXL", $subr_name, $msg);
6335 #------------------------------------------------------------------------------
6336 # Source line:
6337 # 20. for (int64_t r=0; r<repeat_count; r++) {
6339 # Disassembly:
6340 # 0.340 [37] 401fec: addsd %xmm0,%xmm1
6341 # ## 1.311 [36] 401ff0: addq $1,%rax
6342 #------------------------------------------------------------------------------
6344 #------------------------------------------------------------------------------
6345 # Find the hot PCs and store them.
6346 #------------------------------------------------------------------------------
6347 my @hot_program_counters = ();
6348 my @transposed_hot_pc = ();
6349 my @max_metric_values = ();
6351 gp_message ("debug", $subr_name, "determine the maximum metric values");
6352 for (my $line_no=$line_index-1; $line_no <= $#disassembly_file; $line_no++)
6354 my $input_line = $disassembly_file[$line_no];
6356 if ( $input_line =~ /$dis_regex/ )
6358 ## if ( defined ($1) and defined ($2) and defined ($3) and
6359 ## defined ($4) and defined ($5) and defined ($6) )
6360 if ( defined ($1) and defined ($2) and defined ($3) and
6361 defined ($4) and defined ($5) )
6363 $hot_line = $1;
6364 $metric_values = $2;
6365 $src_line = $3;
6366 $dec_instr_address = bigint::hex ($4);
6367 $instruction = $5;
6368 if (defined ($6))
6370 my $white_space_regex = '\s*';
6371 $operands = $6;
6372 $operands =~ s/$white_space_regex//;
6375 if ($hot_line eq "##")
6377 my @metrics = split (" ", $metric_values);
6378 push (@hot_program_counters, [@metrics]);
6383 for my $row (keys @hot_program_counters)
6385 my $msg = "$filename row[" . $row . "] =";
6386 for my $col (keys @{$hot_program_counters[$row]})
6388 $msg .= " $hot_program_counters[$row][$col]";
6389 $transposed_hot_pc[$col][$row] = $hot_program_counters[$row][$col];
6391 gp_message ("debugXL", $subr_name, "hot PC = $msg");
6393 for my $row (keys @transposed_hot_pc)
6395 my $msg = "$filename row[" . $row . "] =";
6396 for my $col (keys @{$transposed_hot_pc[$row]})
6398 $msg .= " $transposed_hot_pc[$row][$col]";
6400 gp_message ("debugXL", $subr_name, "$filename transposed = $msg");
6402 #------------------------------------------------------------------------------
6403 # Get the maximum metric values and if integer, convert to floating-point.
6404 # Since it is easier, we transpose the array and access it over the columns.
6405 #------------------------------------------------------------------------------
6406 for my $row (0 .. $#transposed_hot_pc)
6408 my $max_val = 0;
6409 for my $col (0 .. $#{$transposed_hot_pc[$row]})
6411 $max_val = max ($transposed_hot_pc[$row][$col], $max_val);
6413 if ($max_val =~ /$integer_regex/)
6415 $max_val = sprintf ("%f", $max_val);
6417 gp_message ("debugXL", $subr_name, "$filename row = $row max_val = $max_val");
6418 push (@max_metric_values, $max_val);
6421 for my $metric (0 .. $#max_metric_values)
6423 my $msg = "$filename maximum[$metric] = $max_metric_values[$metric]";
6424 gp_message ("debugM", $subr_name, $msg);
6427 #------------------------------------------------------------------------------
6428 # TBD - Integrate this better.
6430 # Scan the instructions to find the instruction address range. This is used
6431 # to determine if a branch is external to this function.
6432 #------------------------------------------------------------------------------
6433 $dec_instruction_start = undef;
6434 $dec_instruction_end = undef;
6435 for (my $line_no=$line_index-1; $line_no <= $#disassembly_file; $line_no++)
6437 my $input_line = $disassembly_file[$line_no];
6438 if ( $input_line =~ /$dis_regex/ )
6440 # if ( defined ($1) and defined ($2) and defined ($3) and
6441 ## defined ($4) and defined ($5) and defined ($6) )
6442 if ( defined ($1) and defined ($2) and defined ($3) and
6443 defined ($4) and defined ($5) )
6445 $hot_line = $1;
6446 $metric_values = $2;
6447 $src_line = $3;
6448 $dec_instr_address = bigint::hex ($4);
6449 $instruction = $5;
6450 ## $operands = $6;
6451 if (defined ($6))
6453 my $white_space_regex = '\s*';
6454 $operands = $6;
6455 $operands =~ s/$white_space_regex//;
6458 if (defined ($dec_instruction_start))
6460 if ($dec_instr_address < $dec_instruction_start)
6462 $dec_instruction_start = $dec_instr_address;
6465 else
6467 $dec_instruction_start = $dec_instr_address;
6469 if (defined ($dec_instruction_end))
6471 if ($dec_instr_address > $dec_instruction_end)
6473 $dec_instruction_end = $dec_instr_address;
6476 else
6478 $dec_instruction_end = $dec_instr_address;
6484 if (defined ($dec_instruction_start) and defined ($dec_instruction_end))
6486 $hex_instruction_start = sprintf ("%x", $dec_instruction_start);
6487 $hex_instruction_end = sprintf ("%x", $dec_instruction_end);
6489 my $msg;
6490 $msg = "$filename $func dec_instruction_start = " .
6491 "$dec_instruction_start (0x$hex_instruction_start)";
6492 gp_message ("debugXL", $subr_name, $msg);
6493 $msg = "$filename $func dec_instruction_end = " .
6494 "$dec_instruction_end (0x$hex_instruction_end)";
6495 gp_message ("debugXL", $subr_name, $msg);
6498 #------------------------------------------------------------------------------
6499 # This is where all the results from above come together.
6500 #------------------------------------------------------------------------------
6501 for (my $line_no=$line_index-1; $line_no <= $#disassembly_file; $line_no++)
6503 my $input_line = $disassembly_file[$line_no];
6504 gp_message ("debugXL", $subr_name, "input_line[$line_no] = $input_line");
6505 if ( $input_line =~ /$dis_regex/ )
6507 gp_message ("debugXL", $subr_name, "found a disassembly line: $input_line");
6509 if ( defined ($1) and defined ($2) and defined ($3) and
6510 defined ($4) and defined ($5) )
6512 # $branch_target{$hex_branch_target} = 1;
6513 # $extended_branch_target{$instruction_address} = $raw_hex_branch_target;
6514 $hot_line = $1;
6515 $metric_values = $2;
6516 $src_line = $3;
6517 $orig_hex_instr_address = $4;
6518 $instruction = $5;
6519 ## $operands = $6;
6521 my $msg = "disassembly line: $1 $2 $3 $4 $5";
6522 if (defined ($6))
6524 $msg .= " \$6 = $6";
6525 my $white_space_regex = '\s*';
6526 $operands = $6;
6527 $operands =~ s/$white_space_regex//;
6529 gp_message ("debugXL", $subr_name, $msg);
6531 #------------------------------------------------------------------------------
6532 # Pad the line with the metrics to ensure correct alignment.
6533 #------------------------------------------------------------------------------
6534 my $the_length;
6535 my @split_metrics = split (" ", $metric_values);
6536 my $first_metric = $split_metrics[0];
6537 ## if ($first_metric =~ /^\d+$/)
6538 if ($first_metric =~ /$first_integer_regex/)
6540 $the_length = length ($first_metric);
6542 else
6544 my @fields = split (/$decimal_separator/, $first_metric);
6545 $the_length = length ($fields[0]);
6547 my $spaces = $max_length_first_metric - $the_length;
6548 my $pad = "";
6549 for my $p (1 .. $spaces)
6551 $pad .= "&nbsp;";
6553 $metric_values = $pad . $metric_values;
6554 gp_message ("debugXL", $subr_name, "pad = $pad");
6555 gp_message ("debugXL", $subr_name, "metric_values = $metric_values");
6557 #------------------------------------------------------------------------------
6558 # Since the instruction address variable may change and because we need the
6559 # original address without html controls, we use a new variable for the
6560 # (potentially) modified address.
6561 #------------------------------------------------------------------------------
6562 $hex_instr_address = $orig_hex_instr_address;
6563 $add_new_line_before = $FALSE;
6564 $add_new_line_after = $FALSE;
6566 if ($src_line eq "?")
6568 #------------------------------------------------------------------------------
6569 # There is no source line number. Do not add a link.
6570 #------------------------------------------------------------------------------
6572 $modified_line = $hot_line . ' ' . $metric_values . ' [' . $src_line . '] ';
6573 gp_message ("debugXL", $subr_name, "initialized modified_line = $modified_line");
6575 else
6577 #------------------------------------------------------------------------------
6578 # There is a source line number. Mark it as link.
6579 #------------------------------------------------------------------------------
6580 $src_line_ref = "[<a href='#line_".$src_line."'>".$src_line."</a>]";
6581 gp_message ("debugXL", $subr_name, "src_line_ref = $src_line_ref");
6582 gp_message ("debugXL", $subr_name, "hex_instr_address = $hex_instr_address");
6584 $modified_line = $hot_line . ' ' . $metric_values . ' ' . $src_line_ref . ' ';
6585 gp_message ("debugXL", $subr_name, "initialized modified_line = $modified_line");
6588 #------------------------------------------------------------------------------
6589 # Mark control flow instructions. Several cases need to be distinguished.
6591 # In all cases we give the instruction a specific color, mark it boldface
6592 # and add a new-line after the instruction
6593 #------------------------------------------------------------------------------
6594 if ( ($instruction =~ /$control_flow_1_regex/) or
6595 ($instruction =~ /$control_flow_2_regex/) or
6596 ($instruction =~ /$control_flow_3_regex/) )
6598 gp_message ("debugXL", $subr_name, "instruction = $instruction is a control flow instruction");
6600 $add_new_line_after = $TRUE;
6602 $boldface = $TRUE;
6603 $instruction = color_string ($instruction, $boldface, $g_html_color_scheme{"control_flow"});
6606 if (exists ($extended_branch_target{$hex_instr_address}))
6607 #------------------------------------------------------------------------------
6608 # This is a branch instruction and we need to add the target address.
6610 # In case the target address is outside of this load object, the link is
6611 # colored differently.
6613 # TBD: Add the name and if possible, a working link to this code.
6614 #------------------------------------------------------------------------------
6616 $branch_address = $extended_branch_target{$hex_instr_address};
6618 $dec_branch_address = bigint::hex ($branch_address);
6620 if ( ($dec_branch_address >= $dec_instruction_start) and
6621 ($dec_branch_address <= $dec_instruction_end) )
6622 #------------------------------------------------------------------------------
6623 # The instruction is within the range.
6624 #------------------------------------------------------------------------------
6626 $link = "[ <a href='#".$branch_address."'>".$branch_address."</a> ]";
6628 else
6630 #------------------------------------------------------------------------------
6631 # The instruction is outside of the range. Change the color of the link.
6632 #------------------------------------------------------------------------------
6633 gp_message ("debugXL", $subr_name, "address is outside of range");
6635 $link = "[ <a href='#".$branch_address;
6636 $link .= "' style='color:$g_html_color_scheme{'link_outside_range'}'>";
6637 $link .= $branch_address."</a> ]";
6639 gp_message ("debugXL", $subr_name, "address exists new link = $link");
6641 $operands .= ' ' . $link;
6642 gp_message ("debugXL", $subr_name, "update #1 modified_line = $modified_line");
6644 if (exists ($branch_target_no_ref{$hex_instr_address}))
6646 gp_message ("debugXL", $subr_name, "NEWBR branch_target_no_ref{$hex_instr_address} = $branch_target_no_ref{$hex_instr_address}");
6648 ## if (exists ($inverse_branch_target{$hex_instr_address}) or
6649 ## exists ($branch_target_no_ref{$hex_instr_address}))
6650 if (exists ($inverse_branch_target{$hex_instr_address}))
6651 #------------------------------------------------------------------------------
6652 # This is a target address and we need to define the instruction address to be
6653 # a label.
6654 #------------------------------------------------------------------------------
6656 $add_new_line_before = $TRUE;
6658 my $branch_target = $inverse_branch_target{$hex_instr_address};
6659 my $target = "<a name='".$hex_instr_address."'><b>".$hex_instr_address."</b></a>:";
6660 gp_message ("debugXL", $subr_name, "inverse exists - hex_instr_address = $hex_instr_address");
6661 gp_message ("debugXL", $subr_name, "inverse exists - add a target target = $target");
6663 $hex_instr_address = "<a name='".$hex_instr_address."'><b>".$hex_instr_address."</b></a>";
6664 gp_message ("debugXL", $subr_name, "update #2 hex_instr_address = $hex_instr_address");
6665 gp_message ("debugXL", $subr_name, "update #2 modified_line = $modified_line");
6668 $modified_line .= $hex_instr_address . ': ' . $instruction . ' ' . $operands;
6670 gp_message ("debugXL", $subr_name, "final modified_line = $modified_line");
6672 #------------------------------------------------------------------------------
6673 # This is a control flow instruction, but it is the last one and we do not
6674 # want to add a newline.
6675 #------------------------------------------------------------------------------
6676 gp_message ("debugXL", $subr_name, "decide where the <br> should go in the html");
6677 gp_message ("debugXL", $subr_name, "add_new_line_after = $add_new_line_after");
6678 gp_message ("debugXL", $subr_name, "add_new_line_before = $add_new_line_before");
6680 if ( $add_new_line_after and ($orig_hex_instr_address eq $hex_instruction_end) )
6682 $add_new_line_after = $FALSE;
6683 gp_message ("debugXL", $subr_name, "$instruction is the last instruction - do not add a newline");
6686 if ($add_new_line_before)
6689 #------------------------------------------------------------------------------
6690 # Get the previous line, if any, so that we can check what it is.
6691 #------------------------------------------------------------------------------
6692 my $prev_line = pop (@modified_html);
6693 if ( defined ($prev_line) )
6695 gp_message ("debugXL", $subr_name, "prev_line = $prev_line");
6697 #------------------------------------------------------------------------------
6698 # Restore the previously popped line.
6699 #------------------------------------------------------------------------------
6700 push (@modified_html, $prev_line);
6701 if ($prev_line ne $html_new_line)
6703 gp_message ("debugXL", $subr_name, "add_new_line_before = $add_new_line_before pushed $html_new_line");
6704 #------------------------------------------------------------------------------
6705 # There is no new-line yet, so add it.
6706 #------------------------------------------------------------------------------
6707 push (@modified_html, $html_new_line);
6709 else
6711 #------------------------------------------------------------------------------
6712 # It was a new-line, so do nothing and continue.
6713 #------------------------------------------------------------------------------
6714 gp_message ("debugXL", $subr_name, "need to restore $html_new_line");
6718 #------------------------------------------------------------------------------
6719 # Add the newly created line.
6720 #------------------------------------------------------------------------------
6722 if ($hot_line eq "##")
6723 #------------------------------------------------------------------------------
6724 # Highlight the most expensive line.
6725 #------------------------------------------------------------------------------
6727 $modified_line = set_background_color_string (
6728 $modified_line,
6729 $g_html_color_scheme{"background_color_hot"});
6731 #------------------------------------------------------------------------------
6732 # Sub-highlight the lines close enough to the hot line.
6733 #------------------------------------------------------------------------------
6734 else
6736 my @current_metrics = split (" ", $metric_values);
6737 for my $metric (0 .. $#current_metrics)
6739 my $current_value;
6740 my $max_value;
6741 $current_value = $current_metrics[$metric];
6742 #------------------------------------------------------------------------------
6743 # As part of the padding process, non-breaking spaces may have been inserted
6744 # in an earlier phase. Temporarily remove these to make sure that the maximum
6745 # metric values can be computed.
6746 #------------------------------------------------------------------------------
6747 $current_value =~ s/&nbsp;//g;
6748 if (exists ($max_metric_values[$metric]))
6750 $max_value = $max_metric_values[$metric];
6751 gp_message ("debugXL", $subr_name, "metric = $metric current_value = $current_value max_value = $max_value");
6752 if ( ($max_value > 0) and ($current_value > 0) and ($current_value != $max_value) )
6754 # TBD: abs needed?
6755 gp_message ("debugXL", $subr_name, "metric = $metric current_value = $current_value max_value = $max_value");
6756 my $relative_distance = 1.00 - abs ( ($max_value - $current_value)/$max_value );
6757 gp_message ("debugXL", $subr_name, "relative_distance = $relative_distance");
6758 if (($hp_value > 0) and ($relative_distance >= $hp_value/100.0))
6760 gp_message ("debugXL", $subr_name, "metric $metric is within the relative_distance");
6761 gp_message ("debugXL", $subr_name, "change bg modified_line = $modified_line");
6762 $modified_line = set_background_color_string (
6763 $modified_line,
6764 $g_html_color_scheme{"background_color_lukewarm"});
6765 last;
6772 ## my @max_metric_values = ();
6773 push (@modified_html, $modified_line);
6774 if ($add_new_line_after)
6776 gp_message ("debugXL", $subr_name, "add_new_line_after = $add_new_line_after pushed $html_new_line");
6777 push (@modified_html, $html_new_line);
6781 else
6783 my $msg = "parsing line $input_line";
6784 gp_message ("assertion", $subr_name, $msg);
6787 elsif ( $input_line =~ /$src_regex/ )
6789 if ( defined ($1) and defined ($2) )
6791 ####### BUG?
6792 gp_message ("debugXL", $subr_name, "found a source code line: $input_line");
6793 gp_message ("debugXL", $subr_name, "\$1 = $1");
6794 gp_message ("debugXL", $subr_name, "\$2 = $2");
6795 gp_message ("debugXL", $subr_name, "\$3 = $3");
6796 my $blanks = $1;
6797 my $src_line = $2;
6798 my $src_code = $3;
6800 #------------------------------------------------------------------------------
6801 # We need to replace the "<" symbol in the code by "&lt;".
6802 #------------------------------------------------------------------------------
6803 $src_code =~ s/$g_less_than_regex/$g_html_less_than_regex/g;
6805 my $target = "<a name='line_".$src_line."'>".$src_line.".</a>";
6806 gp_message ("debugXL", $subr_name, "src target = $target $src_code");
6808 my $modified_line = $blanks . $target . $src_code;
6809 gp_message ("debugXL", $subr_name, "modified_line = $modified_line");
6810 push (@modified_html, $modified_line);
6812 else
6814 my $msg = "parsing line $input_line";
6815 gp_message ("assertion", $subr_name, $msg);
6818 elsif ( $input_line =~ /$function_regex/ )
6820 my $html_name;
6821 if (defined ($1) and defined ($2))
6823 $func_name_in_dis_file = $2;
6824 my $spaces = $1;
6825 my $boldface = $TRUE;
6826 gp_message ("debugXL", $subr_name, "function_name = $2");
6827 my $function_line = "&lt;Function: " . $func_name_in_dis_file . ">";
6829 ##### HACK
6831 if ($func_name_in_dis_file eq $target_function)
6833 my $color_function_name = color_string (
6834 $function_line,
6835 $boldface,
6836 $g_html_color_scheme{"target_function_name"});
6837 my $label = "<a id=\"" . $g_function_tag_id{$target_function} . "\"></a>";
6838 $html_name = $label . $spaces . "<i>" . $color_function_name . "</i>";
6840 else
6842 my $color_function_name = color_string (
6843 $function_line,
6844 $boldface,
6845 $g_html_color_scheme{"non_target_function_name"});
6846 $html_name = "<i>" . $spaces . $color_function_name . "</i>";
6848 push (@modified_html, $html_name);
6850 else
6852 my $msg = "parsing line $input_line";
6853 gp_message ("assertion", $subr_name, $msg);
6858 #------------------------------------------------------------------------------
6859 # Add an extra line with diagnostics.
6861 # TBD: The same is done in process_source but should be done only once.
6862 #------------------------------------------------------------------------------
6863 if ($hp_value > 0)
6865 my $rounded_percentage = sprintf ("%.1f", $hp_value);
6866 $threshold_line = "<i>The setting for the highlight percentage";
6867 $threshold_line .= " (--highlight-percentage) option:";
6868 $threshold_line .= " " . $rounded_percentage . " (%)</i>";
6870 else
6872 $threshold_line = "<i>The highlight percentage feature has not been";
6873 $threshold_line .= " enabled</i>";
6876 $html_home = ${ generate_home_link ("left") };
6877 $html_end = ${ terminate_html_document () };
6879 push (@modified_html, "</pre>");
6880 push (@modified_html, $html_new_line);
6881 push (@modified_html, $threshold_line);
6882 push (@modified_html, $html_home);
6883 push (@modified_html, $html_new_line);
6884 push (@modified_html, $g_html_credits_line);
6885 push (@modified_html, $html_end);
6887 for my $i (0 .. $#modified_html)
6889 gp_message ("debugXL", $subr_name, "[$i] -> $modified_html[$i]");
6892 for my $i (0 .. $#modified_html)
6894 print HTML_OUTPUT "$modified_html[$i]" . "\n";
6897 close (HTML_OUTPUT);
6898 close (INPUT_DISASSEMBLY);
6900 gp_message ("debug", $subr_name, "output is in file $html_dis_out");
6901 gp_message ("debug", $subr_name ,"completed processing disassembly");
6903 undef %branch_target;
6904 undef %extended_branch_target;
6905 undef %inverse_branch_target;
6907 return (\@source_line, \@metric);
6909 } #-- End of subroutine generate_dis_html
6911 #------------------------------------------------------------------------------
6912 # Generate all the function level information.
6913 #------------------------------------------------------------------------------
6914 sub generate_function_level_info
6916 my $subr_name = get_my_name ();
6918 my ($exp_dir_list_ref, $call_metrics, $summary_metrics, $input_string,
6919 $sort_fields_ref) = @_;
6921 my @exp_dir_list = @{ $exp_dir_list_ref };
6922 my @sort_fields = @{ $sort_fields_ref };
6924 my $expr_name;
6925 my $first_metric;
6926 my $gp_display_text_cmd;
6927 my $gp_functions_cmd;
6928 my $ignore_value;
6929 my $msg;
6930 my $script_pc_metrics;
6932 my $outputdir = append_forward_slash ($input_string);
6934 my $script_file_PC = $outputdir."gp-script-PC";
6935 my $result_file = $outputdir."gp-out-PC.err";
6936 my $gp_error_file = $outputdir."gp-out-PC.err";
6937 my $func_limit = $g_user_settings{func_limit}{current_value};
6939 #------------------------------------------------------------------------------
6940 # The number of entries in the Function Overview includes <Total>, but that is
6941 # not a concern to the user and we add "1" to compensate for this.
6942 #------------------------------------------------------------------------------
6943 $func_limit += 1;
6945 gp_message ("debug", $subr_name, "increased the local value for func_limit = $func_limit");
6947 $expr_name = join (" ", @exp_dir_list);
6949 gp_message ("debug", $subr_name, "expr_name = $expr_name");
6951 for my $i (0 .. $#sort_fields)
6953 gp_message ("debug", $subr_name, "sort_fields[$i] = $sort_fields[$i]");
6956 # Ruud $count = 0;
6958 gp_message ("debug", $subr_name, "calling $GP_DISPLAY_TEXT to get function information files");
6960 open (SCRIPT_PC, ">", $script_file_PC)
6961 or die ("$subr_name - unable to open script file $script_file_PC for writing: '$!'");
6962 gp_message ("debug", $subr_name, "opened file $script_file_PC for writing");
6964 #------------------------------------------------------------------------------
6965 # Get the list of functions.
6966 #------------------------------------------------------------------------------
6968 #------------------------------------------------------------------------------
6969 # Get the first metric.
6970 #------------------------------------------------------------------------------
6971 $summary_metrics =~ /^([^:]+)/;
6972 $first_metric = $1;
6973 $g_first_metric = $1;
6974 $script_pc_metrics = "address:$summary_metrics";
6976 gp_message ("debugXL", $subr_name, "$func_limit");
6977 gp_message ("debugXL", $subr_name, "$summary_metrics");
6978 gp_message ("debugXL", $subr_name, "$first_metric");
6979 gp_message ("debugXL", $subr_name, "$script_pc_metrics");
6981 # Temporarily disabled print SCRIPT_PC "# limit $func_limit\n";
6982 # Temporarily disabled print SCRIPT_PC "limit $func_limit\n";
6983 print SCRIPT_PC "# thread_select all\n";
6984 print SCRIPT_PC "thread_select all\n";
6986 #------------------------------------------------------------------------------
6987 # Empty header.
6988 # TBD: Is still needed? Also, add the header command.
6989 #------------------------------------------------------------------------------
6990 print SCRIPT_PC "# outfile $outputdir"."header\n";
6991 print SCRIPT_PC "outfile $outputdir"."header\n";
6993 #------------------------------------------------------------------------------
6994 # Else the output from the next line goes to last sort.func
6995 #------------------------------------------------------------------------------
6996 print SCRIPT_PC "# outfile $outputdir"."gp-metrics-functions-PC\n";
6997 print SCRIPT_PC "outfile $outputdir"."gp-metrics-functions-PC\n";
6998 print SCRIPT_PC "# metrics $script_pc_metrics\n";
6999 print SCRIPT_PC "metrics $script_pc_metrics\n";
7000 #------------------------------------------------------------------------------
7001 # Not really sorted
7002 #------------------------------------------------------------------------------
7003 print SCRIPT_PC "# outfile $outputdir"."functions.sort.func-PC\n";
7004 print SCRIPT_PC "outfile $outputdir"."functions.sort.func-PC\n";
7005 print SCRIPT_PC "# functions\n";
7006 print SCRIPT_PC "functions\n";
7008 print SCRIPT_PC "# outfile $outputdir"."functions.sort.func-PC2\n";
7009 print SCRIPT_PC "outfile $outputdir"."functions.sort.func-PC2\n";
7010 print SCRIPT_PC "# metrics address:name:$summary_metrics\n";
7011 print SCRIPT_PC "metrics address:name:$summary_metrics\n";
7012 print SCRIPT_PC "# sort $first_metric\n";
7013 print SCRIPT_PC "sort $first_metric\n";
7014 print SCRIPT_PC "# functions\n";
7015 print SCRIPT_PC "functions\n";
7016 #------------------------------------------------------------------------------
7017 # Go through all the possible metrics and sort by each of them.
7018 #------------------------------------------------------------------------------
7019 for my $field (@sort_fields)
7021 gp_message ("debug", $subr_name, "sort_fields field = $field");
7022 #------------------------------------------------------------------------------
7023 # Else the output from the next line goes to last sort.func
7024 #------------------------------------------------------------------------------
7025 print SCRIPT_PC "# outfile $outputdir"."gp-metrics-".$field."-PC\n";
7026 print SCRIPT_PC "outfile $outputdir"."gp-metrics-".$field."-PC\n";
7027 print SCRIPT_PC "# metrics $script_pc_metrics\n";
7028 print SCRIPT_PC "metrics $script_pc_metrics\n";
7029 print SCRIPT_PC "# outfile $outputdir".$field.".sort.func-PC\n";
7030 print SCRIPT_PC "outfile $outputdir".$field.".sort.func-PC\n";
7031 print SCRIPT_PC "# sort $field\n";
7032 print SCRIPT_PC "sort $field\n";
7033 print SCRIPT_PC "# functions\n";
7034 print SCRIPT_PC "functions\n";
7036 print SCRIPT_PC "# metrics address:name:$summary_metrics\n";
7037 print SCRIPT_PC "metrics address:name:$summary_metrics\n";
7038 print SCRIPT_PC "# outfile $outputdir".$field.".sort.func-PC2\n";
7039 print SCRIPT_PC "outfile $outputdir".$field.".sort.func-PC2\n";
7040 print SCRIPT_PC "# sort $field\n";
7041 print SCRIPT_PC "sort $field\n";
7042 print SCRIPT_PC "# functions\n";
7043 print SCRIPT_PC "functions\n";
7046 #------------------------------------------------------------------------------
7047 # Get caller-callee list
7048 #------------------------------------------------------------------------------
7049 print SCRIPT_PC "# outfile " . $outputdir."caller-callee-PC2\n";
7050 print SCRIPT_PC "outfile " . $outputdir."caller-callee-PC2\n";
7051 print SCRIPT_PC "# metrics address:name:$summary_metrics\n";
7052 print SCRIPT_PC "metrics address:name:$summary_metrics\n";
7053 print SCRIPT_PC "# callers-callees\n";
7054 print SCRIPT_PC "callers-callees\n";
7055 #------------------------------------------------------------------------------
7056 # Else the output from the next line goes to last sort.func
7057 #------------------------------------------------------------------------------
7058 print SCRIPT_PC "# outfile $outputdir"."gp-metrics-calls-PC\n";
7059 print SCRIPT_PC "outfile $outputdir"."gp-metrics-calls-PC\n";
7060 #------------------------------------------------------------------------------
7061 # TBD: fix the situation that call_metrics is empty.
7062 #------------------------------------------------------------------------------
7063 if ($call_metrics ne "")
7065 $script_pc_metrics = "address:$call_metrics";
7067 else
7069 $script_pc_metrics = "address";
7070 $msg = "warning: call_metrics is empty - only address field printed";
7071 gp_message ("debug", $subr_name, $msg);
7073 print SCRIPT_PC "# metrics $script_pc_metrics\n";
7074 print SCRIPT_PC "metrics $script_pc_metrics\n";
7076 #------------------------------------------------------------------------------
7077 # Not really sorted
7078 #------------------------------------------------------------------------------
7079 print SCRIPT_PC "# outfile $outputdir"."calls.sort.func-PC\n";
7080 print SCRIPT_PC "outfile $outputdir"."calls.sort.func-PC\n";
7082 #------------------------------------------------------------------------------
7083 # Get caller-callee list
7084 #------------------------------------------------------------------------------
7085 print SCRIPT_PC "# callers-callees\n";
7086 print SCRIPT_PC "callers-callees\n";
7088 #------------------------------------------------------------------------------
7089 # Else the output from the next line goes to last sort.func
7090 #------------------------------------------------------------------------------
7091 print SCRIPT_PC "# outfile $outputdir"."gp-metrics-calltree-PC\n";
7092 print SCRIPT_PC "outfile $outputdir"."gp-metrics-calltree-PC\n";
7093 print SCRIPT_PC "# metrics $script_pc_metrics\n";
7094 print SCRIPT_PC "metrics $script_pc_metrics\n";
7096 if ($g_user_settings{"calltree"}{"current_value"} eq "on")
7098 gp_message ("verbose", $subr_name, "Generate the file with the calltree information");
7099 #------------------------------------------------------------------------------
7100 # Get calltree list
7101 #------------------------------------------------------------------------------
7102 print SCRIPT_PC "# outfile $outputdir"."calltree.sort.func-PC\n";
7103 print SCRIPT_PC "outfile $outputdir"."calltree.sort.func-PC\n";
7104 print SCRIPT_PC "# calltree\n";
7105 print SCRIPT_PC "calltree\n";
7108 #------------------------------------------------------------------------------
7109 # Get the default set of metrics
7110 #------------------------------------------------------------------------------
7111 my $full_metrics_ref;
7112 my $all_metrics;
7113 my $full_function_view = $outputdir . "functions.full";
7115 $full_metrics_ref = get_all_the_metrics (\$expr_name, \$outputdir);
7117 $all_metrics = "address:name:";
7118 $all_metrics .= ${$full_metrics_ref};
7119 gp_message ("debug", $subr_name, "all_metrics = $all_metrics");
7120 #------------------------------------------------------------------------------
7121 # Get the name, address, and full overview of all metrics for all functions
7122 #------------------------------------------------------------------------------
7123 print SCRIPT_PC "# limit 0\n";
7124 print SCRIPT_PC "limit 0\n";
7125 print SCRIPT_PC "# metrics $all_metrics\n";
7126 print SCRIPT_PC "metrics $all_metrics\n";
7127 print SCRIPT_PC "# thread_select all\n";
7128 print SCRIPT_PC "thread_select all\n";
7129 print SCRIPT_PC "# sort default\n";
7130 print SCRIPT_PC "sort default\n";
7131 print SCRIPT_PC "# outfile $full_function_view\n";
7132 print SCRIPT_PC "outfile $full_function_view\n";
7133 print SCRIPT_PC "# functions\n";
7134 print SCRIPT_PC "functions\n";
7136 close (SCRIPT_PC);
7138 $result_file = $outputdir."gp-out-PC.err";
7139 $gp_error_file = $outputdir.$g_gp_error_logfile;
7141 $gp_functions_cmd = "$GP_DISPLAY_TEXT -limit $func_limit ";
7142 $gp_functions_cmd .= "-viewmode machine -compare off ";
7143 $gp_functions_cmd .= "-script $script_file_PC $expr_name";
7145 gp_message ("debug", $subr_name, "calling $GP_DISPLAY_TEXT to get function level information");
7147 $gp_display_text_cmd = "$gp_functions_cmd 1> $result_file 2>> $gp_error_file";
7149 gp_message ("debugXL", $subr_name,"cmd = $gp_display_text_cmd");
7151 my ($error_code, $cmd_output) = execute_system_cmd ($gp_display_text_cmd);
7153 if ($error_code != 0)
7155 $ignore_value = msg_display_text_failure ($gp_display_text_cmd,
7156 $error_code,
7157 $gp_error_file);
7158 gp_message ("abort", $subr_name, "execution terminated");
7161 #------------------------------------------------------------------------------
7162 # Parse the full function view and store the data.
7163 #------------------------------------------------------------------------------
7164 my @input_data = ();
7165 my $empty_line_regex = '^\s*$';
7167 ## my $full_function_view = $outputdir . "functions.full";
7169 open (ALL_FUNC_DATA, "<", $full_function_view)
7170 or die ("$subr_name - unable to open output file $full_function_view for reading '$!'");
7171 gp_message ("debug", $subr_name, "opened file $full_function_view for reading");
7173 chomp (@input_data = <ALL_FUNC_DATA>);
7175 my $start_scanning = $FALSE;
7176 for (my $line = 0; $line <= $#input_data; $line++)
7178 my $input_line = $input_data[$line];
7180 $input_line =~ s/ -- no functions found//;
7181 $input_data[$line] =~ s/ -- no functions found//;
7183 $msg = "line = " . $line . " input_line = " . $input_line;
7184 gp_message ("debugXL", $subr_name, $msg);
7186 # if ($input_line =~ /^<Total>\s+.*/)
7187 if ($input_line =~ /\s*(\d+:0x[a-fA-F0-9]+)\s+(\S+)\s+(.*)/)
7189 $start_scanning = $TRUE;
7191 elsif ($input_line =~ /$empty_line_regex/)
7193 $start_scanning = $FALSE;
7196 if ($start_scanning)
7198 gp_message ("debugXL", $subr_name, "$line: $input_data[$line]");
7200 push (@g_full_function_view_table, $input_data[$line]);
7202 my $hex_address;
7203 my $full_hex_address = $1;
7204 my $routine = $2;
7205 my $all_metrics = $3;
7206 if ($full_hex_address =~ /(\d+):0x(\S+)/)
7208 $hex_address = "0x" . $2;
7210 $g_function_view_all{$routine}{"hex_address"} = $hex_address;
7211 $g_function_view_all{$routine}{"all_metrics"} = $all_metrics;
7215 for my $i (keys %g_function_view_all)
7217 gp_message ("debugXL", $subr_name, "key = $i $g_function_view_all{$i}{'hex_address'} $g_function_view_all{$i}{'all_metrics'}");
7220 for my $i (keys @g_full_function_view_table)
7222 gp_message ("debugXL", $subr_name, "g_full_function_view_table[$i] = $i $g_full_function_view_table[$i]");
7225 return ($script_pc_metrics);
7227 } #-- End of subroutine generate_function_level_info
7229 #------------------------------------------------------------------------------
7230 # Generate all the files needed for the function view.
7231 #------------------------------------------------------------------------------
7232 sub generate_function_view
7234 my $subr_name = get_my_name ();
7236 my ($directory_name_ref, $summary_metrics_ref, $number_of_metrics_ref,
7237 $function_info_ref, $function_view_structure_ref, $function_address_info_ref,
7238 $sort_fields_ref, $exp_dir_list_ref, $addressobjtextm_ref) = @_;
7240 my $directory_name = ${ $directory_name_ref };
7241 my @function_info = @{ $function_info_ref };
7242 my %function_view_structure = %{ $function_view_structure_ref };
7243 my $summary_metrics = ${ $summary_metrics_ref };
7244 my $number_of_metrics = ${ $number_of_metrics_ref };
7245 my %function_address_info = %{ $function_address_info_ref };
7246 my @sort_fields = @{ $sort_fields_ref };
7247 my @exp_dir_list = @{ $exp_dir_list_ref };
7248 my %addressobjtextm = %{ $addressobjtextm_ref };
7250 my @abs_path_exp_dirs = ();
7251 my @experiment_directories;
7253 my $target_function;
7254 my $html_line;
7255 my $ftag;
7256 my $routine_length;
7257 my %html_source_functions = ();
7259 my $href_link;
7260 my $infile;
7261 my $input_experiments;
7262 my $keep_value;
7263 my $loadobj;
7264 my $address_field;
7265 my $address_offset;
7266 my $msg;
7267 my $exe;
7268 my $extra_field;
7269 my $new_target_function;
7270 my $file_title;
7271 my $html_output_file;
7272 my $html_function_view;
7273 my $overview_file;
7274 my $exp_name;
7275 my $exp_type;
7276 my $html_header;
7277 my $routine;
7278 my $length_header;
7279 my $length_metrics;
7280 my $full_index_line;
7281 my $acknowledgement;
7282 my @full_function_view_line = ();
7283 my $spaces;
7284 my $size_text;
7285 my $position_text;
7286 my $html_first_metric_file;
7287 my $html_new_line = "<br>";
7288 my $html_acknowledgement;
7289 my $html_end;
7290 my $html_home;
7291 my $page_title;
7292 my $html_title_header;
7294 my $outputdir = append_forward_slash ($directory_name);
7295 my $LANG = $g_locale_settings{"LANG"};
7296 my $decimal_separator = $g_locale_settings{"decimal_separator"};
7298 $input_experiments = join (", ", @exp_dir_list);
7300 for my $i (0 .. $#exp_dir_list)
7302 my $dir = get_basename ($exp_dir_list[$i]);
7303 push @abs_path_exp_dirs, $dir;
7305 $input_experiments = join (", ", @abs_path_exp_dirs);
7307 gp_message ("debug", $subr_name, "input_experiments = $input_experiments");
7309 #------------------------------------------------------------------------------
7310 # TBD: This should be done only once and much earlier.
7311 #------------------------------------------------------------------------------
7312 @experiment_directories = split (",", $input_experiments);
7314 #------------------------------------------------------------------------------
7315 # For every function in the function overview, set up an html structure with
7316 # the various hyperlinks.
7317 #------------------------------------------------------------------------------
7319 #------------------------------------------------------------------------------
7320 # Core loop that generates an HTML line for each function.
7321 #------------------------------------------------------------------------------
7322 my $top_of_table = $FALSE;
7323 for my $i (0 .. $#function_info)
7325 if (defined ($function_info[$i]{"alt_name"}))
7327 $target_function = $function_info[$i]{"alt_name"};
7329 else
7331 my $msg = "function_info[$i]{\"alt_name\"} is not defined";
7332 gp_message ("assertion", $subr_name, $msg);
7335 $html_source_functions{$target_function} = $function_info[$i]{"html function block"};
7338 for my $i (sort keys %html_source_functions)
7340 gp_message ("debugXL", $subr_name, "html_source_functions{$i} = $html_source_functions{$i}");
7343 $file_title = "Function view for experiments " . $input_experiments;
7345 #------------------------------------------------------------------------------
7346 # Example input file:
7348 # Current metrics: address:name:e.totalcpu:e.cycles:e+insts:e+llm
7349 # Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu )
7350 # Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu )
7351 # Functions sorted by metric: Exclusive Total CPU Time
7353 # PC Addr. Name Excl. Excl. CPU Excl. Excl.
7354 # Total Cycles Instructions Last-Level
7355 # CPU sec. sec. Executed Cache Misses
7356 # 1:0x00000000 <Total> 3.502 4.005 15396819700 24024250
7357 # 2:0x000021ae mxv_core 3.342 3.865 14500538981 23824045
7358 # 6:0x0003af50 erand48_r 0.080 0.084 768240570 0
7359 # 2:0x00001f7b init_data 0.040 0.028 64020043 200205
7360 # 6:0x0003b160 __drand48_iterate 0.020 0. 0 0
7361 # ...
7362 #------------------------------------------------------------------------------
7364 for my $metric (@sort_fields)
7366 $overview_file = $outputdir . $metric . ".sort.func-PC2";
7368 $exp_type = $metric;
7370 if ($metric eq "functions")
7372 $html_function_view .= $g_html_base_file_name{"function_view"} . ".html";
7374 else
7376 $html_function_view = $g_html_base_file_name{"function_view"} . "." . $metric . ".html";
7378 #------------------------------------------------------------------------------
7379 # The default function view is based upon the first metric in the list. We use
7380 # this file in the index.html file.
7381 #------------------------------------------------------------------------------
7382 if ($metric eq $g_first_metric)
7384 $html_first_metric_file = $html_function_view;
7385 my $txt = "g_first_metric = $g_first_metric ";
7386 $txt .= "html_first_metric_file = $html_first_metric_file";
7387 gp_message ("debugXL", $subr_name, $txt);
7390 $html_output_file = $outputdir . $html_function_view;
7392 open (FUNCTION_VIEW, ">", $html_output_file)
7393 or die ("$subr_name - unable to open file $html_output_file for writing - '$!'");
7394 gp_message ("debug", $subr_name, "opened file $html_output_file for writing");
7396 $html_home = ${ generate_home_link ("right") };
7397 $html_header = ${ create_html_header (\$file_title) };
7399 $page_title = "Function View";
7400 $size_text = "h2";
7401 $position_text = "center";
7402 $html_title_header = ${ generate_a_header (\$page_title, \$size_text, \$position_text) };
7404 print FUNCTION_VIEW $html_header;
7405 print FUNCTION_VIEW $html_home;
7406 print FUNCTION_VIEW $html_title_header;
7407 print FUNCTION_VIEW "$_" for @g_html_experiment_stats;
7408 print FUNCTION_VIEW $html_new_line . "\n";
7410 my $function_view_structure_ref = process_function_overview (
7411 \$metric,
7412 \$exp_type,
7413 \$summary_metrics,
7414 \$number_of_metrics,
7415 \@function_info,
7416 \%function_view_structure,
7417 \$overview_file);
7419 my %function_view_structure = %{ $function_view_structure_ref };
7421 #------------------------------------------------------------------------------
7422 # Core part: extract the true function name and find the html code for it.
7423 #------------------------------------------------------------------------------
7424 gp_message ("debugXL", $subr_name, "the final table");
7426 print FUNCTION_VIEW "<pre>\n";
7427 print FUNCTION_VIEW "$_\n" for @{ $function_view_structure{"header"} };
7429 my $max_length_header = $function_view_structure{"max header length"};
7430 my $max_length_metrics = $function_view_structure{"max metrics length"};
7432 #------------------------------------------------------------------------------
7433 # Add 4 more spaces for the distance to the function names. Purely cosmetic.
7434 #------------------------------------------------------------------------------
7435 my $pad = max ($max_length_metrics, $max_length_header) + 4;
7436 my $spaces = "";
7437 for my $i (1 .. $pad)
7439 $spaces .= "&nbsp;";
7442 #------------------------------------------------------------------------------
7443 # Add extra space for the /blank/*/ marker!
7444 #------------------------------------------------------------------------------
7445 $spaces .= "&nbsp;";
7446 my $func_header = $spaces . $function_view_structure{"table name"};
7447 gp_message ("debugXL", $subr_name, "func_header = " . $func_header);
7449 print FUNCTION_VIEW $spaces . "<b>" .
7450 $function_view_structure{"table name"} .
7451 "</b>" . $html_new_line . "\n";
7453 #------------------------------------------------------------------------------
7454 # If the header is longer than the metrics, add spaces to padd the difference.
7455 # Also add the same 4 spaces between the metric values and the function name.
7456 #------------------------------------------------------------------------------
7457 $pad = 0;
7458 if ($max_length_header > $max_length_metrics)
7460 $pad = $max_length_header - $max_length_metrics;
7462 $pad += 4;
7463 $spaces = "";
7464 for my $i (1 .. $pad)
7466 $spaces .= "&nbsp;";
7469 #------------------------------------------------------------------------------
7470 # This is where it literally all comes together. The metrics and function
7471 # parts are combined.
7472 #------------------------------------------------------------------------------
7473 ## for my $i (keys @{ $function_view_structure{"function table"} })
7474 for my $i (0 .. $#{ $function_view_structure{"function table"} })
7476 my $p1 = $function_view_structure{"metrics part"}[$i];
7477 my $p2 = $function_view_structure{"function table"}[$i];
7479 $full_index_line = $p1 . $spaces . $p2;
7481 push (@full_function_view_line, $full_index_line);
7484 print FUNCTION_VIEW "$_\n" for @full_function_view_line;
7486 #------------------------------------------------------------------------------
7487 # Clear the array before filling it up again.
7488 #------------------------------------------------------------------------------
7489 @full_function_view_line = ();
7491 #------------------------------------------------------------------------------
7492 # Get the acknowledgement, return to main link, and final html statements.
7493 #------------------------------------------------------------------------------
7494 $html_home = ${ generate_home_link ("left") };
7495 $html_acknowledgement = ${ create_html_credits () };
7496 $html_end = ${ terminate_html_document () };
7498 print FUNCTION_VIEW "</pre>\n";
7499 print FUNCTION_VIEW $html_home;
7500 print FUNCTION_VIEW $html_new_line . "\n";
7501 print FUNCTION_VIEW $html_acknowledgement;
7502 print FUNCTION_VIEW $html_end;
7504 close (FUNCTION_VIEW);
7507 return (\$html_first_metric_file);
7509 } #-- End of subroutine generate_function_view
7511 #------------------------------------------------------------------------------
7512 # Generate an html line that links back to index.html. The text can either
7513 # be positioned to the left or to the right.
7514 #------------------------------------------------------------------------------
7515 sub generate_home_link
7517 my $subr_name = get_my_name ();
7519 my ($which_side) = @_;
7521 my $html_home_line;
7523 if (($which_side ne "left") and ($which_side ne "right"))
7525 my $msg = "which_side = $which_side not supported";
7526 gp_message ("assertion", $subr_name, $msg);
7529 $html_home_line .= "<div class=\"" . $which_side . "\">";
7530 $html_home_line .= "<br><a href='" . $g_html_base_file_name{"index"};
7531 $html_home_line .= ".html' style='background-color:";
7532 $html_home_line .= $g_html_color_scheme{"index"};
7533 $html_home_line .= "'><b>Return to main view</b></a>";
7534 $html_home_line .= "</div>";
7536 return (\$html_home_line);
7538 } #-- End of subroutine generate_home_link
7540 #------------------------------------------------------------------------------
7541 # Generate a block of html for this function block.
7542 #------------------------------------------------------------------------------
7543 sub generate_html_function_blocks
7545 my $subr_name = get_my_name ();
7547 my (
7548 $index_start_ref,
7549 $index_end_ref,
7550 $hex_addresses_ref,
7551 $the_metrics_ref,
7552 $length_first_metric_ref,
7553 $special_marker_ref,
7554 $the_function_name_ref,
7555 $separator_ref,
7556 $number_of_metrics_ref,
7557 $data_function_block_ref,
7558 $function_info_ref,
7559 $function_view_structure_ref) = @_;
7561 my $index_start = ${ $index_start_ref };
7562 my $index_end = ${ $index_end_ref };
7563 my @hex_addresses = @{ $hex_addresses_ref };
7564 my @the_metrics = @{ $the_metrics_ref };
7565 my @length_first_metric = @{ $length_first_metric_ref };
7566 my @special_marker = @{ $special_marker_ref };
7567 my @the_function_name = @{ $the_function_name_ref};
7569 my $separator = ${ $separator_ref };
7570 my $number_of_metrics = ${ $number_of_metrics_ref };
7571 my $data_function_block = ${ $data_function_block_ref };
7572 my @function_info = @{ $function_info_ref };
7573 my %function_view_structure = %{ $function_view_structure_ref };
7575 my $decimal_separator = $g_locale_settings{"decimal_separator"};
7577 my @html_block_prologue = ();
7578 my @html_code_function_block = ();
7579 my @function_lines = ();
7580 my @fields = ();
7581 my @address_field = ();
7582 my @metric_values = ();
7583 my @function_names = ();
7584 my @final_function_names = ();
7585 my @marker = ();
7586 my @split_number = ();
7587 my @function_tags = ();
7589 my $all_metrics;
7590 my $current_function_name;
7591 my $no_of_fields;
7592 my $name_regex;
7593 my $full_hex_address;
7594 my $hex_address;
7595 my $target_function;
7596 my $marker_function;
7597 my $routine;
7598 my $routine_length;
7599 my $metrics_length;
7600 my $max_metrics_length = 0;
7601 my $modified_line;
7602 my $string_length;
7603 my $addr_offset;
7604 my $current_address;
7605 my $found_a_match;
7606 my $ref_index;
7607 my $alt_name;
7608 my $length_first_field;
7609 my $gap;
7610 my $ipad;
7611 my $html_line;
7612 my $target_tag;
7613 my $tag_for_header;
7614 my $href_file;
7615 my $found_alt_name;
7616 my $name_in_header;
7617 my $create_hyperlinks;
7619 state $first_call = $TRUE;
7620 state $reference_length;
7622 #------------------------------------------------------------------------------
7623 # If the length of the first metric is less than the maximum over all first
7624 # metrics, add spaces to the left to ensure correct alignment.
7625 #------------------------------------------------------------------------------
7626 for my $k ($index_start .. $index_end)
7628 my $pad = $g_max_length_first_metric - $length_first_metric[$k];
7629 if ($pad ge 1)
7631 my $spaces = "";
7632 for my $s (1 .. $pad)
7634 $spaces .= "&nbsp;";
7636 $the_metrics[$k] = $spaces . $the_metrics[$k];
7638 my $msg = "padding spaces = $spaces the_metrics[$k] = $the_metrics[$k]";
7639 gp_message ("debugXL", $subr_name, $msg);
7642 ## my $end_game = "end game3=> pad = $pad" . $hex_addresses[$k] . " " . $the_metrics[$k] . " " . $special_marker[$k] . $the_function_name[$k];
7643 ## gp_message ("debugXL", $subr_name, $end_game);
7646 #------------------------------------------------------------------------------
7647 # An example what @function_lines should look like after the split:
7648 # <empty>
7649 # 6:0x0003ad20 drand48 0.100 0.084 768240570 0
7650 # 6:0x0003af50 *erand48_r 0.080 0.084 768240570 0
7651 # 6:0x0003b160 __drand48_iterate 0.020 0. 0 0
7652 #------------------------------------------------------------------------------
7653 @function_lines = split ($separator, $data_function_block);
7655 #------------------------------------------------------------------------------
7656 # Parse the individual lines. Replace multi-occurrence functions by their
7657 # unique alternative name and mark the target function.
7659 # The above split operation produces an empty first field because the line
7660 # starts with the separator. This is why skip the first field.
7661 #------------------------------------------------------------------------------
7662 for my $i ($index_start .. $index_end)
7664 my $input_line = $the_metrics[$i];
7666 gp_message ("debugXL", $subr_name, "the_metrics[$i] = ". $the_metrics[$i]);
7668 #------------------------------------------------------------------------------
7669 # In case the last metric is 0. only, we append 3 extra characters that
7670 # represent zero. We cannot change the number to 0.000 though because that
7671 # has a different interpretation than 0.
7672 # In a later phase, the "ZZZ" symbol will be removed again, but for now it
7673 # creates consistency in, for example, the length of the metrics part.
7674 #------------------------------------------------------------------------------
7675 if ($input_line =~ /[\w0-9$decimal_separator]*(0$decimal_separator$)/)
7677 if (defined ($1) )
7679 my $decimal_point = $decimal_separator;
7680 $decimal_point =~ s/\\//;
7681 my $txt = "input_line = $input_line = ended with 0";
7682 $txt .= $decimal_point;
7683 gp_message ("debugXL", $subr_name, $txt);
7685 $the_metrics[$i] .= "ZZZ";
7689 $hex_address = $hex_addresses[$i];
7690 $marker_function = $special_marker[$i];
7691 $routine = $the_function_name[$i];
7692 #------------------------------------------------------------------------------
7693 # Get the length of the metrics line before ZZZ is replaced by spaces.
7694 #------------------------------------------------------------------------------
7695 $all_metrics = $the_metrics[$i];
7696 $metrics_length = length ($all_metrics);
7697 $all_metrics =~ s/ZZZ/&nbsp;&nbsp;&nbsp;/g;
7699 $max_metrics_length = max ($max_metrics_length, $metrics_length);
7701 push (@marker, $marker_function);
7702 push (@address_field, $hex_address);
7703 push (@metric_values, $all_metrics);
7704 push (@function_names, $routine);
7706 my $index_into_function_info_ref = get_index_function_info (
7707 \$routine,
7708 \$hex_addresses[$i],
7709 $function_info_ref);
7711 my $index_into_function_info = ${ $index_into_function_info_ref };
7712 $target_tag = $function_info[$index_into_function_info]{"tag_id"};
7713 $alt_name = $function_info[$index_into_function_info]{"alt_name"};
7715 #------------------------------------------------------------------------------
7716 # Keep the name of the target function (the one marked with a *) for later use.
7717 # This is the tag that identifies the block in the caller-callee output. The
7718 # tag is used in the link to the caller-callee in the function overview.
7719 #------------------------------------------------------------------------------
7720 if ($marker_function eq "*")
7722 $tag_for_header = $target_tag;
7723 $name_in_header = $alt_name;
7725 #------------------------------------------------------------------------------
7726 # We need to replace the "<" symbol in the code by "&lt;".
7727 #------------------------------------------------------------------------------
7728 $name_in_header =~ s/$g_less_than_regex/$g_html_less_than_regex/g;
7731 push (@final_function_names, $alt_name);
7732 push (@function_tags, $target_tag);
7734 gp_message ("debugXL", $subr_name, "index_into_function_info = $index_into_function_info");
7735 gp_message ("debugXL", $subr_name, "target_tag = $target_tag");
7736 gp_message ("debugXL", $subr_name, "alt_name = $alt_name");
7738 } #-- End of loop for my $i ($index_start .. $index_end)
7740 my $tag_line = "<a id='" . $tag_for_header . "'></a>";
7741 $html_line = "<br>\n";
7742 $html_line .= $tag_line . "Function name: ";
7743 $html_line .= "<span style='color:" . $g_html_color_scheme{"target_function_name"} . "'>";
7744 $html_line .= "<b>" . $name_in_header . "</b></span>\n";
7745 $html_line .= "<br>";
7747 push (@html_block_prologue, $html_line);
7749 gp_message ("debugXL", $subr_name, "the final function block for $name_in_header");
7751 $href_file = $g_html_base_file_name{"caller_callee"} . ".html";
7753 #------------------------------------------------------------------------------
7754 # Process the function blocks and generate the HTML structure for them.
7755 #------------------------------------------------------------------------------
7756 for my $i (0 .. $#final_function_names)
7758 $current_function_name = $final_function_names[$i];
7759 gp_message ("debugXL", $subr_name, "current_function_name = $current_function_name");
7761 #------------------------------------------------------------------------------
7762 # Do not add hyperlinks for <Total>.
7763 #------------------------------------------------------------------------------
7764 if ($current_function_name eq "<Total>")
7766 $create_hyperlinks = $FALSE;
7768 else
7770 $create_hyperlinks = $TRUE;
7773 #------------------------------------------------------------------------------
7774 # We need to replace the "<" symbol in the code by "&lt;".
7775 #------------------------------------------------------------------------------
7776 $current_function_name =~ s/$g_less_than_regex/$g_html_less_than_regex/g;
7778 $html_line = $metric_values[$i] . " ";
7780 if ($marker[$i] eq "*")
7782 $current_function_name = "<b>" . $current_function_name . "</b>";
7784 $html_line .= " <a href='" . $href_file . "#" . $function_tags[$i] . "'>" . $current_function_name . "</a>";
7786 if ($marker[$i] eq "*")
7788 $html_line = "<br>" . $html_line;
7790 elsif (($marker[$i] ne "*") and ($i == 0))
7792 $html_line = "<br>" . $html_line;
7795 gp_message ("debugXL", $subr_name, "html_line = $html_line");
7797 #------------------------------------------------------------------------------
7798 # Find the index into "function_info" for this particular function.
7799 #------------------------------------------------------------------------------
7800 $routine = $function_names[$i];
7801 $current_address = $address_field[$i];
7803 my $target_index_ref = find_index_in_function_info (\$routine, \$current_address, \@function_info);
7804 my $target_index = ${ $target_index_ref };
7806 gp_message ("debugXL", $subr_name, "routine = $routine current_address = $current_address target_index = $target_index");
7808 #------------------------------------------------------------------------------
7809 # TBD Do this once for each function and store the result. This is a saving
7810 # because functions may and typically will appear more than once.
7811 #------------------------------------------------------------------------------
7812 my $spaces_left = $function_view_structure{"max function length"} - $function_info[$target_index]{"function length"};
7814 #------------------------------------------------------------------------------
7815 # Add the links to the line. Make sure there is at least one space.
7816 #------------------------------------------------------------------------------
7817 my $spaces = "&nbsp;";
7818 for my $k (1 .. $spaces_left)
7820 $spaces .= "&nbsp;";
7823 if ($create_hyperlinks)
7825 $html_line .= $spaces;
7826 $html_line .= $function_info[$target_index]{"href_source"};
7827 $html_line .= "&nbsp;";
7828 $html_line .= $function_info[$target_index]{"href_disassembly"};
7831 push (@html_code_function_block, $html_line);
7834 for my $lines (0 .. $#html_code_function_block)
7836 gp_message ("debugXL", $subr_name, "final html block = " . $html_code_function_block[$lines]);
7839 return (\@html_block_prologue, \@html_code_function_block);
7841 } #-- End of subroutine generate_html_function_blocks
7843 #------------------------------------------------------------------------------
7844 # Get all the metrics available
7846 # (gprofng-display-text) metric_list
7847 # Current metrics: e.totalcpu:i.totalcpu:e.cycles:e+insts:e+llm:name
7848 # Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu )
7849 # Available metrics:
7850 # Exclusive Total CPU Time: e.%totalcpu
7851 # Inclusive Total CPU Time: i.%totalcpu
7852 # Exclusive CPU Cycles: e.+%cycles
7853 # Inclusive CPU Cycles: i.+%cycles
7854 # Exclusive Instructions Executed: e+%insts
7855 # Inclusive Instructions Executed: i+%insts
7856 # Exclusive Last-Level Cache Misses: e+%llm
7857 # Inclusive Last-Level Cache Misses: i+%llm
7858 # Exclusive Instructions Per Cycle: e+IPC
7859 # Inclusive Instructions Per Cycle: i+IPC
7860 # Exclusive Cycles Per Instruction: e+CPI
7861 # Inclusive Cycles Per Instruction: i+CPI
7862 # Size: size
7863 # PC Address: address
7864 # Name: name
7865 #------------------------------------------------------------------------------
7866 sub get_all_the_metrics
7868 my $subr_name = get_my_name ();
7870 my ($experiments_ref, $outputdir_ref) = @_;
7872 my $experiments = ${ $experiments_ref };
7873 my $outputdir = ${ $outputdir_ref };
7875 my $ignore_value;
7876 my $gp_functions_cmd;
7877 my $gp_display_text_cmd;
7879 my $metrics_output_file = $outputdir . "metrics-all";
7880 my $result_file = $outputdir . $g_gp_output_file;
7881 my $gp_error_file = $outputdir . $g_gp_error_logfile;
7882 my $script_file_metrics = $outputdir . "script-metrics";
7884 my @metrics_data = ();
7886 open (SCRIPT_METRICS, ">", $script_file_metrics)
7887 or die ("$subr_name - unable to open script file $script_file_metrics for writing: '$!'");
7888 gp_message ("debug", $subr_name, "opened script file $script_file_metrics for writing");
7890 print SCRIPT_METRICS "# outfile $metrics_output_file\n";
7891 print SCRIPT_METRICS "outfile $metrics_output_file\n";
7892 print SCRIPT_METRICS "# metric_list\n";
7893 print SCRIPT_METRICS "metric_list\n";
7895 close (SCRIPT_METRICS);
7897 $gp_functions_cmd = "$GP_DISPLAY_TEXT -script $script_file_metrics $experiments";
7899 gp_message ("debug", $subr_name, "calling $GP_DISPLAY_TEXT to get all the metrics");
7901 $gp_display_text_cmd = "$gp_functions_cmd 1>> $result_file 2>> $gp_error_file";
7902 gp_message ("debug", $subr_name, "cmd = $gp_display_text_cmd");
7904 my ($error_code, $cmd_output) = execute_system_cmd ($gp_display_text_cmd);
7906 if ($error_code != 0)
7908 $ignore_value = msg_display_text_failure ($gp_display_text_cmd,
7909 $error_code,
7910 $gp_error_file);
7911 gp_message ("abort", $subr_name, "execution terminated");
7914 open (METRICS_INFO, "<", $metrics_output_file)
7915 or die ("$subr_name - unable to open file $metrics_output_file for reading '$!'");
7916 gp_message ("debug", $subr_name, "opened file $metrics_output_file for reading");
7918 #------------------------------------------------------------------------------
7919 # Read the input file into memory.
7920 #------------------------------------------------------------------------------
7921 chomp (@metrics_data = <METRICS_INFO>);
7922 gp_message ("debug", $subr_name, "read all contents of file $metrics_output_file into memory");
7923 gp_message ("debug", $subr_name, "\$#metrics_data = $#metrics_data");
7925 my $input_line;
7926 my $ignore_lines_regex = '^(?:Current|Available|\s+Size:|\s+PC Address:|\s+Name:)';
7927 my $split_line_regex = '(.*): (.*)';
7928 my $empty_line_regex = '^\s*$';
7929 my @metric_list_all = ();
7930 for (my $line_no=0; $line_no <= $#metrics_data; $line_no++)
7933 $input_line = $metrics_data[$line_no];
7935 ## if ( not (($input_line =~ /$ignore_lines_regex/ or ($input_line =~ /^\s*$/))))
7936 if ( not ($input_line =~ /$ignore_lines_regex/) and not ($input_line =~ /$empty_line_regex/) )
7938 if ($input_line =~ /$split_line_regex/)
7940 #------------------------------------------------------------------------------
7941 # Remove the percentages.
7942 #------------------------------------------------------------------------------
7943 my $metric_definition = $2;
7944 $metric_definition =~ s/\%//g;
7945 gp_message ("debug", $subr_name, "line_no = $line_no $metrics_data[$line_no] metric_definition = $metric_definition");
7946 push (@metric_list_all, $metric_definition);
7952 gp_message ("debug", $subr_name, "\@metric_list_all = @metric_list_all");
7954 my $final_list = join (":", @metric_list_all);
7955 gp_message ("debug", $subr_name, "final_list = $final_list");
7957 close (METRICS_INFO);
7959 return (\$final_list);
7961 } #-- End of subroutine get_all_the_metrics
7963 #------------------------------------------------------------------------------
7964 # A simple function to return the basename using fileparse. To keep things
7965 # simple, a suffixlist is not supported. In case this is needed, use the
7966 # fileparse function directly.
7967 #------------------------------------------------------------------------------
7968 sub get_basename
7970 my ($full_name) = @_;
7972 my $ignore_value_1;
7973 my $ignore_value_2;
7974 my $basename_value;
7976 ($basename_value, $ignore_value_1, $ignore_value_2) = fileparse ($full_name);
7978 return ($basename_value);
7980 } #-- End of subroutine get_basename
7982 #------------------------------------------------------------------------------
7983 # Get the details on the experiments and store these in a file. Each
7984 # experiment has its own file. This makes the processing easier.
7985 #------------------------------------------------------------------------------
7986 sub get_experiment_info
7988 my $subr_name = get_my_name ();
7990 my ($outputdir_ref, $exp_dir_list_ref) = @_;
7992 my $outputdir = ${ $outputdir_ref };
7993 my @exp_dir_list = @{ $exp_dir_list_ref };
7995 my $cmd_output;
7996 my $current_slot;
7997 my $error_code;
7998 my $exp_info_file;
7999 my @exp_info = ();
8000 my @experiment_data = ();
8001 my $gp_error_file;
8002 my $gp_display_text_cmd;
8003 my $gp_functions_cmd;
8004 my $gp_log_file;
8005 my $ignore_value;
8006 my $msg;
8007 my $overview_file;
8008 my $result_file;
8009 my $script_file;
8010 my $the_experiments;
8012 $the_experiments = join (" ", @exp_dir_list);
8014 $script_file = $outputdir . "gp-info-exp.script";
8015 $exp_info_file = $outputdir . "gp-info-exp-list.out";
8016 $overview_file = $outputdir . "gp-overview.out";
8017 $gp_log_file = $outputdir . $g_gp_output_file;
8018 $gp_error_file = $outputdir . $g_gp_error_logfile;
8020 open (SCRIPT_EXPERIMENT_INFO, ">", $script_file)
8021 or die ("$subr_name - unable to open script file $script_file for writing: '$!'");
8022 gp_message ("debug", $subr_name, "opened script file $script_file for writing");
8024 #------------------------------------------------------------------------------
8025 # Attributed User CPU Time=a.user : for calltree - see P37 in manual
8026 #------------------------------------------------------------------------------
8027 print SCRIPT_EXPERIMENT_INFO "# compare on\n";
8028 print SCRIPT_EXPERIMENT_INFO "compare on\n";
8029 print SCRIPT_EXPERIMENT_INFO "# outfile $exp_info_file\n";
8030 print SCRIPT_EXPERIMENT_INFO "outfile $exp_info_file\n";
8031 print SCRIPT_EXPERIMENT_INFO "# exp_list\n";
8032 print SCRIPT_EXPERIMENT_INFO "exp_list\n";
8033 print SCRIPT_EXPERIMENT_INFO "# outfile $overview_file\n";
8034 print SCRIPT_EXPERIMENT_INFO "outfile $overview_file\n";
8035 print SCRIPT_EXPERIMENT_INFO "# overview\n";
8036 print SCRIPT_EXPERIMENT_INFO "overview\n";
8038 close SCRIPT_EXPERIMENT_INFO;
8040 $gp_functions_cmd = "$GP_DISPLAY_TEXT -script $script_file $the_experiments";
8042 gp_message ("debug", $subr_name, "executing $GP_DISPLAY_TEXT to get the experiment information");
8044 $gp_display_text_cmd = "$gp_functions_cmd 1>> $gp_log_file 2>> $gp_error_file";
8046 ($error_code, $cmd_output) = execute_system_cmd ($gp_display_text_cmd);
8048 if ($error_code != 0)
8050 $ignore_value = msg_display_text_failure ($gp_display_text_cmd,
8051 $error_code,
8052 $gp_error_file);
8053 gp_message ("abort", $subr_name, "execution terminated");
8056 #------------------------------------------------------------------------------
8057 # The first file has the following format:
8059 # ID Sel PID Experiment
8060 # == === ======= ======================================================
8061 # 1 yes 2078714 <absolute_path/mxv.hwc.1.thr.er
8062 # 2 yes 2078719 <absolute_path/mxv.hwc.2.thr.er
8063 #------------------------------------------------------------------------------
8064 open (EXP_INFO, "<", $exp_info_file)
8065 or die ("$subr_name - unable to open file $exp_info_file for reading '$!'");
8066 gp_message ("debug", $subr_name, "opened script file $exp_info_file for reading");
8068 chomp (@exp_info = <EXP_INFO>);
8070 #------------------------------------------------------------------------------
8071 # TBD - Check for the groups to exist below:
8072 #------------------------------------------------------------------------------
8073 $current_slot = 0;
8074 for my $i (0 .. $#exp_info)
8076 my $input_line = $exp_info[$i];
8078 gp_message ("debug", $subr_name, "$i => exp_info[$i] = $exp_info[$i]");
8080 if ($input_line =~ /^\s*(\d+)\s+(.+)/)
8082 my $exp_id = $1;
8083 my $remainder = $2;
8084 $experiment_data[$current_slot]{"exp_id"} = $exp_id;
8085 $experiment_data[$current_slot]{"exp_data_file"} = $outputdir . "gp-info-exp-" . $exp_id . ".out";
8086 gp_message ("debug", $subr_name, $i . " " . $exp_id . " " . $remainder);
8087 if ($remainder =~ /^(\w+)\s+(\d+)\s+(.+)/)
8089 my $exp_name = $3;
8090 $experiment_data[$current_slot]{"exp_name_full"} = $exp_name;
8091 $experiment_data[$current_slot]{"exp_name_short"} = get_basename ($exp_name);
8092 $current_slot++;
8093 gp_message ("debug", $subr_name, $i . " " . $1 . " " . $2 . " " . $3);
8095 else
8097 $msg = "remainder = $remainder has an unexpected format";
8098 gp_message ("assertion", $subr_name, $msg);
8102 #------------------------------------------------------------------------------
8103 # The experiment IDs and names are known. We can now generate the info for
8104 # each individual experiment.
8105 #------------------------------------------------------------------------------
8106 $gp_log_file = $outputdir . $g_gp_output_file;
8107 $gp_error_file = $outputdir . $g_gp_error_logfile;
8109 $script_file = $outputdir . "gp-details-exp.script";
8111 open (SCRIPT_EXPERIMENT_DETAILS, ">", $script_file)
8112 or die ("$subr_name - unable to open script file $script_file for writing: '$!'");
8113 gp_message ("debug", $subr_name, "opened script file $script_file for writing");
8115 for my $i (sort keys @experiment_data)
8117 my $exp_id = $experiment_data[$i]{"exp_id"};
8119 $result_file = $experiment_data[$i]{"exp_data_file"};
8121 # statistics
8122 # header
8123 print SCRIPT_EXPERIMENT_DETAILS "# outfile " . $result_file . "\n";
8124 print SCRIPT_EXPERIMENT_DETAILS "outfile " . $result_file . "\n";
8125 print SCRIPT_EXPERIMENT_DETAILS "# header " . $exp_id . "\n";
8126 print SCRIPT_EXPERIMENT_DETAILS "header " . $exp_id . "\n";
8127 print SCRIPT_EXPERIMENT_DETAILS "# statistics " . $exp_id . "\n";
8128 print SCRIPT_EXPERIMENT_DETAILS "statistics " . $exp_id . "\n";
8132 close (SCRIPT_EXPERIMENT_DETAILS);
8134 $gp_functions_cmd = "$GP_DISPLAY_TEXT -script $script_file $the_experiments";
8136 $msg = "executing $GP_DISPLAY_TEXT to get the experiment details";
8137 gp_message ("debug", $subr_name, $msg);
8139 $gp_display_text_cmd = "$gp_functions_cmd 1>> $gp_log_file 2>> $gp_error_file";
8141 ($error_code, $cmd_output) = execute_system_cmd ($gp_display_text_cmd);
8143 if ($error_code != 0)
8144 #------------------------------------------------------------------------------
8145 # This is unlikely to happen, but you never know.
8146 #------------------------------------------------------------------------------
8148 $ignore_value = msg_display_text_failure ($gp_display_text_cmd,
8149 $error_code,
8150 $gp_error_file);
8151 gp_message ("abort", $subr_name, "execution terminated");
8154 return (\@experiment_data);
8156 } #-- End of subroutine get_experiment_info
8158 #------------------------------------------------------------------------------
8159 # This subroutine returns a string of the type "size=<n>", where <n> is the
8160 # size of the file passed in. If n > 1024, a unit is appended.
8161 #------------------------------------------------------------------------------
8162 sub getfilesize
8164 my $subr_name = get_my_name ();
8166 my ($filename) = @_;
8168 my $size;
8169 my $file_stat;
8171 if (not -e $filename)
8173 #------------------------------------------------------------------------------
8174 # The return value is used in the caller. This is why we return the empty
8175 # string in case the file does not exist.
8176 #------------------------------------------------------------------------------
8177 gp_message ("debug", $subr_name, "filename = $filename not found");
8178 return ("");
8180 else
8182 $file_stat = stat ($filename);
8183 $size = $file_stat->size;
8185 gp_message ("debug", $subr_name, "filename = $filename");
8186 gp_message ("debug", $subr_name, "size = $size");
8188 if ($size > 1024)
8190 if ($size > 1024*1024)
8192 $size = $size/1024/1024;
8193 $size =~ s/\..*//;
8194 $size = $size."MB";
8196 else
8198 $size = $size/1024;
8199 $size =~ s/\..*//;
8200 $size = $size."KB";
8203 else
8205 $size=$size." bytes";
8207 gp_message ("debug", $subr_name, "size = $size title=\"$size\"");
8209 return ("title=\"$size\"");
8212 } #-- End of subroutine getfilesize
8214 #------------------------------------------------------------------------------
8215 # Parse the fsummary output and for all functions, store all the information
8216 # found in "function_info". In addition to this, several derived structures
8217 # are stored as well, making this structure a "onestop" place to get all the
8218 # info that is needed.
8219 #------------------------------------------------------------------------------
8220 sub get_function_info
8222 my $subr_name = get_my_name ();
8224 my ($FSUMMARY_FILE) = @_;
8226 #------------------------------------------------------------------------------
8227 # The regex section.
8228 #------------------------------------------------------------------------------
8229 my $white_space_regex = '\s*';
8231 my @function_info = ();
8232 my %function_address_and_index = ();
8233 my %LINUX_vDSO = ();
8234 my %function_view_structure = ();
8235 my %addressobjtextm = ();
8236 #------------------------------------------------------------------------------
8237 # TBD: This structure is no longer used and most likely can be removed.
8238 #------------------------------------------------------------------------------
8239 my %functions_index = ();
8241 my $msg;
8243 # TBD: check
8244 my $full_address_field;
8245 my %source_files = ();
8247 my $i;
8248 my $line;
8249 my $routine_flag;
8250 my $value;
8251 my $field;
8252 my $df_flag;
8253 my $address_decimal;
8254 my $routine;
8256 my $num_source_files = 0;
8257 my $number_of_unique_functions = 0;
8258 my $number_of_non_unique_functions = 0;
8260 my $function_info_regex = '\s*(\S+[a-zA-Z\s]*):(.*)';
8261 my $get_hex_address_regex = '(\d+):(0x\S+)';
8262 #------------------------------------------------------------------------------
8263 # Open the file generated using the -fsummary option.
8264 #------------------------------------------------------------------------------
8265 $msg = " - unable to open file $FSUMMARY_FILE for reading:";
8266 open (FSUMMARY_FILE, "<", $FSUMMARY_FILE)
8267 or die ($subr_name . $msg . " " . $!);
8268 $msg = "opened file $FSUMMARY_FILE for reading";
8269 gp_message ("debug", $subr_name, $msg);
8271 #------------------------------------------------------------------------------
8272 # This is the typical structure of the fsummary output:
8274 # Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu )
8275 # Functions sorted by metric: Exclusive Total CPU Time
8277 # <Total>
8278 # Exclusive Total CPU Time: 11.538 (100.0%)
8279 # Inclusive Total CPU Time: 11.538 (100.0%)
8280 # Size: 0
8281 # PC Address: 1:0x00000000
8282 # Source File: (unknown)
8283 # Object File: (unknown)
8284 # Load Object: <Total>
8285 # Mangled Name:
8286 # Aliases:
8288 # a_function_name
8289 # Exclusive Total CPU Time: 4.003 ( 34.7%)
8290 # Inclusive Total CPU Time: 4.003 ( 34.7%)
8291 # Size: 715
8292 # PC Address: 2:0x00006c61
8293 # Source File: <absolute path to source file>
8294 # Object File: <object filename>
8295 # Load Object: <executable name>
8296 # Mangled Name:
8297 # Aliases:
8299 # The previous block is repeated for every function.
8300 #------------------------------------------------------------------------------
8302 #------------------------------------------------------------------------------
8303 # Skip the header. The header is defined to end with a blank line.
8304 #------------------------------------------------------------------------------
8305 while (<FSUMMARY_FILE>)
8307 $line = $_;
8308 chomp ($line);
8309 if ($line =~ /^\s*$/)
8311 last;
8315 #------------------------------------------------------------------------------
8316 # Process the remaining blocks. Note that the first line should be <Total>,
8317 # but this is currently not checked.
8318 #------------------------------------------------------------------------------
8319 $i = 0;
8320 $routine_flag = $TRUE;
8321 while (<FSUMMARY_FILE>)
8323 $line = $_;
8324 chomp ($line);
8326 #------------------------------------------------------------------------------
8327 # Legacy issue to deal with. Up until somewhere between binutils 2.40 and 2.41,
8328 # gprofng display text might print the " -- no functions found" comment.
8329 # No, the two spaces after -- are not my typo ;-)
8331 # Since then, this comment is no longer printed, but the safe approach is to
8332 # remove any occurrence upfront.
8333 #------------------------------------------------------------------------------
8334 $line =~ s/ -- no functions found//;
8336 $msg = "line = " . $line;
8337 gp_message ("debugXL", $subr_name, $msg);
8339 if ($line =~ /^\s*$/)
8340 #------------------------------------------------------------------------------
8341 # Blank line.
8342 #------------------------------------------------------------------------------
8344 $routine_flag = $TRUE;
8345 $df_flag = 0;
8347 #------------------------------------------------------------------------------
8348 # Linux vDSO exception
8350 # TBD: Check if still relevant.
8351 #------------------------------------------------------------------------------
8352 if ($function_info[$i]{"Load Object"} eq "DYNAMIC_FUNCTIONS")
8354 $LINUX_vDSO{substr ($function_info[$i]{"addressobjtext"},1)} = $function_info[$i]{"routine"};
8356 $i++;
8357 next;
8360 if ($routine_flag)
8361 #------------------------------------------------------------------------------
8362 # Should be the first line after the blank line.
8363 #------------------------------------------------------------------------------
8365 $routine = $line;
8366 push (@{ $g_map_function_to_index{$routine} }, $i);
8367 gp_message ("debugXL", $subr_name, "pushed i = $i to g_map_function_to_index{$routine}");
8369 #------------------------------------------------------------------------------
8370 # In a later parsing phase we need to know how many fields there are in a
8371 # function name. For example, "<static>@0x21850 (<libc-2.28.so>)" is name that
8372 # may show up in a function list.
8374 # Here we determine the number of fields and store it.
8376 # REVISIT This may not be needed anymore
8377 #------------------------------------------------------------------------------
8378 my @fields_in_name = split (" ", $routine);
8379 $function_info[$i]{"fields in routine name"} = scalar (@fields_in_name);
8381 #------------------------------------------------------------------------------
8382 # This name may change if the function has multiple occurrences, but in any
8383 # case, at the end of this routine this component has the final name to be
8384 # used.
8385 #------------------------------------------------------------------------------
8386 $function_info[$i]{"alt_name"} = $routine;
8387 if (not exists ($g_function_occurrences{$routine}))
8389 gp_message ("debugXL", $subr_name, "the entry in function_info for $routine does not exist");
8390 $function_info[$i]{"routine"} = $routine;
8391 $g_function_occurrences{$routine} = 1;
8393 gp_message ("debugXL", $subr_name, "g_function_occurrences{$routine} = $g_function_occurrences{$routine}");
8395 else
8397 gp_message ("debugXL", $subr_name, "the entry in function_info for $routine exists already");
8398 $function_info[$i]{"routine"} = $routine;
8399 $g_function_occurrences{$routine} += 1;
8400 if (not exists ($g_multi_count_function{$routine}))
8402 $g_multi_count_function{$routine} = $TRUE;
8404 $msg = "g_function_occurrences{$routine} = ";
8405 $msg .= $g_function_occurrences{$routine};
8406 gp_message ("debugXL", $subr_name, $msg);
8408 #------------------------------------------------------------------------------
8409 # New: used when generating the index.
8410 #------------------------------------------------------------------------------
8411 $function_info[$i]{"function length"} = length ($routine);
8412 $function_info[$i]{"tag_id"} = create_function_tag ($i);
8413 if (not exists ($g_function_tag_id{$routine}))
8415 $g_function_tag_id{$routine} = create_function_tag ($i);
8417 else
8420 #------------------------------------------------------------------------------
8421 ## TBD HACK!!! CHECK!!!!!
8422 #------------------------------------------------------------------------------
8423 $g_function_tag_id{$routine} = $i;
8426 $routine_flag = $FALSE;
8427 gp_message ("debugXL", $subr_name, "stored " . $function_info[$i]{"routine"});
8429 #------------------------------------------------------------------------------
8430 # The $functions_index hash contains an array. After an initial assignment,
8431 # other values that have been found are pushed onto the arrays.
8432 #------------------------------------------------------------------------------
8433 if (not exists ($functions_index{$routine}))
8435 $functions_index{$routine} = [$i];
8437 else
8439 #------------------------------------------------------------------------------
8440 # Add the array index to the list
8441 #------------------------------------------------------------------------------
8442 push (@{$functions_index{$routine}}, $i);
8444 next;
8447 #------------------------------------------------------------------------------
8448 # Example format of an input block, where $line is one of the following:
8449 # Exclusive Total CPU Time: 0.001 ( 0.0%)
8450 # Inclusive Total CPU Time: 0.001 ( 0.0%)
8451 # Size: 92
8452 # PC Address: 5:0x00125de0
8453 # Source File: (unknown)
8454 # Object File: (unknown)
8455 # Load Object: /usr/lib64/libc-2.28.so
8456 # Mangled Name:
8457 # Aliases: __brk
8458 #------------------------------------------------------------------------------
8459 $line =~ s/^\s+//;
8460 if ($line =~ /$function_info_regex/)
8462 if (defined ($1) and defined($2))
8464 $field = $1;
8465 $value = $2;
8466 $value =~ s/$g_rm_surrounding_spaces_regex//g;
8468 $msg = "initial - field = " . $field . " value = " . $value;
8469 gp_message ("debugM", $subr_name, $msg);
8471 else
8473 $msg = "the input line pattern was not recognized";
8474 gp_message ("warning", $subr_name, $msg);
8475 gp_message ("debug", $subr_name, $msg);
8476 $msg = "execution continues, but there may be a problem later";
8477 gp_message ("warning", $subr_name, $msg);
8478 gp_message ("debug", $subr_name, $msg);
8480 $field = "not recognized";
8481 $value = "not recognized";
8483 #------------------------------------------------------------------------------
8484 # The field has no value.
8485 #------------------------------------------------------------------------------
8486 if (length ($value) eq 0)
8487 ## if ($value =~ /^\s+$/)
8488 ## if (length ($2) gt 0)
8489 ## if ($2 == " ")
8491 if ($field eq "Mangled Name")
8493 $value = $routine;
8495 $msg = "no mangled name found - use the routine name ";
8496 $msg .= $routine . " as the mangled name";
8497 gp_message ("debugM", $subr_name, $msg);
8499 else
8501 $value = "no_value_given";
8503 $msg = "no value was found for this field - set to ";
8504 $msg .= $value;
8505 gp_message ("debugM", $subr_name, $msg);
8508 #------------------------------------------------------------------------------
8509 # Remove any leading whitespace characters.
8510 #------------------------------------------------------------------------------
8511 $value =~ s/$white_space_regex//;
8512 #------------------------------------------------------------------------------
8513 # These are the final values that will be used.
8514 #------------------------------------------------------------------------------
8515 $msg = "final - field = " . $field . " value = " . $value;
8516 gp_message ("debugM", $subr_name, $msg);
8518 $function_info[$i]{$field} = $value;
8520 ## $value =~ s/$white_space_regex//;
8522 ## \s*(\S+[a-zA-Z\s]*):\ *(.*)
8524 ### my @input_fields = split (":", $line);
8525 ### my $no_of_elements = scalar (@input_fields);
8527 ### gp_message ("debugXL", $subr_name, "#input_fields = $#input_fields");
8528 ### gp_message ("debugXL", $subr_name, "no_of_elements = $no_of_elements");
8529 ### gp_message ("debugXL", $subr_name, "input_fields[0] = $input_fields[0]");
8531 ### if ($no_of_elements == 1)
8532 #------------------------------------------------------------------------------
8533 # No value
8534 #------------------------------------------------------------------------------
8535 ### {
8536 ### $whatever = $input_fields[0];
8537 ### $value = "";
8538 ### }
8539 ### elsif ($no_of_elements == 2)
8540 ### {
8541 ### #------------------------------------------------------------------------------
8542 ### # Note that $value may consist of multiple fields (e.g. 1.651 ( 95.4%)).
8543 ### #------------------------------------------------------------------------------
8544 ### $whatever = $input_fields[0];
8545 ### $value = $input_fields[1];
8546 ### }
8547 ### elsif ($no_of_elements == 3)
8548 ### {
8549 ### $whatever = $input_fields[0];
8550 ### if ($whatever eq "PC Address")
8551 ### #------------------------------------------------------------------------------
8552 ### # Must be an address field. Restore the second colon.
8553 ### #------------------------------------------------------------------------------
8554 ### {
8555 ### $value = $input_fields[1] . ":" . $input_fields[2];
8556 ### }
8557 ### elsif ($whatever eq "Mangled Name")
8558 ### #------------------------------------------------------------------------------
8559 ### # The mangled name includes a colon (:). Just copy the entire string.
8560 ### #------------------------------------------------------------------------------
8561 ### {
8562 ### $value = $input_fields[2];
8563 ### }
8564 ### }
8565 ### else
8566 ### {
8567 ### if ($whatever eq "Aliases")
8568 ### #------------------------------------------------------------------------------
8569 ### # The mangled name includes a colon (:). Just copy the entire string.
8570 ### #------------------------------------------------------------------------------
8571 ### {
8572 ### $value = $input_fields[2];
8573 ### }
8574 ### else
8575 ### {
8576 ### $msg = "input line = " . $line;
8577 ### gp_message ("debug", $subr_name, $msg);
8578 ### for my $i (keys @input_fields)
8579 ### {
8580 ### $msg = "input_fields[$i] = " . $input_fields[$i];
8581 ### gp_message ("debug", $subr_name, $msg);
8582 ### }
8583 ### $msg = "unexpected input: number of fields = " . $no_of_elements;
8584 ### gp_message ("debug", $subr_name, $msg);
8585 ### ## gp_message ("assertion", $subr_name, $msg);
8586 ### }
8587 ### }
8588 ## $function_info[$i]{$field} = $value;
8590 #------------------------------------------------------------------------------
8591 # TBD: Seems to be not used anymore and can most likely be removed. Check this.
8592 #------------------------------------------------------------------------------
8593 if ($field =~ /Source File/)
8595 if (!exists ($source_files{$value}))
8597 $source_files{$value} = $TRUE;
8598 $num_source_files++;
8602 if ($field =~ /PC Address/)
8604 my $segment;
8605 my $offset;
8606 #------------------------------------------------------------------------------
8607 # The format of the address is assumed to be the following 2:0x000070a8
8608 # Note that the regex is pretty wide. This is from the original code and
8609 # could be made more specific:
8610 # if ($value =~ /\s*(\S+):(\S+)/)
8611 #------------------------------------------------------------------------------
8612 # if ($value =~ /\s*(\S+):(\S+)/)
8613 if ($value =~ /\s*(\d+):0x([0-9a-zA-Z]+)/)
8615 $segment = $1;
8616 $offset = $2;
8617 #------------------------------------------------------------------------------
8618 # Convert to a base 10 number
8619 #------------------------------------------------------------------------------
8620 $address_decimal = bigint::hex ($offset); # decimal
8621 #------------------------------------------------------------------------------
8622 # Construct the address field. Note that we use the hex address here.
8623 # For example @2:0x0003f280
8624 #------------------------------------------------------------------------------
8625 $full_address_field = $segment.":0x".$offset;
8627 $function_info[$i]{"addressobj"} = $address_decimal;
8628 $function_info[$i]{"addressobjtext"} = $full_address_field;
8629 $addressobjtextm{$full_address_field} = $i; # $RI
8631 if (not exists ($function_address_and_index{$routine}{$value}))
8633 $function_address_and_index{$routine}{$value} = $i;
8635 $msg = "function_address_and_index{$routine}{$value} = ";
8636 $msg .= $function_address_and_index{$routine}{$value};
8637 gp_message ("debugXL", $subr_name, $msg);
8639 else
8641 $msg = "function_info: $FSUMMARY_FILE: function $routine";
8642 $msg .= " already has a PC Address";
8643 gp_message ("debugXL", $subr_name, $msg);
8646 $g_total_function_count++;
8649 close (FSUMMARY_FILE);
8651 #------------------------------------------------------------------------------
8652 # For every function in the function overview, set up an html structure with
8653 # the various hyperlinks.
8654 #------------------------------------------------------------------------------
8655 gp_message ("debugXL", $subr_name, "augment function_info with alt_name");
8656 my $target_function;
8657 my $html_line;
8658 my $ftag;
8659 my $routine_length;
8660 my %html_source_functions = ();
8661 for my $i (keys @function_info)
8663 $target_function = $function_info[$i]{"routine"};
8665 gp_message ("debugXL", $subr_name, "i = $i target_function = $target_function");
8667 my $href_link;
8668 ## $href_link = "<a href=\'file." . $i . ".src.new.html#";
8669 $href_link = "<a href=\'file." . $i . ".";
8670 $href_link .= $g_html_base_file_name{"source"};
8671 $href_link .= ".html#";
8672 $href_link .= $function_info[$i]{"tag_id"};
8673 $href_link .= "\'>source</a>";
8674 $function_info[$i]{"href_source"} = $href_link;
8676 $href_link = "<a href=\'file." . $i . ".";
8677 $href_link .= $g_html_base_file_name{"disassembly"};
8678 $href_link .= ".html#";
8679 $href_link .= $function_info[$i]{"tag_id"};
8680 $href_link .= "\'>disassembly</a>";
8681 $function_info[$i]{"href_disassembly"} = $href_link;
8683 $href_link = "<a href=\'";
8684 $href_link .= $g_html_base_file_name{"caller_callee"};
8685 $href_link .= ".html#";
8686 $href_link .= $function_info[$i]{"tag_id"};
8687 $href_link .= "\'>caller-callee</a>";
8688 $function_info[$i]{"href_caller_callee"} = $href_link;
8690 gp_message ("debug", $subr_name, "g_function_occurrences{$target_function} = $g_function_occurrences{$target_function}");
8692 if ($g_function_occurrences{$target_function} > 1)
8694 #------------------------------------------------------------------------------
8695 # In case a function occurs more than one time in the function overview, we
8696 # add the load object and address offset info to make it unique.
8698 # This forces us to update some entries in function_info too.
8699 #------------------------------------------------------------------------------
8700 my $loadobj = $function_info[$i]{"Load Object"};
8701 my $address_field = $function_info[$i]{"addressobjtext"};
8702 my $address_offset;
8704 #------------------------------------------------------------------------------
8705 # The address field has the following format: @<n>:<address_offset>
8706 # We only care about the address offset.
8707 #------------------------------------------------------------------------------
8708 if ($address_field =~ /$get_hex_address_regex/)
8710 $address_offset = $2;
8712 else
8714 my $msg = "failed to extract the address offset from $address_field - use the full field";
8715 gp_message ("warning", $subr_name, $msg);
8716 $address_offset = $address_field;
8718 my $exe = get_basename ($loadobj);
8719 my $extra_field = " (<" . $exe . " $address_offset" .">)";
8720 ### $target_function .= $extra_field;
8721 $function_info[$i]{"alt_name"} = $target_function . $extra_field;
8722 gp_message ("debugXL", $subr_name, "function_info[$i]{\"alt_name\"} = " . $function_info[$i]{"alt_name"});
8724 #------------------------------------------------------------------------------
8725 # Store the length of the function name and get the tag id.
8726 #------------------------------------------------------------------------------
8727 $function_info[$i]{"function length"} = length ($target_function . $extra_field);
8728 $function_info[$i]{"tag_id"} = create_function_tag ($i);
8730 gp_message ("debugXL", $subr_name, "updated function_info[$i]{'routine'} = $function_info[$i]{'routine'}");
8731 gp_message ("debugXL", $subr_name, "updated function_info[$i]{'alt_name'} = $function_info[$i]{'alt_name'}");
8732 gp_message ("debugXL", $subr_name, "updated function_info[$i]{'function length'} = $function_info[$i]{'function length'}");
8733 gp_message ("debugXL", $subr_name, "updated function_info[$i]{'tag_id'} = $function_info[$i]{'tag_id'}");
8736 gp_message ("debug", $subr_name, "augment function_info with alt_name completed");
8738 #------------------------------------------------------------------------------
8739 # Compute the maximum function name length.
8741 # The maximum length is stored in %function_view_structure.
8742 #------------------------------------------------------------------------------
8743 my $max_function_length = 0;
8744 for my $i (0 .. $#function_info)
8746 $max_function_length = List::Util::max ($max_function_length, $function_info[$i]{"function length"});
8748 gp_message ("debugXL", $subr_name, "function_info[$i]{\"alt_name\"} = " . $function_info[$i]{"alt_name"} . " length = " . $function_info[$i]{"function length"});
8751 #------------------------------------------------------------------------------
8752 # Define the name of the table and take the length into account, since it may
8753 # be longer than the function name(s).
8754 #------------------------------------------------------------------------------
8755 $function_view_structure{"table name"} = "Function name";
8757 $max_function_length = max ($max_function_length, length ($function_view_structure{"table name"}));
8759 $function_view_structure{"max function length"} = $max_function_length;
8761 #------------------------------------------------------------------------------
8762 # Core loop that generates an HTML line for each function. This line is
8763 # stored in function_info.
8764 #------------------------------------------------------------------------------
8765 my $top_of_table = $FALSE;
8766 for my $i (keys @function_info)
8768 my $new_target_function;
8770 if (defined ($function_info[$i]{"alt_name"}))
8772 $target_function = $function_info[$i]{"alt_name"};
8773 gp_message ("debugXL", $subr_name, "retrieved function_info[$i]{'alt_name'} = $function_info[$i]{'alt_name'}");
8775 else
8777 my $msg = "function_info[$i]{\"alt_name\"} is not defined";
8778 gp_message ("assertion", $subr_name, $msg);
8781 my $function_length = $function_info[$i]{"function length"};
8782 my $number_of_blanks = $function_view_structure{"max function length"} - $function_length;
8784 my $spaces = "&nbsp;&nbsp;";
8785 for my $i (1 .. $number_of_blanks)
8787 $spaces .= "&nbsp;";
8789 if ($target_function eq "<Total>")
8790 #------------------------------------------------------------------------------
8791 # <Total> is a pseudo function and there is no source, or disassembly for it.
8792 # We could add a link to the caller-callee part, but this is currently not
8793 # done.
8794 #------------------------------------------------------------------------------
8796 $top_of_table = $TRUE;
8797 $html_line = "&nbsp;<b>&lt;Total></b>";
8799 else
8801 #------------------------------------------------------------------------------
8802 # Add the * symbol as a marker in case the same function occurs multiple times.
8803 # Otherwise insert a space.
8804 #------------------------------------------------------------------------------
8805 my $base_function_name = $function_info[$i]{"routine"};
8806 if (exists ($g_function_occurrences{$base_function_name}))
8808 if ($g_function_occurrences{$base_function_name} > 1)
8810 $new_target_function = "*" . $target_function;
8812 else
8814 $new_target_function = "&nbsp;" . $target_function;
8817 else
8819 my $msg = "g_function_occurrences{$base_function_name} does not exist";
8820 gp_message ("assertion", $subr_name, $msg);
8823 #------------------------------------------------------------------------------
8824 # Create the block with the function name, in boldface, plus the links to the
8825 # source, disassembly and caller-callee views.
8826 #------------------------------------------------------------------------------
8828 #------------------------------------------------------------------------------
8829 # We need to replace the "<" symbol in the code by "&lt;".
8830 #------------------------------------------------------------------------------
8831 $new_target_function =~ s/$g_less_than_regex/$g_html_less_than_regex/g;
8833 $html_line = "<b>$new_target_function</b>" . $spaces;
8834 $html_line .= $function_info[$i]{"href_source"} . "&nbsp;";
8835 $html_line .= $function_info[$i]{"href_disassembly"} . "&nbsp;";
8836 $html_line .= $function_info[$i]{"href_caller_callee"};
8839 $msg = "target_function = $target_function html_line = $html_line";
8840 gp_message ("debugM", $subr_name, $msg);
8841 $html_source_functions{$target_function} = $html_line;
8843 #------------------------------------------------------------------------------
8844 # TBD: In the future we want to re-use this block elsewhere.
8845 #------------------------------------------------------------------------------
8846 $function_info[$i]{"html function block"} = $html_line;
8849 for my $i (keys %html_source_functions)
8851 $msg = "html_source_functions{$i} = $html_source_functions{$i}";
8852 gp_message ("debugM", $subr_name, $msg);
8854 for my $i (keys @function_info)
8856 $msg = "function_info[$i]{\"html function block\"} = ";
8857 $msg .= $function_info[$i]{"html function block"};
8858 gp_message ("debugM", $subr_name, $msg);
8861 #------------------------------------------------------------------------------
8862 # Print the key data structure %function_info. This is a nested hash.
8863 #------------------------------------------------------------------------------
8864 for my $i (0 .. $#function_info)
8866 for my $role (sort keys %{ $function_info[$i] })
8868 $msg = "on return: function_info[$i]{$role} = ";
8869 $msg .= $function_info[$i]{$role};
8870 gp_message ("debugM", $subr_name, $msg);
8873 #------------------------------------------------------------------------------
8874 # Print the data structure %function_address_and_index. This is a nested hash.
8875 #------------------------------------------------------------------------------
8876 for my $F (keys %function_address_and_index)
8878 for my $fields (sort keys %{ $function_address_and_index{$F} })
8880 $msg = "on return: function_address_and_index{$F}{$fields} = ";
8881 $msg .= $function_address_and_index{$F}{$fields};
8882 gp_message ("debugM", $subr_name, $msg);
8885 #------------------------------------------------------------------------------
8886 # Print the data structure %functions_index. This is a hash with an arrray.
8887 #------------------------------------------------------------------------------
8888 for my $F (keys %functions_index)
8890 gp_message ("debug", $subr_name, "on return: functions_index{$F} = @{ $functions_index{$F} }");
8891 # alt code for my $i (0 .. $#{ $functions_index{$F} } )
8892 # alt code {
8893 # alt code gp_message ("debug", $subr_name, "on return: \$functions_index{$F} = $functions_index{$F}[$i]");
8894 # alt code }
8897 #------------------------------------------------------------------------------
8898 # Print the data structure %function_view_structure. This is a hash.
8899 #------------------------------------------------------------------------------
8900 for my $F (keys %function_view_structure)
8902 gp_message ("debug", $subr_name, "on return: function_view_structure{$F} = $function_view_structure{$F}");
8905 #------------------------------------------------------------------------------
8906 # Print the data structure %g_function_occurrences and use this structure to
8907 # gather statistics about the functions.
8909 # TBD: add this info to the experiment data overview.
8910 #------------------------------------------------------------------------------
8911 $number_of_unique_functions = 0;
8912 $number_of_non_unique_functions = 0;
8913 for my $F (keys %g_function_occurrences)
8915 gp_message ("debug", $subr_name, "on return: g_function_occurrences{$F} = $g_function_occurrences{$F}");
8916 if ($g_function_occurrences{$F} == 1)
8918 $number_of_unique_functions++;
8920 else
8922 $number_of_non_unique_functions++;
8926 for my $i (keys %g_map_function_to_index)
8928 my $n = scalar (@{ $g_map_function_to_index{$i} });
8929 gp_message ("debug", $subr_name, "on return: g_map_function_to_index [$n] : $i => @{ $g_map_function_to_index{$i} }");
8932 #------------------------------------------------------------------------------
8933 # TBD: Include this info on the page with experiment data. Include names
8934 # with multiple occurrences.
8935 #------------------------------------------------------------------------------
8936 $msg = "Number of source files : " .
8937 $num_source_files;
8938 gp_message ("debug", $subr_name, $msg);
8939 $msg = "Total number of functions : " .
8940 $g_total_function_count;
8941 gp_message ("debug", $subr_name, $msg);
8942 $msg = "Number of functions with a unique name : " .
8943 $number_of_unique_functions;
8944 gp_message ("debug", $subr_name, $msg);
8945 $msg = "Number of functions with more than one occurrence : " .
8946 $number_of_non_unique_functions;
8947 gp_message ("debug", $subr_name, $msg);
8948 my $multi_occurrences = $g_total_function_count -
8949 $number_of_unique_functions;
8950 $msg = "Total number of multiple occurences of the same function name : " .
8951 $multi_occurrences;
8952 gp_message ("debug", $subr_name, $msg);
8954 return (\@function_info, \%function_address_and_index, \%addressobjtextm,
8955 \%LINUX_vDSO, \%function_view_structure);
8957 } #-- End of subroutine get_function_info
8958 #------------------------------------------------------------------------------
8959 # TBD
8960 #------------------------------------------------------------------------------
8961 sub get_hdr_info
8963 my $subr_name = get_my_name ();
8965 my ($outputdir, $file) = @_;
8967 state $first_call = $TRUE;
8969 my $ASORTFILE;
8970 my @HDR;
8971 my $HDR;
8972 my $metric;
8973 my $line;
8974 my $ignore_directory;
8975 my $ignore_suffix;
8976 my $number_of_header_lines;
8978 #------------------------------------------------------------------------------
8979 # Add a "/" to simplify the construction of path names in the remainder.
8980 #------------------------------------------------------------------------------
8981 $outputdir = append_forward_slash ($outputdir);
8983 # Could get more header info from
8984 # <metric>[e.bit_fcount].sort.func file - etc.
8986 gp_message ("debug", $subr_name, "input file->$file<-");
8987 #-----------------------------------------------
8988 if ($file eq $outputdir."calls.sort.func")
8990 $ASORTFILE=$outputdir."calls";
8991 $metric = "calls"
8993 elsif ($file eq $outputdir."calltree.sort.func")
8995 $ASORTFILE=$outputdir."calltree";
8996 $metric = "calltree"
8998 elsif ($file eq $outputdir."functions.sort.func")
9000 $ASORTFILE=$outputdir."functions.func";
9001 $metric = "functions";
9003 else
9005 $ASORTFILE = $file;
9006 # $metric = basename ($file,".sort.func");
9007 ($metric, $ignore_directory, $ignore_suffix) = fileparse ($file, ".sort.func");
9008 gp_message ("debug", $subr_name, "ignore_directory = $ignore_directory ignore_suffix = $ignore_suffix");
9011 gp_message ("debug", $subr_name, "file = $file metric = $metric");
9013 open (ASORTFILE,"<", $ASORTFILE)
9014 or die ("$subr_name - unable to open file $ASORTFILE for reading: '$!'");
9015 gp_message ("debug", $subr_name, "opened file $ASORTFILE for reading");
9017 $number_of_header_lines = 0;
9018 while (<ASORTFILE>)
9020 $line =$_;
9021 chomp ($line);
9023 if ($line =~ /^Current/)
9025 next;
9027 if ($line =~ /^Functions/)
9029 next;
9031 if ($line =~ /^Callers/)
9033 next;
9035 if ($line =~ /^\s*$/)
9037 next;
9039 if (!($line =~ /^\s*\d/))
9041 $HDR[$number_of_header_lines] = $line;
9042 $number_of_header_lines++;
9043 next;
9045 last;
9047 close (ASORTFILE);
9048 #------------------------------------------------------------------------------
9049 # Ruud - Fixed a bug. The output should not be appended, but overwritten.
9050 # open (HI,">>$OUTPUTDIR"."hdrinfo");
9051 #------------------------------------------------------------------------------
9052 my $outfile = $outputdir."hdrinfo";
9054 if ($first_call)
9056 $first_call = $FALSE;
9057 open (HI ,">", $outfile)
9058 or die ("$subr_name - unable to open file $outfile for writing: '$!'");
9059 gp_message ("debug", $subr_name, "opened file $outfile for writing");
9061 else
9063 open (HI ,">>", $outfile)
9064 or die ("$subr_name - unable to open file $outfile in append mode: '$!'");
9065 gp_message ("debug", $subr_name, "opened file $outfile in append mode");
9068 print HI "\#$metric hdrlines=$number_of_header_lines\n";
9069 my $len = 0;
9070 for $HDR (@HDR)
9072 print HI "$HDR\n";
9073 gp_message ("debugXL", $subr_name, "HDR = $HDR\n");
9075 close (HI);
9076 if ($first_call)
9078 gp_message ("debug", $subr_name, "wrote file $outfile");
9080 else
9082 gp_message ("debug", $subr_name, "updated file $outfile");
9084 #-----------------------------------------------
9086 } #-- End of subroutine get_hdr_info
9088 #------------------------------------------------------------------------------
9089 # Get the home directory and the location(s) of the configuration file on the
9090 # current system.
9091 #------------------------------------------------------------------------------
9092 sub get_home_dir_and_rc_path
9094 my $subr_name = get_my_name ();
9096 my ($rc_file_name) = @_;
9098 my @rc_file_paths;
9099 my $target_cmd;
9100 my $home_dir;
9101 my $error_code;
9103 $target_cmd = $g_mapped_cmds{"printenv"} . " HOME";
9105 ($error_code, $home_dir) = execute_system_cmd ($target_cmd);
9107 if ($error_code != 0)
9109 my $msg = "cannot find a setting for HOME - please set this";
9110 gp_message ("assertion", $subr_name, $msg);
9112 else
9114 #------------------------------------------------------------------------------
9115 # The home directory is known and we can define the locations for the
9116 # configuration file.
9117 #------------------------------------------------------------------------------
9119 @rc_file_paths = (".", "$home_dir");
9122 gp_message ("debug", $subr_name, "upon return: \@rc_file_paths = @rc_file_paths");
9124 return ($home_dir, \@rc_file_paths);
9126 } #-- End of subroutine get_home_dir_and_rc_path
9128 #------------------------------------------------------------------------------
9129 # This subroutine generates a list with the hot functions.
9130 #------------------------------------------------------------------------------
9131 sub get_hot_functions
9133 my $subr_name = get_my_name ();
9135 my ($exp_dir_list_ref, $summary_metrics, $input_string) = @_;
9137 my @exp_dir_list = @{ $exp_dir_list_ref };
9139 my $cmd_output;
9140 my $error_code;
9141 my $expr_name;
9142 my $first_metric;
9143 my $gp_display_text_cmd;
9144 my $msg;
9145 my $ignore_value;
9147 my @sort_fields = ();
9149 $expr_name = join (" ", @exp_dir_list);
9151 gp_message ("debug", $subr_name, "expr_name = $expr_name");
9153 my $outputdir = append_forward_slash ($input_string);
9155 my $script_file = $outputdir."gp-fsummary.script";
9156 my $outfile = $outputdir."gp-fsummary.out";
9157 my $result_file = $outputdir."gp-fsummary.stderr";
9158 my $gp_error_file = $outputdir.$g_gp_error_logfile;
9160 @sort_fields = split (":", $summary_metrics);
9162 #-- RUUD
9164 $msg = "summary_metrics = " . $summary_metrics;
9165 gp_message ("debug", $subr_name, $msg);
9166 for my $field (@sort_fields)
9168 $msg = "metric field = " . $field;
9169 gp_message ("debug", $subr_name, $msg);
9171 #------------------------------------------------------------------------------
9172 # This is extremely unlikely to happen, but if so, it is a fatal error.
9173 #------------------------------------------------------------------------------
9174 my $number_of_elements = scalar (@sort_fields);
9176 gp_message ("debug", $subr_name, "number of fields in summary_metrics = $number_of_elements");
9178 if ($number_of_elements == 0)
9180 my $msg = "there are $number_of_elements in the metrics list";
9181 gp_message ("assertion", $subr_name, $msg);
9184 #------------------------------------------------------------------------------
9185 # Get the summary of the hot functions
9186 #------------------------------------------------------------------------------
9187 open (SCRIPT, ">", $script_file)
9188 or die ("$subr_name - unable to open script file $script_file for writing: '$!'");
9189 gp_message ("debug", $subr_name, "opened script file $script_file for writing");
9191 #------------------------------------------------------------------------------
9192 # TBD: Check what this is about:
9193 # Attributed User CPU Time=a.user : for calltree - see P37 in manual
9194 #------------------------------------------------------------------------------
9195 print SCRIPT "# limit 0\n";
9196 print SCRIPT "limit 0\n";
9197 print SCRIPT "# metrics $summary_metrics\n";
9198 print SCRIPT "metrics $summary_metrics\n";
9199 print SCRIPT "# thread_select all\n";
9200 print SCRIPT "thread_select all\n";
9202 #------------------------------------------------------------------------------
9203 # Use first out of summary metrics as first (it doesn't matter which one)
9204 # $first_metric = (split /:/,$summary_metrics)[0];
9205 #------------------------------------------------------------------------------
9207 $first_metric = $sort_fields[0];
9209 print SCRIPT "# outfile $outfile\n";
9210 print SCRIPT "outfile $outfile\n";
9211 print SCRIPT "# sort $first_metric\n";
9212 print SCRIPT "sort $first_metric\n";
9213 print SCRIPT "# fsummary\n";
9214 print SCRIPT "fsummary\n";
9216 close SCRIPT;
9218 my $gp_functions_cmd = "$GP_DISPLAY_TEXT -viewmode machine -compare off -script $script_file $expr_name";
9220 gp_message ("debug", $subr_name, "executing $GP_DISPLAY_TEXT to get the list of functions");
9222 $gp_display_text_cmd = "$gp_functions_cmd 1> $result_file 2>> $gp_error_file";
9224 ($error_code, $cmd_output) = execute_system_cmd ($gp_display_text_cmd);
9226 if ($error_code != 0)
9228 $ignore_value = msg_display_text_failure ($gp_display_text_cmd,
9229 $error_code,
9230 $gp_error_file);
9231 gp_message ("abort", $subr_name, "execution terminated");
9232 my $msg = "error code = $error_code - failure executing command $gp_display_text_cmd";
9233 gp_message ("abort", $subr_name, $msg);
9236 return ($outfile,\@sort_fields);
9238 } #-- End of subroutine get_hot_functions
9240 #------------------------------------------------------------------------------
9241 # For a given function name, return the index into "function_info". This
9242 # index gives access to all the meta data for the input function.
9243 #------------------------------------------------------------------------------
9244 sub get_index_function_info
9246 my $subr_name = get_my_name ();
9248 my ($routine_ref, $hex_address_ref, $function_info_ref) = @_;
9250 my $routine = ${ $routine_ref };
9251 my $hex_address = ${ $hex_address_ref };
9252 my @function_info = @{ $function_info_ref };
9254 my $alt_name = $routine;
9255 my $current_address = $hex_address;
9256 my $found_a_match;
9257 my $index_into_function_info;
9258 my $msg;
9259 my $target_tag;
9261 #------------------------------------------------------------------------------
9262 # Check if this function has multiple occurrences.
9263 #------------------------------------------------------------------------------
9264 $msg = "check for multiple occurrences";
9265 gp_message ("debugM", $subr_name, $msg);
9266 $msg = "target routine name = " . $routine;
9267 gp_message ("debugM", $subr_name, $msg);
9269 if (not exists ($g_multi_count_function{$routine}))
9271 #------------------------------------------------------------------------------
9272 # There is only a single occurrence and it is straightforward to get the tag.
9273 #--------------------------------------------------------------------------
9274 ## push (@final_function_names, $routine);
9275 ## KANWEG for my $key (sort keys %g_map_function_to_index)
9276 ## KANWEG {
9277 ## KANWEG $msg = "g_map_function_to_index{". $key . "} = " . $g_map_function_to_index{$key};
9278 ## KANWEG gp_message ("debugXL", $subr_name, $msg);
9279 ## KANWEG }
9280 if (exists ($g_map_function_to_index{$routine}))
9282 $index_into_function_info = $g_map_function_to_index{$routine}[0];
9284 else
9286 my $msg = "no entry for $routine in g_map_function_to_index";
9287 gp_message ("assertion", $subr_name, $msg);
9290 else
9292 #------------------------------------------------------------------------------
9293 # The function name has more than one occurrence and we need to find the one
9294 # that matches with the address.
9295 #------------------------------------------------------------------------------
9296 $found_a_match = $FALSE;
9297 gp_message ("debug", $subr_name, "$routine: occurrences = $g_function_occurrences{$routine}");
9298 for my $ref (keys @{ $g_map_function_to_index{$routine} })
9300 my $ref_index = $g_map_function_to_index{$routine}[$ref];
9301 my $addr_offset = $function_info[$ref_index]{"addressobjtext"};
9303 gp_message ("debug", $subr_name, "$routine: retrieving duplicate entry at ref_index = $ref_index");
9304 gp_message ("debug", $subr_name, "$routine: addr_offset = $addr_offset");
9306 #------------------------------------------------------------------------------
9307 # TBD: Do this substitution when storing "addressobjtext" in function_info.
9308 #------------------------------------------------------------------------------
9309 $addr_offset =~ s/^@\d+://;
9310 gp_message ("debug", $subr_name, "$routine: addr_offset = $addr_offset");
9311 if ($addr_offset eq $current_address)
9313 $found_a_match = $TRUE;
9314 $index_into_function_info = $ref_index;
9315 last;
9319 #------------------------------------------------------------------------------
9320 # If there is no match, something has gone really wrong and we bail out.
9321 #------------------------------------------------------------------------------
9322 if (not $found_a_match)
9324 my $msg = "cannot find the mapping in function_info for function $routine";
9325 gp_message ("assertion", $subr_name, $msg);
9329 return (\$index_into_function_info);
9331 } #-- End of subroutine get_index_function_info
9333 #------------------------------------------------------------------------------
9334 # Get the setting for LANG, or assign a default if it is not set.
9335 #------------------------------------------------------------------------------
9336 sub get_LANG_setting
9338 my $subr_name = get_my_name ();
9340 my $error_code;
9341 my $lang_setting;
9342 my $target_cmd;
9343 my $command_string;
9344 my $LANG;
9346 $target_cmd = $g_mapped_cmds{"printenv"};
9347 #------------------------------------------------------------------------------
9348 # Use the printenv command to get the settings for LANG.
9349 #------------------------------------------------------------------------------
9350 if ($target_cmd eq "road to nowhere")
9352 $error_code = 1;
9354 else
9356 $command_string = $target_cmd . " LANG";
9357 ($error_code, $lang_setting) = execute_system_cmd ($command_string);
9360 if ($error_code == 0)
9362 chomp ($lang_setting);
9363 $LANG = $lang_setting;
9365 else
9367 $LANG = $g_default_setting_lang;
9368 my $msg = "cannot find a setting for LANG - use a default setting";
9369 gp_message ("warning", $subr_name, $msg);
9372 return ($LANG);
9374 } #-- End of subroutine get_LANG_setting
9376 #------------------------------------------------------------------------------
9377 # This subroutine gathers the basic information about the metrics.
9378 #------------------------------------------------------------------------------
9379 sub get_metrics_data
9381 my $subr_name = get_my_name ();
9383 my ($exp_dir_list_ref, $outputdir, $outfile1, $outfile2, $error_file) = @_;
9385 my @exp_dir_list = @{ $exp_dir_list_ref };
9387 my $cmd_options;
9388 my $cmd_output;
9389 my $error_code;
9390 my $expr_name;
9391 my $metrics_cmd;
9392 my $metrics_output;
9393 my $target_cmd;
9395 $expr_name = join (" ", @exp_dir_list);
9397 gp_message ("debug", $subr_name, "expr_name = $expr_name");
9399 #------------------------------------------------------------------------------
9400 # Execute the $GP_DISPLAY_TEXT tool with the appropriate options. The goal is
9401 # to get all the output in files $outfile1 and $outfile2. These are then
9402 # parsed.
9403 #------------------------------------------------------------------------------
9404 $cmd_options = " -viewmode machine -compare off -thread_select all";
9405 $cmd_options .= " -outfile $outfile2";
9406 $cmd_options .= " -fsingle '<Total>' -metric_list $expr_name";
9408 $metrics_cmd = "$GP_DISPLAY_TEXT $cmd_options 1> $outfile1 2> $error_file";
9410 gp_message ("debug", $subr_name, "command used to gather the information:");
9411 gp_message ("debug", $subr_name, $metrics_cmd);
9413 ($error_code, $metrics_output) = execute_system_cmd ($metrics_cmd);
9415 #------------------------------------------------------------------------------
9416 # Error handling. Any error that occurred is fatal and execution
9417 # should be aborted by the caller.
9418 #------------------------------------------------------------------------------
9419 if ($error_code == 0)
9421 gp_message ("debug", $subr_name, "metrics data in files $outfile1 and $outfile2");
9423 else
9425 $target_cmd = $g_mapped_cmds{"cat"} . " $error_file";
9427 ($error_code, $cmd_output) = execute_system_cmd ($target_cmd);
9429 chomp ($cmd_output);
9431 gp_message ("error", $subr_name, "contents of file $error_file:");
9432 gp_message ("error", $subr_name, $cmd_output);
9435 return ($error_code);
9437 } #-- End of subroutine get_metrics_data
9439 #------------------------------------------------------------------------------
9440 # Wrapper that returns the last part of the subroutine name. The assumption is
9441 # that the last part of the input name is of the form "aa::bb" or just "bb".
9442 #------------------------------------------------------------------------------
9443 sub get_my_name
9445 my $called_by = (caller (1))[3];
9446 my @parts = split ("::", $called_by);
9447 return ($parts[$#parts]);
9449 ## my ($the_full_name_ref) = @_;
9451 ## my $the_full_name = ${ $the_full_name_ref };
9452 ## my $last_part;
9454 #------------------------------------------------------------------------------
9455 # If the regex below fails, use the full name."
9456 #------------------------------------------------------------------------------
9457 ## $last_part = $the_full_name;
9459 #------------------------------------------------------------------------------
9460 # Capture the last part if there are multiple parts separated by "::".
9461 #------------------------------------------------------------------------------
9462 ## if ($the_full_name =~ /.*::(.+)$/)
9463 ## {
9464 ## if (defined ($1))
9465 ## {
9466 ## $last_part = $1;
9467 ## }
9468 ## }
9470 ## return (\$last_part);
9472 } #-- End of subroutine get_my_name
9474 #------------------------------------------------------------------------------
9475 # Determine the characteristics of the current system
9476 #------------------------------------------------------------------------------
9477 sub get_system_config_info
9479 #------------------------------------------------------------------------------
9480 # The output from the "uname" command is used for this. Although not all of
9481 # these are currently used, we store all fields in separate variables.
9482 #------------------------------------------------------------------------------
9484 #------------------------------------------------------------------------------
9485 # The options supported on uname from GNU coreutils 8.22:
9486 #------------------------------------------------------------------------------
9487 # -a, --all print all information, in the following order,
9488 # except omit -p and -i if unknown:
9489 # -s, --kernel-name print the kernel name
9490 # -n, --nodename print the network node hostname
9491 # -r, --kernel-release print the kernel release
9492 # -v, --kernel-version print the kernel version
9493 # -m, --machine print the machine hardware name
9494 # -p, --processor print the processor type or "unknown"
9495 # -i, --hardware-platform print the hardware platform or "unknown"
9496 # -o, --operating-system print the operating system
9497 #------------------------------------------------------------------------------
9498 # Sample output:
9499 # 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
9500 #------------------------------------------------------------------------------
9501 my $subr_name = get_my_name ();
9503 my $error_code;
9504 my $hostname_current;
9505 my $ignore_output;
9506 my $msg;
9507 my $target_cmd;
9508 #------------------------------------------------------------------------------
9509 # Test once if the command succeeds. This avoids we need to check every
9510 # specific # command below.
9511 #------------------------------------------------------------------------------
9512 $target_cmd = $g_mapped_cmds{uname};
9513 ($error_code, $ignore_output) = execute_system_cmd ($target_cmd);
9515 if ($error_code != 0)
9516 #------------------------------------------------------------------------------
9517 # This is unlikely to happen, but you never know.
9518 #------------------------------------------------------------------------------
9520 gp_message ("abort", $subr_name, "failure to execute the uname command");
9523 my $kernel_name = qx ($target_cmd -s); chomp ($kernel_name);
9524 my $nodename = qx ($target_cmd -n); chomp ($nodename);
9525 my $kernel_release = qx ($target_cmd -r); chomp ($kernel_release);
9526 my $kernel_version = qx ($target_cmd -v); chomp ($kernel_version);
9527 my $machine = qx ($target_cmd -m); chomp ($machine);
9528 my $processor = qx ($target_cmd -p); chomp ($processor);
9529 my $hardware_platform = qx ($target_cmd -i); chomp ($hardware_platform);
9530 my $operating_system = qx ($target_cmd -o); chomp ($operating_system);
9532 $local_system_config{"kernel_name"} = $kernel_name;
9533 $local_system_config{"nodename"} = $nodename;
9534 $local_system_config{"kernel_release"} = $kernel_release;
9535 $local_system_config{"kernel_version"} = $kernel_version;
9536 $local_system_config{"machine"} = $machine;
9537 $local_system_config{"processor"} = $processor;
9538 $local_system_config{"hardware_platform"} = $hardware_platform;
9539 $local_system_config{"operating_system"} = $operating_system;
9541 gp_message ("debug", $subr_name, "the output from the $target_cmd command is split into the following variables:");
9542 gp_message ("debug", $subr_name, "kernel_name = $kernel_name");
9543 gp_message ("debug", $subr_name, "nodename = $nodename");
9544 gp_message ("debug", $subr_name, "kernel_release = $kernel_release");
9545 gp_message ("debug", $subr_name, "kernel_version = $kernel_version");
9546 gp_message ("debug", $subr_name, "machine = $machine");
9547 gp_message ("debug", $subr_name, "processor = $processor");
9548 gp_message ("debug", $subr_name, "hardware_platform = $hardware_platform");
9549 gp_message ("debug", $subr_name, "operating_system = $operating_system");
9551 #------------------------------------------------------------------------------
9552 # Check if the system we are running on is supported.
9553 #------------------------------------------------------------------------------
9554 my $is_supported = ${ check_support_for_processor (\$machine) };
9556 if (not $is_supported)
9558 $msg = "the $machine instruction set architecture is not supported";
9559 gp_message ("error", $subr_name, $msg);
9560 gp_message ("diag", $subr_name, "Error: " . $msg);
9562 $msg = "temporarily ignored for development purposes";
9563 gp_message ("error", $subr_name, $msg);
9565 $g_total_error_count++;
9566 exit (0);
9568 #------------------------------------------------------------------------------
9569 # The current hostname is used to compare against the hostname(s) found in the
9570 # experiment directories.
9571 #------------------------------------------------------------------------------
9572 $target_cmd = $g_mapped_cmds{hostname};
9573 $hostname_current = qx ($target_cmd); chomp ($hostname_current);
9574 $error_code = ${^CHILD_ERROR_NATIVE};
9576 if ($error_code == 0)
9578 $local_system_config{"hostname_current"} = $hostname_current;
9580 else
9581 #------------------------------------------------------------------------------
9582 # This is unlikely to happen, but you never know.
9583 #------------------------------------------------------------------------------
9585 gp_message ("abort", $subr_name, "failure to execute the hostname command");
9587 for my $key (sort keys %local_system_config)
9589 gp_message ("debug", $subr_name, "local_system_config{$key} = $local_system_config{$key}");
9592 return (0);
9594 } #-- End of subroutine get_system_config_info
9596 #------------------------------------------------------------------------------
9597 # This subroutine prints a message. Several types of messages are supported.
9598 # In case the type is "abort", or "error", execution is terminated.
9600 # Note that "debug", "warning", and "error" mode, the name of the calling
9601 # subroutine is truncated to 30 characters. In case the name is longer,
9602 # a warning message # is issued so you know this has happened.
9604 # Note that we use lcfirst () and ucfirst () to enforce whether the first
9605 # character is printed in lower or uppercase. It is nothing else than a
9606 # convenience, but creates more consistency across messages.
9607 #------------------------------------------------------------------------------
9608 sub gp_message
9610 my $subr_name = get_my_name ();
9612 my ($action, $caller_name, $comment_line) = @_;
9614 #------------------------------------------------------------------------------
9615 # The debugXL identifier is special. It is accepted, but otherwise ignored.
9616 # This allows to (temporarily) disable debug print statements, but keep them
9617 # around.
9618 #------------------------------------------------------------------------------
9619 my %supported_identifiers = (
9620 "verbose" => "[Verbose]",
9621 "debug" => "[Debug]",
9622 "error" => "[Error]",
9623 "warning" => "[Warning]",
9624 "abort" => "[Abort]",
9625 "assertion" => "[Assertion error]",
9626 "diag" => "",
9629 my $debug_size;
9630 my $identifier;
9631 my $fixed_size_name;
9632 my $ignore_value;
9633 my $string_limit = 30;
9634 my $strlen = length ($caller_name);
9635 my $trigger_debug = $FALSE;
9636 my $truncated_name;
9637 my $msg;
9639 if ($action =~ /debug\s*(.+)/)
9641 if (defined ($1))
9643 my $orig_value = $1;
9644 $debug_size = lc ($1);
9646 if ($debug_size =~ /^s$|^m$|^l$|^xl$/)
9648 if ($g_debug_size{$debug_size})
9650 #------------------------------------------------------------------------------
9651 # All we need to know is whether a debug action is requested and whether the
9652 # size has been enabled. By setting $action to "debug", the code below is
9653 # simplified. Note that only using $trigger_debug below is actually sufficient.
9654 #------------------------------------------------------------------------------
9655 $trigger_debug = $TRUE;
9658 else
9660 die "$subr_name: debug size $orig_value is not supported";
9662 $action = "debug";
9665 elsif ($action eq "debug")
9667 $trigger_debug = $TRUE;
9670 #------------------------------------------------------------------------------
9671 # Catch any non-supported identifier.
9672 #------------------------------------------------------------------------------
9673 if (defined ($supported_identifiers{$action}))
9675 $identifier = $supported_identifiers{$action};
9677 else
9679 die ("$subr_name - input error: $action is not supported");
9681 if (($action eq "debug") and (not $g_debug))
9683 $trigger_debug = $FALSE;
9686 #------------------------------------------------------------------------------
9687 # Unconditionally buffer all warning messages. These are available through the
9688 # index.html page and cannot be disabled.
9690 # If the quiet mode has been enabled, warnings are not printed though.
9691 #------------------------------------------------------------------------------
9692 if ($action eq "warning")
9694 #------------------------------------------------------------------------------
9695 # Remove any leading <br>, capitalize the first letter, and put the <br> back
9696 # before storing the message in the buffer.
9697 #------------------------------------------------------------------------------
9698 if ($comment_line =~ /^$g_html_new_line/)
9700 $msg = $comment_line;
9701 $msg =~ s/$g_html_new_line//;
9702 $comment_line = $g_html_new_line . ucfirst ($msg);
9704 push (@g_warning_msgs, $comment_line);
9706 else
9708 push (@g_warning_msgs, ucfirst ($comment_line));
9712 #------------------------------------------------------------------------------
9713 # Unconditionally buffer all errror messages. These will be printed prior to
9714 # terminate execution.
9715 #------------------------------------------------------------------------------
9716 if ($action eq "error")
9717 #------------------------------------------------------------------------------
9718 # Remove any leading <br>, capitalize the first letter, and put the <br> back.
9719 #------------------------------------------------------------------------------
9721 if ($comment_line =~ /^$g_html_new_line/)
9723 $msg = $comment_line;
9724 $msg =~ s/$g_html_new_line//;
9725 $comment_line = $g_html_new_line . ucfirst ($msg);
9727 push (@g_error_msgs, $comment_line);
9729 else
9731 push (@g_error_msgs, ucfirst ($comment_line));
9735 #------------------------------------------------------------------------------
9736 # Quick return in several cases. Note that "debug", "verbose", "warning", and
9737 # "diag" messages are suppressed in quiet mode, but "error", "abort" and
9738 # "assertion" always pass.
9739 #------------------------------------------------------------------------------
9740 if ((
9741 ($action eq "verbose") and (not $g_verbose))
9742 or (($action eq "debug") and (not $trigger_debug))
9743 or (($action eq "verbose") and ($g_quiet))
9744 or (($action eq "debug") and ($g_quiet))
9745 or (($action eq "warning") and ($g_quiet))
9746 or (($action eq "diag") and ($g_quiet)))
9748 return (0);
9751 #------------------------------------------------------------------------------
9752 # In diag mode, just print the input line and nothing else.
9753 #------------------------------------------------------------------------------
9754 if ((
9755 $action eq "debug")
9756 or ($action eq "abort")
9757 or ($action eq "assertion"))
9758 ## or ($action eq "error"))
9760 #------------------------------------------------------------------------------
9761 # Construct the string to be printed. Include an identifier and the name of
9762 # the function.
9763 #------------------------------------------------------------------------------
9764 if ($strlen > $string_limit)
9766 $truncated_name = substr ($caller_name, 0, $string_limit);
9767 $fixed_size_name = sprintf ("%-"."$string_limit"."s", $truncated_name);
9768 print "Warning in $subr_name - the name of the caller is: " .
9769 $caller_name . "\n";
9770 print "Warning in $subr_name - the string length is $strlen and " .
9771 "exceeds $string_limit\n";
9773 else
9775 $fixed_size_name = sprintf ("%-"."$string_limit"."s", $caller_name);
9778 ## if (($action eq "error") or ($action eq "abort"))
9779 if ($action eq "abort")
9780 #------------------------------------------------------------------------------
9781 # Enforce that the message starts with a lowercase symbol. Since these are
9782 # user errors, the name of the routine is not shown. The same for "abort".
9783 # If you want to display the routine name too, use an assertion.
9784 #------------------------------------------------------------------------------
9786 my $error_identifier = $supported_identifiers{"error"};
9787 if (@g_error_msgs)
9789 $ignore_value = print_errors_buffer (\$error_identifier);
9791 printf ("%-9s %s", $identifier, ucfirst ($comment_line));
9792 printf (" - %s\n", "execution is terminated");
9794 elsif ($action eq "assertion")
9795 #------------------------------------------------------------------------------
9796 # Enforce that the message starts with a lowercase symbol.
9797 #------------------------------------------------------------------------------
9799 #------------------------------------------------------------------------------
9800 # The lines are too long, but breaking the argument list gives this warning:
9801 # printf (...) interpreted as function
9802 #------------------------------------------------------------------------------
9803 printf ("%-17s %-30s", $identifier, $fixed_size_name);
9804 printf (" - %s\n", $comment_line);
9806 elsif (($action eq "debug") and ($trigger_debug))
9807 #------------------------------------------------------------------------------
9808 # Debug messages are printed "as is". Avoids issues when searching for them ;-)
9809 #------------------------------------------------------------------------------
9811 printf ("%-9s %-30s", $identifier, $fixed_size_name);
9812 printf (" - %s\n", $comment_line);
9814 else
9815 #------------------------------------------------------------------------------
9816 # Enforce that the message starts with a lowercase symbol.
9817 #------------------------------------------------------------------------------
9819 printf ("%-9s %-30s", $identifier, $fixed_size_name);
9820 printf (" - %s\n", $comment_line);
9823 elsif ($action eq "verbose")
9824 #------------------------------------------------------------------------------
9825 # The first character in the verbose message is capatilized.
9826 #------------------------------------------------------------------------------
9828 printf ("%s\n", ucfirst ($comment_line));
9830 elsif ($action eq "diag")
9831 #------------------------------------------------------------------------------
9832 # The diag messages are meant to be diagnostics. Only the comment line is
9833 # printed.
9834 #------------------------------------------------------------------------------
9836 printf ("%s\n", $comment_line);
9837 return (0);
9840 #------------------------------------------------------------------------------
9841 # Terminate execution in case the identifier is "abort".
9842 #------------------------------------------------------------------------------
9843 if (($action eq "abort") or ($action eq "assertion"))
9845 ## print "ABORT temporarily disabled for testing purposes\n";
9846 exit (-1);
9848 else
9850 return (0);
9853 } #-- End of subroutine gp_message
9855 #------------------------------------------------------------------------------
9856 # Create an HTML page with the warnings. If there are no warnings, include
9857 # line to this extent. The alternative is to supporess the entire page, but
9858 # that breaks the consistency in the output.
9859 #------------------------------------------------------------------------------
9860 sub html_create_warnings_page
9862 my $subr_name = get_my_name ();
9864 my ($outputdir_ref) = @_;
9866 my $outputdir = ${ $outputdir_ref };
9868 my $file_title;
9869 my $html_acknowledgement;
9870 my $html_end;
9871 my $html_header;
9872 my $html_home_left;
9873 my $html_home_right;
9874 my $html_title_header;
9875 my $msg_no_warnings = "There are no warning messages issued.";
9876 my $page_title;
9877 my $position_text;
9878 my $size_text;
9880 my $outfile = $outputdir . $g_html_base_file_name{"warnings"} . ".html";
9882 gp_message ("debug", $subr_name, "outfile = $outfile");
9884 open (WARNINGS_OUT, ">", $outfile)
9885 or die ("unable to open $outfile for writing - '$!'");
9886 gp_message ("debug", $subr_name, "opened file $outfile for writing");
9888 gp_message ("debug", $subr_name, "building warning file $outfile");
9890 #------------------------------------------------------------------------------
9891 # Generate some of the structures used in the HTML output.
9892 #------------------------------------------------------------------------------
9893 $file_title = "Warning messages";
9894 $html_header = ${ create_html_header (\$file_title) };
9895 $html_home_right = ${ generate_home_link ("right") };
9897 $page_title = "Warning Messages";
9898 $size_text = "h2";
9899 $position_text = "center";
9900 $html_title_header = ${ generate_a_header (\$page_title, \$size_text, \$position_text) };
9902 #------------------------------------------------------------------------------
9903 # Get the acknowledgement, return to main link, and final html statements.
9904 #------------------------------------------------------------------------------
9905 $html_home_left = ${ generate_home_link ("left") };
9906 $html_acknowledgement = ${ create_html_credits () };
9907 $html_end = ${ terminate_html_document () };
9909 #------------------------------------------------------------------------------
9910 # Generate the HTML file.
9911 #------------------------------------------------------------------------------
9912 print WARNINGS_OUT $html_header;
9913 print WARNINGS_OUT $html_home_right;
9914 print WARNINGS_OUT $html_title_header;
9916 if ($g_total_warning_count > 0)
9918 print WARNINGS_OUT "<pre>\n";
9919 print WARNINGS_OUT "$_\n" for @g_warning_msgs;
9920 print WARNINGS_OUT "</pre>\n";
9922 else
9924 print WARNINGS_OUT $msg_no_warnings;
9927 print WARNINGS_OUT $html_home_left;
9928 print WARNINGS_OUT "<br>\n";
9929 print WARNINGS_OUT $html_acknowledgement;
9930 print WARNINGS_OUT $html_end;
9932 close (WARNINGS_OUT);
9934 return (0);
9936 } #-- End of subroutine html_create_warnings_page
9938 #------------------------------------------------------------------------------
9939 # Generate the HTML with the experiment summary.
9940 #------------------------------------------------------------------------------
9941 sub html_generate_exp_summary
9943 my $subr_name = get_my_name ();
9945 my ($outputdir_ref, $experiment_data_ref) = @_;
9947 my $outputdir = ${ $outputdir_ref };
9948 my @experiment_data = @{ $experiment_data_ref };
9949 my $file_title;
9950 my $outfile;
9951 my $page_title;
9952 my $size_text;
9953 my $position_text;
9954 my $html_header;
9955 my $html_home;
9956 my $html_title_header;
9957 my $html_acknowledgement;
9958 my $html_end;
9959 my @html_exp_table_data = ();
9960 my $html_exp_table_data_ref;
9961 my @table_execution_stats = ();
9962 my $table_execution_stats_ref;
9964 gp_message ("debug", $subr_name, "outputdir = $outputdir");
9965 $outputdir = append_forward_slash ($outputdir);
9966 gp_message ("debug", $subr_name, "outputdir = $outputdir");
9968 $file_title = "Experiment information";
9969 $page_title = "Experiment Information";
9970 $size_text = "h2";
9971 $position_text = "center";
9972 $html_header = ${ create_html_header (\$file_title) };
9973 $html_home = ${ generate_home_link ("right") };
9975 $html_title_header = ${ generate_a_header (\$page_title, \$size_text, \$position_text) };
9977 $outfile = $outputdir . $g_html_base_file_name{"experiment_info"} . ".html";
9978 open (EXP_INFO, ">", $outfile)
9979 or die ("unable to open $outfile for writing - '$!'");
9980 gp_message ("debug", $subr_name, "opened file $outfile for writing");
9982 print EXP_INFO $html_header;
9983 print EXP_INFO $html_home;
9984 print EXP_INFO $html_title_header;
9986 ($html_exp_table_data_ref, $table_execution_stats_ref) = html_generate_table_data ($experiment_data_ref);
9988 @html_exp_table_data = @{ $html_exp_table_data_ref };
9989 @table_execution_stats = @{ $table_execution_stats_ref };
9991 print EXP_INFO "$_" for @html_exp_table_data;
9993 ## print EXP_INFO "<pre>\n";
9994 ## print EXP_INFO "$_\n" for @html_caller_callee;
9995 ## print EXP_INFO "</pre>\n";
9997 #------------------------------------------------------------------------------
9998 # Get the acknowledgement, return to main link, and final html statements.
9999 #------------------------------------------------------------------------------
10000 $html_home = ${ generate_home_link ("left") };
10001 $html_acknowledgement = ${ create_html_credits () };
10002 $html_end = ${ terminate_html_document () };
10004 print EXP_INFO $html_home;
10005 print EXP_INFO "<br>\n";
10006 print EXP_INFO $html_acknowledgement;
10007 print EXP_INFO $html_end;
10009 close (EXP_INFO);
10011 return (\@table_execution_stats);
10013 } #-- End of subroutine html_generate_exp_summary
10015 #------------------------------------------------------------------------------
10016 # Generate the index.html file.
10017 #------------------------------------------------------------------------------
10018 sub html_generate_index
10020 my $subr_name = get_my_name ();
10022 my ($outputdir_ref, $html_first_metric_file_ref, $summary_metrics_ref,
10023 $number_of_metrics_ref, $function_info_ref, $function_address_info_ref,
10024 $sort_fields_ref, $exp_dir_list_ref, $addressobjtextm_ref,
10025 $metric_description_reversed_ref, $table_execution_stats_ref) = @_;
10027 my $outputdir = ${ $outputdir_ref };
10028 my $html_first_metric_file = ${ $html_first_metric_file_ref };
10029 my $summary_metrics = ${ $summary_metrics_ref };
10030 my $number_of_metrics = ${ $number_of_metrics_ref };
10031 my @function_info = @{ $function_info_ref };
10032 my %function_address_info = %{ $function_address_info_ref };
10033 my @sort_fields = @{ $sort_fields_ref };
10034 my @exp_dir_list = @{ $exp_dir_list_ref };
10035 my %addressobjtextm = %{ $addressobjtextm_ref };
10036 my %metric_description_reversed = %{ $metric_description_reversed_ref };
10037 my @table_execution_stats = @{ $table_execution_stats_ref };
10039 my @file_contents = ();
10041 my $acknowledgement;
10042 my @abs_path_exp_dirs = ();
10043 my $input_experiments;
10044 my $target_function;
10045 my $html_line;
10046 my $ftag;
10047 my $max_length = 0;
10048 my %html_source_functions = ();
10049 my $html_header;
10050 my @experiment_directories = ();
10051 my $html_acknowledgement;
10052 my $html_file_title;
10053 my $html_output_file;
10054 my $html_function_view;
10055 my $html_caller_callee_view;
10056 my $html_experiment_info;
10057 my $html_warnings_page;
10058 my $href_link;
10059 my $file_title;
10060 my $html_gprofng;
10061 my $html_end;
10062 my $max_length_metrics;
10063 my $page_title;
10064 my $size_text;
10065 my $position_text;
10067 my $ln;
10068 my $base;
10069 my $base_index_page;
10070 my $infile;
10071 my $outfile;
10072 my $rec;
10073 my $skip;
10074 my $callsize;
10075 my $dest;
10076 my $final_string;
10077 my @headers;
10078 my $header;
10079 my $sort_index;
10080 my $pc_address;
10081 my $anchor;
10082 my $directory_name;
10083 my $f2;
10084 my $f3;
10085 my $file;
10086 my $sline;
10087 my $src;
10088 my $srcfile_name;
10089 my $tmp1;
10090 my $tmp2;
10091 my $fullsize;
10092 my $regf2;
10093 my $trimsize;
10094 my $EIL;
10095 my $EEIL;
10096 my $AOBJ;
10097 my $RI;
10098 my $HDR;
10099 my $CALLER_CALLEE;
10100 my $NAME;
10101 my $SRC;
10102 my $TRIMMED;
10104 #------------------------------------------------------------------------------
10105 # Add a forward slash to make it easier when creating file names.
10106 #------------------------------------------------------------------------------
10107 $outputdir = append_forward_slash ($outputdir);
10108 gp_message ("debug", $subr_name, "outputdir = $outputdir");
10110 my $LANG = $g_locale_settings{"LANG"};
10111 my $decimal_separator = $g_locale_settings{"decimal_separator"};
10113 $input_experiments = join (", ", @exp_dir_list);
10115 for my $i (0 .. $#exp_dir_list)
10117 my $dir = get_basename ($exp_dir_list[$i]);
10118 push @abs_path_exp_dirs, $dir;
10120 $input_experiments = join (", ", @abs_path_exp_dirs);
10122 gp_message ("debug", $subr_name, "input_experiments = $input_experiments");
10124 #------------------------------------------------------------------------------
10125 # TBD: Pass in the values for $expr_name and $cmd
10126 #------------------------------------------------------------------------------
10127 $html_file_title = "Main index page";
10129 @experiment_directories = split (",", $input_experiments);
10130 $html_acknowledgement = ${ create_html_credits () };
10132 $html_end = ${ terminate_html_document () };
10134 $html_output_file = $outputdir . $g_html_base_file_name{"index"} . ".html";
10136 open (INDEX, ">", $html_output_file)
10137 or die ("$subr_name - unable to open file $html_output_file for writing - '$!'");
10138 gp_message ("debug", $subr_name, "opened file $html_output_file for writing");
10140 $page_title = "GPROFNG Performance Analysis";
10141 $size_text = "h1";
10142 $position_text = "center";
10143 $html_gprofng = ${ generate_a_header (\$page_title, \$size_text, \$position_text) };
10145 $html_header = ${ create_html_header (\$html_file_title) };
10147 print INDEX $html_header;
10148 print INDEX $html_gprofng;
10149 print INDEX "$_" for @g_html_experiment_stats;
10150 print INDEX "$_" for @table_execution_stats;
10152 $html_experiment_info = "<a href=\'";
10153 $html_experiment_info .= $g_html_base_file_name{"experiment_info"} . ".html";
10154 $html_experiment_info .= "\'><h3>Experiment Details</h3></a>\n";
10156 $html_warnings_page = "<a href=\'";
10157 $html_warnings_page .= $g_html_base_file_name{"warnings"} . ".html";
10158 $html_warnings_page .= "\'><h3>Warnings (" . $g_total_warning_count;
10159 $html_warnings_page .= ")</h3></a>\n";
10161 $html_function_view = "<a href=\'";
10162 $html_function_view .= $html_first_metric_file;
10163 $html_function_view .= "\'><h3>Function View</h3></a>\n";
10165 $html_caller_callee_view = "<a href=\'";
10166 $html_caller_callee_view .= $g_html_base_file_name{"caller_callee"} . ".html";
10167 $html_caller_callee_view .= "\'><h3>Caller Callee View</h3></a>\n";
10169 print INDEX "<br>\n";
10170 ## print INDEX "<b>\n";
10171 print INDEX $html_experiment_info;
10172 print INDEX $html_warnings_page;
10173 ## print INDEX "<br>\n";
10174 ## print INDEX "<br>\n";
10175 print INDEX $html_function_view;
10176 ## print INDEX "<br>\n";
10177 ## print INDEX "<br>\n";
10178 print INDEX $html_caller_callee_view;
10179 ## print INDEX "</b>\n";
10180 ## print INDEX "<br>\n";
10181 ## print INDEX "<br>\n";
10183 print INDEX $html_acknowledgement;
10184 print INDEX $html_end;
10186 close (INDEX);
10188 gp_message ("debug", $subr_name, "closed file $html_output_file");
10190 return (0);
10192 } #-- End of subroutine html_generate_index
10194 #------------------------------------------------------------------------------
10195 # Generate the entries for the tables with the experiment info.
10196 #------------------------------------------------------------------------------
10197 sub html_generate_table_data
10199 my $subr_name = get_my_name ();
10201 my ($experiment_data_ref) = @_;
10203 my @experiment_data = ();
10204 my @html_exp_table_data = ();
10205 my $html_line;
10206 ## my $html_header_line;
10207 my $entry_name;
10208 my $key;
10209 my $size_text;
10210 my $position_text;
10211 my $title_table_1;
10212 my $title_table_2;
10213 my $title_table_3;
10214 my $title_table_summary;
10215 my $html_table_title;
10217 my @experiment_table_1_def = ();
10218 my @experiment_table_2_def = ();
10219 my @experiment_table_3_def = ();
10220 my @exp_table_summary_def = ();
10221 my @experiment_table_1 = ();
10222 my @experiment_table_2 = ();
10223 my @experiment_table_3 = ();
10224 my @exp_table_summary = ();
10225 my @exp_table_selection = ();
10227 @experiment_data = @{ $experiment_data_ref };
10229 for my $i (sort keys @experiment_data)
10231 for my $fields (sort keys %{ $experiment_data[$i] })
10233 gp_message ("debugXL", $subr_name, "$i => experiment_data[$i]{$fields} = $experiment_data[$i]{$fields}");
10237 $title_table_1 = "Target System Configuration";
10238 $title_table_2 = "Experiment Statistics";
10239 $title_table_3 = "Run Time Statistics";
10240 $title_table_summary = "Main Statistics";
10242 $size_text = "h3";
10243 $position_text = "left";
10245 push @experiment_table_1_def, { name => "Experiment name" , key => "exp_name_short"};
10246 push @experiment_table_1_def, { name => "Hostname" , key => "hostname"};
10247 push @experiment_table_1_def, { name => "Operating system", key => "OS"};
10248 push @experiment_table_1_def, { name => "Architecture", key => "architecture"};
10249 push @experiment_table_1_def, { name => "Page size", key => "page_size"};
10251 push @experiment_table_2_def, { name => "Target command" , key => "target_cmd"};
10252 push @experiment_table_2_def, { name => "Date command executed" , key => "start_date"};
10253 push @experiment_table_2_def, { name => "Data collection duration", key => "data_collection_duration"};
10254 push @experiment_table_2_def, { name => "End time of the experiment", key => "end_experiment"};
10256 push @experiment_table_3_def, { name => "User CPU time (seconds)", key => "user_cpu_time"};
10257 ## push @experiment_table_3_def, { name => "User CPU time (percentage)", key => "user_cpu_percentage"};
10258 push @experiment_table_3_def, { name => "System CPU time (seconds)", key => "system_cpu_time"};
10259 ## push @experiment_table_3_def, { name => "System CPU time (percentage)", key => "system_cpu_percentage"};
10260 push @experiment_table_3_def, { name => "Sleep time (seconds)", key => "sleep_time"};
10261 ## push @experiment_table_3_def, { name => "Sleep time (percentage)", key => "sleep_percentage"};
10263 push @exp_table_summary_def, { name => "Experiment name" , key => "exp_name_short"};
10264 push @exp_table_summary_def, { name => "Hostname" , key => "hostname"};
10265 push @exp_table_summary_def, { name => "User CPU time (seconds)", key => "user_cpu_time"};
10266 push @exp_table_summary_def, { name => "System CPU time (seconds)", key => "system_cpu_time"};
10267 push @exp_table_summary_def, { name => "Sleep time (seconds)", key => "sleep_time"};
10269 $html_table_title = ${ generate_a_header (\$title_table_1, \$size_text, \$position_text) };
10271 push (@html_exp_table_data, $html_table_title);
10273 @experiment_table_1 = @{ create_table (\@experiment_data, \@experiment_table_1_def) };
10275 push (@html_exp_table_data, @experiment_table_1);
10277 $html_table_title = ${ generate_a_header (\$title_table_2, \$size_text, \$position_text) };
10279 push (@html_exp_table_data, $html_table_title);
10281 @experiment_table_2 = @{ create_table (\@experiment_data, \@experiment_table_2_def) };
10283 push (@html_exp_table_data, @experiment_table_2);
10285 $html_table_title = ${ generate_a_header (\$title_table_3, \$size_text, \$position_text) };
10287 push (@html_exp_table_data, $html_table_title);
10289 @experiment_table_3 = @{ create_table (\@experiment_data, \@experiment_table_3_def) };
10291 push (@html_exp_table_data, @experiment_table_3);
10293 $html_table_title = ${ generate_a_header (\$title_table_summary, \$size_text, \$position_text) };
10295 push (@exp_table_summary, $html_table_title);
10297 @exp_table_selection = @{ create_table (\@experiment_data, \@exp_table_summary_def) };
10299 push (@exp_table_summary, @exp_table_selection);
10301 return (\@html_exp_table_data, \@exp_table_summary);
10303 } #-- End of subroutine html_generate_table_data
10305 #------------------------------------------------------------------------------
10306 # Generate the HTML text to print in case a file is empty.
10307 #------------------------------------------------------------------------------
10308 sub html_text_empty_file
10310 my $subr_name = get_my_name ();
10312 my ($comment_ref, $error_file_ref) = @_;
10314 my $comment;
10315 my $error_file;
10316 my $error_message;
10317 my $file_title;
10318 my $html_end;
10319 my $html_header;
10320 my $html_home;
10322 my @html_empty_file = ();
10324 $comment = ${ $comment_ref };
10325 $error_file = ${ $error_file_ref };
10327 $file_title = "File is empty";
10328 $html_header = ${ create_html_header (\$file_title) };
10329 $html_end = ${ terminate_html_document () };
10330 $html_home = ${ generate_home_link ("left") };
10332 push (@html_empty_file, $html_header);
10334 $error_message = "<b>" . $comment . "</b>";
10335 $error_message = set_background_color_string ($error_message, $g_html_color_scheme{"error_message"});
10336 push (@html_empty_file, $error_message);
10338 if (not is_file_empty ($error_file))
10340 $error_message = "<p><em>Check file $error_file for more information</em></p>";
10342 push (@html_empty_file, $error_message);
10343 push (@html_empty_file, $html_home);
10344 push (@html_empty_file, "<br>");
10345 push (@html_empty_file, $g_html_credits_line);
10346 push (@html_empty_file, $html_end);
10348 return (\@html_empty_file);
10350 } #-- End of subroutine html_text_empty_file
10352 #------------------------------------------------------------------------------
10353 # This subroutine checks if a file is empty and returns $TRUE or $FALSE.
10354 #------------------------------------------------------------------------------
10355 sub is_file_empty
10357 my $subr_name = get_my_name ();
10359 my ($filename) = @_;
10361 my $is_empty;
10362 my $file_stat;
10363 my $msg;
10364 my $size;
10366 chomp ($filename);
10368 if (not -e $filename)
10370 #------------------------------------------------------------------------------
10371 # The return value is used in the caller. This is why we return the empty
10372 # string in case the file does not exist.
10373 #------------------------------------------------------------------------------
10374 $msg = "filename = $filename not found";
10375 gp_message ("debug", $subr_name, $msg);
10376 $is_empty = $TRUE;
10378 else
10380 $file_stat = stat ($filename);
10381 $size = $file_stat->size;
10382 $is_empty = ($size == 0) ? $TRUE : $FALSE;
10385 $msg = "filename = $filename size = $size is_empty = $is_empty";
10386 gp_message ("debug", $subr_name, $msg);
10388 return ($is_empty);
10390 } #-- End of subroutine is_file_empty
10392 #------------------------------------------------------------------------------
10393 # Check if a file is executable and return $TRUE or $FALSE.
10394 #------------------------------------------------------------------------------
10395 sub is_file_executable
10397 my $subr_name = get_my_name ();
10399 my ($filename) = @_;
10401 my $file_permissions;
10402 my $index_offset;
10403 my $is_executable;
10404 my $mode;
10405 my $number_of_bytes;
10406 my @permission_settings = ();
10407 my %permission_values = ();
10409 chomp ($filename);
10411 gp_message ("debug", $subr_name, "check if filename = $filename is executable");
10413 if (not -e $filename)
10415 #------------------------------------------------------------------------------
10416 # The return value is used in the caller. This is why we return the empty
10417 # string in case the file does not exist.
10418 #------------------------------------------------------------------------------
10419 gp_message ("debug", $subr_name, "filename = $filename not found");
10420 $is_executable = $FALSE;
10422 else
10424 $mode = stat ($filename)->mode;
10426 gp_message ("debugXL", $subr_name, "mode = $mode");
10427 #------------------------------------------------------------------------------
10428 # Get username. We currently do not do anything with this though and the
10429 # code is commented out.
10431 # my $my_name = getlogin () || getpwuid($<) || "Kilroy";
10432 # gp_message ("debug", $subr_name, "my_name = $my_name");
10433 #------------------------------------------------------------------------------
10435 #------------------------------------------------------------------------------
10436 # Convert file permissions to octal, split the individual numbers and store
10437 # the values for the respective users.
10438 #------------------------------------------------------------------------------
10439 $file_permissions = sprintf("%o", $mode & 07777);
10441 @permission_settings = split (//, $file_permissions);
10443 $number_of_bytes = scalar (@permission_settings);
10445 gp_message ("debugXL", $subr_name, "file_permissions = $file_permissions");
10446 gp_message ("debugXL", $subr_name, "permission_settings = @permission_settings");
10447 gp_message ("debugXL", $subr_name, "number_of_settings = $number_of_bytes");
10449 if ($number_of_bytes == 4)
10451 $index_offset = 1;
10453 elsif ($number_of_bytes == 3)
10455 $index_offset = 0;
10457 else
10459 my $msg = "unexpected number of $number_of_bytes bytes " .
10460 "in permission settings: @permission_settings";
10461 gp_message ("assertion", $subr_name, $msg);
10464 $permission_values{user} = $permission_settings[$index_offset++];
10465 $permission_values{group} = $permission_settings[$index_offset++];
10466 $permission_values{other} = $permission_settings[$index_offset];
10468 #------------------------------------------------------------------------------
10469 # The executable bit should be set for user, group and other. If this fails
10470 # we mark the file as not executable. Note that this is gprofng specific.
10471 #------------------------------------------------------------------------------
10472 $is_executable = $TRUE;
10473 for my $k (keys %permission_values)
10475 my $msg = "permission_values{" . $k . "} = " .
10476 $permission_values{$k};
10477 gp_message ("debugXL", $subr_name, $msg);
10479 if ($permission_values{$k} % 2 == 0)
10481 $is_executable = $FALSE;
10482 last;
10487 gp_message ("debug", $subr_name, "is_executable = $is_executable");
10489 return ($is_executable);
10491 } #-- End of subroutine is_file_executable
10493 #------------------------------------------------------------------------------
10494 # Print a message after a failure in $GP_DISPLAY_TEXT.
10495 #------------------------------------------------------------------------------
10496 sub msg_display_text_failure
10498 my $subr_name = get_my_name ();
10500 my ($gp_display_text_cmd, $error_code, $error_file) = @_;
10502 my $msg;
10504 $msg = "error code = $error_code - failure executing the following command:";
10505 gp_message ("error", $subr_name, $msg);
10507 gp_message ("error", $subr_name, $gp_display_text_cmd);
10509 $msg = "check file $error_file for more details";
10510 gp_message ("error", $subr_name, $msg);
10512 return (0);
10514 } #-- End of subroutine msg_display_text_failure
10516 #------------------------------------------------------------------------------
10517 # TBD. Still needed? I think this entire function and usage can be removed.
10518 #------------------------------------------------------------------------------
10519 sub name_regex
10521 my $subr_name = get_my_name ();
10523 my ($metric_description_ref, $metrics, $field, $file) = @_;
10525 my %metric_description = %{ $metric_description_ref };
10527 my $msg;
10529 my @splitted_metrics;
10530 my $splitted_metrics;
10531 my $m;
10532 my $mf;
10533 my $nf;
10534 my $re = "This value should never show up anywhere";
10535 my $Xre;
10536 #------------------------------------------------------------------------------
10537 # Make sure to check for these to have a value.
10538 #------------------------------------------------------------------------------
10539 my $noPCfile = undef;
10540 my $reported_metrics = undef;
10541 my @reported_metrics;
10542 my $hdr_regex;
10543 my $hdr_href_regex;
10544 my $hdr_src_regex;
10545 my $new_metrics;
10546 my $pre;
10547 my $post;
10548 my $rat;
10549 my @moo = ();
10551 my $gp_metrics_file;
10552 my $gp_metrics_dir;
10553 my $suffix_not_used;
10555 my $is_calls = $FALSE;
10556 my $is_calltree = $FALSE;
10558 gp_message ("debugXL", $subr_name,"1:metrics->$metrics<- field->$field<- file->$file<-");
10560 #------------------------------------------------------------------------------
10561 # According to https://perldoc.perl.org/File::Basename, both dirname and
10562 # basename are not reliable and fileparse () is recommended instead.
10564 # Note that $gp_metrics_dir has a trailing "/".
10565 #------------------------------------------------------------------------------
10566 ($gp_metrics_file, $gp_metrics_dir, $suffix_not_used) = fileparse ($file, ".sort.func-PC");
10568 gp_message ("debugXL", $subr_name, "gp_metrics_dir = $gp_metrics_dir gp_metrics_file = $gp_metrics_file");
10569 gp_message ("debugXL", $subr_name, "suffix_not_used = $suffix_not_used");
10571 if ($gp_metrics_file eq "calls")
10573 $is_calls = $TRUE;
10575 if ($gp_metrics_file eq "calltree")
10577 $is_calltree = $TRUE;
10580 $gp_metrics_file = "gp-metrics-" . $gp_metrics_file . "-PC";
10581 $gp_metrics_file = $gp_metrics_dir . $gp_metrics_file;
10583 gp_message ("debugXL", $subr_name, "gp_metrics_file is $gp_metrics_file");
10585 open (GP_METRICS, "<", $gp_metrics_file)
10586 or die ("$subr_name - unable to open gp_metrics file $gp_metrics_file for reading - '$!'");
10587 gp_message ("debug", $subr_name, "opened file $gp_metrics_file for reading");
10589 $new_metrics = $metrics;
10591 while (<GP_METRICS>)
10593 $rat = $_;
10594 chomp ($rat);
10595 gp_message ("debugXL", $subr_name, "rat = $rat - new_metrics = $new_metrics");
10596 #------------------------------------------------------------------------------
10597 # Capture the string after "Current metrics:" and if it ends with ":name",
10598 # remove it.
10599 #------------------------------------------------------------------------------
10600 if ($rat =~ /^\s*Current metrics:\s*(.*)$/)
10602 $new_metrics = $1;
10603 if ($new_metrics =~ /^(.*):name$/)
10605 $new_metrics = $1;
10607 last;
10610 close (GP_METRICS);
10612 if ($is_calls or $is_calltree)
10614 #------------------------------------------------------------------------------
10615 # Remove any inclusive metrics from the list.
10616 #------------------------------------------------------------------------------
10617 while ($new_metrics =~ /(.*)(i\.[^:]+)(.*)$/)
10619 $pre = $1;
10620 $post = $3;
10621 gp_message ("debugXL", $subr_name, "1b: new_metrics = $new_metrics pre = $pre post = $post");
10622 if (substr ($post,0,1) eq ":")
10624 $post = substr ($post,1);
10626 $new_metrics = $pre.$post;
10630 $metrics = $new_metrics;
10632 gp_message ("debugXL", $subr_name, "2:metrics->$metrics<- field->$field<- file->$file<-");
10634 #------------------------------------------------------------------------------
10635 # Find the line starting with "address:" and strip this part away.
10636 #------------------------------------------------------------------------------
10637 if ($metrics =~ /^address:(.*)/)
10639 $reported_metrics = $1;
10640 #------------------------------------------------------------------------------
10641 # Focus on the filename ending with "-PC". When found, strip this part away.
10642 #------------------------------------------------------------------------------
10643 if ($file =~ /^(.*)-PC$/)
10645 $noPCfile = $1;
10646 if ($noPCfile =~ /^(.*)functions.sort.func$/)
10648 $noPCfile = $1."functions.func";
10650 push (@moo, "$reported_metrics\n");
10654 #------------------------------------------------------------------------------
10655 # Split the list into an array with the individual metrics.
10657 # TBD: This should be done only once!
10658 #------------------------------------------------------------------------------
10659 if (not defined($reported_metrics))
10661 $msg = "reported_metrics is not defined";
10662 gp_message ("debug", $subr_name, $msg);
10664 else
10666 $msg = "reported_metrics = " . $reported_metrics;
10667 gp_message ("debug", $subr_name, $msg);
10669 @reported_metrics = split (":", $reported_metrics);
10670 for my $i (@reported_metrics)
10672 gp_message ("debugXL", $subr_name, "reported_metrics = $i");
10675 $hdr_regex = "^\\s*";
10676 $hdr_href_regex = "^\\s*";
10677 $hdr_src_regex = "^(\\s+|<i>\\s+)";
10679 for my $m (@reported_metrics)
10682 my $description = ${ retrieve_metric_description (\$m, \%metric_description) };
10683 gp_message ("debugXL", $subr_name, "m = $m description = $description");
10684 if (substr ($m,0,1) eq "e")
10686 push (@moo,"$m:$description\n");
10687 $hdr_regex .= "(Excl\\.\.*)";
10688 $hdr_href_regex .= "(<a.*>)(Excl\\.)(<\/a>)([^<]+)";
10689 $hdr_src_regex .= "(Excl\\.\.*)";
10690 next;
10692 if (substr ($m,0,1) eq "i")
10694 push (@moo,"$m:$description\n");
10695 $hdr_regex .= "(Incl\\.\.*)";
10696 $hdr_href_regex .= "(<a.*>)(Incl\\.)(<\/a>)([^<]+)";
10697 $hdr_src_regex .= "(Incl\\.\.*)";
10698 next;
10700 if (substr ($m,0,1) eq "a")
10702 my $a;
10703 my $am;
10704 $a = $m;
10705 $a =~ s/^a/e/;
10706 $am = ${ retrieve_metric_description (\$a, \%metric_description) };
10707 $am =~ s/Exclusive/Attributed/;
10708 push (@moo,"$m:$am\n");
10709 $hdr_regex .= "(Attr\\.\.*)";
10710 $hdr_href_regex .= "(<a.*>)(Attr\\.)(<\/a>)([^<]+)";
10711 $hdr_src_regex .= "(Attr\\.\.*)";next;
10716 $hdr_regex .= "(Name\.*)";
10717 $hdr_href_regex .= "(Name\.*)";
10719 @splitted_metrics = split (":","$metrics");
10720 $nf = scalar (@splitted_metrics);
10721 gp_message ("debug", $subr_name,"number of fields in $metrics -> $nf");
10723 if (not defined($noPCfile))
10725 $msg = "noPCfile is not defined";
10726 gp_message ("debug", $subr_name, $msg);
10728 else
10730 $msg = "noPCfile = " . $noPCfile;
10731 gp_message ("debug", $subr_name, $msg);
10733 open (ZMETRICS, ">", "$noPCfile.metrics")
10734 or die ("Not able to open file $noPCfile.metrics for writing - '$!'");
10735 gp_message ("debug", $subr_name, "$noPCfile - opened file $noPCfile.metrics for writing");
10737 print ZMETRICS @moo;
10738 close (ZMETRICS);
10740 gp_message ("debug", $subr_name, "wrote file $noPCfile.metrics");
10742 open (XREGEXP, ">", "$noPCfile.c.regex")
10743 or die ("Not able to open file $noPCfile.c.regex for writing - '$!'");
10744 gp_message ("debug", $subr_name, "$noPCfile - opened file $noPCfile.c.regex for writing");
10746 print XREGEXP "\# Number of metric fields\n";
10747 print XREGEXP "$nf\n";
10748 print XREGEXP "\# Header regex\n";
10749 print XREGEXP "$hdr_regex\n";
10750 print XREGEXP "\# href Header regex\n";
10751 print XREGEXP "$hdr_href_regex\n";
10752 print XREGEXP "\# src Header regex\n";
10753 print XREGEXP "$hdr_src_regex\n";
10755 $mf = 1;
10756 #---------------------------------------------------------------------------
10757 # Find the index of "field" in the metric list, plus one.
10758 #---------------------------------------------------------------------------
10759 if ( ($field eq "functions") or ($field eq "calls") or ($field eq "calltree"))
10761 $mf = $nf + 1;
10763 else
10765 for my $candidate_metric (@splitted_metrics)
10767 gp_message ("debugXL", $subr_name, "field = $field candidate_metric = $candidate_metric and mf = $mf");
10768 if ($candidate_metric eq $field)
10770 last;
10772 $mf++;
10775 gp_message ("debugXL", $subr_name, "Final value mf = $mf");
10777 if ($mf == 1)
10779 $re = "^\\s*(\\S+)"; # metric value
10781 else
10783 $re = "^\\s*\\S+";
10785 $Xre = "^\\s*(\\S+)";
10787 $m = 2;
10788 while (--$nf)
10790 if ($nf)
10792 if ($m == $mf)
10794 $re .= "\\s+(\\S+)"; # metric value
10796 else
10798 $re .= "\\s+\\S+";
10800 if ($nf != 1)
10802 $Xre .= "\\s+(\\S+)";
10804 $m++;
10808 if ($field eq "calltree")
10810 $re .= "\\s+.*\\+-(.*)"; # name
10811 $Xre .= "\\s+.*\\+-(.*)\$"; # name (Right?)
10813 else
10815 $re .= "\\s+(.*)"; # name
10816 $Xre .= "\\s+(.*)\$"; # name
10819 print XREGEXP "\# Metrics and Name regex\n";
10820 print XREGEXP "$Xre\n";
10821 close (XREGEXP);
10823 gp_message ("debug", $subr_name, "wrote file $noPCfile.c.regex");
10824 gp_message ("debugXL", $subr_name, "on return Xre = $Xre");
10825 gp_message ("debugXL", $subr_name, "on return re = $re");
10828 return ($re);
10830 } #-- End of subroutine name_regex
10832 #------------------------------------------------------------------------------
10833 # TBD
10834 #------------------------------------------------------------------------------
10835 sub nosrc
10837 my $subr_name = get_my_name ();
10839 my ($input_string) = @_;
10841 my $directory_name = append_forward_slash ($input_string);
10842 my $LANG = $g_locale_settings{"LANG"};
10843 my $result_file = $directory_name."no_source.html";
10845 gp_message ("debug", $subr_name, "result_file = $result_file");
10847 open (NS, ">", $result_file)
10848 or die ("$subr_name: cannot open file $result_file for writing - '$!'");
10850 print NS "<!doctype html public \"-//w3c//dtd html 3.2//en\">\n<html lang=\"$LANG\">\n<head>\n".
10851 "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n" .
10852 "<title>No source</title></head><body lang=\"$LANG\" bgcolor=".$g_html_color_scheme{"background_color_page"}."><pre>\n";
10853 print NS "<a name=\"line1\"></a><font color=#C80707>"."No source was found"."</font>\n"; # red font
10854 print NS "</pre>\n<pre>Output generated by $version_info</pre>\n";
10855 print NS "</body></html>\n";
10857 close (NS);
10859 return (0);
10861 } #-- End of subroutine nosrc
10863 #------------------------------------------------------------------------------
10864 # TBD.
10865 #------------------------------------------------------------------------------
10866 sub numerically
10868 my $f1;
10869 my $f2;
10871 if ($a =~ /^([^\d]*)(\d+)/)
10873 $f1 = int ($2);
10874 if ($b=~ /^([^\d]*)(\d+)/)
10876 $f2 = int ($2);
10877 $f1 == $f2 ? 0 : ($f1 < $f2 ? -1 : +1);
10880 else
10882 return ($a <=> $b);
10884 } #-- End of subroutine numerically
10886 #------------------------------------------------------------------------------
10887 # Parse the user options. Also perform a basic check. More checks and also
10888 # some more specific to the option, plus cross option checks, will be
10889 # performed soon after this subroutine has executed.
10891 # Warnings, but also errors, are buffered. In this way we can collect as many
10892 # warnings and errors as possible, before bailing out in case of an error.
10893 #------------------------------------------------------------------------------
10894 sub parse_and_check_user_options
10896 my $subr_name = get_my_name ();
10898 my @exp_dir_list;
10900 my $arg;
10901 my $calltree_value;
10902 my $debug_value;
10903 my $default_metrics_value;
10904 my $func_limit_value;
10905 my $found_exp_dir = $FALSE;
10906 my $ignore_metrics_value;
10907 my $ignore_value;
10908 my $msg;
10909 my $outputdir_value;
10910 my $quiet_value;
10911 my $hp_value;
10912 my $valid;
10913 my $verbose_value;
10915 my $number_of_fields;
10917 my $internal_option_name;
10918 my $option_name;
10920 my $verbose = undef;
10921 my $warning = undef;
10923 my @opt_debug = ();
10924 my @opt_highlight_percentage = ();
10925 my @opt_nowarnings = ();
10926 my @opt_obsoleted_hp = ();
10927 my @opt_output = ();
10928 my @opt_overwrite = ();
10929 my @opt_quiet = ();
10930 my @opt_verbose = ();
10931 my @opt_warnings = ();
10933 #------------------------------------------------------------------------------
10934 #------------------------------------------------------------------------------
10935 my $no_of_warnings;
10936 my $total_warning_msgs = 0;
10937 my $option_value;
10938 my $option_warnings;
10939 my $no_of_warnings_ref;
10940 my $no_of_errors_ref;
10942 my $index_exp;
10943 my $first = $TRUE;
10944 my $trigger = $FALSE;
10945 my $found_non_exp = $FALSE;
10946 my $name_non_exp_dir;
10947 my $no_of_experiments = 0;
10949 my @opt_help = ();
10950 my @opt_version = ();
10951 my $stop_execution = $FALSE;
10953 my $option_value_ref;
10954 my $max_occurrences;
10955 #------------------------------------------------------------------------------
10956 # Configure Getopt to:
10957 # - Silence warnings, since these are handled by the code.
10958 # - Enforce case sensitivity in order to support -o and -O for example.
10959 #------------------------------------------------------------------------------
10960 Getopt::Long::Configure("pass_through", "no_ignore_case");
10962 #------------------------------------------------------------------------------
10963 # Check for the --help and --version options. Print a message and exit.
10964 # Note that we support using both options simultaneously on the command line.
10965 #------------------------------------------------------------------------------
10966 GetOptions (
10967 "help" => \@opt_help,
10968 "version" => \@opt_version
10971 if (@opt_help)
10973 $stop_execution = $TRUE;
10974 $ignore_value = print_help_info ();
10976 if (@opt_version)
10978 $stop_execution = $TRUE;
10979 $ignore_value = print_version_info ();
10982 if ($stop_execution)
10984 exit (0);
10987 #------------------------------------------------------------------------------
10988 # First, scan ARGV for the experiment names. If there are no names, or the
10989 # list with the names is not contiguous (meaning there is an non-experiment
10990 # name in this list), an error message is printed and execution is terminated.
10992 # Upon return from this function, the list with the experiment names is
10993 # known and has been removed from ARGV.
10995 # As a result, exp_dir_list is available from there on.
10997 # This makes the subsequent processing of ARGV with GetOptions() easier.
10998 #------------------------------------------------------------------------------
10999 @exp_dir_list = @{ check_the_experiment_list () };
11001 #------------------------------------------------------------------------------
11002 # Configure Getopt to:
11003 # - Silence warnings, since these are handled by the code.
11004 # - Enforce case sensitivity in order to support -o and -O for example.
11005 # - Allow unique abbreviations (also the default).
11006 #------------------------------------------------------------------------------
11007 Getopt::Long::Configure("pass_through", "no_ignore_case", "auto_abbrev");
11008 #------------------------------------------------------------------------------
11009 # Get the remaining command line options.
11011 # Recall:
11012 # = => option requires a value
11013 # : => option value is optional
11014 #------------------------------------------------------------------------------
11016 #------------------------------------------------------------------------------
11017 # All options are considered to be a string.
11019 # We request every option supported to have an optional value. Otherwise,
11020 # GetOptions skips an option that does not have a value.
11022 # The logic that parses the options deals with this and checks if an option
11023 # that should have a value, actually has one.
11024 #------------------------------------------------------------------------------
11025 GetOptions (
11026 "verbose|v:s" => \@opt_verbose,
11027 "debug|d:s" => \@opt_debug,
11028 "warnings|w:s" => \@opt_warnings,
11029 "nowarnings:s" => \@opt_nowarnings,
11030 "quiet|q:s" => \@opt_quiet,
11031 "output|o=s" => \@opt_output,
11032 "overwrite|O=s" => \@opt_overwrite,
11033 "highlight-percentage=s" => \@opt_highlight_percentage,
11034 "hp=s" => \@opt_obsoleted_hp
11037 #------------------------------------------------------------------------------
11038 #------------------------------------------------------------------------------
11039 # Handle the user input and where needed, generate warnings. In a later stage
11040 # we check for (cross option) errors and warnings.
11041 #------------------------------------------------------------------------------
11042 #------------------------------------------------------------------------------
11044 #------------------------------------------------------------------------------
11045 # The very first thing to do is to determine if the user has enabled one of the
11046 # following options and take action accordingly:
11047 # --quiet, --verbose, --debug, --warnings
11049 # We first need to check for quiet mode to be set. If so, all messages need to
11050 # be silenced, regardless of the settings for verbose, debug, and warnings.
11051 #------------------------------------------------------------------------------
11053 #------------------------------------------------------------------------------
11054 # The quiet option.
11055 #------------------------------------------------------------------------------
11056 if (@opt_quiet)
11058 $max_occurrences = 1;
11059 $internal_option_name = "quiet";
11060 $option_name = "--quiet";
11062 my ($valid_ref) = extract_option_value (\@opt_quiet,
11063 \$max_occurrences,
11064 \$internal_option_name,
11065 \$option_name);
11067 $valid = ${ $valid_ref };
11069 if ($valid)
11071 $g_quiet = $g_user_settings{"quiet"}{"current_value"} eq "on" ?
11072 $TRUE : $FALSE;
11076 #------------------------------------------------------------------------------
11077 # The debug option.
11078 #------------------------------------------------------------------------------
11079 if (@opt_debug)
11081 $max_occurrences = 1;
11082 $internal_option_name = "debug";
11083 $option_name = "-d/--debug";
11085 my ($valid_ref) = extract_option_value (\@opt_debug,
11086 \$max_occurrences,
11087 \$internal_option_name,
11088 \$option_name);
11090 $valid = ${ $valid_ref };
11092 if ($valid)
11093 #------------------------------------------------------------------------------
11094 # Set the appropriate debug size (e.g. "XL") in a table that is used in the
11095 # gp_message() subroutine.
11096 #------------------------------------------------------------------------------
11098 $g_debug = $TRUE;
11099 $ignore_value = set_debug_size ();
11103 #------------------------------------------------------------------------------
11104 # The verbose option.
11105 #------------------------------------------------------------------------------
11106 if (@opt_verbose)
11108 $max_occurrences = 1;
11109 $internal_option_name = "verbose";
11110 $option_name = "--verbose";
11112 my ($valid_ref) = extract_option_value (\@opt_verbose,
11113 \$max_occurrences,
11114 \$internal_option_name,
11115 \$option_name);
11116 $valid = ${ $valid_ref };
11118 if ($valid)
11120 $g_verbose = $g_user_settings{"verbose"}{"current_value"} eq "on" ?
11121 $TRUE : $FALSE;
11125 #------------------------------------------------------------------------------
11126 # The nowarnings option.
11127 #------------------------------------------------------------------------------
11128 if (@opt_nowarnings)
11130 $max_occurrences = 1;
11131 $internal_option_name = "nowarnings";
11132 $option_name = "--nowarnings";
11134 my ($valid_ref) = extract_option_value (\@opt_nowarnings,
11135 \$max_occurrences,
11136 \$internal_option_name,
11137 \$option_name);
11139 $valid = ${ $valid_ref };
11141 if ($valid)
11143 $g_warnings =
11144 $g_user_settings{"nowarnings"}{"current_value"} eq "on" ?
11145 $FALSE : $TRUE;
11149 #------------------------------------------------------------------------------
11150 # The warnings option (deprecated).
11151 #------------------------------------------------------------------------------
11152 if (@opt_warnings)
11154 $max_occurrences = 1;
11155 $internal_option_name = "warnings";
11156 $option_name = "--warnings";
11158 my ($valid_ref) = extract_option_value (\@opt_warnings,
11159 \$max_occurrences,
11160 \$internal_option_name,
11161 \$option_name);
11164 #------------------------------------------------------------------------------
11165 # At this point, the debug, verbose, warnings and quiet settings are known.
11166 # This subroutine makes the final decision on these settings. For example, if
11167 # quiet mode has been specified, the settings for debug, verbose and warnings
11168 # are ignored.
11169 #------------------------------------------------------------------------------
11170 $ignore_value = finalize_special_options ();
11172 #------------------------------------------------------------------------------
11173 # A this point we know we can start printing messages in case verbose and/or
11174 # debug mode have been set.
11175 #------------------------------------------------------------------------------
11176 $msg = "the original command line options: " . join (", ", @CopyOfARGV);
11177 gp_message ("debug", $subr_name, $msg);
11179 $msg = "the command line options after the special options: " .
11180 join (", ", @ARGV);
11181 gp_message ("debug", $subr_name, $msg);
11183 gp_message ("verbose", $subr_name, "Parsing the user options");
11185 #------------------------------------------------------------------------------
11186 # The output option.
11187 #------------------------------------------------------------------------------
11188 if (@opt_output)
11190 $max_occurrences = 1;
11191 $internal_option_name = "output";
11192 $option_name = "-o/--output";
11194 my ($valid_ref) = extract_option_value (\@opt_output,
11195 \$max_occurrences,
11196 \$internal_option_name,
11197 \$option_name);
11200 #------------------------------------------------------------------------------
11201 # The overwrite option.
11202 #------------------------------------------------------------------------------
11203 if (@opt_overwrite)
11205 $max_occurrences = 1;
11206 $internal_option_name = "overwrite";
11207 $option_name = "-O/--overwrite";
11209 my ($valid_ref) = extract_option_value (\@opt_overwrite,
11210 \$max_occurrences,
11211 \$internal_option_name,
11212 \$option_name);
11215 #------------------------------------------------------------------------------
11216 # The highlight-percentage option.
11217 #------------------------------------------------------------------------------
11218 if (@opt_highlight_percentage)
11220 $max_occurrences = 1;
11221 $internal_option_name = "highlight_percentage";
11222 $option_name = "--highlight-percentage";
11224 my ($valid_ref) = extract_option_value (\@opt_highlight_percentage,
11225 \$max_occurrences,
11226 \$internal_option_name,
11227 \$option_name);
11230 #------------------------------------------------------------------------------
11231 # The hp option (deprecated)
11232 #------------------------------------------------------------------------------
11233 if (@opt_obsoleted_hp)
11235 $max_occurrences = 1;
11236 $internal_option_name = "hp";
11237 $option_name = "-hp";
11239 my ($valid_ref) = extract_option_value (\@opt_obsoleted_hp,
11240 \$max_occurrences,
11241 \$internal_option_name,
11242 \$option_name);
11245 #------------------------------------------------------------------------------
11246 # By now, all options given on the command line have been processed and the
11247 # list with experiment directories is known.
11249 # Process the remainder of ARGV, but other than the option generated by the
11250 # driver, ARGV should be empty.
11251 #------------------------------------------------------------------------------
11252 $ignore_value = wrap_up_user_options ();
11254 # Temporarily disabled elsif (($arg eq "-fl") or ($arg eq "--func-limit"))
11255 # Temporarily disabled elsif (($arg eq "-ct") or ($arg eq "--calltree"))
11256 # Temporarily disabled elsif (($arg eq "-tp") or ($arg eq "--threshold-percentage"))
11257 # Temporarily disabled elsif (($arg eq "-dm") or ($arg eq "--default-metrics"))
11258 # Temporarily disabled elsif (($arg eq "-im") or ($arg eq "--ignore-metrics"))
11260 if (@exp_dir_list)
11261 #------------------------------------------------------------------------------
11262 # Print the list of the experiment directories found.
11264 # Note that later we also check for these directories to actually exist
11265 # and be valid experiments..
11266 #------------------------------------------------------------------------------
11268 $found_exp_dir = $TRUE;
11269 $msg = "the following experiment directories will be used:";
11270 gp_message ("debug", $subr_name, $msg);
11271 for my $i (keys @exp_dir_list)
11273 my $msg = "exp_dir_list[$i] = $exp_dir_list[$i]";
11274 gp_message ("debug", $subr_name, $msg);
11277 else
11278 #------------------------------------------------------------------------------
11279 # Print a message if the experiment list is not valid, or empty. There will
11280 # also be error messages in the buffer. These will be printed later.
11281 #------------------------------------------------------------------------------
11283 $msg = "experiment directory name(s) are either not valid, or missing";
11284 gp_message ("debug", $subr_name, $msg);
11287 return (\$found_exp_dir, \@exp_dir_list);
11289 } #-- End of subroutine parse_and_check_user_options
11291 #------------------------------------------------------------------------------
11292 # Parse the generated .dis files
11293 #------------------------------------------------------------------------------
11294 sub parse_dis_files
11296 my $subr_name = get_my_name ();
11298 my ($number_of_metrics_ref, $function_info_ref,
11299 $function_address_and_index_ref, $input_string_ref,
11300 $addressobj_index_ref) = @_;
11302 #------------------------------------------------------------------------------
11303 # Note that $function_address_and_index_ref is not used, but we need to pass
11304 # in the address into generate_dis_html.
11305 #------------------------------------------------------------------------------
11306 my $number_of_metrics = ${ $number_of_metrics_ref };
11307 my @function_info = @{ $function_info_ref };
11308 my $input_string = ${ $input_string_ref };
11309 my %addressobj_index = %{ $addressobj_index_ref };
11311 #------------------------------------------------------------------------------
11312 # The regex section.
11313 #------------------------------------------------------------------------------
11314 my $dis_filename_id_regex = 'file\.([0-9]+)\.dis';
11316 my $filename;
11317 my $msg;
11318 my $outputdir = append_forward_slash ($input_string);
11320 my @source_line = ();
11321 my $source_line_ref;
11323 my @metric = ();
11324 my $metric_ref;
11326 my $target_function;
11328 gp_message ("debug", $subr_name, "building disassembly files");
11329 gp_message ("debug", $subr_name, "outputdir = $outputdir");
11331 while (glob ($outputdir.'*.dis'))
11333 gp_message ("debug", $subr_name, "processing disassembly file: $_");
11335 my $base_name = get_basename ($_);
11337 if ($base_name =~ /$dis_filename_id_regex/)
11339 if (defined ($1))
11341 gp_message ("debug", $subr_name, "processing disassembly file: $base_name $1");
11342 if (exists ($function_info[$1]{"routine"}))
11344 $target_function = $function_info[$1]{"routine"};
11345 gp_message ("debug", $subr_name, "processing disassembly file: $base_name target_function = $target_function");
11347 if (exists ($g_function_tag_id{$target_function}))
11349 gp_message ("debug", $subr_name, "target_function = $target_function ftag = $g_function_tag_id{$target_function}");
11351 else
11353 my $msg = "no function tag found for $target_function";
11354 gp_message ("assertion", $subr_name, $msg);
11357 else
11359 gp_message ("debug", $subr_name, "processing disassembly file: $base_name unknown id");
11363 $filename = $_;
11364 gp_message ("verbose", $subr_name, " Processing disassembly file $filename");
11365 ($source_line_ref, $metric_ref) = generate_dis_html (
11366 \$target_function,
11367 \$number_of_metrics,
11368 $function_info_ref,
11369 $function_address_and_index_ref,
11370 \$outputdir,
11371 \$filename,
11372 \@source_line,
11373 \@metric,
11374 \%addressobj_index);
11376 @source_line = @{ $source_line_ref };
11378 #------------------------------------------------------------------------------
11379 # TBD. This part needs work. The return variables from generate_dis_html ()
11380 # are not used, so the code below is meaningless, but awaiting a true fix,
11381 # the problem which appears on aarch64 is bypassed.
11382 #------------------------------------------------------------------------------
11383 if (defined ($metric_ref))
11385 @metric = @{ $metric_ref };
11387 else
11389 $msg = "metric_ref after generate_dis_html is undefined";
11390 gp_message ("debug", $subr_name, $msg);
11394 return (0)
11396 } #-- End of subroutine parse_dis_files
11398 #------------------------------------------------------------------------------
11399 # Parse the .src.txt files
11400 #------------------------------------------------------------------------------
11401 sub parse_source_files
11403 my $subr_name = get_my_name ();
11405 my ($number_of_metrics_ref, $function_info_ref, $outputdir_ref) = @_;
11407 my $number_of_metrics = ${ $number_of_metrics_ref };
11408 my $outputdir = ${ $outputdir_ref };
11409 my $ignore_value;
11411 my $outputdir_with_slash = append_forward_slash ($outputdir);
11413 gp_message ("verbose", $subr_name, "building source files");
11415 while (glob ($outputdir_with_slash.'*.src.txt'))
11417 gp_message ("verbose", $subr_name, " Processing source file: $_");
11418 gp_message ("debug", $subr_name, "processing source file: $_");
11420 my $found_target = process_source (
11421 $number_of_metrics,
11422 $function_info_ref,
11423 $outputdir_with_slash,
11424 $_);
11426 if (not $found_target)
11428 gp_message ("debug", $subr_name, "target function not found");
11432 } #-- End of subroutine parse_source_files
11434 #------------------------------------------------------------------------------
11435 # Routine to prepend \\ to selected symbols.
11436 #------------------------------------------------------------------------------
11437 sub prepend_backslashes
11439 my $subr_name = get_my_name ();
11441 my ($target_string) = @_;
11443 gp_message ("debug", $subr_name, "target_string on entry = $target_string");
11445 $target_string =~ s/\(/\\\(/g;
11446 $target_string =~ s/\)/\\\)/g;
11447 $target_string =~ s/\+/\\\+/g;
11448 $target_string =~ s/\[/\\\[/g;
11449 $target_string =~ s/\]/\\\]/g;
11450 $target_string =~ s/\*/\\\*/g;
11451 $target_string =~ s/\./\\\./g;
11452 $target_string =~ s/\$/\\\$/g;
11453 $target_string =~ s/\^/\\\^/g;
11454 $target_string =~ s/\#/\\\#/g;
11456 gp_message ("debug", $subr_name, "target_string on return = $target_string");
11458 return ($target_string);
11460 } #-- End of subroutine prepend_backslashes
11462 #------------------------------------------------------------------------------
11463 # TBD Still needed?
11464 #------------------------------------------------------------------------------
11465 sub preprocess_function_files
11467 my $subr_name = get_my_name ();
11469 my ($metric_description_ref, $script_pc_metrics, $input_string, $sort_fields_ref) = @_;
11471 my $outputdir = append_forward_slash ($input_string);
11472 my @sort_fields = @{ $sort_fields_ref };
11474 my $error_code;
11475 my $cmd_output;
11476 my $re;
11478 # TBD $outputdir .= "/";
11480 my %metric_description = %{ $metric_description_ref };
11482 for my $m (keys %metric_description)
11484 gp_message ("debug", $subr_name, "metric_description{$m} = $metric_description{$m}");
11487 $re = name_regex ($metric_description_ref, $script_pc_metrics, "functions", $outputdir."functions.sort.func-PC");
11488 ($error_code, $cmd_output) = execute_system_cmd ("echo '$re' > $outputdir"."functions.sort.func-PC.name-regex");
11489 if ($error_code != 0 )
11491 gp_message ("abort", $subr_name, "execution terminated");
11494 for my $field (@sort_fields)
11496 $re = name_regex ($metric_description_ref, $script_pc_metrics, $field, $outputdir."$field.sort.func-PC");
11497 ($error_code, $cmd_output) = execute_system_cmd ("echo '$re' > $outputdir"."$field.sort.func-PC.name-regex");
11498 if ($error_code != 0 )
11500 gp_message ("abort", $subr_name, "execution terminated");
11504 $re = name_regex ($metric_description_ref, $script_pc_metrics, "calls", $outputdir."calls.sort.func-PC");
11505 ($error_code, $cmd_output) = execute_system_cmd ("echo '$re' > $outputdir"."calls.sort.func-PC.name-regex");
11506 if ($error_code != 0 )
11508 gp_message ("abort", $subr_name, "execution terminated");
11511 if ($g_user_settings{"calltree"}{"current_value"} eq "on")
11513 $re = name_regex ($metric_description_ref, $script_pc_metrics, "calltree", $outputdir."calltree.sort.func-PC");
11514 ($error_code, $cmd_output) = execute_system_cmd ("echo '$re' > $outputdir"."calltree.sort.func-PC.name-regex");
11515 if ($error_code != 0 )
11517 gp_message ("abort", $subr_name, "execution terminated");
11521 return (0);
11523 } #-- End of subroutine preprocess_function_files
11525 #------------------------------------------------------------------------------
11526 # Print the original list with the command line options.
11527 #------------------------------------------------------------------------------
11528 sub print_command_line_options
11530 my ($identifier_ref) = @_;
11532 my $identifier = ${ $identifier_ref };
11533 my $msg;
11535 $msg = "The command line options (shown for ease of reference): ";
11536 printf ("%-9s %s\n", $identifier, ucfirst ($msg));
11538 $msg = join (", ", @CopyOfARGV);
11539 printf ("%-9s %s\n", $identifier, $msg);
11541 # printf ("%-9s\n", $identifier);
11543 return (0);
11545 } #-- End of subroutine print_command_line_options
11547 #------------------------------------------------------------------------------
11548 # Print all the errors messages in the buffer.
11549 #------------------------------------------------------------------------------
11550 sub print_errors_buffer
11552 my $subr_name = get_my_name ();
11554 my ($identifier_ref) = @_;
11556 my $ignore_value;
11557 my $msg;
11558 my $plural_or_single;
11559 my $identifier = ${ $identifier_ref };
11561 $plural_or_single = ($g_total_error_count > 1) ? "errors have" : "error has";
11563 if (@g_warning_msgs and $g_warnings)
11564 #------------------------------------------------------------------------------
11565 # Make sure that all warnings are printed in case of an error. This is to
11566 # avoid that warnings get lost in case the program terminates early.
11567 #------------------------------------------------------------------------------
11569 $ignore_value = print_warnings_buffer ();
11572 if (not $g_options_printed)
11573 #------------------------------------------------------------------------------
11574 # The options are printed as part of the warnings, so only if the warnings are
11575 # not printed, we need to print them in case of errors.
11576 #------------------------------------------------------------------------------
11578 $g_options_printed = $TRUE;
11579 $ignore_value = print_command_line_options (\$identifier);
11582 $msg = "a total of " . $g_total_error_count;
11583 $msg .= " fatal " . $plural_or_single . " been detected:";
11584 printf ("%-9s %s\n", $identifier, ucfirst ($msg));
11586 for my $key (keys @g_error_msgs)
11588 $msg = $g_error_msgs[$key];
11589 printf ("%-11s %s\n", $identifier, ucfirst ($msg));
11592 return (0);
11594 } #-- End of subroutine print_errors_buffer
11596 #------------------------------------------------------------------------------
11597 # Print the help overview
11598 #------------------------------------------------------------------------------
11599 sub print_help_info
11601 my $space = " ";
11603 printf("%s\n",
11604 "Usage: $driver_cmd [OPTION(S)] EXPERIMENT(S)");
11605 printf("\n");
11606 printf("%s\n",
11607 "Process one or more experiments to generate a directory containing the");
11608 printf("%s\n",
11609 "index.html file that may be used to browse the experiment data.");
11610 printf("\n");
11611 printf("%s\n",
11612 "Options:");
11613 printf("\n");
11614 #-------Marker line - do not go beyond this line ----------------------------
11615 print_help_line ("--help",
11616 "Print usage information and exit.");
11618 #-------Marker line - do not go beyond this line ----------------------------
11619 print_help_line ("--version",
11620 "Print the version number and exit.");
11622 #-------Marker line - do not go beyond this line ----------------------------
11623 print_help_line ("--verbose",
11624 "Enable verbose mode to show diagnostic messages about the");
11625 print_help_line ("",
11626 "processing of the data. By default verbose mode is disabled.");
11628 #-------Marker line - do not go beyond this line ----------------------------
11629 print_help_line ("-d [<db-vol-size>], --debug[=<db-vol-size>]",
11630 "Control the printing of run time debug information to assist with");
11631 print_help_line ("",
11632 "the troubleshooting, or further development of this tool.");
11633 print_help_line ("",
11634 "The <db-vol-size> parameter controls the output volume and is");
11635 print_help_line ("",
11636 "one from the list {s | S | m | M | l | L | xl | XL}.");
11637 print_help_line ("",
11638 "If db-vol-size is not specified, a modest amount of information");
11639 print_help_line ("",
11640 "is printed. This is equivalent to select size s, or S. The");
11641 print_help_line ("",
11642 "volume of data goes up as the size increases. Note that");
11643 print_help_line ("",
11644 "currently l/L is equivalent to xl/XL, but this is expected to");
11645 print_help_line ("",
11646 "change in future updates. By default debug mode is disabled.");
11648 #-------Marker line - do not go beyond this line ----------------------------
11649 print_help_line ("--highlight-percentage=<value>",
11650 "A percentage value in the interval [0,100] to select and color");
11651 print_help_line ("",
11652 "code source lines, as well as instructions, that are within this");
11653 print_help_line ("",
11654 "percentage of the maximum metric value(s). A value of zero");
11655 print_help_line ("",
11656 "disables this feature. The default value is 90 (%).");
11658 #-------Marker line - do not go beyond this line ----------------------------
11659 print_help_line ("-o <dirname>, --output=<dirname>",
11660 "Use <dirname> as the directory name to store the results in.");
11661 print_help_line ("",
11662 "In absence of this option, the default name is display.<n>.html.");
11663 print_help_line ("",
11664 "This directory is created in the current directory. The number");
11665 print_help_line ("",
11666 "<n> is the first positive integer number not in use in this");
11667 print_help_line ("",
11668 "naming scheme. An existing directory with the same name is not");
11669 print_help_line ("",
11670 "overwritten. Make sure that umask is set to the correct access");
11671 print_help_line ("",
11672 "permissions.");
11674 #-------Marker line - do not go beyond this line --------------------------
11675 print_help_line ("-O <dirname>, --overwrite=<dirname>",
11676 "Use <dirname> as the directory name to store the results in.");
11677 print_help_line ("",
11678 "In absence of this option, the default name is display.<n>.html.");
11679 print_help_line ("",
11680 "This directory is created in the current directory. The number");
11681 print_help_line ("",
11682 "<n> is the first positive integer number not in use in this");
11683 print_help_line ("",
11684 "naming scheme. An existing directory with the same name is");
11685 print_help_line ("",
11686 "silently overwritten. Make sure that umask is set to the");
11687 print_help_line ("",
11688 "correct access permissions.");
11690 #-------Marker line - do not go beyond this line --------------------------
11691 print_help_line ("-q, --quiet",
11692 "Disable the display of all warning, debug, verbose and any");
11693 print_help_line ("",
11694 "other messages. If enabled, the settings for verbose and debug");
11695 print_help_line ("",
11696 "are accepted, but ignored. With this option, there is no screen");
11697 print_help_line ("",
11698 "output, other than errors. By default quiet mode is disabled");
11700 #-------Marker line - do not go beyond this line --------------------------
11701 print_help_line ("--nowarnings",
11702 "Disable the printing of warning messages on stdout. By default");
11703 print_help_line ("",
11704 "warning messages are printed.");
11706 #-------Marker line - do not go beyond this line --------------------------
11707 printf("\n");
11708 printf ("%s\n","Report bugs to <https://sourceware.org/bugzilla/>");
11710 return (0);
11712 } #-- End of subroutine print_help_info
11714 #------------------------------------------------------------------------------
11715 # Print a single line as part of the help output.
11717 # If the first item is not the empty string, it is considered to be the
11718 # option. If the length of the option exceeds the limit set by $max_space,
11719 # it is printed by itself and the text is printed on the next line. Otherwise
11720 # the text follows the option.
11722 # To assist with the development of the help text, we check if the total length
11723 # of the line exceeds the max numbers of columns (79 according to the GNU
11724 # coding standards).
11725 #------------------------------------------------------------------------------
11726 sub print_help_line
11728 my $subr_name = get_my_name ();
11730 my ($item, $help_text) = @_;
11732 my $length_item = length ($item);
11733 my $max_col = 79;
11734 my $max_space = 14;
11735 my $no_of_spaces;
11736 my $pad;
11737 my $space = " ";
11738 my $the_message;
11740 if ($length_item > $max_col)
11742 printf ("Error: $item is $length_item long - exceeds $max_col\n");
11743 exit (0);
11745 elsif ( $length_item == 0 )
11747 $no_of_spaces = $max_space;
11749 $pad = "";
11750 for my $i (1..$no_of_spaces)
11752 $pad .= $space;
11754 $the_message = $pad . $help_text;
11756 else
11758 if ($length_item < $max_space)
11760 $no_of_spaces = $max_space - length ($item);
11761 $pad = "";
11762 for my $i (1..$no_of_spaces)
11764 $pad .= $space;
11766 $the_message = $item . $pad . $help_text;
11768 else
11770 $pad = "";
11771 for my $i (1..$max_space)
11773 $pad .= $space;
11775 printf("%s\n", $item);
11776 $the_message = $pad . $help_text;
11780 if (length ($the_message) <= $max_col)
11782 printf ("%s\n", $the_message);
11784 else
11786 my $delta = length ($the_message) - $max_col;
11787 printf ("%s\n", "$the_message - exceeds $max_col by $delta");
11788 exit (0);
11792 return (0);
11794 } #-- End of subroutine print_help_line
11796 #------------------------------------------------------------------------------
11797 # Print the meta data for each experiment directory.
11798 #------------------------------------------------------------------------------
11799 sub print_meta_data_experiments
11801 my $subr_name = get_my_name ();
11803 my ($mode) = @_;
11805 for my $exp (sort keys %g_exp_dir_meta_data)
11807 for my $meta (sort keys %{$g_exp_dir_meta_data{$exp}})
11809 gp_message ($mode, $subr_name, "$exp => $meta = $g_exp_dir_meta_data{$exp}{$meta}");
11813 return (0);
11815 } #-- End of subroutine print_meta_data_experiments
11817 #------------------------------------------------------------------------------
11818 # Brute force subroutine that prints the contents of a structure with function
11819 # level information. This version is for a top level array structure,
11820 # followed by a hash.
11821 #------------------------------------------------------------------------------
11822 sub print_metric_function_array
11824 my $subr_name = get_my_name ();
11826 my ($metric, $struct_type_name, $target_structure_ref) = @_;
11828 my @target_structure = @{$target_structure_ref};
11830 gp_message ("debugXL", $subr_name, "contents of structure ".$struct_type_name."{".$metric."}:");
11832 for my $fields (sort keys @target_structure)
11834 for my $elems (sort keys % {$target_structure[$fields]})
11836 my $msg = $struct_type_name."{$metric}[$fields]{$elems} = ";
11837 $msg .= $target_structure[$fields]{$elems};
11838 gp_message ("debugXL", $subr_name, $msg);
11842 return (0);
11844 } #-- End of subroutine print_metric_function_array
11846 #------------------------------------------------------------------------------
11847 # Brute force subroutine that prints the contents of a structure with function
11848 # level information. This version is for a top level hash structure. The
11849 # next level may be another hash, or an array.
11850 #------------------------------------------------------------------------------
11851 sub print_metric_function_hash
11853 my $subr_name = get_my_name ();
11855 my ($sub_struct_type, $metric, $struct_type_name, $target_structure_ref) = @_;
11857 my %target_structure = %{$target_structure_ref};
11859 gp_message ("debugXL", $subr_name, "contents of structure ".$struct_type_name."{".$metric."}:");
11861 for my $fields (sort keys %target_structure)
11863 gp_message ("debugXL", $subr_name, "metric = $metric fields = $fields");
11864 if ($sub_struct_type eq "hash_hash")
11866 for my $elems (sort keys %{$target_structure{$fields}})
11868 my $txt = $struct_type_name."{$metric}{$fields}{$elems} = ";
11869 $txt .= $target_structure{$fields}{$elems};
11870 gp_message ("debugXL", $subr_name, $txt);
11873 elsif ($sub_struct_type eq "hash_array")
11875 my $values = "";
11876 for my $elems (sort keys @{$target_structure{$fields}})
11878 $values .= "$target_structure{$fields}[$elems] ";
11880 gp_message ("debugXL", $subr_name, $struct_type_name."{$metric}{$fields} = $values");
11882 else
11884 my $msg = "sub-structure type '$sub_struct_type' is not supported";
11885 gp_message ("assertion", $subr_name, $msg);
11889 return (0);
11891 } #-- End of subroutine print_metric_function_hash
11893 #------------------------------------------------------------------------------
11894 # Print the opening message.
11895 #------------------------------------------------------------------------------
11896 sub print_opening_message
11898 my $subr_name = get_my_name ();
11899 #------------------------------------------------------------------------------
11900 # Since the second argument is an array, we pass it in by reference. The
11901 # alternative is to make it the last argument.
11902 #------------------------------------------------------------------------------
11903 my ($outputdir, $exp_dir_list_ref, $time_percentage_multiplier) = @_;
11905 my @exp_dir_list = @{$exp_dir_list_ref};
11907 my $msg;
11908 my $no_of_dirs = scalar (@exp_dir_list);
11909 #------------------------------------------------------------------------------
11910 # Build a comma separated list with all directory names. If there is only one
11911 # entry, the leading comma will not be inserted.
11912 #------------------------------------------------------------------------------
11913 my $dir_list = join (", ", @exp_dir_list);
11915 #------------------------------------------------------------------------------
11916 # If there are at least two entries, find the last comma and replace it by
11917 # " and". Note that we know there is at least one comma, so the value
11918 # returned by rindex () cannot be -1.
11919 #------------------------------------------------------------------------------
11920 if ($no_of_dirs > 1)
11922 my $last_comma = rindex ($dir_list, ",");
11923 my $ignore_value = substr ($dir_list, $last_comma, 1, " and");
11925 $msg = "start $tool_name, generating directory $outputdir from $dir_list";
11927 gp_message ("verbose", $subr_name, $msg);
11929 if ($time_percentage_multiplier < 1.0)
11931 $msg = "Handle at least ";
11933 else
11935 $msg = "Handle ";
11938 $msg .= ($time_percentage_multiplier*100.0)."% of the time";
11940 gp_message ("verbose", $subr_name, $msg);
11942 } #-- End of subroutine print_opening_message
11944 #------------------------------------------------------------------------------
11945 # TBD.
11946 #------------------------------------------------------------------------------
11947 sub print_program_header
11949 my $subr_name = get_my_name ();
11951 my ($mode, $tool_name, $binutils_version) = @_;
11953 my $header_limit = 60;
11954 my $dashes = "-";
11956 #------------------------------------------------------------------------------
11957 # Generate the dashed line
11958 #------------------------------------------------------------------------------
11959 for (2 .. $header_limit)
11961 $dashes .= "-";
11964 gp_message ($mode, $subr_name, $dashes);
11965 gp_message ($mode, $subr_name, "Tool name: $tool_name");
11966 gp_message ($mode, $subr_name, "Version : $binutils_version");
11967 gp_message ($mode, $subr_name, "Date : " . localtime ());
11968 gp_message ($mode, $subr_name, $dashes);
11970 } #-- End of subroutine print_program_header
11972 #------------------------------------------------------------------------------
11973 # Print a comment string, followed by the values of the options. The list
11974 # with the keywords is sorted alphabetically.
11976 # The value stored in $mode is passed on to gp_message (). The intended use
11977 # for this is to call this function in verbose and/or debug mode.
11979 # The comment string is converted to uppercase.
11981 # In case the length of the comment exceeds the length of the dashed line,
11982 # the comment line is allowed to stick out to the right.
11984 # If the length of the comment is less than the dashed line, it is centered
11985 # relative to the # length of the dashed line.
11987 # If the length of the comment and this line do not divide, an extra space is
11988 # added to the left of the comment.
11990 # For example, if the comment is 55 long, there are 5 spaces to be distributed.
11991 # There will be 3 spaces, followed by the comment.
11992 #------------------------------------------------------------------------------
11993 sub print_table_user_settings
11995 my $subr_name = get_my_name ();
11997 my ($mode, $comment) = @_;
11999 my $data_type;
12000 my $debug_size_value = $g_user_settings{"debug"}{"current_value"};
12001 my $db_size;
12002 my $defined;
12003 my $keyword;
12004 my $leftover;
12005 my $padding;
12006 my $user_option;
12007 my $value;
12009 my $HEADER_LIMIT = 79;
12010 my $header = sprintf ("%-20s %-22s %8s %s",
12011 "keyword", "option", "user set", "internal value");
12013 #------------------------------------------------------------------------------
12014 # Generate the dashed line
12015 #------------------------------------------------------------------------------
12016 my $dashes = "-";
12017 for (2 .. $HEADER_LIMIT)
12019 $dashes .= "-";
12022 #------------------------------------------------------------------------------
12023 # Determine the padding needed to the left of the comment.
12024 #------------------------------------------------------------------------------
12025 my $length_comment = length ($comment);
12027 $leftover = $length_comment%2;
12029 if ($length_comment <= ($HEADER_LIMIT-2))
12031 $padding = ($HEADER_LIMIT - $length_comment + $leftover)/2;
12033 else
12035 $padding = 0;
12038 #------------------------------------------------------------------------------
12039 # Generate the first blank part of the line.
12040 #------------------------------------------------------------------------------
12041 my $blank_line = "";
12042 for (1 .. $padding)
12044 $blank_line .= " ";
12047 #------------------------------------------------------------------------------
12048 # Add the comment line with the first letter in uppercase.
12049 #------------------------------------------------------------------------------
12050 my $final_comment = $blank_line.ucfirst ($comment);
12052 gp_message ($mode, $subr_name, $dashes);
12053 gp_message ($mode, $subr_name, $final_comment);
12054 gp_message ($mode, $subr_name, $dashes);
12055 gp_message ($mode, $subr_name, $header);
12056 gp_message ($mode, $subr_name, $dashes);
12058 #------------------------------------------------------------------------------
12059 # Print a line for each option. The list is sorted alphabetically.
12060 #------------------------------------------------------------------------------
12061 for my $key (sort keys %g_user_settings)
12063 $keyword = $key;
12064 $user_option = $g_user_settings{$key}{"option"};
12065 $defined = ($g_user_settings{$key}{"defined"} ? "set" : "not set");
12066 $data_type = $g_user_settings{$key}{"data_type"};
12068 if (defined ($g_user_settings{$key}{"current_value"}))
12070 $value = $g_user_settings{$key}{"current_value"};
12071 if ($data_type eq "boolean")
12073 $value = $value ? "on" : "off";
12075 #------------------------------------------------------------------------------
12076 # In case of the debug option, we add the "(size)" string to remind the user
12077 # that this is the size.
12078 #------------------------------------------------------------------------------
12079 if ($key eq "debug")
12081 $db_size = ($debug_size_value eq "on") ? "s" : $debug_size_value;
12082 $value = $db_size . " (size)";
12085 else
12087 $value = "undefined";
12090 my $print_line = sprintf ("%-20s %-22s %8s %s",
12091 $keyword, $user_option, $defined, $value);
12093 gp_message ($mode, $subr_name, $print_line);
12095 } #-- End of subroutine print_table_user_settings
12097 #------------------------------------------------------------------------------
12098 # Dump the contents of nested hash "g_user_settings". Some simple formatting
12099 # is applied to make it easier to distinguish the various values.
12100 #------------------------------------------------------------------------------
12101 sub print_user_settings
12103 my $subr_name = get_my_name ();
12105 my ($mode, $comment) = @_;
12107 my $keyword_value_pair;
12109 gp_message ($mode, $subr_name, $comment);
12111 for my $key (keys %g_user_settings)
12113 my $print_line = sprintf ("%-20s =>", $key);
12114 for my $fields (sort keys %{ $g_user_settings{$key} })
12116 if (defined ($g_user_settings{$key}{$fields}))
12118 $keyword_value_pair = $fields." = ".$g_user_settings{$key}{$fields};
12120 else
12122 $keyword_value_pair = $fields." = ". "undefined";
12124 $print_line = join (" ", $print_line, $keyword_value_pair);
12126 gp_message ($mode, $subr_name, $print_line);
12128 } #-- End of subroutine print_user_settings
12130 #------------------------------------------------------------------------------
12131 # Print the version number and license information.
12132 #------------------------------------------------------------------------------
12133 sub print_version_info
12135 print "$version_info\n";
12136 print "Copyright (C) 2023 Free Software Foundation, Inc.\n";
12137 print "License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.\n";
12138 print "This is free software: you are free to change and redistribute it.\n";
12139 print "There is NO WARRANTY, to the extent permitted by law.\n";
12141 return (0);
12143 } #-- End of subroutine print_version_info
12145 #------------------------------------------------------------------------------
12146 # Dump all the warning messages in the buffer.
12147 #------------------------------------------------------------------------------
12148 sub print_warnings_buffer
12150 my $subr_name = get_my_name ();
12152 my $ignore_value;
12153 my $msg;
12155 if (not $g_options_printed)
12156 #------------------------------------------------------------------------------
12157 # Only if the options have not yet been printed, print them.
12158 #------------------------------------------------------------------------------
12160 $g_options_printed = $TRUE;
12161 $ignore_value = print_command_line_options (\$g_warn_keyword);
12164 for my $i (keys @g_warning_msgs)
12166 $msg = $g_warning_msgs[$i];
12167 if ($msg =~ /^$g_html_new_line/)
12169 $msg =~ s/$g_html_new_line//;
12170 printf ("%-9s\n", $g_warn_keyword);
12172 printf ("%-9s %s\n", $g_warn_keyword, ucfirst ($msg));
12175 return (0);
12177 } #-- End of subroutine print_warnings_buffer
12179 #------------------------------------------------------------------------------
12180 # Process the call tree input data and generate HTML output.
12181 #------------------------------------------------------------------------------
12182 sub process_calltree
12184 my $subr_name = get_my_name ();
12186 my ($function_info_ref, $function_address_info_ref, $addressobjtextm_ref,
12187 $input_string) = @_;
12189 my @function_info = @{ $function_info_ref };
12190 my %function_address_info = %{ $function_address_info_ref };
12191 my %addressobjtextm = %{ $addressobjtextm_ref };
12193 my $outputdir = append_forward_slash ($input_string);
12195 my @call_tree_data = ();
12197 my $LANG = $g_locale_settings{"LANG"};
12198 my $decimal_separator = $g_locale_settings{"decimal_separator"};
12200 my $infile = $outputdir . "calltree";
12201 my $outfile = $outputdir . "calltree.html";
12203 open (CALL_TREE_IN, "<", $infile)
12204 or die ("Not able to open calltree file $infile for reading - '$!'");
12205 gp_message ("debug", $subr_name, "opened file $infile for reading");
12207 open (CALL_TREE_OUT, ">", $outfile)
12208 or die ("Not able to open $outfile for writing - '$!'");
12209 gp_message ("debug", $subr_name, "opened file $outfile for writing");
12211 gp_message ("debug", $subr_name, "building calltree file $outfile");
12213 #------------------------------------------------------------------------------
12214 # The directory name is potentially used below, but since it is a constant,
12215 # we get it here and only once.
12216 #------------------------------------------------------------------------------
12217 # my ($ignore_file_name, $directory_name, $ignore_suffix) = fileparse ($infile,"");
12218 # gp_message ("debug", $subr_name, "directory_name = $directory_name");
12220 #------------------------------------------------------------------------------
12221 # Generate some of the structures used in the HTML output.
12222 #------------------------------------------------------------------------------
12223 my $file_title = "Call Tree overview";
12224 my $html_header = ${ create_html_header (\$file_title) };
12225 my $html_home_right = ${ generate_home_link ("right") };
12227 my $page_title = "Call Tree View";
12228 my $size_text = "h2";
12229 my $position_text = "center";
12230 my $html_title_header = ${ generate_a_header (
12231 \$page_title,
12232 \$size_text,
12233 \$position_text) };
12235 #------------------------------------------------------------------------------
12236 # Get the acknowledgement, return to main link, and final html statements.
12237 #------------------------------------------------------------------------------
12238 my $html_home_left = ${ generate_home_link ("left") };
12239 my $html_acknowledgement = ${ create_html_credits () };
12240 my $html_end = ${ terminate_html_document () };
12242 #------------------------------------------------------------------------------
12243 # Read all of the file into array with the name call_tree_data.
12244 #------------------------------------------------------------------------------
12245 chomp (@call_tree_data = <CALL_TREE_IN>);
12246 close (CALL_TREE_IN);
12248 #------------------------------------------------------------------------------
12249 #------------------------------------------------------------------------------
12250 # Process the data here and generate the HTML lines.
12251 #------------------------------------------------------------------------------
12252 #------------------------------------------------------------------------------
12254 #------------------------------------------------------------------------------
12255 # Print the top part of the HTML file.
12256 #------------------------------------------------------------------------------
12257 print CALL_TREE_OUT $html_header;
12258 print CALL_TREE_OUT $html_home_right;
12259 print CALL_TREE_OUT $html_title_header;
12261 #------------------------------------------------------------------------------
12262 # Print the generated HTML structures here.
12263 #------------------------------------------------------------------------------
12264 ## print CALL_TREE_OUT "$_" for @whatever;
12265 ## print CALL_TREE_OUT "<pre>\n";
12266 ## print CALL_TREE_OUT "$_\n" for @whatever2;
12267 ## print CALL_TREE_OUT "</pre>\n";
12269 #------------------------------------------------------------------------------
12270 # Print the last part of the HTML file.
12271 #------------------------------------------------------------------------------
12272 print CALL_TREE_OUT $html_home_left;
12273 print CALL_TREE_OUT "<br>\n";
12274 print CALL_TREE_OUT $html_acknowledgement;
12275 print CALL_TREE_OUT $html_end;
12277 close (CALL_TREE_OUT);
12279 return (0);
12281 } #-- End of subroutine process_calltree
12283 #------------------------------------------------------------------------------
12284 # Process the generated experiment info file(s).
12285 #------------------------------------------------------------------------------
12286 sub process_experiment_info
12288 my $subr_name = get_my_name ();
12290 my ($experiment_data_ref) = @_;
12292 my @exp_info;
12293 my @experiment_data = @{ $experiment_data_ref };
12295 my $exp_id;
12296 my $exp_name;
12297 my $exp_data_file;
12298 my $input_line;
12299 my $target_cmd;
12300 my $hostname ;
12301 my $OS;
12302 my $page_size;
12303 my $architecture;
12304 my $start_date;
12305 my $end_experiment;
12306 my $data_collection_duration;
12307 my $total_thread_time;
12308 my $user_cpu_time;
12309 my $user_cpu_percentage;
12310 my $system_cpu_time;
12311 my $system_cpu_percentage;
12312 my $sleep_time;
12313 my $sleep_percentage;
12315 #------------------------------------------------------------------------------
12316 # Define the regular expressions used to capture the info.
12317 #------------------------------------------------------------------------------
12318 # Target command (64-bit): './../bindir/mxv-pthreads.exe -m 3000 -n 2000 -t 2'
12320 my $target_cmd_regex = '\s*Target command\s+(\(.+\)):\s+\'(.+)\'';
12322 # Host `ruudvan-vm-haswell-2-20210609', OS `Linux 5.4.17-2102.202.5.el8uek.x86_64', page size 4096, architecture `x86_64'
12324 my $host_system_regex = '\s*Host\s+\`(.+)\',\s+OS\s+\`(.+)\',\s+page size\s+(\d+),\s+architecture\s+\`(.+)\'';
12326 # Experiment started Mon Aug 30 13:03:20 2021
12328 my $start_date_regex = '\s*Experiment started\s+(.+)';
12330 # Experiment Ended: 1.812441219
12332 my $end_experiment_regex = '\s*Experiment Ended:\s+(.+)';
12334 # Data Collection Duration: 1.812441219
12336 my $data_collection_duration_regex = '\s*Data Collection Duration:\s+(.+)';
12338 # Total Thread Time (sec.): 1.812
12340 my $total_thread_time_regex = '\s*Total Thread Time (sec.):\s+(.+)';
12342 # User CPU: 1.685 ( 95.0%)
12344 my $user_cpu_regex = '\s*User CPU:\s+(.+)\s+\(\s*(.+)\)';
12346 # System CPU: 0.088 ( 5.0%)
12348 my $system_cpu_regex = '\s*System CPU:\s+(.+)\s+\(\s*(.+)\)';
12350 # Sleep: 0. ( 0. %)
12352 my $sleep_regex = '\s*Sleep:\s+(.+)\s+\(\s*(.+)\)';
12354 #------------------------------------------------------------------------------
12355 # Scan the experiment data and select the info of interest.
12356 #------------------------------------------------------------------------------
12357 for my $i (sort keys @experiment_data)
12359 $exp_id = $experiment_data[$i]{"exp_id"};
12360 $exp_name = $experiment_data[$i]{"exp_name_full"};
12361 $exp_data_file = $experiment_data[$i]{"exp_data_file"};
12363 my $msg = "exp_id = $exp_id name = $exp_name file = $exp_data_file";
12364 gp_message ("debug", $subr_name, $msg);
12366 open (EXPERIMENT_INFO, "<", $exp_data_file)
12367 or die ("$subr_name - unable to open file $exp_data_file for reading '$!'");
12368 gp_message ("debug", $subr_name, "opened file $exp_data_file for reading");
12370 chomp (@exp_info = <EXPERIMENT_INFO>);
12372 #------------------------------------------------------------------------------
12373 # Process the info for the current experiment.
12374 #------------------------------------------------------------------------------
12375 for my $line (0 .. $#exp_info)
12377 $input_line = $exp_info[$line];
12379 my $msg = "exp_id = $exp_id: input_line = $input_line";
12380 gp_message ("debugM", $subr_name, $msg);
12382 if ($input_line =~ /$target_cmd_regex/)
12384 $target_cmd = $2;
12385 gp_message ("debugM", $subr_name, "$exp_id => $target_cmd");
12386 $experiment_data[$i]{"target_cmd"} = $target_cmd;
12388 elsif ($input_line =~ /$host_system_regex/)
12390 $hostname = $1;
12391 $OS = $2;
12392 $page_size = $3;
12393 $architecture = $4;
12394 gp_message ("debugM", $subr_name, "$exp_id => $hostname $OS $page_size $architecture");
12395 $experiment_data[$i]{"hostname"} = $hostname;
12396 $experiment_data[$i]{"OS"} = $OS;
12397 $experiment_data[$i]{"page_size"} = $page_size;
12398 $experiment_data[$i]{"architecture"} = $architecture;
12400 elsif ($input_line =~ /$start_date_regex/)
12402 $start_date = $1;
12403 gp_message ("debugM", $subr_name, "$exp_id => $start_date");
12404 $experiment_data[$i]{"start_date"} = $start_date;
12406 elsif ($input_line =~ /$end_experiment_regex/)
12408 $end_experiment = $1;
12409 gp_message ("debugM", $subr_name, "$exp_id => $end_experiment");
12410 $experiment_data[$i]{"end_experiment"} = $end_experiment;
12412 elsif ($input_line =~ /$data_collection_duration_regex/)
12414 $data_collection_duration = $1;
12415 gp_message ("debugM", $subr_name, "$exp_id => $data_collection_duration");
12416 $experiment_data[$i]{"data_collection_duration"} = $data_collection_duration;
12418 #------------------------------------------------------------------------------
12419 # Start Label: Total
12420 # End Label: Total
12421 # Start Time (sec.): 0.000
12422 # End Time (sec.): 1.812
12423 # Duration (sec.): 1.812
12424 # Total Thread Time (sec.): 1.812
12425 # Average number of Threads: 1.000
12427 # Process Times (sec.):
12428 # User CPU: 1.666 ( 91.9%)
12429 # System CPU: 0.090 ( 5.0%)
12430 # Trap CPU: 0. ( 0. %)
12431 # User Lock: 0. ( 0. %)
12432 # Data Page Fault: 0. ( 0. %)
12433 # Text Page Fault: 0. ( 0. %)
12434 # Kernel Page Fault: 0. ( 0. %)
12435 # Stopped: 0. ( 0. %)
12436 # Wait CPU: 0. ( 0. %)
12437 # Sleep: 0.056 ( 3.1%)
12438 #------------------------------------------------------------------------------
12439 elsif ($input_line =~ /$total_thread_time_regex/)
12441 $total_thread_time = $1;
12442 gp_message ("debugM", $subr_name, "$exp_id => $total_thread_time");
12443 $experiment_data[$i]{"total_thread_time"} = $total_thread_time;
12445 elsif ($input_line =~ /$user_cpu_regex/)
12447 $user_cpu_time = $1;
12448 $user_cpu_percentage = $2;
12449 gp_message ("debugM", $subr_name, "$exp_id => $user_cpu_time $user_cpu_percentage");
12450 $experiment_data[$i]{"user_cpu_time"} = $user_cpu_time . "&nbsp; (" . $user_cpu_percentage . ")";
12451 $experiment_data[$i]{"user_cpu_percentage"} = $user_cpu_percentage;
12453 elsif ($input_line =~ /$system_cpu_regex/)
12455 $system_cpu_time = $1;
12456 $system_cpu_percentage = $2;
12457 gp_message ("debugM", $subr_name, "$exp_id => $system_cpu_time $system_cpu_percentage");
12458 $experiment_data[$i]{"system_cpu_time"} = $system_cpu_time . "&nbsp; (" . $system_cpu_percentage . ")";
12459 $experiment_data[$i]{"system_cpu_percentage"} = $system_cpu_percentage;
12461 elsif ($input_line =~ /$sleep_regex/)
12463 $sleep_time = $1;
12464 $sleep_percentage = $2;
12465 $experiment_data[$i]{"sleep_time"} = $sleep_time . "&nbsp; (" . $sleep_percentage . ")";
12466 $experiment_data[$i]{"sleep_percentage"} = $sleep_percentage;
12468 my $msg = "exp_id = $exp_id => sleep_time = $sleep_time " .
12469 "sleep_percentage = $sleep_percentage";
12470 gp_message ("debugM", $subr_name, $msg);
12475 for my $keys (0 .. $#experiment_data)
12477 for my $fields (sort keys %{ $experiment_data[$keys] })
12479 my $msg = "experiment_data[$keys]{$fields} = " .
12480 $experiment_data[$keys]{$fields};
12481 gp_message ("debugM", $subr_name, $msg);
12485 return (\@experiment_data);
12487 } #-- End of subroutine process_experiment_info
12489 #------------------------------------------------------------------------------
12490 # TBD
12491 #------------------------------------------------------------------------------
12492 sub process_function_files
12494 my $subr_name = get_my_name ();
12496 my ($exp_dir_list_ref, $executable_name, $time_percentage_multiplier,
12497 $summary_metrics, $process_all_functions, $elf_loadobjects_found,
12498 $outputdir, $sort_fields_ref, $function_info_ref,
12499 $function_address_and_index_ref, $LINUX_vDSO_ref,
12500 $metric_description_ref, $elf_arch, $base_va_executable,
12501 $ARCHIVES_MAP_NAME, $ARCHIVES_MAP_VADDR, $elf_rats_ref) = @_;
12503 my $old_fsummary;
12504 my $total_attributed_time;
12505 my $current_attributed_time;
12506 my $value;
12508 my @exp_dir_list = @{ $exp_dir_list_ref };
12509 my @function_info = @{ $function_info_ref };
12510 my %function_address_and_index = %{ $function_address_and_index_ref };
12511 my @sort_fields = @{ $sort_fields_ref };
12512 my %metric_description = %{ $metric_description_ref };
12513 my %elf_rats = %{ $elf_rats_ref };
12515 #------------------------------------------------------------------------------
12516 # The regex section.
12518 # TBD: Remove the part regarding clones. Legacy.
12519 #------------------------------------------------------------------------------
12520 my $find_clone_regex = '^(.*)(\s+--\s+cloned\s+version\s+\[)([^]]+)(\])';
12521 my $remove_number_regex = '^\d+:';
12522 my $replace_quote_regex = '"/\"';
12524 my %addressobj_index = ();
12525 my %function_address_info = ();
12526 my $function_address_info_ref;
12528 $outputdir = append_forward_slash ($outputdir);
12530 my %functions_per_metric_indexes = ();
12531 my $functions_per_metric_indexes_ref;
12533 my %functions_per_metric_first_index = ();
12534 my $functions_per_metric_first_index_ref;
12536 my %routine_list = ();
12537 my %handled_routines = ();
12539 #------------------------------------------------------------------------------
12540 # TBD: Name cleanup needed.
12541 #------------------------------------------------------------------------------
12542 my $msg;
12544 my $number_of_metrics;
12545 my $expr_name;
12546 my $routine;
12547 my $tmp;
12548 my $loadobj;
12549 my $PCA;
12550 my $address_field;
12551 my $limit_txt;
12552 my $n_metrics_text;
12553 my $disfile;
12554 my $srcfile;
12555 my $RIN;
12556 my $gp_listings_cmd;
12557 my $gp_display_text_cmd;
12558 my $ignore_value;
12560 my $result_file = $outputdir . "gp-listings.out";
12561 my $gp_error_file = $outputdir . "gp-listings.err";
12563 my $convert_to_dot = $g_locale_settings{"convert_to_dot"};
12564 my $decimal_separator = $g_locale_settings{"decimal_separator"};
12565 my $length_of_string = length ($outputdir);
12567 $expr_name = join (" ", @exp_dir_list);
12569 gp_message ("debug", $subr_name, "expr_name = $expr_name");
12571 #------------------------------------------------------------------------------
12572 # Loop over the files in $outputdir.
12573 #------------------------------------------------------------------------------
12574 while (glob ($outputdir.'*.sort.func-PC'))
12576 my $metric;
12577 my $infile;
12578 my $ignore_value;
12579 my $suffix_not_used;
12581 $infile = $_;
12583 ($metric, $ignore_value, $suffix_not_used) = fileparse ($infile, ".sort.func-PC");
12585 gp_message ("debugXL", $subr_name, "suffix_not_used = $suffix_not_used");
12586 gp_message ("debugXL", $subr_name, "func-PC->$infile<- metric->$metric<-");
12588 # Function_info creates the functions files from the PC ones
12589 # as well as culling PC and metric information
12591 ($function_address_info_ref,
12592 $functions_per_metric_first_index_ref,
12593 $functions_per_metric_indexes_ref) = function_info (
12594 $outputdir,
12595 $infile,
12596 $metric,
12597 $LINUX_vDSO_ref);
12599 @{$function_address_info{$metric}} = @{$function_address_info_ref};
12600 %{$functions_per_metric_indexes{$metric}} = %{$functions_per_metric_indexes_ref};
12601 %{$functions_per_metric_first_index{$metric}} = %{$functions_per_metric_first_index_ref};
12603 $ignore_value = print_metric_function_array ($metric,
12604 "function_address_info",
12605 \@{$function_address_info{$metric}});
12606 $ignore_value = print_metric_function_hash ("hash_hash", $metric,
12607 "functions_per_metric_first_index",
12608 \%{$functions_per_metric_first_index{$metric}});
12609 $ignore_value = print_metric_function_hash ("hash_array", $metric,
12610 "functions_per_metric_indexes",
12611 \%{$functions_per_metric_indexes{$metric}});
12614 #------------------------------------------------------------------------------
12615 # Get header info for use in post processing er_html output
12616 #------------------------------------------------------------------------------
12617 gp_message ("debugXL", $subr_name, "get_hdr_info section");
12619 get_hdr_info ($outputdir, $outputdir."functions.sort.func");
12621 for my $field (@sort_fields)
12623 get_hdr_info ($outputdir, $outputdir."$field.sort.func");
12626 #------------------------------------------------------------------------------
12627 # Caller-callee
12628 #------------------------------------------------------------------------------
12629 get_hdr_info ($outputdir, $outputdir."calls.sort.func");
12631 #------------------------------------------------------------------------------
12632 # Calltree
12633 #------------------------------------------------------------------------------
12634 if ($g_user_settings{"calltree"}{"current_value"} eq "on")
12636 get_hdr_info ($outputdir, $outputdir."calltree.sort.func");
12639 gp_message ("debug", $subr_name, "process functions");
12641 my $scriptfile = $outputdir.'gp-script';
12642 my $script_metrics = "$summary_metrics";
12643 my $func_limit = $g_user_settings{"func_limit"}{"current_value"};
12645 open (SCRIPT, ">", $scriptfile)
12646 or die ("Unable to create script file $scriptfile - '$!'");
12647 gp_message ("debug", $subr_name, "opened script file $scriptfile for writing");
12649 print SCRIPT "# limit $func_limit\n";
12650 print SCRIPT "limit $func_limit\n";
12651 print SCRIPT "# thread_select all\n";
12652 print SCRIPT "thread_select all\n";
12653 print SCRIPT "# metrics $script_metrics\n";
12654 print SCRIPT "metrics $script_metrics\n";
12656 for my $metric (@sort_fields)
12658 gp_message ("debug", $subr_name, "handling $metric->$metric_description{$metric}");
12660 $total_attributed_time = 0;
12661 $current_attributed_time = 0;
12663 $value = $function_address_info{$metric}[0]{"metric_value"}; # <Total>
12664 if ($convert_to_dot)
12666 $value =~ s/$decimal_separator/\./;
12668 $total_attributed_time = $value;
12670 #------------------------------------------------------------------------------
12671 # start at 1 - skipping <Total>
12672 #------------------------------------------------------------------------------
12673 for my $INDEX (1 .. $#{$function_address_info{$metric}})
12675 #------------------------------------------------------------------------------
12676 #Looking to handle at least 99% of the time - or what the user asked for
12677 #------------------------------------------------------------------------------
12678 $value = $function_address_info{$metric}[$INDEX]{"metric_value"};
12679 $routine = $function_address_info{$metric}[$INDEX]{"routine"};
12681 gp_message ("debugXL", $subr_name, " total $total_attributed_time current $current_attributed_time");
12682 gp_message ("debugXL", $subr_name, " (found routine $routine : value $value)");
12684 if ($convert_to_dot)
12686 $value =~ s/$decimal_separator/\./;
12689 if ( ($value > $total_attributed_time*(1-$time_percentage_multiplier)) or
12690 ( ($total_attributed_time == 0) and ($value>0) ) or
12691 $process_all_functions)
12693 $PCA = $function_address_info{$metric}[$INDEX]{"PC Address"};
12695 if (not exists ($functions_per_metric_first_index{$metric}{$routine}{$PCA}))
12697 gp_message ("debugXL", $subr_name, "not exists: functions_per_metric_first_index{$metric}{$routine}{$PCA}");
12699 if (not exists ($function_address_and_index{$routine}{$PCA}))
12701 gp_message ("debugXL", $subr_name, "not exists: function_address_and_index{$routine}{$PCA}");
12704 if (exists ($functions_per_metric_first_index{$metric}{$routine}{$PCA}) and
12705 exists ($function_address_and_index{$routine}{$PCA}))
12707 #------------------------------------------------------------------------------
12708 # handled_routines now contains $RI from "first_metric" (?)
12709 #------------------------------------------------------------------------------
12710 $handled_routines{$function_address_and_index{$routine}{$PCA}} = 1;
12711 my $description = ${ retrieve_metric_description (\$metric, \%metric_description) };
12712 if ($metric_description{$metric} =~ /Exclusive Total CPU Time/)
12714 $routine_list{$routine} = 1
12717 gp_message ("debugXL", $subr_name, " $routine is candidate");
12719 else
12721 die ("internal error for metric $metric and routine $routine");
12724 $current_attributed_time += $value;
12728 #------------------------------------------------------------------------------
12729 # Sort numerically in ascending order.
12730 #------------------------------------------------------------------------------
12731 for my $routine_index (sort {$a <=> $b} keys %handled_routines)
12733 $routine = $function_info[$routine_index]{"routine"};
12734 gp_message ("debugXL", $subr_name, "routine_index = $routine_index routine = $routine");
12735 next unless $routine_list{$routine};
12737 # not used $source = $function_info[$routine_index]{"Source File"};
12739 $function_info[$routine_index]{"srcline"} = "";
12740 $address_field = $function_info[$routine_index]{"addressobjtext"};
12742 #------------------------------------------------------------------------------
12743 # Strip the internal number from the address field.
12744 #------------------------------------------------------------------------------
12745 $msg = "address_field before regex = " . $address_field;
12746 gp_message ("debugXL", $subr_name, $msg);
12747 $address_field =~ s/$remove_number_regex//;
12748 $msg = "address_field after regex = " . $address_field;
12749 gp_message ("debugXL", $subr_name, $msg);
12751 ## $disfile = "file\.$routine_index\.dis";
12752 $disfile = "file." . $routine_index . "." . $g_html_base_file_name{"disassembly"};
12753 $srcfile = "";
12754 $srcfile = "file\.$routine_index\.src.txt";
12756 #------------------------------------------------------------------------------
12757 # If the file is unknown, we can disassemble anyway and add disassembly
12758 # to the script.
12759 #------------------------------------------------------------------------------
12760 print SCRIPT "# outfile $outputdir"."$disfile\n";
12761 print SCRIPT "outfile $outputdir"."$disfile\n";
12762 #------------------------------------------------------------------------------
12763 # TBD: Legacy. Not sure why this is needed, but it won't harm things. I hope.
12764 #------------------------------------------------------------------------------
12765 $tmp = $routine;
12766 $tmp =~ s/$replace_quote_regex//g;
12767 print SCRIPT "# disasm \"$tmp\" $address_field\n";
12768 #------------------------------------------------------------------------------
12769 ## TBD: adding the address is not supported. Need to find a way to figure
12770 ## out the ID of the function.
12771 ## print SCRIPT "disasm \"$tmp\" $address_field\n";
12772 ## print SCRIPT "source \"$tmp\" $address_field\n";
12773 #------------------------------------------------------------------------------
12774 print SCRIPT "disasm \"$tmp\"\n";
12775 if ($srcfile=~/file/)
12777 print SCRIPT "# outfile $outputdir"."$srcfile\n";
12778 print SCRIPT "outfile $outputdir"."$srcfile\n";
12779 print SCRIPT "# source \"$tmp\" $address_field\n";
12780 print SCRIPT "source \"$tmp\"\n";
12783 if ($routine =~ /$find_clone_regex/)
12785 my ($clone_routine) = $1.$2.$3.$4;
12786 my ($clone) = $3;
12789 close SCRIPT;
12791 #------------------------------------------------------------------------------
12792 # Remember the number of handled routines depends on the limit setting passed
12793 # to er_print together with the sorting order on the metrics, which usually results
12794 # in different routines at the top. Thus $RIN below can be greater than the limit.
12795 #------------------------------------------------------------------------------
12797 $RIN = scalar (keys %handled_routines);
12799 if (!$func_limit)
12801 $limit_txt = "unlimited";
12803 else
12805 $limit_txt = $func_limit - 1;
12808 $number_of_metrics = scalar (@sort_fields);
12810 $n_metrics_text = ($number_of_metrics == 1) ? "metric" : "metrics";
12812 gp_message ("debugXL", $subr_name, "built function list with $RIN functions");
12813 gp_message ("debugXL", $subr_name, "$number_of_metrics $n_metrics_text and a function limit of $limit_txt");
12815 # add ELF program header offset
12817 for my $routine_index (sort {$a <=> $b} keys %handled_routines)
12819 $routine = $function_info[$routine_index]{"routine"};
12820 $loadobj = $function_info[$routine_index]{"Load Object"};
12822 gp_message ("debugXL", $subr_name, "routine = $routine loadobj = $loadobj elf_arch = $elf_arch");
12824 if ($loadobj ne '')
12826 # <Truncated-stack> is associated with <Total>. Its load object is <Total>
12827 if ($loadobj eq "<Total>")
12829 next;
12831 # Have seen a routine called <Unknown>. Its load object is <Unknown>
12832 if ($loadobj eq "<Unknown>")
12834 next;
12836 ###############################################################################
12837 ## RUUD: The new approach gives a different result. Investigate this.
12839 # Turns out the new code improves the result. The addresses are now correct
12840 # and as a result, more ftag's are created later on.
12841 ###############################################################################
12842 gp_message ("debugXL", $subr_name, "before function_info[$routine_index]{addressobj} = $function_info[$routine_index]{'addressobj'}");
12844 $function_info[$routine_index]{"addressobj"} += bigint::hex (
12845 determine_base_va_address (
12846 $executable_name,
12847 $base_va_executable,
12848 $loadobj,
12849 $routine));
12850 $addressobj_index{$function_info[$routine_index]{"addressobj"}} = $routine_index;
12852 gp_message ("debugXL", $subr_name, "after function_info[$routine_index]{addressobj} = $function_info[$routine_index]{'addressobj'}");
12853 gp_message ("debugXL", $subr_name, "after addressobj_index{function_info[$routine_index]{addressobj}} = $addressobj_index{$function_info[$routine_index]{'addressobj'}}");
12857 #------------------------------------------------------------------------------
12858 # Get the disassembly and source code output.
12859 #------------------------------------------------------------------------------
12860 $gp_listings_cmd = "$GP_DISPLAY_TEXT -limit $func_limit -viewmode machine " .
12861 "-compare off -script $scriptfile $expr_name";
12863 $gp_display_text_cmd = "$gp_listings_cmd 1> $result_file 2>> $gp_error_file";
12865 gp_message ("debugXL", $subr_name,"gp_display_text_cmd = $gp_display_text_cmd");
12867 gp_message ("debug", $subr_name, "calling $GP_DISPLAY_TEXT to produce disassembly and source code output");
12869 my ($error_code, $cmd_output) = execute_system_cmd ($gp_display_text_cmd);
12871 if ($error_code != 0)
12873 $ignore_value = msg_display_text_failure ($gp_display_text_cmd,
12874 $error_code,
12875 $gp_error_file);
12876 gp_message ("abort", $subr_name, "execution terminated");
12879 return (\@function_info, \%function_address_info, \%addressobj_index);
12881 } #-- End of subroutine process_function_files
12883 #------------------------------------------------------------------------------
12884 # Process the information found in the function overview file passed in.
12886 # Example input:
12888 # Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu )
12889 # Functions sorted by metric: Exclusive Total CPU Time
12891 # PC Addr. Name Excl. Excl. CPU Excl. Excl. Excl. Excl.
12892 # Total Cycles Instructions Last-Level IPC CPI
12893 # CPU sec. sec. Executed Cache Misses
12894 # 1:0x00000000 <Total> 3.713 4.256 15396819712 27727992 1.577 0.634
12895 # 2:0x000021ae mxv_core 3.532 4.116 14500538992 27527781 1.536 0.651
12896 # 2:0x00001f7b init_data 0.070 0.084 64020034 200211 0.333 3.000
12897 #------------------------------------------------------------------------------
12898 sub process_function_overview
12900 my $subr_name = get_my_name ();
12902 my ($metric_ref, $exp_type_ref, $summary_metrics_ref, $number_of_metrics_ref,
12903 $function_info_ref, $function_view_structure_ref, $overview_file_ref) = @_;
12905 my $metric = ${ $metric_ref };
12906 my $exp_type = ${ $exp_type_ref };
12907 my $summary_metrics = ${ $summary_metrics_ref };
12908 my $number_of_metrics = ${ $number_of_metrics_ref };
12909 my @function_info = @{ $function_info_ref };
12910 my %function_view_structure = %{ $function_view_structure_ref };
12911 my $overview_file = ${ $overview_file_ref };
12913 my $all_metrics;
12914 my $decimal_separator = $g_locale_settings{"decimal_separator"};
12915 my $length_of_block;
12916 my $elements_in_name;
12917 my $full_hex_address;
12918 my $header_line;
12919 my $hex_address;
12920 my $html_line;
12921 my $input_line;
12922 my $marker;
12923 my $name_regex;
12924 my $no_of_fields;
12925 my $metrics_length;
12926 my $missing_digits;
12927 my $msg;
12928 my $remaining_part_header;
12929 my $routine;
12930 my $routine_length;
12931 my $scan_header = $FALSE;
12932 my $scan_function_data = $FALSE;
12933 my $string_length;
12934 my $total_header_lines;
12936 my @address_field = ();
12937 my @fields = ();
12938 my @function_data = ();
12939 my @function_names = ();
12940 my @function_view_array = ();
12941 my @function_view_modified = ();
12942 my @header_lines = ();
12943 my @metrics_part = ();
12944 my @metric_values = ();
12946 #------------------------------------------------------------------------------
12947 # The regex section.
12948 #------------------------------------------------------------------------------
12949 my $header_name_regex = '(.*\.)(\s+)(Name)\s+(.*)';
12950 my $total_marker_regex = '\s*(\d+:0x[a-fA-F0-9]+)\s+(<Total>)\s+(.*)';
12951 my $empty_line_regex = '^\s*$';
12952 my $catch_all_regex = '\s*(.*)';
12953 my $get_hex_address_regex = '(\d+):0x(\S+)';
12954 my $get_addr_offset_regex = '^@\d+:';
12955 my $zero_dot_at_end_regex = '[\w0-9' . $decimal_separator . ']*(0' . $decimal_separator . '$)';
12956 my $backward_slash_regex = '\/';
12958 $msg = "enter subroutine " . $subr_name;
12959 gp_message ("debug", $subr_name, $msg);
12961 #------------------------------------------------------------------------------
12962 if (is_file_empty ($overview_file))
12964 gp_message ("assertion", $subr_name, "file $overview_file is empty");
12967 open (FUNC_OVERVIEW, "<", $overview_file)
12968 or die ("$subr_name - unable to open file $overview_file for reading '$!'");
12969 gp_message ("debug", $subr_name, "opened file $overview_file for reading");
12971 gp_message ("debug", $subr_name, "processing file for exp_type = $exp_type");
12973 gp_message ("debugM", $subr_name, "header_name_regex = $header_name_regex");
12974 gp_message ("debugM", $subr_name, "total_marker_regex = $total_marker_regex");
12975 gp_message ("debugM", $subr_name, "empty_line_regex = $empty_line_regex");
12976 gp_message ("debugM", $subr_name, "catch_all_regex = $catch_all_regex");
12977 gp_message ("debugM", $subr_name, "get_hex_address_regex = $get_hex_address_regex");
12978 gp_message ("debugM", $subr_name, "get_addr_offset_regex = $get_addr_offset_regex");
12979 gp_message ("debugM", $subr_name, "zero_dot_at_end_regex = $zero_dot_at_end_regex");
12980 gp_message ("debugM", $subr_name, "backward_slash_regex = $backward_slash_regex");
12982 #------------------------------------------------------------------------------
12983 # Read the input file into memory.
12984 #------------------------------------------------------------------------------
12985 chomp (@function_data = <FUNC_OVERVIEW>);
12986 gp_message ("debug", $subr_name, "read all of file $overview_file into memory");
12988 #------------------------------------------------------------------------------
12989 # Remove a legacy redundant string, if any.
12990 #------------------------------------------------------------------------------
12991 @function_data = @{ remove_redundant_string (\@function_data)};
12993 #------------------------------------------------------------------------------
12994 # Parse the function view info and store the data.
12995 #------------------------------------------------------------------------------
12996 my $max_header_length = 0;
12997 my $max_metrics_length = 0;
12999 #------------------------------------------------------------------------------
13000 # Loop over all the lines. Extract the header, metric values, function names,
13001 # and the addresses.
13003 # This is also where the maximum lengths for the header and metric lines are
13004 # computed. This is used to get the correct alignment in the HTML output.
13005 #------------------------------------------------------------------------------
13006 for (my $line = 0; $line <= $#function_data; $line++)
13008 $input_line = $function_data[$line];
13009 ## $input_line =~ s/ -- no functions found//;
13011 gp_message ("debugXL", $subr_name, "input_line = $input_line");
13013 #------------------------------------------------------------------------------
13014 # The table header is assumed to start at the line that has "Name" in it.
13015 # The header ends when we see the function name "<Total>".
13016 #------------------------------------------------------------------------------
13017 if ($input_line =~ /$header_name_regex/)
13019 $scan_header = $TRUE;
13021 elsif ($input_line =~ /$total_marker_regex/)
13023 $scan_header = $FALSE;
13024 $scan_function_data = $TRUE;
13027 if ($scan_header)
13029 #------------------------------------------------------------------------------
13030 # This group is only defined for the first line of the header and $4 contains
13031 # the remaining part of the line after "Name", without the leading spaces.
13032 #------------------------------------------------------------------------------
13033 if (defined ($4))
13035 $remaining_part_header = $4;
13036 $msg = "remaining_part_header = $remaining_part_header";
13037 gp_message ("debugXL", $subr_name, $msg);
13039 #------------------------------------------------------------------------------
13040 # Determine the maximum length of the header. This needs to be done before
13041 # the HTML controls are added.
13042 #------------------------------------------------------------------------------
13043 my $header_length = length ($remaining_part_header);
13044 $max_header_length = max ($max_header_length, $header_length);
13046 #------------------------------------------------------------------------------
13047 # TBD Should change this and not yet include html in header_lines
13048 #------------------------------------------------------------------------------
13049 $html_line = "<b>" . $remaining_part_header . "</b>";
13051 push (@header_lines, $html_line);
13053 gp_message ("debugXL", $subr_name, "max_header_length = $max_header_length");
13054 gp_message ("debugXL", $subr_name, "html_line = $html_line");
13056 #------------------------------------------------------------------------------
13057 # Captures the subsequent header lines. Assume they exist.
13058 #------------------------------------------------------------------------------
13059 elsif ($input_line =~ /$catch_all_regex/)
13061 $header_line = $1;
13062 gp_message ("debugXL", $subr_name, "header_line = $header_line");
13064 my $header_length = length ($header_line);
13065 $max_header_length = max ($max_header_length, $header_length);
13067 #------------------------------------------------------------------------------
13068 # TBD Should change this and not yet include html in header_lines
13069 #------------------------------------------------------------------------------
13070 $html_line = "<b>" . $header_line . "</b>";
13072 push (@header_lines, $html_line);
13074 gp_message ("debugXL", $subr_name, "max_header_length = $max_header_length");
13075 gp_message ("debugXL", $subr_name, "html_line = $html_line");
13078 #------------------------------------------------------------------------------
13079 # This is a line with function data.
13080 #------------------------------------------------------------------------------
13081 if ($scan_function_data and (not ($input_line =~ /$empty_line_regex/)))
13083 $msg = "detected a line with function data";
13084 gp_message ("debugXL", $subr_name, $msg);
13086 my ($hex_address_ref, $marker_ref, $reduced_line_ref,
13087 $list_with_metrics_ref) =
13088 split_function_data_line (\$input_line);
13090 $full_hex_address = ${ $hex_address_ref };
13091 $marker = ${ $marker_ref };
13092 $routine = ${ $reduced_line_ref };
13093 $all_metrics = ${ $list_with_metrics_ref };
13095 $msg = "RESULT full_hex_address = " . $full_hex_address;
13096 $msg .= " -- metric values = " . $all_metrics;
13097 $msg .= " -- marker = " . $marker;
13098 $msg .= " -- function name = " . $routine;
13099 gp_message ("debugXL", $subr_name, $msg);
13101 @fields = split (" ", $input_line);
13103 $no_of_fields = $#fields + 1;
13104 $elements_in_name = $no_of_fields - $number_of_metrics - 1;
13106 $msg = "no_of_fields = " . $no_of_fields;
13107 $msg .= " elements_in_name = " . $elements_in_name;
13108 gp_message ("debugXL", $subr_name, $msg);
13110 #------------------------------------------------------------------------------
13111 # In case the last metric is 0. only, we append 3 extra characters that
13112 # represent zero. We cannot change the number to 0.000 though because that
13113 # has a different interpretation than 0.
13114 # In a later phase, the "ZZZ" symbol will be removed again, but for now it
13115 # creates consistency in, for example, the length of the metrics part.
13116 #------------------------------------------------------------------------------
13117 if ($all_metrics =~ /$zero_dot_at_end_regex/)
13119 if (defined ($1) )
13121 #------------------------------------------------------------------------------
13122 # Somewhat overkill, but remove the leading "\" from the decimal separator
13123 # in the debug print since it is used for internal purposes only.
13124 #------------------------------------------------------------------------------
13125 my $decimal_point = $decimal_separator;
13126 $decimal_point =~ s/$backward_slash_regex//;
13127 my $txt = "all_metrics = $all_metrics ended with 0";
13128 $txt .= "$decimal_point ($decimal_separator)";
13129 gp_message ("debugXL", $subr_name, $txt);
13131 $all_metrics .= "ZZZ";
13134 $metrics_length = length ($all_metrics);
13135 $max_metrics_length = max ($max_metrics_length, $metrics_length);
13136 gp_message ("debugXL", $subr_name, "$routine all_metrics = $all_metrics metrics_length = $metrics_length");
13138 $msg = "verify full_hex_address = " . $full_hex_address;
13139 gp_message ("debugXL", $subr_name, $msg);
13141 if ($full_hex_address =~ /$get_hex_address_regex/)
13143 $hex_address = "0x" . $2;
13145 else
13147 $msg = "full_hex_address = $full_hex_address has the wrong format";
13148 gp_message ("assertion", $subr_name, $msg);
13151 push (@address_field, $full_hex_address);
13153 $msg = "pushed full_hex_address = " . $full_hex_address;
13154 gp_message ("debugXL", $subr_name, $msg);
13156 push (@metric_values, $all_metrics);
13158 #------------------------------------------------------------------------------
13159 # Record the function name "as is". Below we figure out what the final name
13160 # should be in case there are multiple occurrences of the same name.
13162 # The reason to decouple this is to avoid the code gets too complex here.
13163 #------------------------------------------------------------------------------
13164 push (@function_names, $routine);
13166 } #-- End of loop over the input lines
13168 #------------------------------------------------------------------------------
13169 # Store the maximum lengths for the header and metrics.
13170 #------------------------------------------------------------------------------
13171 gp_message ("debugXL", $subr_name, "final max_header_length = $max_header_length");
13172 gp_message ("debugXL", $subr_name, "final max_metrics_length = $max_metrics_length");
13174 $function_view_structure{"max header length"} = $max_header_length;
13175 $function_view_structure{"max metrics length"} = $max_metrics_length;
13177 #------------------------------------------------------------------------------
13178 # Determine the final name for the functions and set up the HTML block.
13179 #------------------------------------------------------------------------------
13180 my @final_html_function_block = ();
13181 my @function_index_list = ();
13183 #------------------------------------------------------------------------------
13184 # First, an index list is built. If we are to index the functions in order of
13185 # appearance in the function overview from 0 to n-1, the value of the array
13186 # for index "i" is the index into the large "function_info" structure. This
13187 # has the final name, the html function block, etc.
13188 #------------------------------------------------------------------------------
13190 for my $i (keys @address_field)
13192 $msg = "address_field[" . $i ."] = " . $address_field[$i];
13193 gp_message ("debugM", $subr_name, $msg);
13195 #------------------------------------------------------------------------------
13196 ## TBD: Use get_index_function_info??!!
13197 #------------------------------------------------------------------------------
13198 for my $i (keys @function_names)
13200 #------------------------------------------------------------------------------
13201 # Get the function name and the address from the function overview. The
13202 # address is used to differentiate in case a function has multiple occurences.
13203 #------------------------------------------------------------------------------
13204 my $routine = $function_names[$i];
13205 my $current_address = $address_field[$i];
13207 my $final_function_name;
13208 my $found_a_match = $FALSE;
13209 my $msg;
13210 my $ref_index;
13212 $msg = "on entry - routine = " . $routine;
13213 $msg .= " current_address = " . $current_address;
13214 gp_message ("debugM", $subr_name, $msg);
13216 #------------------------------------------------------------------------------
13217 # Check if there are duplicate entries for this function. If there are, use
13218 # the address to find the right match in the function_info structure.
13219 #------------------------------------------------------------------------------
13220 gp_message ("debugXL", $subr_name, "$routine: first check for multiple occurrences");
13221 if (exists ($g_multi_count_function{$routine}))
13223 $msg = "$g_multi_count_function{$routine} exists";
13224 gp_message ("debugXL", $subr_name, $msg);
13225 $msg = "g_function_occurrences{$routine} = ";
13226 $msg .= $g_function_occurrences{$routine};
13227 gp_message ("debugXL", $subr_name, $msg);
13229 for my $ref (keys @{ $g_map_function_to_index{$routine} })
13231 my $ref_index = $g_map_function_to_index{$routine}[$ref];
13232 my $addr_offset = $function_info[$ref_index]{"addressobjtext"};
13233 #------------------------------------------------------------------------------
13234 # The address has the following format: 6:0x0003af50, but we only need the
13235 # part after the colon and remove the first part.
13236 #------------------------------------------------------------------------------
13237 $addr_offset =~ s/$get_addr_offset_regex//;
13239 gp_message ("debugXL", $subr_name, "$routine: ref_index = $ref_index");
13240 gp_message ("debugXL", $subr_name, "$routine: function_info[$ref_index]{'alt_name'} = $function_info[$ref_index]{'alt_name'}");
13241 gp_message ("debugXL", $subr_name, "$routine: addr_offset = $addr_offset");
13243 if ($addr_offset eq $current_address)
13244 #------------------------------------------------------------------------------
13245 # There is a match and we can store the index.
13246 #------------------------------------------------------------------------------
13248 $found_a_match = $TRUE;
13249 push (@function_index_list, $ref_index);
13250 last;
13254 else
13256 #------------------------------------------------------------------------------
13257 # This is the easy case. There is only one index value. We do check if the
13258 # array element that contains it, exists. If this is not the case, something
13259 # has gone horribly wrong earlier and we need to bail out.
13260 #------------------------------------------------------------------------------
13261 if (defined ($g_map_function_to_index{$routine}[0]))
13263 $found_a_match = $TRUE;
13264 $ref_index = $g_map_function_to_index{$routine}[0];
13265 push (@function_index_list, $ref_index);
13266 my $final_function_name = $function_info[$ref_index]{"routine"};
13267 gp_message ("debugXL", $subr_name, "pushed single occurrence: ref_index = $ref_index final_function_name = $final_function_name");
13270 if (not $found_a_match)
13271 #------------------------------------------------------------------------------
13272 # This should not happen. All we can do is print an error message and stop.
13273 #------------------------------------------------------------------------------
13275 $msg = "cannot find the index for $routine: found_a_match = ";
13276 $msg .= ($found_a_match == $TRUE) ? "TRUE" : "FALSE";
13277 gp_message ("assertion", $subr_name, $msg);
13281 #------------------------------------------------------------------------------
13282 # The loop over all function names has completed and @function_index_list
13283 # contains the index values into @function_info for the functions.
13285 # All we now need to do is to retrieve the correct field(s) from the array.
13286 #------------------------------------------------------------------------------
13287 for my $i (keys @function_index_list)
13289 my $index_for_function = $function_index_list[$i];
13290 push (@final_html_function_block, $function_info[$index_for_function]{"html function block"});
13292 for my $i (keys @final_html_function_block)
13294 my $txt = "final_html_function_block[$i] = $final_html_function_block[$i]";
13295 gp_message ("debugXL", $subr_name, $txt);
13298 #------------------------------------------------------------------------------
13299 # Since the numbers are right aligned, we know that any difference between the
13300 # metric line length and the maximum must be caused by the first column. All
13301 # we need to do is to prepend spaces in case of a difference.
13303 # While we have the line with the metric values, we also replace ZZZ by 3
13304 # spaces.
13305 #------------------------------------------------------------------------------
13306 for my $i (keys @metric_values)
13308 if (length ($metric_values[$i]) < $max_metrics_length)
13310 my $pad = $max_metrics_length - length ($metric_values[$i]);
13311 my $spaces = "";
13312 for my $s (1 .. $pad)
13314 $spaces .= "&nbsp;";
13316 $metric_values[$i] = $spaces . $metric_values[$i];
13318 $metric_values[$i] =~ s/ZZZ/&nbsp;&nbsp;&nbsp;/g;
13321 #------------------------------------------------------------------------------
13322 # Determine the column widths. The start and end index of the words in the
13323 # input line are stored in elements 0 and 1 of @word_index_values.
13325 # The assumption made is that the first digit of a metric value on the first
13326 # line is left # aligned with the header text. These are the Total values
13327 # and other than for some derived metrics, e.g. CPI, should be the largest.
13329 # The positions of the start of the value is what we should then use for the
13330 # word "(sort)" to start.
13332 # For example:
13334 # Excl. Excl. CPU Excl. Excl. Excl. Excl.
13335 # Total Cycles Instructions Last-Level IPC CPI
13336 # CPU sec. sec. Executed Cache Misses
13337 # 174.664 179.250 175838403203 1166209617 0.428 2.339
13338 #------------------------------------------------------------------------------
13340 my $foundit_ref;
13341 my $foundit;
13342 my @index_values = ();
13343 my $index_values_ref;
13345 #------------------------------------------------------------------------------
13346 # Search for "Excl." in the top row. The metric values are aligned with this
13347 # word and we can use it to position "(sort)" in the last header line.
13349 # In @index_values, we store the position(s) of "Excl." in the header line.
13350 # If none can be found, an exception is raised because at least one should
13351 # be there.
13353 # TBD: Check if this can be done only once.
13354 #------------------------------------------------------------------------------
13355 my $target_keyword = "Excl.";
13357 ($foundit_ref, $index_values_ref) = find_keyword_in_string (
13358 \$remaining_part_header,
13359 \$target_keyword);
13361 $foundit = ${ $foundit_ref };
13362 @index_values = @{ $index_values_ref };
13364 if ($foundit)
13366 for my $i (keys @index_values)
13368 my $txt = "index_values[$i] = $index_values[$i]";
13369 gp_message ("debugXL", $subr_name, $txt);
13372 else
13374 $msg = "keyword $target_keyword not found in $remaining_part_header";
13375 gp_message ("assertion", $subr_name, $msg);
13378 #------------------------------------------------------------------------------
13379 # Compute the number of spaces we need to add between the "(sort)" strings.
13381 # For example:
13383 # 01234567890123456789
13385 # Excl. Excl.
13386 # (sort) (sort)
13387 # xxxxxxxx
13389 # The number of spaces required is 14 - 6 = 8.
13391 # The number of spaces to be added is stored in @padding_values. These are
13392 # the spaces to be added before the occurrence of "(sort)". This is why the
13393 # first padding value is 0.
13394 #------------------------------------------------------------------------------
13396 #------------------------------------------------------------------------------
13397 # TBD: This needs to be done only once.
13398 #------------------------------------------------------------------------------
13399 my @padding_values = ();
13400 my $P_previous = 0;
13401 for my $i (keys @index_values)
13403 my $L = $index_values[$i];
13404 my $P = $L + length ("(sort)");
13405 my $pad_spaces = $L - $P_previous;
13407 push (@padding_values, $pad_spaces);
13409 $P_previous = $P;
13412 for my $i (keys @padding_values)
13414 my $txt = "padding_values[$i] = $padding_values[$i]";
13415 gp_message ("debugXL", $subr_name, $txt);
13418 #------------------------------------------------------------------------------
13419 # Build up the sort line. Mark the current metric and make sure the line is
13420 # aligned with the header.
13421 #------------------------------------------------------------------------------
13422 my $sort_string = "(sort)";
13423 my $length_sort_string = length ($sort_string);
13424 my $sort_line = "";
13425 my @active_metrics = split (":", $summary_metrics);
13426 for my $i (0 .. $number_of_metrics-1)
13428 my $pad = $padding_values[$i];
13429 my $metric_value = $active_metrics[$i];
13431 my $spaces = "";
13432 for my $s (1 .. $pad)
13434 $spaces .= "&nbsp;";
13437 gp_message ("debugXL", $subr_name, "i = $i metric_value = $metric_value pad = $pad");
13439 if ($metric_value eq $exp_type)
13440 #------------------------------------------------------------------------------
13441 # The current metric should have a different background color.
13442 #------------------------------------------------------------------------------
13444 $sort_string = "<a href=\'" . $g_html_base_file_name{"function_view"} .
13445 "." . $metric_value . ".html' style='background-color:" .
13446 $g_html_color_scheme{"background_selected_sort"} .
13447 "\'><b>(sort)</b></a>";
13449 elsif (($exp_type eq "functions") and ($metric_value eq $g_first_metric))
13450 #------------------------------------------------------------------------------
13451 # Set the background color for the sort metric in the main function overview.
13452 #------------------------------------------------------------------------------
13454 $sort_string = "<a href=\'" . $g_html_base_file_name{"function_view"} .
13455 "." . $metric_value . ".html' style='background-color:" .
13456 $g_html_color_scheme{"background_selected_sort"} .
13457 "'><b>(sort)</b></a>";
13459 else
13460 #------------------------------------------------------------------------------
13461 # Do not set a specific background for all other metrics.
13462 #------------------------------------------------------------------------------
13464 $sort_string = "<a href=\'" . $g_html_base_file_name{"function_view"} .
13465 "." . $metric_value . ".html'>(sort)</a>";
13468 #------------------------------------------------------------------------------
13469 # Prepend the spaces to ensure correct alignment with the rest of the header.
13470 #------------------------------------------------------------------------------
13471 $sort_line .= $spaces . $sort_string;
13474 push (@header_lines, $sort_line);
13476 #------------------------------------------------------------------------------
13477 # Print the final results for the header and metrics.
13478 #------------------------------------------------------------------------------
13479 for my $i (keys @header_lines)
13481 gp_message ("debugXL", $subr_name, "header_lines[$i] = $header_lines[$i]");
13483 for my $i (keys @metric_values)
13485 gp_message ("debugXL", $subr_name, "metric_values[$i] = $metric_values[$i]");
13488 #------------------------------------------------------------------------------
13489 # Construct the lines for the function overview.
13491 # TBD: We could eliminate two structures here because metric_values and
13492 # final_html_function_block are only copied and the result stored.
13493 #------------------------------------------------------------------------------
13494 for my $i (keys @function_names)
13496 push (@metrics_part, $metric_values[$i]);
13497 push (@function_view_array, $final_html_function_block[$i]);
13500 for my $i (0 .. $#function_view_array)
13502 $msg = "function_view_array[$i] = $function_view_array[$i]";
13503 gp_message ("debugXL", $subr_name, $msg);
13505 #------------------------------------------------------------------------------
13506 # Element "function table" contains the array with all the function view data.
13507 #------------------------------------------------------------------------------
13508 $function_view_structure{"header"} = [@header_lines];
13509 $function_view_structure{"metrics part"} = [@metrics_part];
13510 $function_view_structure{"function table"} = [@function_view_array];
13512 $msg = "leave subroutine " . $subr_name;
13513 gp_message ("debug", $subr_name, $msg);
13515 return (\%function_view_structure);
13517 } #-- End of subroutine process_function_overview
13519 #------------------------------------------------------------------------------
13520 # TBD
13521 #------------------------------------------------------------------------------
13522 sub process_metrics
13524 my $subr_name = get_my_name ();
13526 my ($input_string, $sort_fields_ref, $metric_description_ref, $ignored_metrics_ref) = @_;
13528 my @sort_fields = @{ $sort_fields_ref };
13529 my %metric_description = %{ $metric_description_ref };
13530 my %ignored_metrics = %{ $ignored_metrics_ref };
13532 my $outputdir = append_forward_slash ($input_string);
13533 my $LANG = $g_locale_settings{"LANG"};
13534 my $max_len = 0;
13535 my $metric_comment;
13537 my ($imetricn,$outfile);
13538 my ($html_metrics_record,$imetric,$metric);
13540 $html_metrics_record =
13541 "<!doctype html public \"-//w3c//dtd html 3.2//EN\">\n<html lang=\"$LANG\">\n<head>\n" .
13542 "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n" .
13543 "<title>Function Metrics</title></head><body lang=\"$LANG\" bgcolor=".$g_html_color_scheme{"background_color_page"}."<pre>\n";
13545 $outfile = $outputdir . "metrics.html";
13547 open (METRICSOUT, ">", $outfile)
13548 or die ("$subr_name - unable to open file $outfile for writing - '$!'");
13549 gp_message ("debug", $subr_name, "opened file $outfile for writing");
13551 for $metric (@sort_fields)
13553 $max_len = max ($max_len, length ($metric));
13554 gp_message ("debug", $subr_name, "sort_fields: metric = $metric max_len = $max_len");
13557 # TBD: Check this
13558 # for $imetric (@IMETRICS)
13559 for $imetric (keys %ignored_metrics)
13561 $max_len = max ($max_len, length ($imetric));
13562 gp_message ("debug", $subr_name, "ignored_metrics imetric = $imetric max_len = $max_len");
13565 $max_len++;
13567 gp_message ("debug", $subr_name, "max_len = $max_len");
13569 $html_metrics_record .= "<p style=\"font-size:14px;color:red\"> Metrics used (".($#sort_fields + 1).")\n</p><p style=\"font-size:14px\">";
13570 for $metric (@sort_fields)
13572 my $description = ${ retrieve_metric_description (\$metric, \%metric_description) };
13573 gp_message ("debug", $subr_name, "handling metric metric = $metric->$description");
13574 $html_metrics_record .= " $metric".(' ' x ($max_len - length ($metric)))."$description\n";
13577 # $imetricn = scalar (keys %IMETRICS);
13578 $imetricn = scalar (keys %ignored_metrics);
13579 if ($imetricn)
13581 $html_metrics_record .= "</p><p style=\"font-size:14px;color:red\"> Metrics ignored ($imetricn)\n</p><p style=\"font-size:14px\">";
13582 # for $imetric (sort keys %IMETRICS){
13583 for $imetric (sort keys %ignored_metrics)
13585 $metric_comment = "(inclusive, exclusive, and percentages)";
13586 $html_metrics_record .= " $imetric".(' ' x ($max_len - length ($imetric))).$metric_comment."\n";
13587 gp_message ("debug", $subr_name, "handling metric imetric = $imetric $metric_comment");
13591 print METRICSOUT $html_metrics_record;
13592 print METRICSOUT $g_html_credits_line;
13593 close (METRICSOUT);
13595 gp_message ("debug", $subr_name, "closed metrics file $outfile");
13597 return (0);
13599 } #-- End of subroutine process_metrics
13601 #------------------------------------------------------------------------------
13602 # TBD
13603 #------------------------------------------------------------------------------
13604 sub process_metrics_data
13606 my $subr_name = get_my_name ();
13608 my ($outfile1, $outfile2, $ignored_metrics_ref) = @_;
13610 my %ignored_metrics = %{ $ignored_metrics_ref };
13612 my %metric_value = ();
13613 my %metric_description = ();
13614 my %metric_found = ();
13616 my $user_metrics;
13617 my $system_metrics;
13618 my $wall_metrics;
13619 my $metric_spec;
13620 my $metric_flavor;
13621 my $metric_visibility;
13622 my $metric_name;
13623 my $metric_text;
13624 my $metricdata;
13625 my $metric_line;
13626 my $msg;
13628 my $summary_metrics;
13629 my $detail_metrics;
13630 my $detail_metrics_system;
13631 my $call_metrics;
13633 #------------------------------------------------------------------------------
13634 # The regex section.
13635 #------------------------------------------------------------------------------
13636 my $metrics_line_regex = '\s*(.*):\s+(\d+\.?\d*)';
13637 my $metric_of_interest_1_regex = '^Exclusive\ *';
13638 my $metric_of_interest_2_regex = '^Inclusive\ *';
13640 if ($g_user_settings{"default_metrics"}{"current_value"} eq "off")
13642 $msg = "g_user_settings{default_metrics}{current_value} = ";
13643 $msg .= $g_user_settings{"default_metrics"}{"current_value"};
13644 gp_message ("debug", $subr_name, $msg);
13645 # get metrics
13647 $summary_metrics = '';
13648 $detail_metrics = '';
13649 $detail_metrics_system = '';
13650 $call_metrics = '';
13651 $user_metrics = 0;
13652 $system_metrics = 0;
13653 $wall_metrics = 0;
13655 my ($last_metric,$metric,$value,$i,$r);
13657 open (METRICTOTALS, "<", $outfile2)
13658 or die ("Unable to open metric value data file $outfile2 for reading - '$!'");
13659 gp_message ("debug", $subr_name, "opened $outfile2 to parse metric value data");
13661 #------------------------------------------------------------------------------
13662 # Below an example of the file that has just been opened.
13663 #------------------------------------------------------------------------------
13664 # <Total>
13665 # Exclusive Total CPU Time: 3.232 (100.0%)
13666 # Inclusive Total CPU Time: 3.232 (100.0%)
13667 # Exclusive insts Events: 7628146366 (100.0%)
13668 # Inclusive insts Events: 7628146366 (100.0%)
13669 # Exclusive cycles Events: 5167454376 (100.0%)
13670 # Inclusive cycles Events: 5167454376 (100.0%)
13671 # Exclusive dTLB-load-misses Events: 0 ( 0. %)
13672 # Inclusive dTLB-load-misses Events: 0 ( 0. %)
13673 # Exclusive Instructions Per Cycle: 1.476
13674 # Inclusive Instructions Per Cycle: 1.476
13675 # Exclusive Cycles Per Instruction: 0.677
13676 # Inclusive Cycles Per Instruction: 0.677
13677 # Exclusive branch-instructions Events: 1268741580 (100.0%)
13678 # Inclusive branch-instructions Events: 1268741580 (100.0%)
13679 # Size: 0
13680 # PC Address: 1:0x00000000
13681 # Source File: (unknown)
13682 # Object File: (unknown)
13683 # Load Object: <Total>
13684 # Mangled Name:
13685 # Aliases:
13686 #------------------------------------------------------------------------------
13688 while (<METRICTOTALS>)
13690 $metricdata = $_; chomp ($metricdata);
13692 $msg = "file metrictotals: input line = " . $metricdata;
13693 gp_message ("debug", $subr_name, $msg);
13695 #------------------------------------------------------------------------------
13696 # Ignoring whitespace, search for any line with a ":" in it, followed by
13697 # a number with, or without, a dot. So, an integer or floating-point number.
13698 #------------------------------------------------------------------------------
13699 if ($metricdata =~ /$metrics_line_regex/)
13701 $msg = "selected input line for processing";
13702 gp_message ("debug", $subr_name, $msg);
13704 if (defined($1) and defined($2))
13706 $metric = $1;
13707 $value = $2;
13708 $msg = "metric = " . $metric;
13709 gp_message ("debug", $subr_name, $msg);
13710 $msg = "value = " . $value;
13711 gp_message ("debug", $subr_name, $msg);
13713 else
13715 $msg = "unexpected input in " . $metricdata;
13716 gp_message ("assertion", $subr_name, $msg);
13719 #------------------------------------------------------------------------------
13720 # Select the metrics of interest.
13721 #------------------------------------------------------------------------------
13722 if (($metric =~ /$metric_of_interest_1_regex/) or
13723 ($metric =~ /$metric_of_interest_2_regex/) )
13725 $msg = "metric of interest = " . $metric;
13726 $msg .= " - proceed with processing";
13727 gp_message ("debug", $subr_name, $msg);
13729 else
13731 $msg = "metric = " . $metric;
13732 $msg .= " - ignored and further processing is skipped";
13733 gp_message ("debug", $subr_name, $msg);
13734 next;
13737 #------------------------------------------------------------------------------
13738 # When we get here, it means that this is a metric we want to process.
13739 #------------------------------------------------------------------------------
13741 #------------------------------------------------------------------------------
13742 # TBD - Still needed? Don't see it in the input anymore (?)
13743 #------------------------------------------------------------------------------
13744 if ($metric eq '" count')
13745 #------------------------------------------------------------------------------
13746 # Hardware counter experiments have this info. Note that this line is not the
13747 # first one to be encountered, so $last_metric has been defined already.
13748 #------------------------------------------------------------------------------
13750 $metric = $last_metric . " Count";
13751 $msg = "last_metric = $last_metric metric = $metric";
13752 gp_message ("debug", $subr_name, $msg);
13755 $metric_value{$metric} = $value;
13756 $msg = "archived: metric_value{$metric} = " .
13757 $metric_value{$metric};
13758 gp_message ("debug", $subr_name, $msg);
13759 #------------------------------------------------------------------------------
13760 # Preserve the current metric.
13761 #------------------------------------------------------------------------------
13762 $last_metric = $metric;
13765 close (METRICTOTALS);
13768 if (scalar (keys %metric_value) == 0)
13769 #------------------------------------------------------------------------------
13770 # This means that there are no metrics in the input file. That is a fatal
13771 # error and execution is terminated.
13772 #------------------------------------------------------------------------------
13774 $msg = "no metrics have been found in the input file";
13775 gp_message ("assertion", $subr_name, $msg);
13777 else
13778 #------------------------------------------------------------------------------
13779 # All is well. Print the metrics that have been found.
13780 #------------------------------------------------------------------------------
13782 $msg = "stored the following metrics and values:";
13783 gp_message ("debug", $subr_name, $msg);
13784 for my $metric (sort keys %metric_value)
13786 $msg = "metric_value{$metric} = " . $metric_value{$metric};
13787 gp_message ("debug", $subr_name, $msg);
13791 gp_message ("debug", $subr_name, "proceed to process file $outfile1");
13793 #------------------------------------------------------------------------------
13794 # Open and process the metrics file.
13795 #------------------------------------------------------------------------------
13796 open (METRICS, "<", $outfile1)
13797 or die ("Unable to open metrics file $outfile1: '$!'");
13798 gp_message ("debug", $subr_name, "opened file $outfile1 for reading");
13800 #------------------------------------------------------------------------------
13801 # Parse the file. This is a typical example:
13803 # Exp Sel Total
13804 # === === =====
13805 # 1 all 2
13806 # 2 all 1
13807 # 3 all 2
13808 # Current metrics: e.totalcpu:i.totalcpu:e.cycles:e+insts:e+llm:name
13809 # Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu )
13810 # Available metrics:
13811 # Exclusive Total CPU Time: e.%totalcpu
13812 # Inclusive Total CPU Time: i.%totalcpu
13813 # Exclusive CPU Cycles: e.+%cycles
13814 # Inclusive CPU Cycles: i.+%cycles
13815 # Exclusive Instructions Executed: e+%insts
13816 # Inclusive Instructions Executed: i+%insts
13817 # Exclusive Last-Level Cache Misses: e+%llm
13818 # Inclusive Last-Level Cache Misses: i+%llm
13819 # Exclusive Instructions Per Cycle: e+IPC
13820 # Inclusive Instructions Per Cycle: i+IPC
13821 # Exclusive Cycles Per Instruction: e+CPI
13822 # Inclusive Cycles Per Instruction: i+CPI
13823 # Size: size
13824 # PC Address: address
13825 # Name: name
13826 #------------------------------------------------------------------------------
13827 while (<METRICS>)
13829 $metric_line = $_;
13830 chomp ($metric_line);
13832 gp_message ("debug", $subr_name, "processing line: $metric_line");
13833 #------------------------------------------------------------------------------
13834 # The original regex has bugs because the line should not be allowed to start
13835 # with a ":". So this is wrong:
13836 # if (($metric =~ /\s*(.*):\s+(\S)((\.\S+)|(\+\S+))/) and !($metric =~/^Current/))
13838 # This is better:
13839 # if (($metric =~ /\s*(.+):\s+(\S)((\.\S+)|(\+\S+))/) and !($metric =~/^Current/))
13841 # In general, this regex has some potential issues and has been replaced by
13842 # the one shown below.
13844 # We select a line that does not start with "Current" and aside from whitespace
13845 # starts with anything (although it should be a string with words only),
13846 # followed by whitespace and either an "e" or "i". This is called the "flavor"
13847 # and is followed by a visibility marker (.,+,%, or !) and a metric name.
13848 #------------------------------------------------------------------------------
13849 # Ruud if (($metric =~ /\s*(.*):\s+(\S)((\.\S+)|(\+\S+))/) && !($metric =~/^Current/)){
13851 ($metric_spec, $metric_flavor, $metric_visibility, $metric_name,
13852 $metric_text) =
13853 extract_metric_specifics ($metric_line);
13855 # if (($metric_line =~ /\s*(.+):\s+([ei])([\.\+%]+)(\S*)/) and !($metric_line =~/^Current/))
13856 if ($metric_spec eq "skipped")
13858 $msg = "skipped processing line: " . $metric_line;
13859 gp_message ("debug", $subr_name, $msg);
13860 next
13862 $msg = "line of interest: " . $metric_line;
13863 gp_message ("debug", $subr_name, $msg);
13865 $metric_found{$metric_spec} = $TRUE;
13867 #------------------------------------------------------------------------------
13868 # TBD
13869 # Currently always FALSE since this feature has not been fully implemented yet.
13870 #------------------------------------------------------------------------------
13871 if ($g_user_settings{"ignore_metrics"}{"defined"})
13873 gp_message ("debug", $subr_name, "check for $metric_spec");
13874 if (exists ($ignored_metrics{$metric_name}))
13876 $msg = "user asked to ignore metric " . $metric_name;
13877 gp_message ("debug", $subr_name, $msg);
13878 $msg = "further processing of line of interest is skipped";
13879 gp_message ("debug", $subr_name, $msg);
13880 next;
13884 #------------------------------------------------------------------------------
13885 # This metric is not on the ignored list and qualifies, so store it.
13886 #------------------------------------------------------------------------------
13887 $metric_description{$metric_spec} = $metric_text;
13889 # TBD: add for other visibilities too, like +
13890 $msg = "stored metric_description{$metric_spec} = ";
13891 $msg .= $metric_description{$metric_spec};
13892 gp_message ("debug", $subr_name, $msg);
13894 if ($metric_flavor ne "e")
13896 $msg = "metric $metric_spec is ignored";
13897 gp_message ("debug", $subr_name, $msg);
13898 $msg = "further processing of this line is skipped";
13899 gp_message ("debug", $subr_name, $msg);
13901 else
13902 #------------------------------------------------------------------------------
13903 # Only the exclusive metrics are shown.
13904 #------------------------------------------------------------------------------
13906 $msg = "metric $metric_spec ($metric_text) is considered";
13907 gp_message ("debug", $subr_name, $msg);
13909 #------------------------------------------------------------------------------
13910 # Legacy metrics, but may re-appear one day and so the code is left in here.
13911 #------------------------------------------------------------------------------
13912 if ($metric_spec =~ /user/)
13914 $user_metrics = $TRUE;
13915 $msg = "user_metrics set to TRUE";
13916 gp_message ("debug", $subr_name, $msg);
13918 elsif ($metric_spec =~ /system/)
13920 $system_metrics = $TRUE;
13921 $msg = "system_metrics set to TRUE";
13922 gp_message ("debug", $subr_name, $msg);
13924 elsif ($metric_spec =~ /wall/)
13926 $wall_metrics = $TRUE;
13927 $msg = "wall_metrics set to TRUE";
13928 gp_message ("debug", $subr_name, $msg);
13930 elsif (defined ($metric_value{$metric_text}))
13932 $msg = "total attributed to this metric ";
13933 $msg .= "metric_value{" . $metric_text . "} = ";
13934 $msg .= $metric_value{$metric_text};
13935 gp_message ("debug", $subr_name, $msg);
13937 if ($summary_metrics ne '')
13939 $summary_metrics .= ':' . $metric_spec;
13940 $msg = "updated summary_metrics = " . $summary_metrics;
13941 gp_message ("debug", $subr_name, $msg);
13943 else
13945 $summary_metrics = $metric_spec;
13946 $msg = "initialized summary_metrics = " . $summary_metrics;
13947 gp_message ("debug", $subr_name, $msg);
13949 gp_message ("debug", $subr_name, "metric $metric_spec added");
13951 else
13953 #------------------------------------------------------------------------------
13954 # TBD: This doesn't seem to make much sense.
13955 #------------------------------------------------------------------------------
13956 $msg = "no action taken for " . $metric_spec;
13957 gp_message ("debug", $subr_name, $msg);
13962 close METRICS;
13964 if ($wall_metrics > 0)
13966 $msg = "adding e.wall to summary_metrics";
13967 gp_message ("debug", $subr_name, $msg);
13968 $summary_metrics = "e.wall:".$summary_metrics;
13969 $msg = "after update summary_metrics = " . $summary_metrics;
13970 gp_message ("debug", $subr_name, $msg);
13973 if ($system_metrics > 0)
13975 $msg = "adding e.system to summary_metrics and detail_metrics_system";
13976 gp_message ("debug", $subr_name, $msg);
13978 $summary_metrics = "e.system:" . $summary_metrics;
13979 $detail_metrics_system = "e.system:" . $detail_metrics_system;
13981 $msg = "adding i.system to call_metrics";
13982 gp_message ("debug", $subr_name, $msg);
13984 $call_metrics = "i.system:" . $call_metrics;
13986 $msg = "after update summary_metrics = " . $summary_metrics;
13987 gp_message ("debug", $subr_name, $msg);
13988 $msg = "after update call_metrics = " . $call_metrics;
13989 gp_message ("debug", $subr_name, $msg);
13990 $msg = "after update detail_metrics_system = " . $detail_metrics_system;
13991 gp_message ("debug", $subr_name, $msg);
13995 #------------------------------------------------------------------------------
13996 # TBD: e.user and i.user do not always exist!!
13997 #------------------------------------------------------------------------------
13999 if ($user_metrics > 0)
14001 # Ruud if (!exists ($IMETRICS{"i.user"})){
14002 if ($g_user_settings{"ignore_metrics"}{"defined"} and exists ($ignored_metrics{"user"}))
14004 $summary_metrics = "e.user:".$summary_metrics;
14006 else
14008 $summary_metrics = "e.user:i.user:".$summary_metrics;
14011 $detail_metrics = "e.user:".$detail_metrics;
14012 $detail_metrics_system = "e.user:".$detail_metrics_system;
14014 if ($g_user_settings{"ignore_metrics"}{"defined"} and
14015 exists ($ignored_metrics{"user"}))
14017 $call_metrics = "a.user:".$call_metrics;
14019 else
14021 $call_metrics = "a.user:i.user:".$call_metrics;
14023 $msg = "updated summary_metrics = " . $summary_metrics;
14024 gp_message ("debug", $subr_name, $msg);
14025 $msg = "updated detail_metrics = " . $detail_metrics;
14026 gp_message ("debug", $subr_name, $msg);
14027 $msg = "updated detail_metrics_system = " . $detail_metrics_system;
14028 gp_message ("debug", $subr_name, $msg);
14029 $msg = "updated call_metrics = " . $call_metrics;
14030 gp_message ("debug", $subr_name, $msg);
14034 #------------------------------------------------------------------------------
14035 # TBD
14036 # It doesn't look right in case call_metrics ends up being set to ""
14037 #------------------------------------------------------------------------------
14038 if ($call_metrics eq "")
14040 $call_metrics = $detail_metrics;
14041 $msg = "call_metrics is not set, setting it to " . $call_metrics;
14042 gp_message ("debug", $subr_name, $msg);
14043 if ($detail_metrics eq '')
14045 $msg = "detail_metrics and call_metrics are blank and could";
14046 $msg .= " cause trouble later on";
14047 gp_message ("debug", $subr_name, $msg);
14051 for my $metric (sort keys %ignored_metrics)
14053 if ($ignored_metrics{$metric})
14055 $msg = "active metric, but ignored: " . $metric;
14056 gp_message ("debug", $subr_name, $msg);
14061 return (\%metric_value, \%metric_description, \%metric_found, $user_metrics,
14062 $system_metrics, $wall_metrics, $summary_metrics, $detail_metrics,
14063 $detail_metrics_system, $call_metrics);
14065 } #-- End of subroutine process_metrics_data
14067 #------------------------------------------------------------------------------
14068 # Process source lines that are not part of the target function.
14070 # Generate straightforward HTML, but define an anchor based on the source line
14071 # number in the list.
14072 #------------------------------------------------------------------------------
14073 sub process_non_target_source
14075 my $subr_name = get_my_name ();
14077 my ($start_scan, $end_scan,
14078 $src_times_regex, $function_regex, $number_of_metrics,
14079 $file_contents_ref, $modified_html_ref) = @_;
14081 my @file_contents = @{ $file_contents_ref };
14082 my @modified_html = @{ $modified_html_ref };
14083 my $colour_code_line = $FALSE;
14084 my $input_line;
14085 my $line_id;
14086 my $modified_line;
14088 #------------------------------------------------------------------------------
14089 # Main loop to parse all of the source code and take action as needed.
14090 #------------------------------------------------------------------------------
14091 for (my $line_no=$start_scan; $line_no <= $end_scan; $line_no++)
14093 $input_line = $file_contents[$line_no];
14095 #------------------------------------------------------------------------------
14096 # Generate straightforward HTML, but define an anchor based on the source line
14097 # number in the list.
14098 #------------------------------------------------------------------------------
14099 $line_id = extract_source_line_number ($src_times_regex,
14100 $function_regex,
14101 $number_of_metrics,
14102 $input_line);
14104 if ($input_line =~ /$function_regex/)
14106 $colour_code_line = $TRUE;
14109 #------------------------------------------------------------------------------
14110 # We need to replace the "<" symbol in the code by "&lt;".
14111 #------------------------------------------------------------------------------
14112 $input_line =~ s/$g_less_than_regex/$g_html_less_than_regex/g;
14114 #------------------------------------------------------------------------------
14115 # Add an id.
14116 #------------------------------------------------------------------------------
14117 $modified_line = "<a id=\"line_" . $line_id . "\"></a>";
14119 my $coloured_line;
14120 if ($colour_code_line)
14122 my $boldface = $TRUE;
14123 $coloured_line = color_string (
14124 $input_line,
14125 $boldface,
14126 $g_html_color_scheme{"non_target_function_name"});
14127 $colour_code_line = $FALSE;
14128 $modified_line .= "$coloured_line";
14130 else
14132 $modified_line .= "$input_line";
14134 gp_message ("debugXL", $subr_name, " $line_no : modified_line = $modified_line");
14135 push (@modified_html, $modified_line);
14138 return (\@modified_html);
14140 } #-- End of subroutine process_non_target_source
14142 #------------------------------------------------------------------------------
14143 # This function scans the configuration file and adapts the internal settings
14144 # accordingly.
14146 # Errors are stored during the parsing and processing phase. They are printed
14147 # at the end and sorted by line number.
14150 # TBD: Does not yet use the warnings/error system. This needs to be fixed.
14151 #------------------------------------------------------------------------------
14152 sub process_rc_file
14154 my $subr_name = get_my_name ();
14156 my ($rc_file_name, $rc_file_paths_ref) = @_;
14158 #------------------------------------------------------------------------------
14159 # Local structures.
14160 #------------------------------------------------------------------------------
14161 # Stores the values extracted from the config file:
14162 my %rc_settings_user = ();
14163 my %error_and_warning_msgs = ();
14164 my @rc_file_paths = ();
14166 my @split_line;
14167 my @my_fields;
14169 my $msg;
14170 my $first_part;
14171 my $line;
14172 my $line_number;
14173 my $no_of_arguments;
14174 my $number_of_fields;
14175 my $number_of_paths;
14176 my $parse_errors; #-- Count the number of errors
14177 my $parse_warnings; #-- Count the number of errors
14179 my $rc_config_file;
14180 my $rc_file_found;
14181 my $rc_keyword;
14182 my $rc_value;
14184 @rc_file_paths = @{$rc_file_paths_ref};
14185 $number_of_paths = scalar (@rc_file_paths);
14187 if ($number_of_paths == 0)
14188 #------------------------------------------------------------------------------
14189 # This should not happen, but is a good safety net to add.
14190 #------------------------------------------------------------------------------
14192 my $msg = "search path list is empty";
14193 gp_message ("assertion", $subr_name, $msg);
14196 #------------------------------------------------------------------------------
14197 # Check for the presence of a configuration file.
14198 #------------------------------------------------------------------------------
14199 $msg = "number_of_paths = $number_of_paths rc_file_paths = @rc_file_paths";
14200 gp_message ("debug", $subr_name, $msg);
14202 $rc_file_found = $FALSE;
14203 for my $path_name (@rc_file_paths)
14205 $rc_config_file = $path_name . "/" . $rc_file_name;
14206 $msg = "looking for configuration file " . $rc_config_file;
14207 gp_message ("debug", $subr_name, $msg);
14208 if (-f $rc_config_file)
14210 $msg = "found configuration file " . $rc_config_file;
14211 gp_message ("debug", $subr_name, $msg);
14212 $rc_file_found = $TRUE;
14213 last;
14217 if (not $rc_file_found)
14218 #------------------------------------------------------------------------------
14219 # There is no configuration file and we can skip this subroutine.
14220 #------------------------------------------------------------------------------
14222 $msg = "configuration file $rc_file_name not found";
14223 gp_message ("verbose", $subr_name, $msg);
14224 return (0);
14226 else
14228 $msg = "unable to open file $rc_config_file for reading:";
14229 open (GP_DISPLAY_HTML_RC, "<", "$rc_config_file")
14230 or die ($subr_name . " - " . $msg . " " . $!);
14231 #------------------------------------------------------------------------------
14232 # The configuration file has been opened for reading.
14233 #------------------------------------------------------------------------------
14234 $msg = "file $rc_config_file has been opened for reading";
14235 gp_message ("debug", $subr_name, $msg);
14238 $msg = "found configuration file $rc_config_file";
14239 gp_message ("verbose", $subr_name, $msg);
14240 $msg = "processing configuration file " . $rc_config_file;
14241 gp_message ("debug", $subr_name, $msg);
14243 #------------------------------------------------------------------------------
14244 # Here we scan the configuration file for the settings.
14246 # A setting consists of a keyword, optionally followed by a value. It is
14247 # optional because not all keywords may require a value.
14249 # At the end of this block, all keyword/value pairs are stored in a hash.
14251 # We do not yet check for the validity of these pairs. This is done next.
14253 # The original code had this all integrated, but it made the code very
14254 # complex with deeply nested if-statements. The flow was also hard to follow.
14255 #------------------------------------------------------------------------------
14256 $parse_errors = 0;
14257 $parse_warnings = 0;
14258 $line_number = 0;
14259 while (my $line = <GP_DISPLAY_HTML_RC>)
14261 chomp ($line);
14262 $line_number++;
14264 gp_message ("debug", $subr_name, "read input line = $line");
14266 #------------------------------------------------------------------------------
14267 # Ignore a line with whitespace only
14268 #------------------------------------------------------------------------------
14269 if ($line =~ /^\s*$/)
14271 gp_message ("debug", $subr_name, "ignored a line with whitespace");
14272 next;
14275 #------------------------------------------------------------------------------
14276 # Ignore a comment line, defined by starting with a "#", possibly prepended by
14277 # whitespace.
14278 #------------------------------------------------------------------------------
14279 if ($line =~ /^\s*\#/)
14281 gp_message ("debug", $subr_name, "ignored a full comment line");
14282 next;
14285 #------------------------------------------------------------------------------
14286 # Split the input line using the "#" symbol as a separator. We have already
14287 # handled the case of an isolated comment line, so there may only be an
14288 # embedded comment.
14290 # Regardless of this, we are only interested in the first part.
14291 #------------------------------------------------------------------------------
14292 @split_line = split ("#", $line);
14294 for my $i (@split_line)
14296 gp_message ("debug", $subr_name, "elements after split of line: $i");
14299 $first_part = $split_line[0];
14300 gp_message ("debug", $subr_name, "relevant part = $first_part");
14302 if ($first_part =~ /[&\^\*\@\$]+/)
14303 #------------------------------------------------------------------------------
14304 # The &, ^, *, @ and $ symbols should not occur. If they do, we flag an error
14305 # an fetch the next line.
14306 #------------------------------------------------------------------------------
14308 $parse_errors++;
14309 $msg = "non-supported character(s) (\&,\^,\*,\@,\$) found: $line";
14310 $error_and_warning_msgs{"error"}{$line_number}{"message"} = $msg;
14311 next;
14313 else
14314 #------------------------------------------------------------------------------
14315 # Split the first part on whitespace and verify the number of fields to be
14316 # valid. Although we currently only have keywords with a value, a keyword
14317 # without value is supported to.
14319 # If the number of fields is valid, the keyword and value are stored. In case
14320 # of a single field, the value is assigned a special string.
14322 # Although this situation should not occur, we do abort if something unexpected
14323 # is encountered here.
14324 #------------------------------------------------------------------------------
14326 @my_fields = split (/\s/, $split_line[0]);
14328 $number_of_fields = scalar (@my_fields);
14329 $msg = "number of fields = " . $number_of_fields;
14330 gp_message ("debug", $subr_name, $msg);
14333 if ($number_of_fields ge 3)
14334 #------------------------------------------------------------------------------
14335 # This is not supported.
14336 #------------------------------------------------------------------------------
14338 $parse_errors++;
14339 $msg = "more than 2 fields found: $first_part";
14340 $error_and_warning_msgs{"error"}{$line_number}{"message"} = $msg;
14341 next;
14343 elsif ($number_of_fields eq 2)
14345 $rc_keyword = $my_fields[0];
14346 $rc_value = $my_fields[1];
14348 elsif ($number_of_fields eq 1)
14350 $rc_keyword = $my_fields[0];
14351 $rc_value = "the_field_is_empty";
14353 else
14355 $msg = "[line $line_number] $rc_config_file -";
14356 $msg .= " number of fields = $number_of_fields";
14357 gp_message ("assertion", $subr_name, $msg);
14360 #------------------------------------------------------------------------------
14361 # Store the keyword, value and line number.
14362 #------------------------------------------------------------------------------
14363 if (exists ($rc_settings_user{$rc_keyword}))
14365 $parse_warnings++;
14366 my $prev_value = $rc_settings_user{$rc_keyword}{"value"};
14367 my $prev_line_number = $rc_settings_user{$rc_keyword}{"line_no"};
14368 if ($rc_value ne $prev_value)
14370 $msg = "option $rc_keyword previously set at line";
14371 $msg .= " $prev_line_number: new value '$rc_value'";
14372 $msg .= " ' overrides '$prev_value'";
14374 else
14376 $msg = "option $rc_keyword previously set to the same value";
14377 $msg .= " at line $prev_line_number";
14379 $error_and_warning_msgs{"warning"}{$line_number}{"message"} = $msg;
14381 $rc_settings_user{$rc_keyword}{"value"} = $rc_value;
14382 $rc_settings_user{$rc_keyword}{"line_no"} = $line_number;
14384 gp_message ("debug", $subr_name, "stored keyword = $rc_keyword");
14385 gp_message ("debug", $subr_name, "stored value = $rc_value");
14386 gp_message ("debug", $subr_name, "stored line number = $line_number");
14389 #------------------------------------------------------------------------------
14390 # Completed the parsing of the configuration file. It can be closed.
14391 #------------------------------------------------------------------------------
14392 close (GP_DISPLAY_HTML_RC);
14394 #------------------------------------------------------------------------------
14395 # Print the raw input as just collected from the configuration file.
14396 #------------------------------------------------------------------------------
14397 gp_message ("debug", $subr_name, "contents of %rc_settings_user:");
14398 for my $keyword (keys %rc_settings_user)
14400 my $key_value = $rc_settings_user{$keyword}{"value"};
14401 $msg = "keyword = " . $keyword . " value = " . $key_value;
14402 gp_message ("debug", $subr_name, $msg);
14405 for my $rc_keyword (keys %g_user_settings)
14407 for my $fields (keys %{ $g_user_settings{$rc_keyword} })
14409 $msg = "before config file: $rc_keyword $fields =";
14410 $msg .= " " . $g_user_settings{$rc_keyword}{$fields};
14411 gp_message ("debug", $subr_name, $msg);
14415 #------------------------------------------------------------------------------
14416 # We are almost done. Check for all keywords found whether they are valid.
14417 # Also verify that the corresponding value is valid.
14419 # Update the g_user_settings table if everything is okay.
14420 #------------------------------------------------------------------------------
14422 for my $rc_keyword (keys %rc_settings_user)
14424 my $rc_value = $rc_settings_user{$rc_keyword}{"value"};
14426 if (exists ( $g_user_settings{$rc_keyword}))
14429 #------------------------------------------------------------------------------
14430 # This is a supported keyword. There are two more things left to do:
14431 # - Check how many values it requires (currently exactly one is supported)
14432 # - Is the value a valid number or string?
14433 #------------------------------------------------------------------------------
14434 $no_of_arguments = $g_user_settings{$rc_keyword}{"no_of_arguments"};
14436 if ($no_of_arguments eq 1)
14438 my $input_value = $rc_value;
14439 if ($input_value ne "the_field_is_empty")
14441 #------------------------------------------------------------------------------
14442 # So far, so good. We only need to check if the value is valid for the keyword.
14443 #------------------------------------------------------------------------------
14445 my $data_type = $g_user_settings{$rc_keyword}{"data_type"};
14446 my $valid_input =
14447 verify_if_input_is_valid ($input_value, $data_type);
14448 #------------------------------------------------------------------------------
14449 # Check if the value is valid.
14450 #------------------------------------------------------------------------------
14451 if ($valid_input)
14453 $g_user_settings{$rc_keyword}{"current_value"} =
14454 $rc_value;
14455 $g_user_settings{$rc_keyword}{"defined"} = $TRUE;
14457 else
14459 $parse_errors++;
14460 $line_number = $rc_settings_user{$rc_keyword}{"line_no"};
14461 $msg = "input value '$input_value' for keyword";
14462 $msg .= " $rc_keyword is not valid";
14463 $error_and_warning_msgs{"error"}{$line_number}{"message"}
14464 = $msg;
14465 next;
14468 else
14469 #------------------------------------------------------------------------------
14470 # This keyword requires a value, but none has been found.
14471 #------------------------------------------------------------------------------
14473 $parse_errors++;
14474 $line_number = $rc_settings_user{$rc_keyword}{"line_no"};
14475 $msg = "missing value for keyword '$rc_keyword'";
14476 $error_and_warning_msgs{"error"}{$line_number}{"message"}
14477 = $msg;
14478 next;
14481 elsif ($no_of_arguments eq 0)
14482 #------------------------------------------------------------------------------
14483 # Currently a theoretical scenario since all commands require a value, but in
14484 # case this is no longer true, we need to at least flag the fact the user set
14485 # this command.
14486 #------------------------------------------------------------------------------
14488 $g_user_settings{$rc_keyword}{"defined"} = $TRUE;
14490 else
14491 #------------------------------------------------------------------------------
14492 # The code is not prepared for the situation one command has multiple values,
14493 # but this situation should never occur. Still it won't hurt to add a check.
14494 #------------------------------------------------------------------------------
14496 my $msg = "cannot handle $no_of_arguments in the input";
14497 gp_message ("assertion", $subr_name, $msg);
14500 else
14501 #------------------------------------------------------------------------------
14502 # A non-valid keyword is found. This is flagged as an error.
14503 #------------------------------------------------------------------------------
14505 $parse_errors++;
14506 $line_number = $rc_settings_user{$rc_keyword}{"line_no"};
14507 $msg = "keyword $rc_keyword is not supported";
14508 $error_and_warning_msgs{"error"}{$line_number}{"message"} = $msg;
14511 for my $rc_keyword (keys %g_user_settings)
14513 for my $fields (keys %{ $g_user_settings{$rc_keyword} })
14515 $msg = "after config file: $rc_keyword $fields =";
14516 $msg .= " " . $g_user_settings{$rc_keyword}{$fields};
14517 gp_message ("debug", $subr_name, $msg);
14520 print_table_user_settings ("debug", "upon the return from $subr_name");
14522 if ( ($parse_errors == 0) and ($parse_warnings == 0) )
14524 $msg = "successfully parsed and processed the configuration file";
14525 gp_message ("verbose", $subr_name, $msg);
14527 else
14529 if ($parse_errors > 0)
14531 my $plural_or_single = ($parse_errors > 1) ? "errors" : "error";
14532 $msg = $g_error_keyword . "found $parse_errors fatal";
14533 $msg .= " " . $plural_or_single . " in the configuration file:";
14534 gp_message ("debug", $subr_name, $msg);
14535 #------------------------------------------------------------------------------
14536 # Sort the hash keys, the line numbers, alphabetically and print the
14537 # corresponding error messages.
14538 #------------------------------------------------------------------------------
14539 for my $line_no (sort {$a <=> $b}
14540 (keys %{ $error_and_warning_msgs{"error"} }))
14542 $msg = $g_error_keyword . "[line $line_no] in file";
14543 $msg .= $rc_config_file . " - ";
14544 $msg .= $error_and_warning_msgs{"error"}{$line_no}{"message"};
14545 gp_message ("debug", $subr_name, $msg);
14549 if (not $g_quiet)
14551 if ($parse_warnings > 0)
14553 $msg = $g_warn_keyword . " found $parse_warnings warnings in";
14554 $msg .= " the configuration file:";
14555 gp_message ("debug", $subr_name, $msg);
14556 for my $line_no (sort {$a <=> $b}
14557 (keys %{ $error_and_warning_msgs{"warning"} }))
14559 $msg = $g_warn_keyword;
14560 $msg .= " [line $line_no] in file $rc_config_file - ";
14561 $msg .= $error_and_warning_msgs{"warning"}{$line_no}{"message"};
14562 gp_message ("debug", $subr_name, $msg);
14568 return ($parse_errors);
14570 } #-- End of subroutine process_rc_file
14572 #------------------------------------------------------------------------------
14573 # Generate the annotated html file for the source listing.
14574 #------------------------------------------------------------------------------
14575 sub process_source
14577 my $subr_name = get_my_name ();
14579 my ($number_of_metrics, $function_info_ref,
14580 $outputdir, $input_filename) = @_;
14582 my @function_info = @{ $function_info_ref };
14584 #------------------------------------------------------------------------------
14585 # The regex section
14586 #------------------------------------------------------------------------------
14587 my $end_src1_header_regex = '(^\s+)(\d+)\.\s+(.*)';
14588 my $end_src2_header_regex = '(^\s+)(<Function: )(.*)>';
14589 my $function_regex = '^(\s*)<Function:\s(.*)>';
14590 my $function2_regex = '^(\s*)&lt;Function:\s(.*)>';
14591 my $src_regex = '(\s*)(\d+)\.(.*)';
14592 my $txt_ext_regex = '\.txt$';
14593 my $src_filename_id_regex = '^file\.(\d+)\.src\.txt$';
14594 my $integer_only_regex = '\d+';
14595 #------------------------------------------------------------------------------
14596 # Computed dynamically below.
14597 # TBD: Try to move this up.
14598 #------------------------------------------------------------------------------
14599 my $src_times_regex;
14600 my $hot_lines_regex;
14601 my $metric_regex;
14602 my $metric_extra_regex;
14604 my @components = ();
14605 my @fields_in_line = ();
14606 my @file_contents = ();
14607 my @hot_source_lines = ();
14608 my @max_metric_values = ();
14609 my @modified_html = ();
14610 my @transposed_hot_lines = ();
14612 my $colour_coded_line;
14613 my $colour_coded_line_ref;
14614 my $line_id;
14615 my $ignore_value;
14616 my $func_name_in_src_file;
14617 my $html_new_line = "<br>";
14618 my $input_line;
14619 my $metric_values;
14620 my $modified_html_ref;
14621 my $modified_line;
14622 my $is_empty;
14623 my $start_all_source;
14624 my $start_target_source;
14625 my $end_target_source;
14626 my $output_line;
14627 my $hot_line;
14628 my $src_line_no;
14629 my $src_code_line;
14631 my $decimal_separator = $g_locale_settings{"decimal_separator"};
14632 my $hp_value = $g_user_settings{"highlight_percentage"}{"current_value"};
14634 my $file_title;
14635 my $found_target;
14636 my $html_dis_record;
14637 my $html_end;
14638 my $html_header;
14639 my $html_home;
14640 my $rounded_percentage;
14641 my $start_tracking;
14642 my $threshold_line;
14644 my $base;
14645 my $boldface;
14646 my $msg;
14647 my $routine;
14649 my $LANG = $g_locale_settings{"LANG"};
14650 my $the_title = set_title ($function_info_ref, $input_filename,
14651 "process source");
14652 my $outfile = $input_filename . ".html";
14654 #------------------------------------------------------------------------------
14655 # Remove the .txt from file.<n>.src.txt
14656 #------------------------------------------------------------------------------
14657 my $html_output_file = $input_filename;
14658 $html_output_file =~ s/$txt_ext_regex/.html/;
14660 gp_message ("debug", $subr_name, "input_filename = $input_filename");
14661 gp_message ("debug", $subr_name, "the_title = $the_title");
14663 $file_title = $the_title;
14664 $html_header = ${ create_html_header (\$file_title) };
14665 $html_home = ${ generate_home_link ("right") };
14667 push (@modified_html, $html_header);
14668 push (@modified_html, $html_home);
14669 push (@modified_html, "<pre>");
14671 #------------------------------------------------------------------------------
14672 # Open the html file used for the output.
14673 #------------------------------------------------------------------------------
14674 open (NEW_HTML, ">", $html_output_file)
14675 or die ("$subr_name - unable to open file $html_output_file for writing: '$!'");
14676 gp_message ("debug", $subr_name , "opened file $html_output_file for writing");
14678 $base = get_basename ($input_filename);
14680 gp_message ("debug", $subr_name, "base = $base");
14682 if ($base =~ /$src_filename_id_regex/)
14684 my $file_id = $1;
14685 if (defined ($function_info[$file_id]{"routine"}))
14687 $routine = $function_info[$file_id]{"routine"};
14689 gp_message ("debugXL", $subr_name, "target routine = $routine");
14691 else
14693 my $msg = "cannot retrieve routine name for file_id = $file_id";
14694 gp_message ("assertion", $subr_name, $msg);
14698 #------------------------------------------------------------------------------
14699 # Check if the input file is empty. If so, generate a short text in the html
14700 # file and return. Otherwise open the file and read the contents.
14701 #------------------------------------------------------------------------------
14702 $is_empty = is_file_empty ($input_filename);
14704 if ($is_empty)
14706 #------------------------------------------------------------------------------
14707 # The input file is empty. Write a diagnostic message in the html file and exit.
14708 #------------------------------------------------------------------------------
14709 gp_message ("debug", $subr_name ,"file $input_filename is empty");
14711 my $comment = "No source listing generated by $tool_name - " .
14712 "file $input_filename is empty";
14713 my $error_file = $outputdir . "gp-listings.err";
14715 my $html_empty_file_ref = html_text_empty_file (\$comment, \$error_file);
14716 my @html_empty_file = @{ $html_empty_file_ref };
14718 print NEW_HTML "$_\n" for @html_empty_file;
14720 close NEW_HTML;
14722 return (0);
14724 else
14725 #------------------------------------------------------------------------------
14726 # Open the input file with the source code
14727 #------------------------------------------------------------------------------
14729 open (SRC_LISTING, "<", $input_filename)
14730 or die ("$subr_name - unable to open file $input_filename for reading: '$!'");
14731 gp_message ("debug", $subr_name, "opened file $input_filename for reading");
14734 #------------------------------------------------------------------------------
14735 # Generate the regex for the metrics. This depends on the number of metrics.
14736 #------------------------------------------------------------------------------
14737 gp_message ("debug", $subr_name, "decimal_separator = $decimal_separator<--");
14739 $metric_regex = '';
14740 $metric_extra_regex = '';
14741 for my $metric_used (1 .. $number_of_metrics)
14743 $metric_regex .= '(\d+' . $decimal_separator . '*\d*)\s+';
14745 $metric_extra_regex = $metric_regex . '(\d+' . $decimal_separator . ')';
14747 $hot_lines_regex = '^(#{2})\s+';
14748 $hot_lines_regex .= '('.$metric_regex.')';
14749 $hot_lines_regex .= '([0-9?]+)\.\s+(.*)';
14751 $src_times_regex = '^(#{2}|\s{2})\s+';
14752 $src_times_regex .= '('.$metric_extra_regex.')';
14753 $src_times_regex .= '(.*)';
14755 gp_message ("debugXL", $subr_name, "metric_regex = $metric_regex");
14756 gp_message ("debugXL", $subr_name, "hot_lines_regex = $hot_lines_regex");
14757 gp_message ("debugXL", $subr_name, "src_times_regex = $src_times_regex");
14758 gp_message ("debugXL", $subr_name, "src_regex = $src_regex");
14760 gp_message ("debugXL", $subr_name, "end_src1_header_regex = $end_src1_header_regex");
14761 gp_message ("debugXL", $subr_name, "end_src2_header_regex = $end_src2_header_regex");
14762 gp_message ("debugXL", $subr_name, "function_regex = $function_regex");
14763 gp_message ("debugXL", $subr_name, "function2_regex = $function2_regex");
14764 gp_message ("debugXL", $subr_name, "src_regex = $src_regex");
14766 #------------------------------------------------------------------------------
14767 # Read the file into memory.
14768 #------------------------------------------------------------------------------
14769 chomp (@file_contents = <SRC_LISTING>);
14771 #------------------------------------------------------------------------------
14772 # Identify the header lines. Make the minimal assumptions.
14774 # In both cases, the first line after the header has whitespace. This is
14775 # followed by either one of the following:
14777 # - <line_no>.
14778 # - <Function:
14780 # These are the characteristics we use below.
14781 #------------------------------------------------------------------------------
14782 for (my $line_number=0; $line_number <= $#file_contents; $line_number++)
14784 $input_line = $file_contents[$line_number];
14786 #------------------------------------------------------------------------------
14787 # We found the first source code line. Bail out.
14788 #------------------------------------------------------------------------------
14789 if (($input_line =~ /$end_src1_header_regex/) or
14790 ($input_line =~ /$end_src2_header_regex/))
14792 gp_message ("debugXL", $subr_name, "header time is over - hit source line");
14793 gp_message ("debugXL", $subr_name, "line_number = $line_number");
14794 gp_message ("debugXL", $subr_name, "input_line = $input_line");
14795 last;
14797 else
14798 #------------------------------------------------------------------------------
14799 # Store the header lines in the html structure.
14800 #------------------------------------------------------------------------------
14802 $modified_line = "<i>" . $input_line . "</i>";
14803 push (@modified_html, $modified_line);
14806 #------------------------------------------------------------------------------
14807 # We know the source code starts at this index value:
14808 #------------------------------------------------------------------------------
14809 $start_all_source = scalar (@modified_html);
14810 gp_message ("debugXL", $subr_name, "source starts at start_all_source = $start_all_source");
14812 #------------------------------------------------------------------------------
14813 # Scan the file to identify where the target source starts and ends.
14814 #------------------------------------------------------------------------------
14815 gp_message ("debugXL", $subr_name, "search for target function $routine");
14816 $start_tracking = $FALSE;
14817 $found_target = $FALSE;
14818 for (my $line_number=0; $line_number <= $#file_contents; $line_number++)
14820 $input_line = $file_contents[$line_number];
14822 gp_message ("debugXL", $subr_name, "[$line_number] $input_line");
14824 if ($input_line =~ /$function_regex/)
14826 if (defined ($1) and defined ($2))
14828 $func_name_in_src_file = $2;
14829 my $msg = "found a function - name = $func_name_in_src_file";
14830 gp_message ("debugXL", $subr_name, $msg);
14832 if ($start_tracking)
14834 $start_tracking = $FALSE;
14835 $end_target_source = $line_number - 1;
14836 my $msg = "end_target_source = $end_target_source";
14837 gp_message ("debugXL", $subr_name, $msg);
14838 last;
14841 if ($func_name_in_src_file eq $routine)
14843 $found_target = $TRUE;
14844 $start_tracking = $TRUE;
14845 $start_target_source = $line_number;
14847 gp_message ("debugXL", $subr_name, "found target function $routine");
14848 gp_message ("debugXL", $subr_name, "function_name = $2 routine = $routine");
14849 gp_message ("debugXL", $subr_name, "routine = $routine start_tracking = $start_tracking");
14850 gp_message ("debugXL", $subr_name, "start_target_source = $start_target_source");
14853 else
14855 my $msg = "parsing line $input_line";
14856 gp_message ("assertion", $subr_name, $msg);
14861 #------------------------------------------------------------------------------
14862 # This is not supposed to happen, but it is not a fatal error either. The
14863 # hyperlinks related to this function will not work, so a warning is issued.
14864 # A message is issued both in debug mode, and as a warning.
14865 #------------------------------------------------------------------------------
14866 if (not $found_target)
14868 my $msg;
14870 $msg = "target function $routine not found in $base - " .
14871 "links to source code involving this function will not work";
14872 gp_message ("debug", $subr_name, $msg);
14873 gp_message ("warning", $subr_name, $msg);
14874 $g_total_warning_count++;
14876 return ($found_target);
14879 #------------------------------------------------------------------------------
14880 # Catch the line number of the last function.
14881 #------------------------------------------------------------------------------
14882 if ($start_tracking)
14884 $end_target_source = $#file_contents;
14886 gp_message ("debugXL", $subr_name, "routine = $routine start_tracking = $start_tracking");
14887 gp_message ("debugXL", $subr_name, "start_target_source = $start_target_source");
14888 gp_message ("debugXL", $subr_name, "end_target_source = $end_target_source");
14890 #------------------------------------------------------------------------------
14891 # We now have the index range for the function of interest and will parse it.
14892 # Since we already handled the first line with the function marker, we start
14893 # with the line following.
14894 #------------------------------------------------------------------------------
14896 #------------------------------------------------------------------------------
14897 # Find the hot source lines and store them.
14898 #------------------------------------------------------------------------------
14899 gp_message ("debugXL", $subr_name, "determine the maximum metric values");
14900 for (my $line_number=$start_target_source+1; $line_number <= $end_target_source; $line_number++)
14902 $input_line = $file_contents[$line_number];
14903 gp_message ("debugXL", $subr_name, " $line_number : check input_line = $input_line");
14905 if ( $input_line =~ /$hot_lines_regex/ )
14907 gp_message ("debugXL", $subr_name, " $line_number : found a hot line");
14908 #------------------------------------------------------------------------------
14909 # We found a hot line and the metric fields are stored in $2. We turn this
14910 # string into an array and add it as a row to hot_source_lines.
14911 #------------------------------------------------------------------------------
14912 $hot_line = $1;
14913 $metric_values = $2;
14915 gp_message ("debugXL", $subr_name, "hot_line = $hot_line");
14916 gp_message ("debugXL", $subr_name, "metric_values = $metric_values");
14918 my @metrics = split (" ", $metric_values);
14919 push (@hot_source_lines, [@metrics]);
14921 gp_message ("debugXL", $subr_name, " $line_number : completed check for hot line");
14924 #------------------------------------------------------------------------------
14925 # Transpose the array with the hot lines. This means each row has all the
14926 # values for a metrict and it makes it easier to determine the maximum values.
14927 #------------------------------------------------------------------------------
14928 for my $row (keys @hot_source_lines)
14930 my $msg = "row[" . $row . "] =";
14931 for my $col (keys @{$hot_source_lines[$row]})
14933 $msg .= " $hot_source_lines[$row][$col]";
14934 $transposed_hot_lines[$col][$row] = $hot_source_lines[$row][$col];
14938 #------------------------------------------------------------------------------
14939 # Print the maximum metric values found. Each row contains the data for a
14940 # different metric.
14941 #------------------------------------------------------------------------------
14942 for my $row (keys @transposed_hot_lines)
14944 my $msg = "row[" . $row . "] =";
14945 for my $col (keys @{$transposed_hot_lines[$row]})
14947 $msg .= " $transposed_hot_lines[$row][$col]";
14949 gp_message ("debugXL", $subr_name, "hot lines = $msg");
14952 #------------------------------------------------------------------------------
14953 # Determine the maximum value for each metric.
14954 #------------------------------------------------------------------------------
14955 for my $row (keys @transposed_hot_lines)
14957 my $max_val = 0;
14958 for my $col (keys @{$transposed_hot_lines[$row]})
14960 $max_val = max ($transposed_hot_lines[$row][$col], $max_val);
14962 #------------------------------------------------------------------------------
14963 # Convert to a floating point number.
14964 #------------------------------------------------------------------------------
14965 if ($max_val =~ /$integer_only_regex/)
14967 $max_val = sprintf ("%f", $max_val);
14969 push (@max_metric_values, $max_val);
14972 for my $metric (keys @max_metric_values)
14974 my $msg = "$input_filename max_metric_values[$metric] = " .
14975 $max_metric_values[$metric];
14976 gp_message ("debugXL", $subr_name, $msg);
14979 #------------------------------------------------------------------------------
14980 # Process those functions that are not the current target.
14981 #------------------------------------------------------------------------------
14982 $modified_html_ref = process_non_target_source ($start_all_source,
14983 $start_target_source-1,
14984 $src_times_regex,
14985 $function_regex,
14986 $number_of_metrics,
14987 \@file_contents,
14988 \@modified_html);
14989 @modified_html = @{ $modified_html_ref };
14991 #------------------------------------------------------------------------------
14992 # This is the core part to process the information for the target function.
14993 #------------------------------------------------------------------------------
14994 gp_message ("debugXL", $subr_name, "parse and process the target source");
14995 $modified_html_ref = process_target_source ($start_target_source,
14996 $end_target_source,
14997 $routine,
14998 \@max_metric_values,
14999 $src_times_regex,
15000 $function2_regex,
15001 $number_of_metrics,
15002 \@file_contents,
15003 \@modified_html);
15004 @modified_html = @{ $modified_html_ref };
15006 if ($end_target_source < $#file_contents)
15008 $modified_html_ref = process_non_target_source ($end_target_source+1,
15009 $#file_contents,
15010 $src_times_regex,
15011 $function_regex,
15012 $number_of_metrics,
15013 \@file_contents,
15014 \@modified_html);
15015 @modified_html = @{ $modified_html_ref };
15018 gp_message ("debug", $subr_name, "completed reading source");
15020 #------------------------------------------------------------------------------
15021 # Add an extra line with diagnostics.
15023 # TBD: The same is done in generate_dis_html but should be done only once.
15024 #------------------------------------------------------------------------------
15025 if ($hp_value > 0)
15027 my $rounded_percentage = sprintf ("%.1f", $hp_value);
15028 $threshold_line = "<i>The setting for the highlight percentage";
15029 $threshold_line .= " (--highlight-percentage) option:";
15030 $threshold_line .= " " . $rounded_percentage . " (%)</i>";
15032 else
15034 $threshold_line = "<i>The highlight percentage feature has not been";
15035 $threshold_line .= " enabled</i>";
15038 $html_home = ${ generate_home_link ("left") };
15039 $html_end = ${ terminate_html_document () };
15041 push (@modified_html, "</pre>");
15042 push (@modified_html, "<br>");
15043 push (@modified_html, $threshold_line);
15044 push (@modified_html, $html_home);
15045 push (@modified_html, "<br>");
15046 push (@modified_html, $g_html_credits_line);
15047 push (@modified_html, $html_end);
15049 for my $i (0 .. $#modified_html)
15051 gp_message ("debugXL", $subr_name, "[$i] -> $modified_html[$i]");
15054 #------------------------------------------------------------------------------
15055 # Write the generated HTML text to file.
15056 #------------------------------------------------------------------------------
15057 for my $i (0 .. $#modified_html)
15059 print NEW_HTML "$modified_html[$i]" . "\n";
15061 close (NEW_HTML);
15062 close (SRC_LISTING);
15064 return ($found_target);
15066 } #-- End of subroutine process_source
15068 #------------------------------------------------------------------------------
15069 # Process the source lines for the target function.
15070 #------------------------------------------------------------------------------
15071 sub process_target_source
15073 my $subr_name = get_my_name ();
15075 my ($start_scan, $end_scan, $target_function, $max_metric_values_ref,
15076 $src_times_regex, $function2_regex, $number_of_metrics,
15077 $file_contents_ref, $modified_html_ref) = @_;
15079 my @file_contents = @{ $file_contents_ref };
15080 my @modified_html = @{ $modified_html_ref };
15081 my @max_metric_values = @{ $max_metric_values_ref };
15083 my @components = ();
15085 my $colour_coded_line;
15086 my $colour_coded_line_ref;
15087 my $hot_line;
15088 my $input_line;
15089 my $line_id;
15090 my $modified_line;
15091 my $metric_values;
15092 my $src_code_line;
15093 my $src_line_no;
15095 gp_message ("debug", $subr_name, "parse and process the core loop");
15097 for (my $line_number=$start_scan; $line_number <= $end_scan; $line_number++)
15099 $input_line = $file_contents[$line_number];
15101 #------------------------------------------------------------------------------
15102 # We need to replace the "<" symbol in the code by "&lt;".
15103 #------------------------------------------------------------------------------
15104 $input_line =~ s/$g_less_than_regex/$g_html_less_than_regex/g;
15106 $line_id = extract_source_line_number ($src_times_regex,
15107 $function2_regex,
15108 $number_of_metrics,
15109 $input_line);
15111 gp_message ("debug", $subr_name, "line_number = $line_number : input_line = $input_line line_id = $line_id");
15113 if ($input_line =~ /$function2_regex/)
15114 #------------------------------------------------------------------------------
15115 # Found the function marker.
15116 #------------------------------------------------------------------------------
15118 if (defined ($1) and defined ($2))
15120 my $func_name_in_file = $2;
15121 my $spaces = $1;
15122 my $boldface = $TRUE;
15123 gp_message ("debug", $subr_name, "function_name = $2");
15124 my $function_line = "&lt;Function: " . $func_name_in_file . ">";
15125 my $color_function_name = color_string (
15126 $function_line,
15127 $boldface,
15128 $g_html_color_scheme{"target_function_name"});
15129 my $ftag;
15130 if (exists ($g_function_tag_id{$target_function}))
15132 $ftag = $g_function_tag_id{$target_function};
15133 gp_message ("debug", $subr_name, "target_function = $target_function ftag = $ftag");
15135 else
15137 my $msg = "no ftag found for $target_function";
15138 gp_message ("assertion", $subr_name, $msg);
15140 $modified_line = "<a id=\"" . $ftag . "\"></a>";
15141 $modified_line .= $spaces . "<i>" . $color_function_name . "</i>";
15144 elsif ($input_line =~ /$src_times_regex/)
15145 #------------------------------------------------------------------------------
15146 # This is a line with metric values.
15147 #------------------------------------------------------------------------------
15149 gp_message ("debug", $subr_name, "input line has metrics");
15151 $hot_line = $1;
15152 $metric_values = $2;
15153 $src_line_no = $3;
15154 $src_code_line = $4;
15156 gp_message ("debug", $subr_name, "hot_line = $hot_line");
15157 gp_message ("debug", $subr_name, "metric_values = $metric_values");
15158 gp_message ("debug", $subr_name, "src_line_no = $src_line_no");
15159 gp_message ("debug", $subr_name, "src_code_line = $src_code_line");
15161 if ($hot_line eq "##")
15162 #------------------------------------------------------------------------------
15163 # Highlight the most expensive line.
15164 #------------------------------------------------------------------------------
15166 @components = split (" ", $input_line, 1+$number_of_metrics+2);
15167 $modified_line = set_background_color_string (
15168 $input_line,
15169 $g_html_color_scheme{"background_color_hot"});
15171 else
15173 #------------------------------------------------------------------------------
15174 # Highlight those lines close enough to the most expensive line.
15175 #------------------------------------------------------------------------------
15176 @components = split (" ", $input_line, $number_of_metrics + 2);
15177 for my $i (0 .. $number_of_metrics-1)
15179 gp_message ("debugXL", $subr_name, "$line_number : time check components[$i] = $components[$i]");
15182 $colour_coded_line_ref = check_metric_values ($metric_values, \@max_metric_values);
15184 $colour_coded_line = $ {$colour_coded_line_ref};
15185 if ($colour_coded_line)
15187 gp_message ("debugXL", $subr_name, "$line_number : change background colour modified_line = $modified_line");
15188 $modified_line = set_background_color_string ($input_line, $g_html_color_scheme{"background_color_lukewarm"});
15190 else
15192 $modified_line = "<a id=\"line_" . $line_id . "\"></a>";
15193 $modified_line .= "$input_line";
15197 else
15198 #------------------------------------------------------------------------------
15199 # This is a regular line that is not modified.
15200 #------------------------------------------------------------------------------
15202 #------------------------------------------------------------------------------
15203 # Add an id.
15204 #------------------------------------------------------------------------------
15205 gp_message ("debug", $subr_name, "$line_number : input line is a regular line");
15206 $modified_line = "<a id=\"line_" . $line_id . "\"></a>";
15207 $modified_line .= "$input_line";
15209 gp_message ("debug", $subr_name, "$line_number : mod = $modified_line");
15210 push (@modified_html, $modified_line);
15213 return (\@modified_html);
15215 } #-- End of subroutine process_target_source
15217 #------------------------------------------------------------------------------
15218 # Process the options. Set associated variables and check the options for
15219 # correctness. For example, detect if conflicting options have been set.
15220 #------------------------------------------------------------------------------
15221 sub process_user_options
15223 my $subr_name = get_my_name ();
15225 my ($exp_dir_list_ref) = @_;
15227 my @exp_dir_list = @{ $exp_dir_list_ref };
15229 my %ignored_metrics = ();
15231 my $abs_path_dir;
15232 my @candidate_ignored_metrics = ();
15233 my $error_code;
15234 my $hp_value;
15235 my $msg;
15237 my $outputdir;
15239 my $target_cmd;
15240 my $rm_output_msg;
15241 my $mkdir_output_msg;
15242 my $time_percentage_multiplier;
15243 my $process_all_functions;
15245 #------------------------------------------------------------------------------
15246 # The -o and -O options are mutually exclusive.
15247 #------------------------------------------------------------------------------
15248 my $define_new_output_dir = $g_user_settings{"output"}{"defined"};
15249 my $overwrite_output_dir = $g_user_settings{"overwrite"}{"defined"};
15250 my $dir_o_option = $g_user_settings{"output"}{"current_value"};
15251 my $dir_O_option = $g_user_settings{"overwrite"}{"current_value"};
15253 if ($define_new_output_dir and $overwrite_output_dir)
15255 $msg = "the -o/--output and -O/--overwrite options are both set, " .
15256 "but are mutually exclusive";
15257 gp_message ("error", $subr_name, $msg);
15259 $msg = "(setting for -o = $dir_o_option, " .
15260 "setting for -O = $dir_O_option)";
15261 gp_message ("error", $subr_name, $msg);
15263 $g_total_error_count++;
15266 #------------------------------------------------------------------------------
15267 # The warnings option is deprecated. Print a warning to this extent and point
15268 # to the --nowarnings option.
15269 #------------------------------------------------------------------------------
15270 #------------------------------------------------------------------------------
15271 # Handle the situation that both or one of the highlight-percentage and hp
15272 # options are set.
15273 #------------------------------------------------------------------------------
15274 if ($g_user_settings{"warnings"}{"defined"})
15276 $msg = "<br>" . "the --warnings option has been deprecated and";
15277 $msg .= " will be ignored";
15278 gp_message ("warning", $subr_name, $msg);
15280 if ($g_user_settings{"nowarnings"}{"defined"})
15282 $msg = "since the --nowarnings option is also used, warnings";
15283 $msg .= " are disabled";
15284 gp_message ("warning", $subr_name, $msg);
15286 else
15288 $msg = "by default, warnings are enabled and can be disabled with";
15289 gp_message ("warning", $subr_name, $msg);
15290 $msg = " the --nowarnings option";
15291 gp_message ("warning", $subr_name, $msg);
15293 $g_total_warning_count++;
15296 #------------------------------------------------------------------------------
15297 # In case both the --highlight-percentage and -hp option are set, issue a
15298 # warning and continue with the --highlight-percentage value.
15299 #------------------------------------------------------------------------------
15300 if ($g_user_settings{"hp"}{"defined"})
15302 $msg = "<br>" . "the -hp option has been deprecated and";
15303 $msg .= " will be ignored";
15304 gp_message ("warning", $subr_name, $msg);
15306 if ($g_user_settings{"highlight_percentage"}{"defined"})
15308 $msg = "since the --highlight-percentage option is also used,";
15309 $msg .= " the value of ";
15310 $msg .= $g_user_settings{"highlight_percentage"}{"current_value"};
15311 $msg .= " will be applied";
15312 gp_message ("warning", $subr_name, $msg);
15314 else
15316 #------------------------------------------------------------------------------
15317 # If only the -hp option is set, we use it, because we do not want to break
15318 # compatibility (yet) and force the user to change the option.
15319 #------------------------------------------------------------------------------
15321 ## FUTURE $msg = "instead, the default setting of "
15322 ## FUTURE $msg .= $g_user_settings{"highlight_percentage"}{"current_value"};
15323 ## FUTURE $msg .= " for the --highlight-percentage will be used";
15324 ## FUTURE gp_message ("warning", $subr_name, $msg);
15326 ## FUTURE $msg = "please use this option to set the highlighting value";
15327 ## FUTURE gp_message ("warning", $subr_name, $msg);
15329 $g_user_settings{"highlight_percentage"}{"current_value"} =
15330 $g_user_settings{"hp"}{"current_value"};
15332 $g_user_settings{"highlight_percentage"}{"defined"} = $TRUE;
15334 $msg = "for now, the value of " .
15335 $g_user_settings{"hp"}{"current_value"} .
15336 " for the -hp option is used, but please change the" .
15337 " option to --highlight-percentage";
15338 gp_message ("warning", $subr_name, $msg);
15341 $g_total_warning_count++;
15344 #------------------------------------------------------------------------------
15345 # Regardless of the use of the -hp option, we continue with the value for
15346 # highlight-percentage. Some more checks are carried out now.
15347 #------------------------------------------------------------------------------
15349 #------------------------------------------------------------------------------
15350 # This value should be in the interval [0,100].
15351 # the number to be positive, but the limits have not been checked yet.
15352 #------------------------------------------------------------------------------
15353 $hp_value = $g_user_settings{"highlight_percentage"}{"current_value"};
15355 if (($hp_value < 0) or ($hp_value > 100))
15357 $msg = "the value for the highlight percentage is set to $hp_value,";
15358 $msg .= " but must be in the range [0, 100]";
15359 gp_message ("error", $subr_name, $msg);
15361 $g_total_error_count++;
15363 elsif ($hp_value == 0.0)
15364 #------------------------------------------------------------------------------
15365 # A value of zero is interpreted to mean that highlighting should be disabled.
15366 # To make the checks for this later on easier, set it to an integer value of 0.
15367 #------------------------------------------------------------------------------
15369 $g_user_settings{"highlight_percentage"}{"current_value"} = 0;
15371 $msg = "reset the highlight percentage value from 0.0 to";
15372 $msg .= " " . $g_user_settings{"highlight_percentage"}{"current_value"};
15373 gp_message ("debug", $subr_name, $msg);
15376 #------------------------------------------------------------------------------
15377 # The value for TP should be in the interval (0,100]. We already enforced
15378 # the number to be positive, but the limits have not been checked yet.
15379 #------------------------------------------------------------------------------
15380 my $tp_value = $g_user_settings{"threshold_percentage"}{"current_value"};
15382 if (($tp_value < 0) or ($tp_value > 100))
15384 $msg = "the value for the total percentage is set to $tp_value,";
15385 $msg .= " but must be in the range (0, 100]";
15386 gp_message ("error", $subr_name, $msg);
15388 $g_total_error_count++;
15390 else
15392 $time_percentage_multiplier = $tp_value/100.0;
15394 # Ruud if (($TIME_PERCENTAGE_MULTIPLIER*100.) >= 100.)
15396 if ($tp_value == 100)
15398 $process_all_functions = $TRUE; # ensure that all routines are handled
15400 else
15402 $process_all_functions = $FALSE;
15405 $msg = "value of time_percentage_multiplier = " .
15406 $time_percentage_multiplier;
15407 gp_message ("debugM", $subr_name, $msg);
15408 $msg = "value of process_all_functions = " .
15409 ($process_all_functions ? "TRUE" : "FALSE");
15410 gp_message ("debugM", $subr_name, $msg);
15413 #------------------------------------------------------------------------------
15414 # If imetrics has been set, split the list into the individual metrics that
15415 # need to be excluded. The associated hash called $ignore_metrics has the
15416 # to be excluded metrics as an index. The value of $TRUE assigned does not
15417 # really matter.
15418 #------------------------------------------------------------------------------
15419 if ($g_user_settings{"ignore_metrics"}{"defined"})
15421 @candidate_ignored_metrics =
15422 split (":", $g_user_settings{"ignore_metrics"}{"current_value"});
15424 for my $metric (@candidate_ignored_metrics)
15426 # TBD: bug? $ignored_metrics{$metric} = $FALSE;
15427 $ignored_metrics{$metric} = $TRUE;
15429 for my $metric (keys %ignored_metrics)
15431 my $msg = "ignored_metrics{$metric} = $ignored_metrics{$metric}";
15432 gp_message ("debugM", $subr_name, $msg);
15435 #------------------------------------------------------------------------------
15436 # Check if the experiment directories exist and if they do, add the absolute
15437 # path. This is easier in the remainder.
15438 #------------------------------------------------------------------------------
15439 for my $i (0 .. $#exp_dir_list)
15441 if (-d $exp_dir_list[$i])
15443 $abs_path_dir = Cwd::abs_path ($exp_dir_list[$i]);
15444 $exp_dir_list[$i] = $abs_path_dir;
15446 $msg = "directory $exp_dir_list[$i] exists";
15447 gp_message ("debugM", $subr_name, $msg);
15451 return (\%ignored_metrics, $outputdir, $time_percentage_multiplier,
15452 $process_all_functions, \@exp_dir_list);
15454 } #-- End of subroutine process_user_options
15456 #------------------------------------------------------------------------------
15457 # This function addresses a legacy issue.
15459 # In binutils 2.40, the "gprofng display text" tool may add a string in the
15460 # function overviews. This did not add any value and was disruptive to the
15461 # output. It has been removed in 2.41, but in order to support the older
15462 # versions of gprofng, the string is removed before the data is processed.
15464 # Note: the double space in "-- no" is not a typo in this code!
15465 #------------------------------------------------------------------------------
15466 sub remove_redundant_string
15468 my $subr_name = get_my_name ();
15470 my ($target_array_ref) = @_;
15472 my @target_array = @{ $target_array_ref };
15474 my $msg;
15475 my $redundant_string = " -- no functions found";
15477 for (my $line = 0; $line <= $#target_array; $line++)
15479 $target_array[$line] =~ s/$redundant_string//;
15482 $msg = "removed any occurrence of " . $redundant_string;
15483 gp_message ("debugM", $subr_name, $msg);
15485 return (\@target_array);
15487 } #-- End of subroutine remove_redundant_string
15489 #------------------------------------------------------------------------------
15490 # This is a hopefully temporary routine to disable/ignore selected user
15491 # settings. As the functionality expands, this list will get shorter.
15492 #------------------------------------------------------------------------------
15493 sub reset_selected_settings
15495 my $subr_name = get_my_name ();
15497 $g_locale_settings{"decimal_separator"} = "\\.";
15498 $g_locale_settings{"convert_to_dot"} = $FALSE;
15499 $g_user_settings{func_limit}{current_value} = 1000000;
15501 gp_message ("debug", $subr_name, "reset selected settings");
15503 return (0);
15505 } #-- End of subroutine reset_selected_settings
15507 #------------------------------------------------------------------------------
15508 # There may be various different visibility characters in a metric definition.
15509 # For example: e+%CPI.
15511 # Internally we use a normalized definition that only uses the dot (e.g.
15512 # e.CPI) as an index into the description structure.
15514 # Here we reduce the incoming metric definition to the normalized form, look
15515 # up the text, and return a pointer to it.
15516 #------------------------------------------------------------------------------
15517 sub retrieve_metric_description
15519 my $subr_name = get_my_name ();
15521 my ($metric_name_ref, $metric_description_ref) = @_;
15523 my $metric_name = ${ $metric_name_ref };
15524 my %metric_description = %{ $metric_description_ref };
15526 my $description;
15527 my $normalized_metric;
15529 $metric_name =~ /([ei])([\.\+%]+)(.*)/;
15531 if (defined ($1) and defined ($3))
15533 $normalized_metric = $1 . "." . $3;
15535 else
15537 my $msg = "metric $metric_name has an unknown format";
15538 gp_message ("assertion", $subr_name, $msg);
15541 if (defined ($metric_description{$normalized_metric}))
15543 $description = $metric_description{$normalized_metric};
15545 else
15547 my $msg = "description for normalized metric $normalized_metric not found";
15548 gp_message ("assertion", $subr_name, $msg);
15551 return (\$description);
15553 } #-- End of subroutine retrieve_metric_description
15555 #------------------------------------------------------------------------------
15556 # TBD.
15557 #------------------------------------------------------------------------------
15558 sub rnumerically
15560 my ($f1,$f2);
15561 if ($a =~ /^([^\d]*)(\d+)/)
15563 $f1 = int ($2);
15564 if ($b=~ /^([^\d]*)(\d+)/)
15566 $f2 = int ($2);
15567 $f1 == $f2 ? 0 : ($f1 > $f2 ? -1 : +1);
15570 else
15572 return ($b <=> $a);
15574 } #-- End of subroutine rnumerically
15576 #------------------------------------------------------------------------------
15577 # TBD: Remove - not used any longer.
15578 # Set the architecture and associated regular expressions.
15579 #------------------------------------------------------------------------------
15580 sub set_arch_and_regexes
15582 my $subr_name = get_my_name ();
15584 my ($arch_uname) = @_;
15586 my $architecture_supported;
15588 gp_message ("debug", $subr_name, "arch_uname = $arch_uname");
15590 if ($arch_uname eq "x86_64")
15592 #x86/x64 hardware uses jump
15593 $architecture_supported = $TRUE;
15594 # $arch='x64';
15595 # $regex=':\s+(j).*0x[0-9a-f]+';
15596 # $subexp='(\[\s*)(0x[0-9a-f]+)';
15597 # $linksubexp='(\[\s*)(0x[0-9a-f]+)';
15598 gp_message ("debug", $subr_name, "detected $arch_uname hardware");
15600 $architecture_supported = $TRUE;
15601 $g_arch_specific_settings{"arch_supported"} = $TRUE;
15602 $g_arch_specific_settings{"arch"} = 'x64';
15603 $g_arch_specific_settings{"regex"} = ':\s+(j).*0x[0-9a-f]+';
15604 $g_arch_specific_settings{"subexp"} = '(\[\s*)(0x[0-9a-f]+)';
15605 $g_arch_specific_settings{"linksubexp"} = '(\[\s*)(0x[0-9a-f]+)';
15607 #------------------------------------------------------------------------------
15608 # TBD: Remove the elsif block
15609 #------------------------------------------------------------------------------
15610 elsif ($arch_uname=~m/sparc/s)
15612 #sparc hardware uses branch
15613 $architecture_supported = $FALSE;
15614 # $arch='sparc';
15615 # $regex=':\s+(c|b|fb).*0x[0-9a-f]+\s*$';
15616 # $subexp='(\s*)(0x[0-9a-f]+)\s*$';
15617 # $linksubexp='(\s*)(0x[0-9a-f]+\s*$)';
15618 # gp_message ("debug", $subr_name, "detected $arch_uname hardware arch = $arch - this is no longer supported");
15619 $architecture_supported = $FALSE;
15620 $g_arch_specific_settings{arch_supported} = $FALSE;
15621 $g_arch_specific_settings{arch} = 'sparc';
15622 $g_arch_specific_settings{regex} = ':\s+(c|b|fb).*0x[0-9a-f]+\s*$';
15623 $g_arch_specific_settings{subexp} = '(\s*)(0x[0-9a-f]+)\s*$';
15624 $g_arch_specific_settings{linksubexp} = '(\s*)(0x[0-9a-f]+\s*$)';
15626 else
15628 $architecture_supported = $FALSE;
15629 gp_message ("debug", $subr_name, "detected $arch_uname hardware - this not supported; limited functionality");
15632 return ($architecture_supported);
15634 } #-- End of subroutine set_arch_and_regexes
15636 #------------------------------------------------------------------------------
15637 # Set the background color of the input string.
15639 # For supported colors, see:
15640 # https://www.w3schools.com/colors/colors_names.asp
15641 #------------------------------------------------------------------------------
15642 sub set_background_color_string
15644 my $subr_name = get_my_name ();
15646 my ($input_string, $color) = @_;
15648 my $background_color_string;
15649 my $msg;
15651 $msg = "color = $color input_string = $input_string";
15652 gp_message ("debugXL", $subr_name, $msg);
15654 $background_color_string = "<span style='background-color: " . $color .
15655 "'>" . $input_string . "</span>";
15657 $msg = "color = $color background_color_string = " .
15658 $background_color_string;
15659 gp_message ("debugXL", $subr_name, $msg);
15661 return ($background_color_string);
15663 } #-- End of subroutine set_background_color_string
15665 #------------------------------------------------------------------------------
15666 # Set the g_debug_size structure for a given value for "size". Also set the
15667 # value in $g_user_settings{"debug"}{"current_value"}
15668 #------------------------------------------------------------------------------
15669 sub set_debug_size
15671 my $subr_name = get_my_name ();
15673 my $debug_value = lc ($g_user_settings{"debug"}{"current_value"});
15675 #------------------------------------------------------------------------------
15676 # Set the corresponding sizes in the table. A value of "on" is equivalent to
15677 # size "s".
15678 #------------------------------------------------------------------------------
15679 if (($debug_value eq "on") or ($debug_value eq "s"))
15681 $g_debug_size{"on"} = $TRUE;
15682 $g_debug_size{"s"} = $TRUE;
15684 elsif ($debug_value eq "m")
15686 $g_debug_size{"on"} = $TRUE;
15687 $g_debug_size{"s"} = $TRUE;
15688 $g_debug_size{"m"} = $TRUE;
15690 elsif ($debug_value eq "l")
15692 $g_debug_size{"on"} = $TRUE;
15693 $g_debug_size{"s"} = $TRUE;
15694 $g_debug_size{"m"} = $TRUE;
15695 $g_debug_size{"l"} = $TRUE;
15697 elsif ($debug_value eq "xl")
15699 $g_debug_size{"on"} = $TRUE;
15700 $g_debug_size{"s"} = $TRUE;
15701 $g_debug_size{"m"} = $TRUE;
15702 $g_debug_size{"l"} = $TRUE;
15703 $g_debug_size{"xl"} = $TRUE;
15705 else
15706 #------------------------------------------------------------------------------
15707 # Any other value is considered to disable debugging.
15708 #------------------------------------------------------------------------------
15710 ## $g_user_settings{"debug"}{"current_value"} = "off";
15711 $g_debug = $FALSE;
15712 $g_debug_size{"on"} = $FALSE;
15713 $g_debug_size{"s"} = $FALSE;
15714 $g_debug_size{"m"} = $FALSE;
15715 $g_debug_size{"l"} = $FALSE;
15716 $g_debug_size{"xl"} = $FALSE;
15719 #------------------------------------------------------------------------------
15720 # Activate in case of an emergency :-)
15721 #------------------------------------------------------------------------------
15722 my $show_sizes = $FALSE;
15724 if ($show_sizes)
15726 if ($g_debug_size{$debug_value})
15728 for my $i (keys %g_debug_size)
15730 print "$subr_name g_debug_size{$i} = $g_debug_size{$i}\n";
15735 return (0);
15737 } #-- End of subroutine set_debug_size
15739 #------------------------------------------------------------------------------
15740 # This subroutine defines the default metrics.
15741 #------------------------------------------------------------------------------
15742 sub set_default_metrics
15744 my $subr_name = get_my_name ();
15746 my ($outfile1, $ignored_metrics_ref) = @_;
15748 my %ignored_metrics = %{ $ignored_metrics_ref };
15750 my %metric_description = ();
15751 my %metric_found = ();
15753 my $detail_metrics;
15754 my $detail_metrics_system;
15756 my $call_metrics = "";
15757 my $summary_metrics = "";
15759 open (METRICS, "<", $outfile1)
15760 or die ("Unable to open metrics file $outfile1 for reading - '$!'");
15761 gp_message ("debug", $subr_name, "opened $outfile1 for reading");
15763 while (<METRICS>)
15765 my $metric_line = $_;
15766 chomp ($metric_line);
15768 gp_message ("debug", $subr_name,"the value of metric_line = $metric_line");
15770 #------------------------------------------------------------------------------
15771 # Decode the metric part of the input line. If a valid line, return the
15772 # metric components. Otherwise return "skipped" in the metric_spec field.
15773 #------------------------------------------------------------------------------
15774 my ($metric_spec, $metric_flavor, $metric_visibility, $metric_name,
15775 $metric_description) = extract_metric_specifics ($metric_line);
15777 gp_message ("debug", $subr_name, "metric_spec = $metric_spec");
15778 gp_message ("debug", $subr_name, "metric_flavor = $metric_flavor");
15780 if ($metric_spec eq "skipped")
15781 #------------------------------------------------------------------------------
15782 # Not a valid input line.
15783 #------------------------------------------------------------------------------
15785 gp_message ("debug", $subr_name, "skipped line: $metric_line");
15787 else
15789 #------------------------------------------------------------------------------
15790 # A valid metric field has been found.
15791 #------------------------------------------------------------------------------
15792 gp_message ("debug", $subr_name, "metric_name = $metric_name");
15793 gp_message ("debug", $subr_name, "metric_description = $metric_description");
15795 # if (exists ($IMETRICS{$m})){
15796 if ($g_user_settings{"ignore_metrics"}{"defined"} and exists ($ignored_metrics{$metric_name}))
15798 gp_message ("debug", $subr_name, "user requested to ignore metric $metric_name");
15799 next;
15802 #------------------------------------------------------------------------------
15803 # Only the exclusive metric is selected.
15804 #------------------------------------------------------------------------------
15805 if ($metric_flavor eq "e")
15807 $metric_found{$metric_spec} = $TRUE;
15808 $metric_description{$metric_spec} = $metric_description;
15810 # TBD: remove the -AO:
15811 gp_message ("debug", $subr_name,"-AO metric_description{$metric_spec} = $metric_description{$metric_spec}");
15813 $summary_metrics .= $metric_spec.":";
15814 $call_metrics .= "a.".$metric_name.":";
15818 close (METRICS);
15820 chop ($call_metrics);
15821 chop ($summary_metrics);
15823 $detail_metrics = $summary_metrics;
15824 $detail_metrics_system = $summary_metrics;
15826 return (\%metric_description, \%metric_found,
15827 $summary_metrics, $detail_metrics, $detail_metrics_system, $call_metrics);
15829 } #-- End of subroutine set_default_metrics
15831 #------------------------------------------------------------------------------
15832 # Set various system specific variables. These depend upon both the processor
15833 # architecture and OS. The values are stored in global structure
15834 # g_arch_specific_settings.
15835 #------------------------------------------------------------------------------
15836 sub set_system_specific_variables
15838 my $subr_name = get_my_name ();
15840 my ($arch_uname, $arch_uname_s) = @_;
15842 my $elf_arch;
15843 my $read_elf_cmd;
15844 my $elf_support;
15845 my $architecture_supported;
15846 my $arch;
15847 my $regex;
15848 my $subexp;
15849 my $linksubexp;
15851 if ($arch_uname eq "x86_64")
15853 #------------------------------------------------------------------------------
15854 # x86/x64 hardware uses jump
15855 #------------------------------------------------------------------------------
15856 $architecture_supported = $TRUE;
15857 $arch = 'x64';
15858 $regex =':\s+(j).*0x[0-9a-f]+';
15859 $subexp ='(\[\s*)(0x[0-9a-f]+)';
15860 $linksubexp ='(\[\s*)(0x[0-9a-f]+)';
15862 # gp_message ("debug", $subr_name, "detected $arch_uname hardware arch = $arch");
15864 $g_arch_specific_settings{"arch_supported"} = $TRUE;
15865 $g_arch_specific_settings{"arch"} = 'x64';
15866 #------------------------------------------------------------------------------
15867 # Define the regular expressions to parse branch instructions.
15868 #------------------------------------------------------------------------------
15870 #------------------------------------------------------------------------------
15871 # TBD: Need much more than these
15872 #------------------------------------------------------------------------------
15873 $g_arch_specific_settings{"regex"} = '\.*([0-9a-fA-F]*):\s+(j).*\s*0x([0-9a-fA-F]+)';
15874 $g_arch_specific_settings{"subexp"} = '(0x[0-9a-f]+)';
15875 $g_arch_specific_settings{"linksubexp"} = '(\s*)(0x[0-9a-f]+)';
15877 else
15879 $architecture_supported = $FALSE;
15880 $g_arch_specific_settings{"arch_supported"} = $FALSE;
15883 #------------------------------------------------------------------------------
15884 # TBD Ruud: need to handle this better
15885 #------------------------------------------------------------------------------
15886 if ($arch_uname_s eq "Linux")
15888 $elf_arch = $arch_uname_s;
15889 $read_elf_cmd = $g_mapped_cmds{"readelf"};
15891 if ($read_elf_cmd eq "road to nowhere")
15893 $elf_support = $FALSE;
15895 else
15897 $elf_support = $TRUE;
15899 gp_message ("debugXL", $subr_name, "elf_support = $elf_support read_elf_cmd = $read_elf_cmd elf_arch = $elf_arch");
15901 else
15903 gp_message ("abort", $subr_name, "the $arch_uname_s operating system is not supported");
15906 return ($architecture_supported, $elf_arch, $elf_support);
15908 } #-- End of subroutine set_system_specific_variables
15910 #------------------------------------------------------------------------------
15911 # TBD
15912 #------------------------------------------------------------------------------
15913 sub set_title
15915 my $subr_name = get_my_name ();
15917 my ($function_info_ref, $func, $from_where) = @_ ;
15919 my $msg;
15920 my @function_info = @{$function_info_ref};
15921 my $filename = $func ;
15923 my $base;
15924 my $first_line;
15925 my $file_is_empty;
15926 my $src_file;
15927 my $RI;
15928 my $the_title;
15929 my $routine = "?";
15930 my $DIS;
15931 my $SRC;
15933 chomp ($filename);
15935 $base = get_basename ($filename);
15937 gp_message ("debug", $subr_name, "from_where = $from_where");
15938 gp_message ("debug", $subr_name, "base = $base filename = $filename");
15940 if ($from_where eq "process source")
15942 if ($base =~ /^file\.(\d+)\.src\.txt$/)
15944 if (defined ($1))
15946 $RI = $1;
15948 else
15950 $msg = "unexpected error encountered parsing $filename";
15951 gp_message ("assertion", $subr_name, $msg);
15954 $the_title = "Source";
15956 elsif ($from_where eq "disassembly")
15958 if ($base =~ /^file\.(\d+)\.dis$/)
15960 if (defined ($1))
15962 $RI = $1;
15964 else
15966 $msg = "unexpected error encountered parsing $filename";
15967 gp_message ("assertion", $subr_name, $msg);
15970 $the_title = "Disassembly";
15972 else
15974 $msg = "called from unknown routine - $from_where";
15975 gp_message ("assertion", $subr_name, $msg);
15978 if (defined ($function_info[$RI]{"routine"}))
15980 $routine = $function_info[$RI]{"routine"};
15983 if ($from_where eq "process source")
15985 $file_is_empty = is_file_empty ($filename);
15987 if ($file_is_empty)
15989 $src_file = "";
15991 else
15993 open ($SRC, "<", $filename)
15994 or die ("$subr_name - unable to open source file $filename for reading:'$!'");
15995 gp_message ("debug", $subr_name, "opened file $filename for reading");
15997 $first_line = <$SRC>;
15998 chomp ($first_line);
16000 close ($SRC);
16002 gp_message ("debug", $subr_name, "first_line = $first_line");
16004 if ($first_line =~ /^Source\s+file:\s+([^\s]+)/)
16006 $src_file = $1
16008 else
16010 $src_file = "";
16014 elsif ($from_where eq "disassembly")
16016 $msg = "unable to open disassembly file $filename for reading:";
16017 open ($DIS, "<", $filename)
16018 or die ($subr_name . " - " . $msg . " " . $!);
16019 gp_message ("debug", $subr_name, "opened file $filename for reading");
16021 $file_is_empty = is_file_empty ($filename);
16023 if ($file_is_empty)
16024 #------------------------------------------------------------------------------
16025 # Currently, the disassembly file for <static> functions appears to be empty
16026 # on aarch64. This might be a bug, but it is in any case better to handle
16027 # this situation.
16028 #------------------------------------------------------------------------------
16030 $first_line = "";
16031 $msg = "file $filename is empty";
16032 gp_message ("debugM", $subr_name, $msg);
16034 else
16036 $first_line = <$DIS>;
16039 close ($DIS);
16041 if ($first_line =~ /^Source\s+file:\s+([^\s]+)/)
16043 $src_file = "$1"
16045 else
16047 $src_file = "";
16051 if (length ($routine))
16053 $the_title .= " $routine";
16056 if (length ($src_file))
16058 if ($src_file ne "(unknown)")
16060 $the_title .= " ($src_file)";
16062 else
16064 $the_title .= " $src_file";
16068 return ($the_title);
16070 } #-- End of subroutine set_title
16072 #------------------------------------------------------------------------------
16073 # Handles where the output should go. If needed, a directory to store the
16074 # results in is created.
16075 #------------------------------------------------------------------------------
16076 sub set_up_output_directory
16078 my $subr_name = get_my_name ();
16080 my $error_code;
16081 my $msg;
16082 my $mkdir_output_msg;
16083 my $outputdir = "does_not_exist_yet";
16084 my $rm_output_msg;
16085 my $success;
16086 my $target_cmd;
16088 my $define_new_output_dir = $g_user_settings{"output"}{"defined"};
16089 my $overwrite_output_dir = $g_user_settings{"overwrite"}{"defined"};
16091 if ((not $define_new_output_dir) and (not $overwrite_output_dir))
16092 #------------------------------------------------------------------------------
16093 # If neither -o or -O are set, find the next number to be used in the name for
16094 # the default output directory.
16095 #------------------------------------------------------------------------------
16097 my $dir_id = 1;
16098 while (-d "display.".$dir_id.".html")
16099 { $dir_id++; }
16100 $outputdir = "display.".$dir_id.".html";
16102 elsif ($define_new_output_dir)
16103 #------------------------------------------------------------------------------
16104 # The output directory is defined with the -o option.
16105 #------------------------------------------------------------------------------
16107 $outputdir = $g_user_settings{"output"}{"current_value"};
16109 elsif ($overwrite_output_dir)
16110 #------------------------------------------------------------------------------
16111 # The output directory is defined with the -O option.
16112 #------------------------------------------------------------------------------
16114 $outputdir = $g_user_settings{"overwrite"}{"current_value"};
16117 #------------------------------------------------------------------------------
16118 # The name of the output directory is known and we can proceed.
16119 #------------------------------------------------------------------------------
16120 $msg = "the target output directory is $outputdir";
16121 gp_message ("debug", $subr_name, $msg);
16123 if (-d $outputdir)
16125 #------------------------------------------------------------------------------
16126 # The -o option is used, but the directory already exists.
16127 #------------------------------------------------------------------------------
16128 if ($define_new_output_dir)
16130 $msg = "directory $outputdir already exists";
16131 gp_message ("error", $subr_name, $msg);
16132 $msg = "use the -O/--overwite option to overwrite an";
16133 $msg .= " existing directory";
16134 gp_message ("error", $subr_name, $msg);
16136 $g_total_error_count++;
16138 gp_message ("abort", $subr_name, $g_abort_msg);
16141 elsif ($overwrite_output_dir)
16142 #------------------------------------------------------------------------------
16143 # It is a bit risky to remove this directory and so we proceed with caution.
16144 # What if the user decides to call it "*" e.g. "-O \*" for example? While this
16145 # should have been caught when processing the options, we still like to
16146 # be very cautious here before executing /bin/rm -rf.
16147 #------------------------------------------------------------------------------
16149 if ($outputdir eq "*")
16151 $msg = "it is not allowed to use * as a value for the -O option";
16152 gp_message ("error", $subr_name, $msg);
16154 $g_total_error_count++;
16156 gp_message ("abort", $subr_name, $g_abort_msg);
16158 else
16160 #------------------------------------------------------------------------------
16161 # The output directory exists, but it is okay to overwrite it. It is
16162 # removed here and created again below.
16163 #------------------------------------------------------------------------------
16164 $target_cmd = $g_mapped_cmds{"rm"} . " -rf " . $outputdir;
16165 ($error_code, $rm_output_msg) = execute_system_cmd ($target_cmd);
16167 if ($error_code != 0)
16169 $msg = "fatal error when trying to remove $outputdir";
16170 gp_message ("error", $subr_name, $rm_output_msg);
16171 gp_message ("error", $subr_name, $msg);
16173 $g_total_error_count++;
16175 gp_message ("abort", $subr_name, $g_abort_msg);
16177 else
16179 $msg = "directory $outputdir has been removed";
16180 gp_message ("debug", $subr_name, $msg);
16184 } #-- End of if-check for $outputdir
16186 #------------------------------------------------------------------------------
16187 # When we get here, the fatal scenarios have not occurred and the name for
16188 # $outputdir is known. Time to create it. Note that recursive creation is
16189 # supported and the user umask settings control the access permissions.
16190 #------------------------------------------------------------------------------
16191 $target_cmd = $g_mapped_cmds{"mkdir"} . " -p " . $outputdir;
16192 ($error_code, $mkdir_output_msg) = execute_system_cmd ($target_cmd);
16194 if ($error_code != 0)
16196 $msg = "a fatal problem occurred when creating directory $outputdir";
16197 gp_message ("error", $subr_name, $mkdir_output_msg);
16198 gp_message ("error", $subr_name, $msg);
16200 $g_total_error_count++;
16202 gp_message ("abort", $subr_name, $g_abort_msg);
16204 else
16206 $msg = "created output directory $outputdir";
16207 gp_message ("debug", $subr_name, $msg);
16210 return ($outputdir);
16212 } #-- End of subroutine set_up_output_directory
16214 #------------------------------------------------------------------------------
16215 # Split a line with function data into 3 components.
16216 #------------------------------------------------------------------------------
16217 sub split_function_data_line
16219 my $subr_name = get_my_name ();
16221 my ($input_line_ref) = @_;
16223 my $input_line = ${ $input_line_ref };
16225 my $decimal_separator = $g_locale_settings{"decimal_separator"};
16226 my $full_hex_address;
16227 my $function_name;
16228 my $hex_address;
16229 my $length_metric_list;
16230 my $length_remainder;
16231 my $length_target_string;
16232 my $list_with_metrics;
16233 my $marker;
16234 my $msg;
16235 my $reduced_line;
16236 my $remainder;
16238 my @hex_addresses = ();
16239 my @special_marker = ();
16240 my @the_function_name = ();
16242 my $find_hex_address_regex = '\s*(\d+:0x[a-fA-F0-9]+)\s+(.*)';
16243 my $find_marker_regex = '(^\*).*';
16244 my $find_metrics_1_regex = '\)*\ +([0-9,' . $decimal_separator;
16245 $find_metrics_1_regex .= '\ ]*$)';
16246 my $find_metrics_2_regex = '\)*\ +\[.+\]\s+([0-9,' . $decimal_separator;
16247 $find_metrics_2_regex = '\ ]*$)';
16248 my $get_hex_address_regex = '(\d+):0x(\S+)';
16250 $reduced_line = $input_line;
16252 if ($input_line =~ /$find_hex_address_regex/)
16254 if (defined ($1) )
16256 $full_hex_address = $1;
16257 $reduced_line =~ s/$full_hex_address//;
16259 $msg = "full_hex_address = " . $full_hex_address;
16260 gp_message ("debugXL", $subr_name, $msg);
16261 $msg = "reduced_line = " . $reduced_line;
16262 gp_message ("debugXL", $subr_name, $msg);
16264 if (defined ($2) )
16266 $remainder = $2;
16267 $msg = "remainder = " . $remainder;
16268 gp_message ("debugXL", $subr_name, $msg);
16270 if (($remainder =~ /$find_metrics_1_regex/) or
16271 ($remainder =~ /$find_metrics_2_regex/))
16273 if (defined ($1))
16275 $list_with_metrics = $1;
16276 $msg = "before list_with_metrics = " . $list_with_metrics;
16277 gp_message ("debugXL", $subr_name, $msg);
16279 $list_with_metrics =~ s/$g_rm_surrounding_spaces_regex//g;
16280 $msg = "after list_with_metrics = " . $list_with_metrics;
16281 gp_message ("debugXL", $subr_name, $msg);
16283 #------------------------------------------------------------------------------
16284 # Remove the function name from the string.
16285 #------------------------------------------------------------------------------
16286 $length_remainder = length ($remainder);
16287 $length_metric_list = length ($list_with_metrics);
16289 $msg = "length remainder = " . $length_remainder;
16290 gp_message ("debugXL", $subr_name, $msg);
16292 $msg = "length list_with_metrics = " . $length_metric_list;
16293 gp_message ("debugXL", $subr_name, $msg);
16295 $length_target_string = $length_remainder -
16296 $length_metric_list - 1;
16297 $function_name = substr ($remainder, 0,
16298 $length_target_string, '');
16300 $msg = "new function_name = " . $function_name;
16301 gp_message ("debugXL", $subr_name, $msg);
16303 $reduced_line = $function_name;
16304 $reduced_line =~ s/$g_rm_surrounding_spaces_regex//g;
16306 $msg = "reduced_line = " . $reduced_line;
16307 gp_message ("debugXL", $subr_name, $msg);
16309 #------------------------------------------------------------------------------
16310 # In some lines, the function name has a "*" prepended. Isolate this marker
16311 # and later on remove it from the function name.
16312 # TBD: Can probably be done more efficiently.
16313 #------------------------------------------------------------------------------
16314 if ($reduced_line =~ /$find_marker_regex/)
16316 if (defined ($1))
16318 $marker = $1;
16319 $msg = "found the marker = " . $marker;
16320 gp_message ("debugXL", $subr_name, $msg);
16322 else
16324 $msg = "first character in " . $reduced_line ;
16325 $msg .= " is not expected";
16326 gp_message ("assertion", $subr_name, $msg);
16329 else
16331 $marker = "X";
16334 else
16336 $msg = "failure to find metric values following the ";
16337 $msg .= "function name";
16338 gp_message ("assertion", $subr_name, $msg);
16341 else
16343 $msg = "cannot find metric values in remainder";
16344 gp_message ("debugXL", $subr_name, $msg);
16345 gp_message ("assertion", $subr_name, $msg);
16348 #------------------------------------------------------------------------------
16349 # We now have the 3 main objects from the input line. Next, they are processed
16350 # and stored.
16351 #------------------------------------------------------------------------------
16352 if ($full_hex_address =~ /$get_hex_address_regex/)
16354 if (defined ($1) and defined ($2))
16356 $hex_address = "0x" . $2;
16357 push (@hex_addresses, $full_hex_address);
16359 $msg = "pushed full_hex_address = " . $full_hex_address;
16360 gp_message ("debugXL", $subr_name, $msg);
16363 else
16365 $msg = "full_hex_address = $full_hex_address has an unknown format";
16366 gp_message ("assertion", $subr_name, $msg);
16368 if ($marker eq "*")
16370 push (@special_marker, "*");
16372 else
16374 push (@special_marker, "X");
16377 $reduced_line =~ s/^\*//;
16379 $msg = "RESULT full_hex_address = " . $full_hex_address;
16380 $msg .= " -- metric values = " . $list_with_metrics;
16381 $msg .= " -- marker = " . $marker;
16382 $msg .= " -- function name = " . $reduced_line;
16383 gp_message ("debugXL", $subr_name, $msg);
16386 return (\$full_hex_address, \$marker, \$reduced_line, \$list_with_metrics);
16388 } #-- End of subroutine split_function_data_line
16390 #------------------------------------------------------------------------------
16391 # Routine to generate webfriendly names
16392 #------------------------------------------------------------------------------
16393 sub tag_name
16395 my $subr_name = get_my_name ();
16397 my ($target_name) = @_;
16399 #------------------------------------------------------------------------------
16400 # Keeps track how many names have been tagged already.
16401 #------------------------------------------------------------------------------
16402 state $S_total_tagged_names = 0;
16404 my $msg;
16405 my $unique_name;
16407 gp_message ("debug", $subr_name, "target_name on entry = $target_name");
16409 #------------------------------------------------------------------------------
16410 # Undo conversion of < in to &lt;
16411 #------------------------------------------------------------------------------
16413 #------------------------------------------------------------------------------
16414 # TBD: Legacy - What is going on here and is this really needed?!
16415 # We need to replace the "<" symbol in the code by "&lt;".
16416 #------------------------------------------------------------------------------
16417 $target_name =~ s/$g_html_less_than_regex/$g_less_than_regex/g;
16419 #------------------------------------------------------------------------------
16420 # Remove inlining info
16421 #------------------------------------------------------------------------------
16422 $target_name =~ s/, instructions from source file.*//;
16424 if (defined $g_tagged_names{$target_name})
16426 $msg = "target_name = $target_name is already defined: ";
16427 $msg .= $g_tagged_names{$target_name};
16428 gp_message ("debug", $subr_name, $msg);
16430 $msg = "target_name on return = $target_name";
16431 gp_message ("debug", $subr_name, $msg);
16433 return ($g_tagged_names{$target_name});
16435 else
16437 $unique_name = "ftag".$S_total_tagged_names;
16438 $S_total_tagged_names++;
16439 $g_tagged_names{$target_name} = $unique_name;
16441 $msg = "target_name = $target_name is new and added: ";
16442 $msg .= "g_tagged_names{$target_name} = $g_tagged_names{$target_name}";
16443 gp_message ("debug", $subr_name, $msg);
16445 $msg = "target_name on return = $target_name";
16446 gp_message ("debug", $subr_name, $msg);
16448 return ($unique_name);
16451 } #-- End of subroutine tag_name
16453 #------------------------------------------------------------------------------
16454 # Generate a string to terminate the HTML document.
16455 #------------------------------------------------------------------------------
16456 sub terminate_html_document
16458 my $subr_name = get_my_name ();
16460 my $html_line;
16462 $html_line = "</body>\n";
16463 $html_line .= "</html>";
16465 return (\$html_line);
16467 } #-- End of subroutine terminate_html_document
16469 #------------------------------------------------------------------------------
16470 # Perform some basic checks to ensure the input data is consistent. This part
16471 # could be refined and expanded over time. For example by using a checksum
16472 # mechanism to verify the consistency of the executables.
16473 #------------------------------------------------------------------------------
16474 sub verify_consistency_experiments
16476 my $subr_name = get_my_name ();
16478 my ($exp_dir_list_ref) = @_;
16480 my @exp_dir_list = @{ $exp_dir_list_ref };
16482 my $executable_name;
16483 my $full_path_executable_name;
16484 my $msg;
16485 my $ref_executable_name;
16487 my $first_exp_dir = $TRUE;
16488 my $count_differences = 0;
16490 #------------------------------------------------------------------------------
16491 # Enforce that the full path names to the executable are the same. This could
16492 # be overkill and a checksum approach would be more flexible.
16493 #------------------------------------------------------------------------------
16494 for my $full_exp_dir (@exp_dir_list)
16496 my $exp_dir = get_basename ($full_exp_dir);
16497 gp_message ("debug", $subr_name, "exp_dir = $exp_dir");
16498 if ($first_exp_dir)
16500 $first_exp_dir = $FALSE;
16501 $ref_executable_name =
16502 $g_exp_dir_meta_data{$exp_dir}{"full_path_exec"};
16503 $msg = "ref_executable_name = " . $ref_executable_name;
16504 gp_message ("debug", $subr_name, $msg);
16505 next;
16507 $full_path_executable_name =
16508 $g_exp_dir_meta_data{$exp_dir}{"full_path_exec"};
16509 $msg = "full_path_executable_name = " . $full_path_executable_name;
16510 gp_message ("debug", $subr_name, $msg);
16512 if ($full_path_executable_name ne $ref_executable_name)
16514 $count_differences++;
16515 $msg = $full_path_executable_name . " does not match";
16516 $msg .= " " . $ref_executable_name;
16517 gp_message ("debug", $subr_name, $msg);
16521 $executable_name = get_basename ($ref_executable_name);
16523 return ($count_differences, $executable_name);
16525 } #-- End of subroutine verify_consistency_experiments
16527 #------------------------------------------------------------------------------
16528 # Check if the input item is valid for the data type specified. Validity is
16529 # verified in the context of gprofng. The definition for the metrics is a
16530 # good example of that.
16531 #------------------------------------------------------------------------------
16532 sub verify_if_input_is_valid
16534 my $subr_name = get_my_name ();
16536 my ($input_item, $data_type) = @_;
16538 my $msg;
16539 my $return_value = $FALSE;
16541 #------------------------------------------------------------------------------
16542 # These value are allowed to be case insensitive, so we convert to lower
16543 # case first.
16544 #------------------------------------------------------------------------------
16545 if (($data_type eq "onoff") or ($data_type eq "size"))
16547 $input_item = lc ($input_item);
16550 if ($data_type eq "metrics")
16551 #------------------------------------------------------------------------------
16552 # A gprofng metric definition. Either consists of "default" only, or starts
16553 # with e or i, followed by one or more from the set {.,%,!,+} and a keyword.
16554 # This pattern may be repeated with a ":" as the separator.
16555 #------------------------------------------------------------------------------
16557 my @metric_list = split (":", $input_item);
16559 #------------------------------------------------------------------------------
16560 # Check if the pattern is valid. If not, bail out and return $FALSE.
16561 #------------------------------------------------------------------------------
16562 for my $metric (@metric_list)
16564 if ($metric =~ /^default$|^[ei]*[\.%\!\+]+[a-z]*$/)
16566 $return_value = $TRUE;
16568 else
16570 $return_value = $FALSE;
16571 last;
16575 elsif ($data_type eq "metric_names")
16576 #------------------------------------------------------------------------------
16577 # A gprofng metric definition but without the flavour and visibility . Either
16578 # the name consists of "default" only, or a keyword with lowercase letters
16579 # only. This pattern may be repeated with a ":" as the separator.
16580 #------------------------------------------------------------------------------
16582 my @metric_list = split (":", $input_item);
16584 #------------------------------------------------------------------------------
16585 # Check if the pattern is valid. If not, bail out and return $FALSE.
16586 #------------------------------------------------------------------------------
16587 for my $metric (@metric_list)
16589 if ($metric =~ /^default$|^[a-z]*$/)
16591 $return_value = $TRUE;
16593 else
16595 $return_value = $FALSE;
16596 last;
16600 elsif ($data_type eq "path")
16601 #------------------------------------------------------------------------------
16602 # This can be almost anything, including "/" and "."
16603 #------------------------------------------------------------------------------
16605 if ($input_item =~ /^[\w\/\.\-]*$/)
16607 $return_value = $TRUE;
16610 elsif ($data_type eq "boolean")
16612 #------------------------------------------------------------------------------
16613 # This is TRUE (=1) or FALSE (0).
16614 #------------------------------------------------------------------------------
16615 if ($input_item =~ /^[01]$/)
16617 $return_value = $TRUE;
16620 elsif ($data_type eq "onoff")
16621 #------------------------------------------------------------------------------
16622 # This is either "on" OR "off".
16623 #------------------------------------------------------------------------------
16625 if ($input_item =~ /^on$|^off$/)
16627 $return_value = $TRUE;
16630 elsif ($data_type eq "size")
16631 #------------------------------------------------------------------------------
16632 # Supported values are "on", "off", "s", "m", "l", or "xl".
16633 #------------------------------------------------------------------------------
16635 if ($input_item =~ /^on$|^off$|^s$|^m$|^l$|^xl$/)
16637 $return_value = $TRUE;
16640 elsif ($data_type eq "pinteger")
16641 #------------------------------------------------------------------------------
16642 # This is a positive integer.
16643 #------------------------------------------------------------------------------
16645 if ($input_item =~ /^\d*$/)
16647 $return_value = $TRUE;
16650 elsif ($data_type eq "integer")
16651 #------------------------------------------------------------------------------
16652 # This is a positive or negative integer.
16653 #------------------------------------------------------------------------------
16655 if ($input_item =~ /^\-?\d*$/)
16657 $return_value = $TRUE;
16660 elsif ($data_type eq "pfloat")
16661 #------------------------------------------------------------------------------
16662 # This is a positive floating point number, but we accept a positive integer
16663 # number as well.
16665 # TBD: Note that we use the "." here. Maybe should support a "," too.
16666 #------------------------------------------------------------------------------
16668 if (($input_item =~ /^\d*\.\d*$/) or ($input_item =~ /^\d*$/))
16670 $return_value = $TRUE;
16673 elsif ($data_type eq "float")
16674 #------------------------------------------------------------------------------
16675 # This is a positive or negative floating point number, but we accept an
16676 # integer number as well.
16678 # TBD: Note that we use the "." here. Maybe should support a "," too.
16679 #------------------------------------------------------------------------------
16681 if (($input_item =~ /^\-?\d*\.\d*$/) or ($input_item =~ /^\-?\d*$/))
16683 $return_value = $TRUE;
16686 else
16688 $msg = "the $data_type data type for input $input_item is not supported";
16689 gp_message ("assertion", $subr_name, $msg);
16692 return ($return_value);
16694 } #-- End of subroutine verify_if_input_is_valid
16696 #------------------------------------------------------------------------------
16697 # Scan the leftovers in ARGV. Other than the option generated by the driver,
16698 # this list should be empty. Anything left here is considered to be a fatal
16699 # error and pushed into the g_error_msgs buffer.
16701 # We use two different arrays for the errors found. This allows us to group
16702 # the same type of errors.
16703 #------------------------------------------------------------------------------
16704 sub wrap_up_user_options
16706 my $subr_name = get_my_name ();
16708 my @opt_unsupported = ();
16709 my @opt_ignored = ();
16711 my $current_option;
16712 my $driver_inserted = "--whoami=gprofng display html";
16713 my $ignore_option;
16714 my $msg;
16715 my $option_delimiter = "--";
16717 if (@ARGV)
16719 $msg = "items in ARGV: " . join (" ", @ARGV);
16720 gp_message ("debugXL", $subr_name, $msg);
16722 $ignore_option = $FALSE;
16723 for my $i (keys @ARGV)
16725 $current_option = $ARGV[$i];
16727 $msg = "ARGV[$i] = $current_option";
16729 if ($current_option eq $option_delimiter)
16730 #------------------------------------------------------------------------------
16731 # The user may use a feature of GetOptions to delimit the options. After
16732 # this, only experiment names are allowed and these have been handled already,
16733 # so anything found after this delimite is an error.
16735 # This is why we set a flag if the delimiter has been found.
16736 #------------------------------------------------------------------------------
16738 $ignore_option = $TRUE;
16739 gp_message ("debugXL", $subr_name, $msg . " (option delimiter)");
16741 elsif ($ignore_option)
16742 #------------------------------------------------------------------------------
16743 # We have seen the delimiter, but there are still options, or other strings.
16744 # In any case, it is not allowed.
16745 #------------------------------------------------------------------------------
16747 push (@opt_ignored, $current_option);
16748 gp_message ("debugXL", $subr_name, $msg . " (ignored)");
16750 elsif ($current_option ne $driver_inserted)
16751 #------------------------------------------------------------------------------
16752 # The gprofng driver inserts this and it should be ignored. This is why we
16753 # only recorded those options different than the one inserted by the driver.
16754 #------------------------------------------------------------------------------
16756 push (@opt_unsupported, $current_option);
16757 gp_message ("debugXL", $subr_name, $msg . " (unsupported)");
16759 else
16760 #------------------------------------------------------------------------------
16761 # The gprofng driver inserts this option and it should be ignored.
16762 #------------------------------------------------------------------------------
16764 gp_message ("debugXL", $subr_name, $msg .
16765 " (driver inserted and ignored)");
16770 #------------------------------------------------------------------------------
16771 # Store any illegal input in the g_error_msgs buffer.
16772 #------------------------------------------------------------------------------
16773 if (@opt_ignored)
16775 $msg = "the following input is out of place:";
16776 for my $i (keys @opt_ignored)
16778 $msg .= " " . $opt_ignored[$i];
16780 gp_message ("error", $subr_name, $msg);
16782 $g_total_error_count++;
16784 if (@opt_unsupported)
16786 $msg = "the following items in the input are not supported:";
16787 for my $i (keys @opt_unsupported)
16789 $msg .= " " . $opt_unsupported[$i];
16791 gp_message ("error", $subr_name, $msg);
16793 $msg = "perhaps an error in the option name, or an option value";
16794 $msg .= " is missing?";
16795 gp_message ("error", $subr_name, $msg);
16797 $g_total_error_count++;
16800 return (0);
16802 } #-- End of subroutine wrap_up_user_options