mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
377 lines
15 KiB
Mathematica
377 lines
15 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1995-1997,2000, 2004-2006, 2011 The University of Melbourne.
|
|
% Copyright (C) 2015, 2021, 2024-2025 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: process_file.m
|
|
% Main author: petdr.
|
|
%
|
|
% Process the files that contain the label declarations, label counts and
|
|
% the caller-callee pairs, also builds the dynamic call graph if the option
|
|
% set.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module process_file.
|
|
:- interface.
|
|
|
|
:- import_module prof_info.
|
|
|
|
:- import_module digraph.
|
|
:- import_module io.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred process_profiling_data_files(io.text_output_stream::in,
|
|
io.text_output_stream::in, prof::out, digraph(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module globals.
|
|
:- import_module options.
|
|
:- import_module read.
|
|
|
|
:- import_module bool.
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module unit.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
process_profiling_data_files(ProgressStream, ErrorStream,
|
|
Prof, DynamicCallGraph, !IO) :-
|
|
globals.io_lookup_bool_option(very_verbose, VVerbose, !IO),
|
|
globals.io_lookup_string_option(declfile, DeclFile, !IO),
|
|
globals.io_lookup_string_option(countfile, CountFile, !IO),
|
|
globals.io_lookup_string_option(pairfile, PairFile, !IO),
|
|
% globals.io_lookup_string_option(libraryfile, LibFile, !IO),
|
|
globals.io_lookup_bool_option(dynamic_cg, Dynamic, !IO),
|
|
|
|
% process the decl file
|
|
maybe_write_string(ProgressStream, VVerbose, "\n\t% Processing ", !IO),
|
|
maybe_write_string(ProgressStream, VVerbose, DeclFile, !IO),
|
|
maybe_write_string(ProgressStream, VVerbose, "...", !IO),
|
|
process_addr_decl(AddrDeclMap0, ProfNodeMap0, !IO),
|
|
maybe_write_string(ProgressStream, VVerbose, " done.\n", !IO),
|
|
|
|
% process the timing counts file
|
|
maybe_write_string(ProgressStream, VVerbose, "\t% Processing ", !IO),
|
|
maybe_write_string(ProgressStream, VVerbose, CountFile, !IO),
|
|
maybe_write_string(ProgressStream, VVerbose, "...", !IO),
|
|
process_addr(ErrorStream, WhatToProfile, Scale, Units, TotalCounts,
|
|
ProfNodeMap0, ProfNodeMap1, !IO),
|
|
maybe_write_string(ProgressStream, VVerbose, " done.\n", !IO),
|
|
|
|
% Process the call pair counts file.
|
|
maybe_write_string(ProgressStream, VVerbose, "\t% Processing ", !IO),
|
|
maybe_write_string(ProgressStream, VVerbose, PairFile, !IO),
|
|
maybe_write_string(ProgressStream, VVerbose, "...", !IO),
|
|
process_addr_pair(DynamicCallGraph,
|
|
ProfNodeMap1, ProfNodeMap, AddrDeclMap0, AddrDeclMap, !IO),
|
|
maybe_write_string(ProgressStream, VVerbose, " done.\n", !IO),
|
|
|
|
map.init(CycleMap),
|
|
prof_set_entire(Scale, Units, TotalCounts, AddrDeclMap,
|
|
ProfNodeMap, CycleMap, Prof),
|
|
globals.io_get_globals(Globals0, !IO),
|
|
globals.set_what_to_profile(WhatToProfile, Globals0, Globals),
|
|
globals.io_set_globals(Globals, !IO),
|
|
|
|
(
|
|
Dynamic = no
|
|
% maybe_write_string(ProgressStream, VVerbose, "\t% Processing "),
|
|
% maybe_write_string(ProgressStream, VVerbose, LibFile),
|
|
% maybe_write_string(ProgressStream, VVerbose, "..."),
|
|
% process_library_callgraph(_, _),
|
|
% maybe_write_string(ProgressStream, VVerbose, " done.\n"),
|
|
;
|
|
Dynamic = yes
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% process_addr_decl(AddrDeclMap, ProfNodeMap, !IO):
|
|
%
|
|
% Reads in the Prof.Decl file.
|
|
% Builds the addrdecl map which associates label names(key)
|
|
% with label addresses.
|
|
% Also builds the prof_node_map which associates label addresses
|
|
% with the prof_node structure. Initialises and inserts the label name
|
|
% into the structure at the same time.
|
|
%
|
|
:- pred process_addr_decl(addrdecl::out, prof_node_map::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_addr_decl(AddrDeclMap, ProfNodeMap, !IO) :-
|
|
globals.io_lookup_string_option(declfile, DeclFile, !IO),
|
|
io.open_input(DeclFile, DeclResult, !IO),
|
|
(
|
|
DeclResult = ok(DeclFileStream),
|
|
process_addr_decl_2(DeclFileStream,
|
|
map.init, AddrDeclMap, map.init, ProfNodeMap, !IO),
|
|
io.close_input(DeclFileStream, !IO)
|
|
;
|
|
DeclResult = error(Error),
|
|
ErrorStr = "error opening declaration file `" ++ DeclFile ++
|
|
"': " ++ io.error_message(Error) ++ "\n",
|
|
error(ErrorStr)
|
|
).
|
|
|
|
:- pred process_addr_decl_2(io.text_input_stream::in,
|
|
addrdecl::in, addrdecl::out, prof_node_map::in, prof_node_map::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_addr_decl_2(InputStream, !AddrDecl, !ProfNodeMap, !IO) :-
|
|
maybe_read_label_addr(InputStream, MaybeLabelAddr, !IO),
|
|
(
|
|
MaybeLabelAddr = yes(LabelAddr),
|
|
read_label_name(InputStream, LabelName, !IO),
|
|
ProfNode = prof_node_init(LabelName),
|
|
map.det_insert(LabelName, LabelAddr, !AddrDecl),
|
|
|
|
% Labels with different names but the same addresses.
|
|
( if map.insert(LabelAddr, ProfNode, !ProfNodeMap) then
|
|
true
|
|
else
|
|
lookup_addr(LabelAddr, ProfNode0, !AddrDecl, !ProfNodeMap),
|
|
prof_node_concat_to_name_list(LabelName, ProfNode0, NewProfNode),
|
|
map.det_update(LabelAddr, NewProfNode, !ProfNodeMap)
|
|
),
|
|
process_addr_decl_2(InputStream, !AddrDecl, !ProfNodeMap, !IO)
|
|
;
|
|
MaybeLabelAddr = no
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% process_addr(!ProfNodeMap, WhatToProfile, Scale, Units, TotalCounts,
|
|
% !IO):
|
|
%
|
|
% Reads in the Prof.Counts file and stores all the counts in the prof_node
|
|
% structure. Also sums the total counts at the same time.
|
|
%
|
|
:- pred process_addr(io.text_output_stream::in,
|
|
what_to_profile::out, float::out, string::out, int::out,
|
|
prof_node_map::in, prof_node_map::out, io::di, io::uo) is det.
|
|
|
|
process_addr(ErrorStream, WhatToProfile, Scale, Units, TotalCounts,
|
|
!ProfNodeMap, !IO) :-
|
|
globals.io_lookup_string_option(countfile, CountFile, !IO),
|
|
io.open_input(CountFile, CountResult, !IO),
|
|
(
|
|
CountResult = ok(CountFileStream),
|
|
read_what_to_profile(CountFileStream, WhatToProfile, !IO),
|
|
read_float(CountFileStream, Scale, !IO),
|
|
read_string(CountFileStream, Units, !IO),
|
|
process_addr_2(CountFileStream, ErrorStream,
|
|
0, TotalCounts, !ProfNodeMap, !IO),
|
|
io.close_input(CountFileStream, !IO)
|
|
;
|
|
CountResult = error(Error),
|
|
io.error_message(Error, ErrorMsg),
|
|
io.format(ErrorStream, "\nWarning: error opening `%s': %s\n",
|
|
[s(CountFile), s(ErrorMsg)], !IO),
|
|
io.write_string(ErrorStream,
|
|
"The generated profile will only include call counts.\n\n", !IO),
|
|
TotalCounts = 0,
|
|
% We can use any arbitrary values for WhatToProfile and Scale;
|
|
% the values specified here won't be used,
|
|
% since all the times will be zero.
|
|
WhatToProfile = user_plus_system_time,
|
|
Scale = 1.0,
|
|
Units = ""
|
|
).
|
|
|
|
:- pred process_addr_2(io.text_input_stream::in, io.text_output_stream::in,
|
|
int::in, int::out, prof_node_map::in, prof_node_map::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_addr_2(InputStream, ErrorStream, !TotalCounts, !ProfNodeMap, !IO) :-
|
|
maybe_read_label_addr(InputStream, MaybeLabelAddr, !IO),
|
|
(
|
|
MaybeLabelAddr = yes(LabelAddr),
|
|
read_int(InputStream, Count, !IO),
|
|
|
|
% Add to initial counts if we have a ProfNode structure
|
|
% for the address otherwise ignore it.
|
|
( if map.search(!.ProfNodeMap, LabelAddr, ProfNode0) then
|
|
prof_node_get_initial_counts(ProfNode0, InitCount0),
|
|
InitCount = InitCount0 + Count,
|
|
prof_node_set_initial_counts(InitCount, ProfNode0, ProfNode),
|
|
map.set(LabelAddr, ProfNode, !ProfNodeMap),
|
|
!:TotalCounts = !.TotalCounts + Count
|
|
else
|
|
io.format(ErrorStream,
|
|
"\nWarning address %d not found! " ++
|
|
"Ignoring address and continuing computation.\n",
|
|
[i(LabelAddr)], !IO)
|
|
),
|
|
process_addr_2(InputStream, ErrorStream,
|
|
!TotalCounts, !ProfNodeMap, !IO)
|
|
;
|
|
MaybeLabelAddr = no
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% process_addr_pair(!ProfNodeMap, !AddrDecl, DynamicCallGraph, !IO):
|
|
%
|
|
% Reads in the Prof.CallPair file and stores the data in the relevant
|
|
% lists of the prof_node structure. Also calculates the number of
|
|
% times a predicate is called.
|
|
%
|
|
:- pred process_addr_pair(digraph(string)::out,
|
|
prof_node_map::in, prof_node_map::out, addrdecl::in, addrdecl::out,
|
|
io::di, io::uo) is det.
|
|
|
|
process_addr_pair(DynamicCallGraph, !ProfNodeMap, !AddrDecl, !IO) :-
|
|
globals.io_lookup_bool_option(dynamic_cg, Dynamic, !IO),
|
|
globals.io_lookup_string_option(pairfile, PairFile, !IO),
|
|
io.open_input(PairFile, Result, !IO),
|
|
(
|
|
Result = ok(PairFileStream),
|
|
process_addr_pair_2(PairFileStream, Dynamic,
|
|
digraph.init, DynamicCallGraph, !ProfNodeMap, !AddrDecl, !IO),
|
|
io.close_input(PairFileStream, !IO)
|
|
;
|
|
Result = error(Error),
|
|
ErrorStr = "error opening pair file `" ++ PairFile ++
|
|
"': " ++ io.error_message(Error) ++ "\n",
|
|
error(ErrorStr)
|
|
).
|
|
|
|
:- pred process_addr_pair_2(io.text_input_stream::in, bool::in,
|
|
digraph(string)::in, digraph(string)::out,
|
|
prof_node_map::in, prof_node_map::out,
|
|
addrdecl::in, addrdecl::out, io::di, io::uo) is det.
|
|
|
|
process_addr_pair_2(InputStream, Dynamic, !DynamicCallGraph,
|
|
!ProfNodeMap, !AddrDecl, !IO) :-
|
|
maybe_read_label_addr(InputStream, MaybeLabelAddr, !IO),
|
|
(
|
|
MaybeLabelAddr = yes(CallerAddr),
|
|
read_label_addr(InputStream, CalleeAddr, !IO),
|
|
read_int(InputStream, Count, !IO),
|
|
|
|
% Get child and parent information.
|
|
lookup_addr(CallerAddr, CallerProfNode0, !AddrDecl, !ProfNodeMap),
|
|
lookup_addr(CalleeAddr, CalleeProfNode0, !AddrDecl, !ProfNodeMap),
|
|
prof_node_get_pred_name(CallerProfNode0, CallerName),
|
|
prof_node_get_pred_name(CalleeProfNode0, CalleeName),
|
|
|
|
% Insert child information.
|
|
prof_node_concat_to_child(CalleeName, Count, CallerProfNode0,
|
|
CallerProfNode),
|
|
map.set(CallerAddr, CallerProfNode, !ProfNodeMap),
|
|
|
|
% Update the total calls field if not self recursive.
|
|
( if CalleeAddr = CallerAddr then
|
|
prof_node_set_self_calls(Count, CalleeProfNode0, CalleeProfNode)
|
|
else
|
|
prof_node_get_total_calls(CalleeProfNode0, TotalCalls0),
|
|
TotalCalls = TotalCalls0 + Count,
|
|
prof_node_set_total_calls(TotalCalls, CalleeProfNode0,
|
|
CalleeProfNode1),
|
|
prof_node_concat_to_parent(CallerName, Count,
|
|
CalleeProfNode1, CalleeProfNode)
|
|
),
|
|
|
|
% Insert parent information.
|
|
map.set(CalleeAddr, CalleeProfNode, !ProfNodeMap),
|
|
|
|
% Add edge to call graph if generating dynamic call graph.
|
|
(
|
|
Dynamic = yes,
|
|
digraph.add_vertex(CallerName, CallerKey, !DynamicCallGraph),
|
|
digraph.add_vertex(CalleeName, CalleeKey, !DynamicCallGraph),
|
|
digraph.add_edge(CallerKey, CalleeKey, !DynamicCallGraph)
|
|
;
|
|
Dynamic = no
|
|
),
|
|
process_addr_pair_2(InputStream, Dynamic, !DynamicCallGraph,
|
|
!ProfNodeMap, !AddrDecl, !IO)
|
|
;
|
|
MaybeLabelAddr = no
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% process_library_callgraph(LibraryATSort, LibPredMap, !IO):
|
|
%
|
|
% XXX Either use and document this predicate, or delete it.
|
|
%
|
|
:- pred process_library_callgraph(list(string)::out, map(string, unit)::out,
|
|
io::di, io::uo) is det.
|
|
:- pragma consider_used(pred(process_library_callgraph/4)).
|
|
|
|
process_library_callgraph(LibraryATSort, LibPredMap, !IO) :-
|
|
globals.io_lookup_string_option(libraryfile, LibFile, !IO),
|
|
LibraryATSort0 = [],
|
|
map.init(LibPredMap0),
|
|
io.open_input(LibFile, Result, !IO),
|
|
(
|
|
Result = ok(LibFileStream),
|
|
process_library_callgraph_2(LibFileStream,
|
|
LibraryATSort0, LibraryATSort, LibPredMap0, LibPredMap, !IO),
|
|
io.close_input(LibFileStream, !IO)
|
|
;
|
|
Result = error(Error),
|
|
io.error_message(Error, ErrorMsg),
|
|
io.stderr_stream(StdErr, !IO),
|
|
io.format(StdErr, "mprof: error opening pair file `%s': %s\n",
|
|
[s(LibFile), s(ErrorMsg)], !IO),
|
|
LibraryATSort = LibraryATSort0,
|
|
LibPredMap = LibPredMap0
|
|
).
|
|
|
|
:- pred process_library_callgraph_2(io.text_input_stream::in,
|
|
list(string)::in, list(string)::out,
|
|
map(string, unit)::in, map(string, unit)::out, io::di, io::uo) is det.
|
|
|
|
process_library_callgraph_2(InputStream, !LibATSort, !LibPredMap, !IO) :-
|
|
maybe_read_label_name(InputStream, MaybeLabelName, !IO),
|
|
(
|
|
MaybeLabelName = yes(LabelName),
|
|
map.det_insert(LabelName, unit, !LibPredMap),
|
|
list.cons(LabelName, !LibATSort),
|
|
process_library_callgraph_2(InputStream, !LibATSort, !LibPredMap, !IO)
|
|
;
|
|
MaybeLabelName = no
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Attempt to lookup the addr in the prof_node_map, if it does not exist
|
|
% then record the name as unknown__<address> in the relevant data
|
|
% structures.
|
|
%
|
|
:- pred lookup_addr(int::in, prof_node::out, addrdecl::in, addrdecl::out,
|
|
prof_node_map::in, prof_node_map::out) is det.
|
|
|
|
lookup_addr(Addr, ProfNode, !AddrDeclMap, !ProfNodeMap) :-
|
|
( if map.search(!.ProfNodeMap, Addr, ProfNode0) then
|
|
ProfNode = ProfNode0
|
|
else
|
|
Str = string.format("unknown__%d", [i(Addr)]),
|
|
ProfNode = prof_node_init(Str),
|
|
map.det_insert(Addr, ProfNode, !ProfNodeMap),
|
|
map.det_insert(Str, Addr, !AddrDeclMap)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module process_file.
|
|
%---------------------------------------------------------------------------%
|