Files
mercury/profiler/process_file.m
2025-01-24 13:38:10 +11:00

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.
%---------------------------------------------------------------------------%