mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
deep_profiler/var_use_analysis.m:
Pass never-varying and slower-varying input arguments first.
Consistently pass goal lists before the position of the first goal
in the overall list.
browser/declarative_analyser.m:
browser/declarative_edt.m:
deep_profiler/analysis_utils.m:
deep_profiler/autopar_reports.m:
deep_profiler/autopar_types.m:
deep_profiler/program_representation_utils.m:
deep_profiler/read_profile.m:
deep_profiler/recursion_patterns.m:
deep_profiler/timeout.m:
deep_profiler/top_procs.m:
deep_profiler/util.m:
Minor improvements in programming style.
1591 lines
58 KiB
Mathematica
1591 lines
58 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2001, 2004-2012 The University of Melbourne.
|
|
% Copyright (C) 2013, 2015-2017, 2021-2022, 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: read_profile.m.
|
|
% Authors: conway, zs.
|
|
%
|
|
% This module contains code for reading in a deep profiling data file.
|
|
% Such files, named Deep.data, are created by deep profiled executables.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module read_profile.
|
|
:- interface.
|
|
|
|
:- import_module profile.
|
|
|
|
:- import_module io.
|
|
:- import_module maybe.
|
|
|
|
:- pred read_call_graph(string::in, maybe_error(initial_deep)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module array_util.
|
|
:- import_module io_combinator.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.goal_path.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.program_representation.
|
|
:- import_module measurements.
|
|
|
|
:- import_module array.
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module char.
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type maybe_error2(T1, T2)
|
|
---> ok2(T1, T2)
|
|
; error2(string).
|
|
|
|
:- type ptr_kind
|
|
---> ps
|
|
; pd
|
|
; css
|
|
; csd.
|
|
|
|
read_call_graph(FileName, MaybeInitDeep, !IO) :-
|
|
io.open_binary_input(FileName, OpenResult, !IO),
|
|
(
|
|
OpenResult = ok(FileStream),
|
|
read_deep_id_string(FileStream, MaybeAcceptableVersion, !IO),
|
|
(
|
|
MaybeAcceptableVersion = ok(AcceptableVersion),
|
|
% In the future, we could use different code to read in
|
|
% profiling data files with different version numbers.
|
|
io_combinator.maybe_error_sequence_11(
|
|
read_string(FileStream),
|
|
read_fixed_size_int(FileStream),
|
|
read_fixed_size_int(FileStream),
|
|
read_fixed_size_int(FileStream),
|
|
read_fixed_size_int(FileStream),
|
|
read_fixed_size_int(FileStream),
|
|
read_num(FileStream),
|
|
read_num(FileStream),
|
|
read_num(FileStream),
|
|
read_num(FileStream),
|
|
read_ptr(FileStream, pd),
|
|
maybe_init_deep,
|
|
MaybeInitDeepHeader, !IO),
|
|
(
|
|
MaybeInitDeepHeader = ok(InitDeep),
|
|
% When we implement compression of data files, we would
|
|
% want to pipe the rest of the input stream through the
|
|
% decompression mechanism.
|
|
read_nodes_outer_loop(FileStream, AcceptableVersion,
|
|
InitDeep, MaybeInitDeep, !IO)
|
|
;
|
|
MaybeInitDeepHeader = error(Error),
|
|
MaybeInitDeep = error(Error)
|
|
)
|
|
;
|
|
MaybeAcceptableVersion = error(Msg),
|
|
MaybeInitDeep = error(Msg)
|
|
),
|
|
io.close_binary_input(FileStream, !IO)
|
|
;
|
|
OpenResult = error(Error),
|
|
io.error_message(Error, Msg),
|
|
MaybeInitDeep = error(Msg)
|
|
).
|
|
|
|
:- type acceptable_version
|
|
---> av_8
|
|
; av_9.
|
|
|
|
:- pred read_deep_id_string(io.binary_input_stream::in,
|
|
maybe_error(acceptable_version)::out, io::di, io::uo) is det.
|
|
|
|
read_deep_id_string(InputStream, MaybeAcceptableVersion, !IO) :-
|
|
% The 10 extra chars should be ample for the version number and newline.
|
|
FirstLineLenLimit = string.length(deep_id_prefix) + 10,
|
|
read_line(InputStream, FirstLineLenLimit, MaybeLine, !IO),
|
|
(
|
|
MaybeLine = ok(Line0),
|
|
Line = string.chomp(Line0),
|
|
( if
|
|
string.append(deep_id_prefix, Suffix, Line),
|
|
string.to_int(Suffix, VersionNumber)
|
|
then
|
|
( if acceptable_version(VersionNumber, AcceptableVersion) then
|
|
MaybeAcceptableVersion = ok(AcceptableVersion)
|
|
else
|
|
MaybeAcceptableVersion = error("version number mismatch")
|
|
)
|
|
else
|
|
MaybeAcceptableVersion = error("not a deep profiling data file")
|
|
)
|
|
;
|
|
MaybeLine = error(Error),
|
|
MaybeAcceptableVersion = error(Error)
|
|
).
|
|
|
|
% Return the constant prefix of the string identifying a file
|
|
% as a deep profiling data file. The first line of a profiling data file
|
|
% should contain this prefix and the version number of format used by the
|
|
% file.
|
|
%
|
|
% This string must match the string written by the function
|
|
% MR_write_out_deep_id_string in runtime/mercury_deep_profiling.c.
|
|
%
|
|
:- func deep_id_prefix = string.
|
|
|
|
deep_id_prefix = "Mercury deep profiler data version ".
|
|
|
|
:- pred acceptable_version(int::in, acceptable_version::out) is semidet.
|
|
|
|
acceptable_version(8, av_8).
|
|
acceptable_version(9, av_9).
|
|
|
|
:- pred maybe_init_deep(string::in, int::in, int::in, int::in, int::in,
|
|
int::in, int::in, int::in, int::in, int::in, int::in,
|
|
maybe_error(initial_deep)::out) is det.
|
|
|
|
maybe_init_deep(ProgName, FlagsInt, MaxCSD, MaxCSS, MaxPD, MaxPS, TicksPerSec,
|
|
InstrumentQuanta, UserQuanta, NumCallSeqs, RootPDI, MaybeInitDeep) :-
|
|
maybe_deep_flags(FlagsInt, MaybeFlags),
|
|
(
|
|
MaybeFlags = ok(Flags),
|
|
InitStats = profile_stats(ProgName, MaxCSD, MaxCSS, MaxPD, MaxPS,
|
|
TicksPerSec, InstrumentQuanta, UserQuanta, NumCallSeqs, Flags),
|
|
InitDeep = initial_deep(
|
|
InitStats,
|
|
make_pdptr(RootPDI),
|
|
array.init(MaxCSD + 1,
|
|
call_site_dynamic(
|
|
make_dummy_pdptr,
|
|
make_dummy_pdptr,
|
|
zero_own_prof_info
|
|
)),
|
|
array.init(MaxPD + 1, proc_dynamic(make_dummy_psptr, array([]),
|
|
no)),
|
|
array.init(MaxCSS + 1,
|
|
call_site_static(
|
|
make_dummy_psptr, -1,
|
|
normal_call_and_callee(make_dummy_psptr, ""),
|
|
"", -1, rgp_nil
|
|
)),
|
|
array.init(MaxPS + 1,
|
|
proc_static(dummy_proc_id, "", "", "", "", "", -1, no,
|
|
array([]), array([]), no, not_zeroed))
|
|
),
|
|
MaybeInitDeep = ok(InitDeep)
|
|
;
|
|
MaybeFlags = error(Error),
|
|
MaybeInitDeep = error(Error)
|
|
).
|
|
|
|
:- pred maybe_deep_flags(int::in, maybe_error(deep_flags)::out) is det.
|
|
|
|
maybe_deep_flags(FlagsInt, MaybeFlags) :-
|
|
BytesPerInt = (FlagsInt /\ deep_flag_bytes_per_int_mask)
|
|
>> deep_flag_bytes_per_int_shift,
|
|
Canonical = (FlagsInt /\ deep_flag_canonical_mask)
|
|
>> deep_flag_canonical_shift,
|
|
Compression = (FlagsInt /\ deep_flag_compression_mask)
|
|
>> deep_flag_compression_shift,
|
|
Coverage = (FlagsInt /\ deep_flag_coverage_mask)
|
|
>> deep_flag_coverage_shift,
|
|
( if
|
|
(
|
|
Canonical = 0,
|
|
CanonicalFlag = maybe_not_canonical
|
|
;
|
|
Canonical = 1,
|
|
CanonicalFlag = is_canonical
|
|
),
|
|
(
|
|
Compression = 0,
|
|
CompressionFlag = no_compression
|
|
% There is no compression alternative yet
|
|
),
|
|
(
|
|
Coverage = 0,
|
|
CoverageFlag = no_coverage_data
|
|
;
|
|
Coverage = 1,
|
|
CoverageFlag = static_coverage_data
|
|
;
|
|
Coverage = 2,
|
|
CoverageFlag = dynamic_coverage_data
|
|
),
|
|
0 = ((\ deep_flag_all_fields_mask) /\ FlagsInt)
|
|
then
|
|
MaybeFlags = ok(deep_flags(BytesPerInt, CanonicalFlag, CompressionFlag,
|
|
CoverageFlag))
|
|
else
|
|
MaybeFlags = error(
|
|
string.format("Error parsing flags in file header, flags are 0x%x",
|
|
[i(FlagsInt)]))
|
|
).
|
|
|
|
% Flags masks and shifts.
|
|
% The following line provides a ruler to line up the hexadecimal values
|
|
% with.
|
|
%
|
|
% 48 32 16 0
|
|
%
|
|
% Bytes_per_int occupies the bottom 8 bits, bit 0 to 7.
|
|
% The canonical flag occupies the next 2 bits, bit 8 to 9.
|
|
% The compression flag occupies the next 2 bits, bit 10 to 11.
|
|
% The coverage indication occupies the next 2 bits, bit 12 to 13.
|
|
%
|
|
% Some of these have more bits than they currently need, in order to
|
|
% accommodate possible future growth.
|
|
|
|
:- func deep_flag_bytes_per_int_mask = int.
|
|
deep_flag_bytes_per_int_mask = 0x00000000000000FF.
|
|
:- func deep_flag_bytes_per_int_shift = int.
|
|
deep_flag_bytes_per_int_shift = 0.
|
|
|
|
:- func deep_flag_canonical_mask = int.
|
|
deep_flag_canonical_mask = 0x0000000000000300.
|
|
:- func deep_flag_canonical_shift = int.
|
|
deep_flag_canonical_shift = 8.
|
|
|
|
:- func deep_flag_compression_mask = int.
|
|
deep_flag_compression_mask = 0x0000000000000C00.
|
|
:- func deep_flag_compression_shift = int.
|
|
deep_flag_compression_shift = 10.
|
|
|
|
:- func deep_flag_coverage_mask = int.
|
|
deep_flag_coverage_mask = 0x0000000000003000.
|
|
:- func deep_flag_coverage_shift = int.
|
|
deep_flag_coverage_shift = 12.
|
|
|
|
:- func deep_flag_all_fields_mask = int.
|
|
deep_flag_all_fields_mask =
|
|
deep_flag_bytes_per_int_mask \/
|
|
deep_flag_canonical_mask \/
|
|
deep_flag_compression_mask \/
|
|
deep_flag_coverage_mask.
|
|
|
|
:- pred read_nodes_outer_loop(io.binary_input_stream::in,
|
|
acceptable_version::in, initial_deep::in, maybe_error(initial_deep)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_nodes_outer_loop(InputStream, Version, InitDeep0, MaybeInitDeep, !IO) :-
|
|
% We use two nested loops to ensure that this code works even in grades
|
|
% that lack tail recursion, such as debugging grades. read_nodes_inner_loop
|
|
% will return after it has processed 50000 nodes, unwinding its stack.
|
|
% This outer loop predicate will continue as long as read_nodes_inner_loop
|
|
% thinks that more work remains.
|
|
%
|
|
% The value of 50,000 has been chosen as it is roughly less than half the
|
|
% stack depth that causes crashes during debugging.
|
|
read_nodes_inner_loop(InputStream, Version, 50000,
|
|
InitDeep0, MaybeInitDeep0, !IO),
|
|
(
|
|
MaybeInitDeep0 = init_deep_complete(InitDeep),
|
|
MaybeInitDeep = ok(InitDeep)
|
|
;
|
|
MaybeInitDeep0 = error(Error),
|
|
MaybeInitDeep = error(Error)
|
|
;
|
|
MaybeInitDeep0 = init_deep_incomplete(InitDeep1),
|
|
read_nodes_outer_loop(InputStream, Version,
|
|
InitDeep1, MaybeInitDeep, !IO)
|
|
).
|
|
|
|
:- type maybe_init_deep_complete
|
|
---> init_deep_complete(initial_deep)
|
|
; init_deep_incomplete(initial_deep)
|
|
; error(string).
|
|
|
|
:- pred read_nodes_inner_loop(io.binary_input_stream::in,
|
|
acceptable_version::in, int::in,
|
|
initial_deep::in, maybe_init_deep_complete::out, io::di, io::uo) is det.
|
|
|
|
read_nodes_inner_loop(InputStream, Version, !.NumLeft,
|
|
!.InitDeep, MaybeInitDeep, !IO) :-
|
|
( if !.NumLeft < 1 then
|
|
MaybeInitDeep = init_deep_incomplete(!.InitDeep)
|
|
else
|
|
!:NumLeft = !.NumLeft - 1,
|
|
ProfileStats = !.InitDeep ^ init_profile_stats,
|
|
read_byte(InputStream, MaybeByte, !IO),
|
|
(
|
|
MaybeByte = ok(Byte),
|
|
( if is_next_item_token(Byte, NextItem) then
|
|
(
|
|
NextItem = deep_item_call_site_dynamic,
|
|
read_call_site_dynamic(InputStream, MaybeCSD, !IO),
|
|
(
|
|
MaybeCSD = ok2(CallSiteDynamic, CSDI),
|
|
CSDs0 = !.InitDeep ^ init_call_site_dynamics,
|
|
deep_insert(CSDI, CallSiteDynamic, CSDs0, CSDs),
|
|
!InitDeep ^ init_call_site_dynamics := CSDs,
|
|
read_nodes_inner_loop(InputStream, Version, !.NumLeft,
|
|
!.InitDeep, MaybeInitDeep, !IO)
|
|
;
|
|
MaybeCSD = error2(Error),
|
|
MaybeInitDeep = error(Error)
|
|
)
|
|
;
|
|
NextItem = deep_item_proc_dynamic,
|
|
read_proc_dynamic(InputStream, ProfileStats, MaybePD, !IO),
|
|
(
|
|
MaybePD = ok2(ProcDynamic, PDI),
|
|
PDs0 = !.InitDeep ^ init_proc_dynamics,
|
|
deep_insert(PDI, ProcDynamic, PDs0, PDs),
|
|
!InitDeep ^ init_proc_dynamics := PDs,
|
|
read_nodes_inner_loop(InputStream, Version, !.NumLeft,
|
|
!.InitDeep, MaybeInitDeep, !IO)
|
|
;
|
|
MaybePD = error2(Error),
|
|
MaybeInitDeep = error(Error)
|
|
)
|
|
;
|
|
NextItem = deep_item_call_site_static,
|
|
read_call_site_static(InputStream, Version, MaybeCSS, !IO),
|
|
(
|
|
MaybeCSS = ok({CallSiteStatic, CSSI}),
|
|
CSSs0 = !.InitDeep ^ init_call_site_statics,
|
|
deep_insert(CSSI, CallSiteStatic, CSSs0, CSSs),
|
|
!InitDeep ^ init_call_site_statics := CSSs,
|
|
read_nodes_inner_loop(InputStream, Version, !.NumLeft,
|
|
!.InitDeep, MaybeInitDeep, !IO)
|
|
;
|
|
MaybeCSS = error(Error),
|
|
MaybeInitDeep = error(Error)
|
|
)
|
|
;
|
|
NextItem = deep_item_proc_static,
|
|
read_proc_static(InputStream, ProfileStats, MaybePS, !IO),
|
|
(
|
|
MaybePS = ok2(ProcStatic, PSI),
|
|
PSs0 = !.InitDeep ^ init_proc_statics,
|
|
deep_insert(PSI, ProcStatic, PSs0, PSs),
|
|
!InitDeep ^ init_proc_statics := PSs,
|
|
read_nodes_inner_loop(InputStream, Version, !.NumLeft,
|
|
!.InitDeep, MaybeInitDeep, !IO)
|
|
;
|
|
MaybePS = error2(Error),
|
|
MaybeInitDeep = error(Error)
|
|
)
|
|
;
|
|
NextItem = deep_item_end,
|
|
MaybeInitDeep = init_deep_complete(!.InitDeep)
|
|
)
|
|
else
|
|
string.format("unexpected token %d", [i(Byte)], Msg),
|
|
MaybeInitDeep = error(Msg)
|
|
)
|
|
;
|
|
MaybeByte = eof,
|
|
% XXX: Shouldn't this be an error? Shouldn't we expect
|
|
% a deep_item_end token before eof?
|
|
MaybeInitDeep = init_deep_complete(!.InitDeep)
|
|
;
|
|
MaybeByte = error(Error),
|
|
io.error_message(Error, Msg),
|
|
MaybeInitDeep = error(Msg)
|
|
)
|
|
).
|
|
|
|
:- pred read_call_site_static(io.binary_input_stream::in,
|
|
acceptable_version::in, maybe_error({call_site_static, int})::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_call_site_static(InputStream, Version, MaybeCSS, !IO) :-
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.write_string(OutputStream, "reading call_site_static.\n", !TIO)
|
|
),
|
|
(
|
|
Version = av_8,
|
|
io_combinator.maybe_error_sequence_4(
|
|
read_ptr(InputStream, css),
|
|
read_call_site_kind_and_callee(InputStream),
|
|
read_num(InputStream),
|
|
read_string(InputStream),
|
|
( pred(CSSI0::in, Kind::in, LineNumber::in,
|
|
GoalPathStr::in, CSS::out) is det :-
|
|
DummyPSPtr = make_dummy_psptr,
|
|
DummySlotNum = -1,
|
|
rev_goal_path_from_string_det(GoalPathStr, RevGoalPath0),
|
|
rev_goal_path_remove_type_info(RevGoalPath0, RevGoalPath),
|
|
% Setting the FileName slot to "" asks users of this field
|
|
% to copy the file name in the call site's proc_static.
|
|
CallSiteStatic0 = call_site_static(DummyPSPtr,
|
|
DummySlotNum, Kind, "", LineNumber, RevGoalPath),
|
|
CSS = ok({CallSiteStatic0, CSSI0})
|
|
),
|
|
MaybeCSS, !IO)
|
|
;
|
|
Version = av_9,
|
|
io_combinator.maybe_error_sequence_5(
|
|
read_ptr(InputStream, css),
|
|
read_call_site_kind_and_callee(InputStream),
|
|
read_string(InputStream),
|
|
read_num(InputStream),
|
|
read_string(InputStream),
|
|
( pred(CSSI0::in, Kind::in, FileName::in, LineNumber::in,
|
|
GoalPathStr::in, CSS::out) is det :-
|
|
DummyPSPtr = make_dummy_psptr,
|
|
DummySlotNum = -1,
|
|
rev_goal_path_from_string_det(GoalPathStr, RevGoalPath0),
|
|
rev_goal_path_remove_type_info(RevGoalPath0, RevGoalPath),
|
|
CallSiteStatic0 = call_site_static(DummyPSPtr,
|
|
DummySlotNum, Kind, FileName, LineNumber, RevGoalPath),
|
|
CSS = ok({CallSiteStatic0, CSSI0})
|
|
),
|
|
MaybeCSS, !IO)
|
|
),
|
|
(
|
|
MaybeCSS = ok({CallSiteStatic, CSSI}),
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.format(OutputStream,
|
|
"read call_site_static %d:", [i(CSSI)], !TIO),
|
|
io.write(OutputStream, CallSiteStatic, !TIO),
|
|
io.write_string(OutputStream, "\n", !TIO)
|
|
)
|
|
;
|
|
MaybeCSS = error(_)
|
|
).
|
|
|
|
:- pred read_proc_static(io.binary_input_stream::in, profile_stats::in,
|
|
maybe_error2(proc_static, int)::out, io::di, io::uo) is det.
|
|
|
|
read_proc_static(InputStream, ProfileStats, MaybePS, !IO) :-
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.write_string(OutputStream, "reading proc_static.\n", !TIO)
|
|
),
|
|
io_combinator.maybe_error_sequence_6(
|
|
read_ptr(InputStream, ps),
|
|
read_proc_id(InputStream),
|
|
read_string(InputStream),
|
|
read_num(InputStream),
|
|
read_deep_byte(InputStream),
|
|
read_num(InputStream),
|
|
( pred(PSI0::in, Id0::in, F0::in, L0::in, I0::in,
|
|
NCS0::in, ProcId::out) is det :-
|
|
ProcId = ok({PSI0, Id0, F0, L0, I0, NCS0})
|
|
),
|
|
MaybeProcId, !IO),
|
|
(
|
|
MaybeProcId = ok({PSI, Id, FileName, LineNumber, Interface, NCS}),
|
|
read_n_things(NCS, read_ptr(InputStream, css), MaybeCSSIs, !IO),
|
|
(
|
|
MaybeCSSIs = ok(CSSIs),
|
|
maybe_read_ps_coverage_points(InputStream, ProfileStats,
|
|
MaybeCoveragePoints, !IO),
|
|
(
|
|
MaybeCoveragePoints = ok(CPInfos - MaybeCPs),
|
|
CSSPtrs = list.map(make_cssptr, CSSIs),
|
|
DeclModule = decl_module(Id),
|
|
create_refined_proc_ids(Id, UnQualRefinedStr, QualRefinedStr),
|
|
RawStr = raw_proc_id_to_string(Id),
|
|
( if Interface = 0 then
|
|
IsInInterface = no
|
|
else
|
|
IsInInterface = yes
|
|
),
|
|
% The `not_zeroed' for whether the procedure's proc_static
|
|
% is ever zeroed is the default. The startup phase will set it
|
|
% to `zeroed' in the proc_statics which are ever zeroed.
|
|
ProcStatic = proc_static(Id, DeclModule,
|
|
UnQualRefinedStr, QualRefinedStr, RawStr,
|
|
FileName, LineNumber, IsInInterface,
|
|
array(CSSPtrs), CPInfos, MaybeCPs, not_zeroed),
|
|
MaybePS = ok2(ProcStatic, PSI),
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.format(OutputStream,
|
|
"read proc_static %d:", [i(PSI)], !TIO),
|
|
io.write(OutputStream, ProcStatic, !TIO),
|
|
io.write_string(OutputStream, "\n", !TIO)
|
|
)
|
|
;
|
|
MaybeCoveragePoints = error(Error),
|
|
MaybePS = error2(Error)
|
|
)
|
|
;
|
|
MaybeCSSIs = error(Error),
|
|
MaybePS = error2(Error)
|
|
)
|
|
;
|
|
MaybeProcId = error(Error),
|
|
MaybePS = error2(Error)
|
|
).
|
|
|
|
:- pred maybe_read_ps_coverage_points(io.binary_input_stream::in,
|
|
profile_stats::in, maybe_error(pair(array(coverage_point_info),
|
|
maybe(array(int))))::out, io::di, io::uo) is det.
|
|
|
|
maybe_read_ps_coverage_points(InputStream, ProfileStats, MaybeCoveragePoints,
|
|
!IO) :-
|
|
CoverageDataType = ProfileStats ^ prs_deep_flags ^ df_coverage_data_type,
|
|
(
|
|
CoverageDataType = no_coverage_data,
|
|
MaybeCoveragePoints0 = ok([] - no)
|
|
;
|
|
( CoverageDataType = static_coverage_data
|
|
; CoverageDataType = dynamic_coverage_data
|
|
),
|
|
read_num(InputStream, MaybeNCP, !IO),
|
|
(
|
|
MaybeNCP = ok(NCP),
|
|
(
|
|
CoverageDataType = static_coverage_data,
|
|
read_n_things(NCP,
|
|
read_coverage_point_static_and_num(InputStream),
|
|
MaybeCPPairs, !IO),
|
|
(
|
|
MaybeCPPairs = ok(CPPairs),
|
|
keys_and_values(CPPairs, CPInfos, CPs),
|
|
MaybeCoveragePoints0 = ok(CPInfos - yes(CPs))
|
|
;
|
|
MaybeCPPairs = error(Error0),
|
|
MaybeCoveragePoints0 = error(Error0)
|
|
)
|
|
;
|
|
CoverageDataType = dynamic_coverage_data,
|
|
read_n_things(NCP, read_coverage_point_static(InputStream),
|
|
MaybeCPInfos, !IO),
|
|
(
|
|
MaybeCPInfos = ok(CPInfos),
|
|
MaybeCoveragePoints0 = ok(CPInfos - no)
|
|
;
|
|
MaybeCPInfos = error(Error0),
|
|
MaybeCoveragePoints0 = error(Error0)
|
|
)
|
|
)
|
|
;
|
|
MaybeNCP = error(Error0),
|
|
MaybeCoveragePoints0 = error(Error0)
|
|
)
|
|
),
|
|
(
|
|
MaybeCoveragePoints0 = ok(CPInfosList - MaybeCPsList),
|
|
CPInfosArray = array(CPInfosList),
|
|
MaybeCPsArray = map_maybe(array, MaybeCPsList),
|
|
MaybeCoveragePoints = ok(CPInfosArray - MaybeCPsArray)
|
|
;
|
|
MaybeCoveragePoints0 = error(Error),
|
|
MaybeCoveragePoints = error(Error)
|
|
).
|
|
|
|
:- pred maybe_read_pd_coverage_points(io.binary_input_stream::in,
|
|
profile_stats::in, maybe_error(maybe(array(int)))::out,
|
|
io::di, io::uo) is det.
|
|
|
|
maybe_read_pd_coverage_points(InputStream, ProfileStats,
|
|
MaybeCoveragePoints, !IO) :-
|
|
CoverageDataType = ProfileStats ^ prs_deep_flags ^ df_coverage_data_type,
|
|
(
|
|
( CoverageDataType = no_coverage_data
|
|
; CoverageDataType = static_coverage_data
|
|
),
|
|
MaybeCoveragePoints = ok(no)
|
|
;
|
|
CoverageDataType = dynamic_coverage_data,
|
|
read_num(InputStream, ResN, !IO),
|
|
(
|
|
ResN = ok(N),
|
|
read_n_things(N, read_num(InputStream), MaybeCPs, !IO),
|
|
(
|
|
MaybeCPs = ok(CPsList),
|
|
MaybeCoveragePoints = ok(yes(array(CPsList)))
|
|
;
|
|
MaybeCPs = error(Error),
|
|
MaybeCoveragePoints = error(Error)
|
|
)
|
|
;
|
|
ResN = error(Error),
|
|
MaybeCoveragePoints = error(Error)
|
|
)
|
|
).
|
|
|
|
:- pred read_proc_id(io.binary_input_stream::in,
|
|
maybe_error(string_proc_label)::out, io::di, io::uo) is det.
|
|
|
|
read_proc_id(InputStream, MaybeProcId, !IO) :-
|
|
read_deep_byte(InputStream, MaybeByte, !IO),
|
|
(
|
|
MaybeByte = ok(Byte),
|
|
( if is_proclabel_kind(Byte, ProcLabelKind) then
|
|
(
|
|
ProcLabelKind = proclabel_special,
|
|
read_proc_id_uci_pred(InputStream, MaybeProcId, !IO)
|
|
;
|
|
ProcLabelKind = proclabel_user_predicate,
|
|
read_proc_id_user_defined(InputStream, pf_predicate,
|
|
MaybeProcId, !IO)
|
|
;
|
|
ProcLabelKind = proclabel_user_function,
|
|
read_proc_id_user_defined(InputStream, pf_function,
|
|
MaybeProcId, !IO)
|
|
)
|
|
else
|
|
string.format("unexpected proclabel_kind %d", [i(Byte)], Msg),
|
|
MaybeProcId = error(Msg)
|
|
)
|
|
;
|
|
MaybeByte = error(Error),
|
|
MaybeProcId = error(Error)
|
|
).
|
|
|
|
:- pred read_proc_id_uci_pred(io.binary_input_stream::in,
|
|
maybe_error(string_proc_label)::out, io::di, io::uo) is det.
|
|
|
|
read_proc_id_uci_pred(InputStream, MaybeProcLabel, !IO) :-
|
|
io_combinator.maybe_error_sequence_6(
|
|
read_string(InputStream),
|
|
read_string(InputStream),
|
|
read_string(InputStream),
|
|
read_string(InputStream),
|
|
read_num(InputStream),
|
|
read_num(InputStream),
|
|
( pred(TypeName::in, TypeModule::in, DefModule::in,
|
|
PredName::in, Arity::in, Mode::in, ProcId::out) is det :-
|
|
ProcId = ok(str_special_proc_label(TypeName, TypeModule,
|
|
DefModule, PredName, Arity, Mode))
|
|
),
|
|
MaybeProcLabel, !IO).
|
|
|
|
:- pred read_proc_id_user_defined(io.binary_input_stream::in, pred_or_func::in,
|
|
maybe_error(string_proc_label)::out, io::di, io::uo) is det.
|
|
|
|
read_proc_id_user_defined(InputStream, PredOrFunc, MaybeProcLabel, !IO) :-
|
|
io_combinator.maybe_error_sequence_5(
|
|
read_string(InputStream),
|
|
read_string(InputStream),
|
|
read_string(InputStream),
|
|
read_num(InputStream),
|
|
read_num(InputStream),
|
|
( pred(DeclModule::in, DefModule::in, Name::in,
|
|
Arity::in, Mode::in, ProcId::out) is det :-
|
|
ProcId = ok(str_ordinary_proc_label(PredOrFunc, DeclModule,
|
|
DefModule, Name, Arity, Mode))
|
|
),
|
|
MaybeProcLabel, !IO).
|
|
|
|
% Read the description of a coverage point, and its execution count.
|
|
%
|
|
% The description of a coverage point is stored in the proc static
|
|
% regardless of whether we are using static or dynamic coverage profiling.
|
|
% If we are using static coverage profiling, then this description will be
|
|
% immediately followed in the profiling data file by the number of times
|
|
% that the coverage point was executed.
|
|
%
|
|
:- pred read_coverage_point_static_and_num(io.binary_input_stream::in,
|
|
maybe_error(pair(coverage_point_info, int))::out, io::di, io::uo) is det.
|
|
|
|
read_coverage_point_static_and_num(InputStream, MaybeCP, !IO) :-
|
|
io_combinator.maybe_error_sequence_2(
|
|
read_coverage_point_static(InputStream),
|
|
read_num(InputStream),
|
|
( pred(CPInfo::in, Count::in, ok(CPI)::out) is det :-
|
|
CPI = CPInfo - Count
|
|
), MaybeCP, !IO).
|
|
|
|
% Read the description of a coverage point.
|
|
%
|
|
% The description of a coverage point is stored in the proc static
|
|
% regardless of whether we are using static or dynamic coverage profiling.
|
|
%
|
|
:- pred read_coverage_point_static(io.binary_input_stream::in,
|
|
maybe_error(coverage_point_info)::out, io::di, io::uo) is det.
|
|
|
|
read_coverage_point_static(InputStream, MaybeCP, !IO) :-
|
|
io_combinator.maybe_error_sequence_2(
|
|
read_string(InputStream),
|
|
read_cp_type(InputStream),
|
|
( pred(GoalPathString::in, CPType::in, MaybeCPI::out) is det :-
|
|
rev_goal_path_from_string_det(GoalPathString, RevGoalPath0),
|
|
rev_goal_path_remove_type_info(RevGoalPath0, RevGoalPath),
|
|
MaybeCPI = ok(coverage_point_info(RevGoalPath, CPType))
|
|
), MaybeCP, !IO).
|
|
|
|
:- func raw_proc_id_to_string(string_proc_label) = string.
|
|
|
|
raw_proc_id_to_string(ProcLabel) = Str :-
|
|
(
|
|
ProcLabel = str_special_proc_label(TypeName, TypeModule, _DefModule,
|
|
PredName, Arity, Mode),
|
|
string.format("%s for %s.%s/%d mode %d",
|
|
[s(PredName), s(TypeModule), s(TypeName), i(Arity), i(Mode)], Str)
|
|
;
|
|
ProcLabel = str_ordinary_proc_label(PredOrFunc, DeclModule,
|
|
_DefModule, Name, Arity, Mode),
|
|
string.format("%s.%s/%d%s-%d",
|
|
[s(DeclModule), s(Name), i(Arity),
|
|
s(plus_one_for_function(PredOrFunc)), i(Mode)], Str)
|
|
).
|
|
|
|
:- pred create_refined_proc_ids(string_proc_label::in,
|
|
string::out, string::out) is det.
|
|
|
|
create_refined_proc_ids(ProcLabel, UnQualName, QualName) :-
|
|
(
|
|
ProcLabel = str_special_proc_label(TypeName, TypeModule,
|
|
_DefModule, RawPredName, Arity, Mode),
|
|
( if RawPredName = "__Unify__" then
|
|
PredName = "Unify"
|
|
else if RawPredName = "__Compare__" then
|
|
PredName = "Compare"
|
|
else if RawPredName = "__CompareRep__" then
|
|
PredName = "CompareRep"
|
|
else if RawPredName = "__Index__" then
|
|
PredName = "Index"
|
|
else
|
|
Msg = "unknown special predicate name " ++ RawPredName,
|
|
error(Msg)
|
|
),
|
|
Prefix = PredName ++ " for ",
|
|
AritySuffix = "/" ++ string.int_to_string(Arity),
|
|
UnQualName0 = Prefix ++ TypeName ++ AritySuffix,
|
|
QualName0 = Prefix ++ TypeModule ++ "." ++ TypeName ++ AritySuffix,
|
|
( if Mode = 0 then
|
|
UnQualName = UnQualName0,
|
|
QualName = QualName0
|
|
else
|
|
ModeSuffix = " mode " ++ int_to_string(Mode),
|
|
UnQualName = UnQualName0 ++ ModeSuffix,
|
|
QualName = QualName0 ++ ModeSuffix
|
|
)
|
|
;
|
|
ProcLabel = str_ordinary_proc_label(PredOrFunc, DeclModule,
|
|
_DefModule, ProcName, Arity, Mode),
|
|
PlusOne = plus_one_for_function(PredOrFunc),
|
|
( if
|
|
string.append("TypeSpecOf__", ProcName1, ProcName),
|
|
( if string.append("pred__", ProcName2A, ProcName1) then
|
|
ProcName2 = ProcName2A
|
|
else if string.append("func__", ProcName2B, ProcName1) then
|
|
ProcName2 = ProcName2B
|
|
else if string.append("pred_or_func__", ProcName2C, ProcName1) then
|
|
ProcName2 = ProcName2C
|
|
else
|
|
error("typespec: neither pred nor func")
|
|
),
|
|
string.to_char_list(ProcName2, ProcName2Chars),
|
|
fix_type_spec_suffix(ProcName2Chars, ProcNameChars, SpecInfo)
|
|
then
|
|
RefinedProcName = string.from_char_list(ProcNameChars),
|
|
string.format("%s/%d%s-%d [%s]",
|
|
[s(RefinedProcName), i(Arity), s(PlusOne), i(Mode),
|
|
s(SpecInfo)],
|
|
UnQualName)
|
|
else if
|
|
string.append("IntroducedFrom__", ProcName1, ProcName),
|
|
( if string.append("pred__", ProcName2A, ProcName1) then
|
|
ProcName2 = ProcName2A
|
|
else if string.append("func__", ProcName2B, ProcName1) then
|
|
ProcName2 = ProcName2B
|
|
else
|
|
error("lambda: neither pred nor func")
|
|
),
|
|
string.to_char_list(ProcName2, ProcName2Chars),
|
|
split_lambda_name(ProcName2Chars, Segments),
|
|
glue_lambda_name(Segments, ContainingNameChars, LineNumberChars)
|
|
then
|
|
string.from_char_list(ContainingNameChars, ContainingName),
|
|
string.from_char_list(LineNumberChars, LineNumber),
|
|
string.format("%s lambda line %s/%d%s",
|
|
[s(ContainingName), s(LineNumber), i(Arity), s(PlusOne)],
|
|
UnQualName)
|
|
else
|
|
string.format("%s/%d%s-%d",
|
|
[s(ProcName), i(Arity), s(PlusOne), i(Mode)],
|
|
UnQualName)
|
|
),
|
|
QualName = DeclModule ++ "." ++ UnQualName
|
|
).
|
|
|
|
:- func plus_one_for_function(pred_or_func) = string.
|
|
|
|
plus_one_for_function(pf_function) = "+1".
|
|
plus_one_for_function(pf_predicate) = "".
|
|
|
|
:- pred fix_type_spec_suffix(list(char)::in, list(char)::out, string::out)
|
|
is semidet.
|
|
|
|
fix_type_spec_suffix(Chars0, Chars, SpecInfoStr) :-
|
|
( if Chars0 = ['_', '_', '[' | SpecInfo0 ] then
|
|
Chars = [],
|
|
list.take_while(non_right_bracket, SpecInfo0, SpecInfo),
|
|
string.from_char_list(SpecInfo, SpecInfoStr)
|
|
else if Chars0 = [Char | TailChars0] then
|
|
fix_type_spec_suffix(TailChars0, TailChars, SpecInfoStr),
|
|
Chars = [Char | TailChars]
|
|
else
|
|
fail
|
|
).
|
|
|
|
:- pred non_right_bracket(char::in) is semidet.
|
|
|
|
non_right_bracket(C) :-
|
|
C \= ']'.
|
|
|
|
:- pred split_lambda_name(list(char)::in, list(list(char))::out) is det.
|
|
|
|
split_lambda_name([], []).
|
|
split_lambda_name([Char0 | Chars0], StringList) :-
|
|
( if Chars0 = ['_', '_' | Chars1 ] then
|
|
split_lambda_name(Chars1, StringList0),
|
|
StringList = [[Char0] | StringList0]
|
|
else
|
|
split_lambda_name(Chars0, StringList0),
|
|
(
|
|
StringList0 = [],
|
|
StringList = [[Char0]]
|
|
;
|
|
StringList0 = [String0 | StringList1],
|
|
StringList = [[Char0 | String0] | StringList1]
|
|
)
|
|
).
|
|
|
|
:- pred glue_lambda_name(list(list(char))::in, list(char)::out,
|
|
list(char)::out) is semidet.
|
|
|
|
glue_lambda_name(Segments, PredName, LineNumber) :-
|
|
( if Segments = [LineNumberPrime, _] then
|
|
PredName = [],
|
|
LineNumber = LineNumberPrime
|
|
else if Segments = [Segment | TailSegments] then
|
|
glue_lambda_name(TailSegments, PredName1, LineNumber),
|
|
(
|
|
PredName1 = [],
|
|
PredName = Segment
|
|
;
|
|
PredName1 = [_ | _],
|
|
list.append(Segment, ['_', '_' | PredName1], PredName)
|
|
)
|
|
else
|
|
fail
|
|
).
|
|
|
|
:- pred read_proc_dynamic(io.binary_input_stream::in, profile_stats::in,
|
|
maybe_error2(proc_dynamic, int)::out, io::di, io::uo) is det.
|
|
|
|
read_proc_dynamic(InputStream, ProfileStats, MaybePD, !IO) :-
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.write_string(OutputStream, "reading proc_dynamic.\n", !TIO)
|
|
),
|
|
io_combinator.maybe_error_sequence_3(
|
|
read_ptr(InputStream, pd),
|
|
read_ptr(InputStream, ps),
|
|
read_num(InputStream),
|
|
( pred(PDI0::in, PSI0::in, N0::in, Stuff0::out) is det :-
|
|
Stuff0 = ok({PDI0, PSI0, N0})
|
|
),
|
|
MaybePDHeader, !IO),
|
|
(
|
|
MaybePDHeader = ok({PDI, PSI, N}),
|
|
io_combinator.maybe_error_sequence_2(
|
|
maybe_read_pd_coverage_points(InputStream, ProfileStats),
|
|
read_n_things(N, read_call_site_slot(InputStream)),
|
|
( pred(MaybeCPs0::in, Slots0::in, CPsAndSlots0::out) is det :-
|
|
CPsAndSlots0 = ok({MaybeCPs0, Slots0})
|
|
),
|
|
MaybeCPsAndSlots, !IO),
|
|
(
|
|
MaybeCPsAndSlots = ok({MaybeCPs, Refs}),
|
|
PSPtr = make_psptr(PSI),
|
|
ProcDynamic = proc_dynamic(PSPtr, array(Refs), MaybeCPs),
|
|
MaybePD = ok2(ProcDynamic, PDI),
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.format(OutputStream,
|
|
"read proc_dynamic %d:", [i(PDI)], !TIO),
|
|
io.write(OutputStream, ProcDynamic, !TIO),
|
|
io.write_string(OutputStream, "\n", !TIO)
|
|
)
|
|
;
|
|
MaybeCPsAndSlots = error(Error),
|
|
MaybePD = error2(Error)
|
|
)
|
|
;
|
|
MaybePDHeader = error(Error),
|
|
MaybePD = error2(Error)
|
|
).
|
|
|
|
:- pred read_call_site_dynamic(io.binary_input_stream::in,
|
|
maybe_error2(call_site_dynamic, int)::out, io::di, io::uo) is det.
|
|
|
|
read_call_site_dynamic(InputStream, MaybeCSD, !IO) :-
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.write_string(OutputStream, "reading call_site_dynamic.\n", !TIO)
|
|
),
|
|
read_ptr(InputStream, csd, MaybeCSDI, !IO),
|
|
(
|
|
MaybeCSDI = ok(CSDI),
|
|
read_ptr(InputStream, pd, MaybePDI, !IO),
|
|
(
|
|
MaybePDI = ok(PDI),
|
|
read_profile(InputStream, MaybeProfile, !IO),
|
|
(
|
|
MaybeProfile = ok(Profile),
|
|
PDPtr = make_pdptr(PDI),
|
|
CallerPDPtr = make_dummy_pdptr,
|
|
CallSiteDynamic = call_site_dynamic(CallerPDPtr, PDPtr,
|
|
Profile),
|
|
MaybeCSD = ok2(CallSiteDynamic, CSDI),
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.format(OutputStream,
|
|
"read call_site_dynamic %d: ", [i(CSDI)], !TIO),
|
|
io.write(OutputStream, CallSiteDynamic, !TIO),
|
|
io.write_string(OutputStream, "\n", !TIO)
|
|
)
|
|
;
|
|
MaybeProfile = error(Error),
|
|
MaybeCSD = error2(Error)
|
|
)
|
|
;
|
|
MaybePDI = error(Error),
|
|
MaybeCSD = error2(Error)
|
|
)
|
|
;
|
|
MaybeCSDI = error(Error),
|
|
MaybeCSD = error2(Error)
|
|
).
|
|
|
|
:- pred read_profile(io.binary_input_stream::in,
|
|
maybe_error(own_prof_info)::out, io::di, io::uo) is det.
|
|
|
|
read_profile(InputStream, MaybeProfile, !IO) :-
|
|
read_num(InputStream, MaybeMask, !IO),
|
|
(
|
|
MaybeMask = ok(Mask),
|
|
|
|
% The masks here must correspond exactly with the masks in
|
|
% MR_write_out_call_site_dynamic in mercury_deep_profiling.c
|
|
% in the runtime.
|
|
some [!MaybeError] (
|
|
!:MaybeError = no,
|
|
% We normally assume that the configuration macro
|
|
% MR_DEEP_PROFILING_EXPLICIT_CALL_COUNTS is not
|
|
% defined, and thus mercury_deep_profiling.m never
|
|
% writes out call counts (instead, call counts are
|
|
% computed from other port counts in measurements.m).
|
|
% maybe_read_num_handle_error(Mask, 0x0001, Calls,
|
|
% !MaybeError, !IO),
|
|
maybe_read_num_handle_error(InputStream, Mask, 0x0002, Exits,
|
|
!MaybeError, !IO),
|
|
maybe_read_num_handle_error(InputStream, Mask, 0x0004, Fails,
|
|
!MaybeError, !IO),
|
|
maybe_read_num_handle_error(InputStream, Mask, 0x0040, Redos,
|
|
!MaybeError, !IO),
|
|
maybe_read_num_handle_error(InputStream, Mask, 0x0080, Excps,
|
|
!MaybeError, !IO),
|
|
maybe_read_num_handle_error(InputStream, Mask, 0x0100, Quanta,
|
|
!MaybeError, !IO),
|
|
maybe_read_num_handle_error(InputStream, Mask, 0x0008, CallSeqs,
|
|
!MaybeError, !IO),
|
|
maybe_read_num_handle_error(InputStream, Mask, 0x0010, Allocs,
|
|
!MaybeError, !IO),
|
|
maybe_read_num_handle_error(InputStream, Mask, 0x0020, Words,
|
|
!MaybeError, !IO),
|
|
LastMaybeError = !.MaybeError
|
|
),
|
|
(
|
|
LastMaybeError = yes(Error),
|
|
MaybeProfile = error(Error)
|
|
;
|
|
LastMaybeError = no,
|
|
MaybeProfile = ok(compress_profile(Exits, Fails, Redos, Excps,
|
|
Quanta, CallSeqs, Allocs, Words))
|
|
)
|
|
;
|
|
MaybeMask = error(Error),
|
|
MaybeProfile = error(Error)
|
|
).
|
|
|
|
:- pred maybe_read_num_handle_error(io.binary_input_stream::in,
|
|
int::in, int::in, int::out, maybe(string)::in, maybe(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
maybe_read_num_handle_error(InputStream, MaskWord, MaskValue, Num,
|
|
!MaybeError, !IO) :-
|
|
( if MaskWord /\ MaskValue \= 0 then
|
|
read_num(InputStream, MaybeNum, !IO),
|
|
(
|
|
MaybeNum = ok(Num)
|
|
;
|
|
MaybeNum = error(Error),
|
|
Num = 0,
|
|
!:MaybeError = yes(Error)
|
|
)
|
|
else
|
|
Num = 0
|
|
).
|
|
|
|
:- pred read_call_site_slot(io.binary_input_stream::in,
|
|
maybe_error(call_site_array_slot)::out, io::di, io::uo) is det.
|
|
|
|
read_call_site_slot(InputStream, MaybeSlot, !IO) :-
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.write_string(OutputStream, "reading call_site_slot.\n", !TIO)
|
|
),
|
|
read_call_site_kind(InputStream, MaybeKind, !IO),
|
|
(
|
|
MaybeKind = ok(Kind),
|
|
(
|
|
Kind = normal_call,
|
|
read_ptr(InputStream, csd, MaybeCSDI, !IO),
|
|
(
|
|
MaybeCSDI = ok(CSDI),
|
|
CSDPtr = make_csdptr(CSDI),
|
|
MaybeSlot = ok(slot_normal(CSDPtr)),
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.write_string(OutputStream,
|
|
"normal call_site slot ", !TIO),
|
|
io.write_int(OutputStream, CSDI, !TIO),
|
|
io.write_string(OutputStream, "\n", !TIO)
|
|
)
|
|
;
|
|
MaybeCSDI = error(Error),
|
|
MaybeSlot = error(Error)
|
|
)
|
|
;
|
|
(
|
|
Kind = higher_order_call,
|
|
Zeroed = zeroed
|
|
;
|
|
Kind = method_call,
|
|
Zeroed = zeroed
|
|
;
|
|
Kind = special_call,
|
|
Zeroed = not_zeroed
|
|
;
|
|
Kind = callback,
|
|
Zeroed = not_zeroed
|
|
),
|
|
read_multi_call_site_csdis(InputStream, MaybeCSDIs, !IO),
|
|
(
|
|
MaybeCSDIs = ok(CSDIs),
|
|
CSDPtrs = list.map(make_csdptr, CSDIs),
|
|
MaybeSlot = ok(slot_multi(Zeroed, array(CSDPtrs))),
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.write_string(OutputStream,
|
|
"multi call_site slots ", !TIO),
|
|
io.write(OutputStream, CSDIs, !TIO),
|
|
io.write_string(OutputStream, "\n", !TIO)
|
|
)
|
|
;
|
|
MaybeCSDIs = error(Error),
|
|
MaybeSlot = error(Error)
|
|
)
|
|
)
|
|
;
|
|
MaybeKind = error(Error),
|
|
MaybeSlot = error(Error)
|
|
).
|
|
|
|
:- pred read_multi_call_site_csdis(io.binary_input_stream::in,
|
|
maybe_error(list(int))::out, io::di, io::uo) is det.
|
|
|
|
read_multi_call_site_csdis(InputStream, MaybeCSDIs, !IO) :-
|
|
read_multi_call_site_csdis_2(InputStream, [], MaybeCSDIs, !IO).
|
|
|
|
% We keep reading CSD node numbers until we find a zero byte.
|
|
% The reason why a zero byte works as a sentinel is that a CSD node
|
|
% number in the list of CSD node numbers at a multi call site cannot be
|
|
% zero, and our encoding scheme ensures that non-zero node numbers
|
|
% cannot start with a zero byte.
|
|
%
|
|
% We return the list of CSD node numbers in the reversed order, but
|
|
% this is OK because our caller does not pay attention to the order
|
|
% anyway.
|
|
|
|
:- pred read_multi_call_site_csdis_2(io.binary_input_stream::in, list(int)::in,
|
|
maybe_error(list(int))::out, io::di, io::uo) is det.
|
|
|
|
read_multi_call_site_csdis_2(InputStream, CSDIs0, MaybeCSDIs, !IO) :-
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.format(OutputStream, "reading multi_call_site_csdi.\n", [], !TIO)
|
|
),
|
|
read_deep_byte(InputStream, MaybeByte, !IO),
|
|
(
|
|
MaybeByte = ok(Byte),
|
|
( if Byte = 0 then
|
|
MaybeCSDIs = ok(CSDIs0)
|
|
else
|
|
putback_byte(InputStream, Byte, !IO),
|
|
read_ptr(InputStream, csd, MaybeCSDI, !IO),
|
|
(
|
|
MaybeCSDI = ok(CSDI),
|
|
read_multi_call_site_csdis_2(InputStream,
|
|
[CSDI | CSDIs0], MaybeCSDIs, !IO)
|
|
;
|
|
MaybeCSDI = error(Error),
|
|
MaybeCSDIs = error(Error)
|
|
)
|
|
)
|
|
;
|
|
MaybeByte = error(Error),
|
|
MaybeCSDIs = error(Error)
|
|
).
|
|
|
|
:- pred read_call_site_kind(io.binary_input_stream::in,
|
|
maybe_error(call_site_kind)::out, io::di, io::uo) is det.
|
|
|
|
read_call_site_kind(InputStream, MaybeKind, !IO) :-
|
|
read_deep_byte(InputStream, MaybeByte, !IO),
|
|
(
|
|
MaybeByte = ok(Byte),
|
|
( if is_call_site_kind(Byte, CallSiteKind) then
|
|
MaybeKind = ok(CallSiteKind)
|
|
else
|
|
string.format("unexpected call_site_kind %d", [i(Byte)], Msg),
|
|
MaybeKind = error(Msg)
|
|
),
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.write_string(OutputStream, "call_site_kind ", !TIO),
|
|
io.write(OutputStream, MaybeKind, !TIO),
|
|
io.write_string(OutputStream, "\n", !TIO)
|
|
)
|
|
;
|
|
MaybeByte = error(Error),
|
|
MaybeKind = error(Error)
|
|
).
|
|
|
|
:- pred read_call_site_kind_and_callee(io.binary_input_stream::in,
|
|
maybe_error(call_site_kind_and_callee)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_call_site_kind_and_callee(InputStream, MaybeKindAndCallee, !IO) :-
|
|
read_deep_byte(InputStream, MaybeByte, !IO),
|
|
(
|
|
MaybeByte = ok(Byte),
|
|
( if is_call_site_kind(Byte, CallSiteKind) then
|
|
(
|
|
CallSiteKind = normal_call,
|
|
read_num(InputStream, MaybeCSS, !IO),
|
|
(
|
|
MaybeCSS = ok(CalleeProcStatic),
|
|
read_string(InputStream, MaybeTypeSubst, !IO),
|
|
(
|
|
MaybeTypeSubst = ok(TypeSubst),
|
|
MaybeKindAndCallee = ok(normal_call_and_callee(
|
|
proc_static_ptr(CalleeProcStatic), TypeSubst))
|
|
;
|
|
MaybeTypeSubst = error(Error),
|
|
MaybeKindAndCallee = error(Error)
|
|
)
|
|
;
|
|
MaybeCSS = error(Error),
|
|
MaybeKindAndCallee = error(Error)
|
|
)
|
|
;
|
|
CallSiteKind = special_call,
|
|
MaybeKindAndCallee = ok(special_call_and_no_callee)
|
|
;
|
|
CallSiteKind = higher_order_call,
|
|
MaybeKindAndCallee = ok(higher_order_call_and_no_callee)
|
|
;
|
|
CallSiteKind = method_call,
|
|
MaybeKindAndCallee = ok(method_call_and_no_callee)
|
|
;
|
|
CallSiteKind = callback,
|
|
MaybeKindAndCallee = ok(callback_and_no_callee)
|
|
)
|
|
else
|
|
string.format("unexpected call_site_kind %d", [i(Byte)], Msg),
|
|
MaybeKindAndCallee = error(Msg)
|
|
),
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.write_string(OutputStream, "call_site_kind_and_callee ", !TIO),
|
|
io.write(OutputStream, MaybeKindAndCallee, !TIO),
|
|
io.write_string(OutputStream, "\n", !TIO)
|
|
)
|
|
;
|
|
MaybeByte = error(Error),
|
|
MaybeKindAndCallee = error(Error)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred read_n_things(int::in,
|
|
pred(maybe_error(T), io, io)::in(pred(out, di, uo) is det),
|
|
maybe_error(list(T))::out, io::di, io::uo) is det.
|
|
|
|
read_n_things(N, ItemReader, MaybeItems, !IO) :-
|
|
read_n_things_loop(N, ItemReader, [], MaybeRevItems, !IO),
|
|
(
|
|
MaybeRevItems = ok(RevItems),
|
|
list.reverse(RevItems, Items),
|
|
MaybeItems = ok(Items)
|
|
;
|
|
MaybeRevItems = error(Error),
|
|
MaybeItems = error(Error)
|
|
).
|
|
|
|
:- pred read_n_things_loop(int::in,
|
|
pred(maybe_error(T), io, io)::in(pred(out, di, uo) is det),
|
|
list(T)::in, maybe_error(list(T))::out, io::di, io::uo) is det.
|
|
|
|
read_n_things_loop(N, ItemReader, RevItems0, MaybeItems, !IO) :-
|
|
( if N =< 0 then
|
|
MaybeItems = ok(RevItems0)
|
|
else
|
|
call(ItemReader, MaybeItem, !IO),
|
|
(
|
|
MaybeItem = ok(Item),
|
|
read_n_things_loop(N - 1, ItemReader,
|
|
[Item | RevItems0], MaybeItems, !IO)
|
|
;
|
|
MaybeItem = error(Error),
|
|
MaybeItems = error(Error)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred read_line(io.binary_input_stream::in, int::in,
|
|
maybe_error(string)::out, io::di, io::uo) is det.
|
|
|
|
read_line(InputStream, Limit, MaybeLine, !IO) :-
|
|
read_line_acc(InputStream, Limit, [], MaybeLine, !IO).
|
|
|
|
:- pred read_line_acc(io.binary_input_stream::in, int::in, list(char)::in,
|
|
maybe_error(string)::out, io::di, io::uo) is det.
|
|
|
|
read_line_acc(InputStream, Limit, !.RevChars, MaybeLine, !IO) :-
|
|
( if Limit > 0 then
|
|
read_byte(InputStream, MaybeByte, !IO),
|
|
(
|
|
MaybeByte = ok(Byte),
|
|
( if char.to_int(Char, Byte) then
|
|
% Include the newline in the string.
|
|
!:RevChars = [Char | !.RevChars],
|
|
( if Char = '\n' then
|
|
list.reverse(!.RevChars, Chars),
|
|
string.from_char_list(Chars, Str),
|
|
MaybeLine = ok(Str)
|
|
else
|
|
read_line_acc(InputStream, Limit - 1, !.RevChars,
|
|
MaybeLine, !IO)
|
|
)
|
|
else
|
|
MaybeLine = error("unexpected end of file")
|
|
)
|
|
;
|
|
MaybeByte = eof,
|
|
MaybeLine = error("unexpected end of file")
|
|
;
|
|
MaybeByte = error(Error),
|
|
io.error_message(Error, Msg),
|
|
MaybeLine = error(Msg)
|
|
)
|
|
else
|
|
list.reverse(!.RevChars, Chars),
|
|
string.from_char_list(Chars, Str),
|
|
MaybeLine = ok(Str)
|
|
).
|
|
|
|
:- pred read_string(io.binary_input_stream::in, maybe_error(string)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_string(InputStream, MaybeStr, !IO) :-
|
|
read_num(InputStream, MaybeNum, !IO),
|
|
(
|
|
MaybeNum = ok(Length),
|
|
( if Length = 0 then
|
|
MaybeStr = ok("")
|
|
else
|
|
read_n_byte_string(InputStream, Length, MaybeStr, !IO)
|
|
)
|
|
;
|
|
MaybeNum = error(Error),
|
|
MaybeStr = error(Error)
|
|
).
|
|
|
|
:- pred read_n_byte_string(io.binary_input_stream::in, int::in,
|
|
maybe_error(string)::out, io::di, io::uo) is det.
|
|
|
|
read_n_byte_string(InputStream, Length, MaybeStr, !IO) :-
|
|
read_n_bytes(InputStream, Length, MaybeNBytes, !IO),
|
|
(
|
|
MaybeNBytes = ok(Bytes),
|
|
( if
|
|
list.map((pred(I::in, C::out) is semidet :- char.to_int(C, I)),
|
|
Bytes, Chars)
|
|
then
|
|
string.from_char_list(Chars, Str),
|
|
MaybeStr = ok(Str)
|
|
else
|
|
MaybeStr = error("string contained bad char")
|
|
)
|
|
;
|
|
MaybeNBytes = error(Error),
|
|
MaybeStr = error(Error)
|
|
),
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.write_string(OutputStream, "string ", !TIO),
|
|
io.write(OutputStream, MaybeStr, !TIO),
|
|
io.write_string(OutputStream, "\n", !TIO)
|
|
).
|
|
|
|
:- pred read_ptr(io.binary_input_stream::in, ptr_kind::in,
|
|
maybe_error(int)::out, io::di, io::uo) is det.
|
|
|
|
read_ptr(InputStream, _Kind, MaybePtr, !IO) :-
|
|
read_num(InputStream, MaybePtr, !IO),
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.write_string(OutputStream, "ptr ", !TIO),
|
|
io.write(OutputStream, MaybePtr, !TIO),
|
|
io.write_string(OutputStream, "\n", !TIO)
|
|
).
|
|
|
|
:- pred read_cp_type(io.binary_input_stream::in, maybe_error(cp_type)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_cp_type(InputStream, MaybeCPType, !IO) :-
|
|
read_num(InputStream, MaybeNum, !IO),
|
|
(
|
|
MaybeNum = ok(Num),
|
|
num_to_cp_type(Num, CPType),
|
|
MaybeCPType = ok(CPType)
|
|
;
|
|
MaybeNum = error(Msg),
|
|
MaybeCPType = error(Msg)
|
|
).
|
|
|
|
:- pred num_to_cp_type(int::in, cp_type::out) is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
num_to_cp_type(Int::in, CPType::out),
|
|
[will_not_call_mercury, thread_safe, promise_pure],
|
|
"
|
|
CPType = Int;
|
|
").
|
|
|
|
:- pred read_num(io.binary_input_stream::in, maybe_error(int)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_num(InputStream, MaybeNum, !IO) :-
|
|
read_num_acc(InputStream, 0, MaybeNum, !IO),
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.write_string(OutputStream, "num ", !TIO),
|
|
io.write(OutputStream, MaybeNum, !TIO),
|
|
io.write_string(OutputStream, "\n", !TIO)
|
|
).
|
|
|
|
:- pred read_num_acc(io.binary_input_stream::in, int::in,
|
|
maybe_error(int)::out, io::di, io::uo) is det.
|
|
|
|
read_num_acc(InputStream, Num0, MaybeNum, !IO) :-
|
|
read_byte(InputStream, MaybeByte, !IO),
|
|
(
|
|
MaybeByte = ok(Byte),
|
|
Num1 = (Num0 << 7) \/ (Byte /\ 0x7F),
|
|
( if Byte /\ 0x80 \= 0 then
|
|
read_num_acc(InputStream, Num1, MaybeNum, !IO)
|
|
else
|
|
MaybeNum = ok(Num1)
|
|
)
|
|
;
|
|
MaybeByte = eof,
|
|
MaybeNum = error("unexpected end of file")
|
|
;
|
|
MaybeByte = error(Error),
|
|
io.error_message(Error, Msg),
|
|
MaybeNum = error(Msg)
|
|
).
|
|
|
|
:- func fixed_size_int_bytes = int.
|
|
|
|
% Must correspond to MR_FIXED_SIZE_INT_BYTES
|
|
% in runtime/mercury_deep_profiling.c.
|
|
fixed_size_int_bytes = 8.
|
|
|
|
:- pred read_fixed_size_int(io.binary_input_stream::in, maybe_error(int)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_fixed_size_int(InputStream, MaybeInt, !IO) :-
|
|
read_fixed_size_int_acc(InputStream, fixed_size_int_bytes, 0, 0,
|
|
MaybeInt, !IO),
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.format(OutputStream, "fixed size int %s\n",
|
|
[s(string(MaybeInt))], !TIO)
|
|
).
|
|
|
|
:- pred read_fixed_size_int_acc(io.binary_input_stream::in, int::in, int::in,
|
|
int::in, maybe_error(int)::out, io::di, io::uo) is det.
|
|
|
|
read_fixed_size_int_acc(InputStream, BytesLeft, Num0, ShiftBy, MaybeInt,
|
|
!IO) :-
|
|
( if BytesLeft =< 0 then
|
|
MaybeInt = ok(Num0)
|
|
else
|
|
read_deep_byte(InputStream, MaybeByte, !IO),
|
|
(
|
|
MaybeByte = ok(Byte),
|
|
Num1 = Num0 \/ ( Byte << ShiftBy),
|
|
read_fixed_size_int_acc(InputStream, BytesLeft - 1, Num1,
|
|
ShiftBy + 8, MaybeInt, !IO)
|
|
;
|
|
MaybeByte = error(Error),
|
|
MaybeInt = error(Error)
|
|
)
|
|
).
|
|
|
|
:- pred read_n_bytes(io.binary_input_stream::in, int::in,
|
|
maybe_error(list(int))::out, io::di, io::uo) is det.
|
|
|
|
read_n_bytes(InputStream, N, MaybeNBytes, !IO) :-
|
|
read_n_bytes_acc(InputStream, N, [], MaybeRevNBytes, !IO),
|
|
(
|
|
MaybeRevNBytes = ok(RevBytes),
|
|
list.reverse(RevBytes, Bytes),
|
|
MaybeNBytes = ok(Bytes)
|
|
;
|
|
MaybeRevNBytes = error(Error),
|
|
MaybeNBytes = error(Error)
|
|
).
|
|
|
|
:- pred read_n_bytes_acc(io.binary_input_stream::in, int::in, list(int)::in,
|
|
maybe_error(list(int))::out, io::di, io::uo) is det.
|
|
|
|
read_n_bytes_acc(InputStream, N, RevBytes0, MaybeNBytes, !IO) :-
|
|
( if N =< 0 then
|
|
MaybeNBytes = ok(RevBytes0)
|
|
else
|
|
read_deep_byte(InputStream, MaybeByte, !IO),
|
|
(
|
|
MaybeByte = ok(Byte),
|
|
read_n_bytes_acc(InputStream, N - 1,
|
|
[Byte | RevBytes0], MaybeNBytes, !IO)
|
|
;
|
|
MaybeByte = error(Error),
|
|
MaybeNBytes = error(Error)
|
|
)
|
|
).
|
|
|
|
:- pred read_deep_byte(io.binary_input_stream::in, maybe_error(int)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_deep_byte(InputStream, MaybeByte, !IO) :-
|
|
read_byte(InputStream, MaybeRawByte, !IO),
|
|
(
|
|
MaybeRawByte = ok(Byte),
|
|
MaybeByte = ok(Byte)
|
|
;
|
|
MaybeRawByte = eof,
|
|
MaybeByte = error("unexpected end of file")
|
|
;
|
|
MaybeRawByte = error(Error),
|
|
io.error_message(Error, Msg),
|
|
MaybeByte = error(Msg)
|
|
),
|
|
trace [compile_time(flag("debug_read_profdeep")), io(!TIO)] (
|
|
io.output_stream(OutputStream, !TIO),
|
|
io.write_string(OutputStream, "byte ", !TIO),
|
|
io.write(OutputStream, MaybeByte, !TIO),
|
|
io.write_string(OutputStream, "\n", !TIO)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred deep_insert(int::in, T::in, array(T)::in, array(T)::out) is det.
|
|
|
|
deep_insert(Ind, Item, A0, A) :-
|
|
array.max(A0, Max),
|
|
( if Ind > Max then
|
|
error("deep_insert: array bounds violation")
|
|
% array.lookup(A0, 0, X),
|
|
% array.resize(u(A0), 2 * (Max + 1), X, A1),
|
|
% deep_insert(A1, Ind, Item, A)
|
|
else
|
|
set(Ind, Item, u(A0), A)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func make_csdptr(int) = call_site_dynamic_ptr.
|
|
:- func make_cssptr(int) = call_site_static_ptr.
|
|
:- func make_pdptr(int) = proc_dynamic_ptr.
|
|
:- func make_psptr(int) = proc_static_ptr.
|
|
|
|
make_csdptr(CSDI) = call_site_dynamic_ptr(CSDI).
|
|
make_cssptr(CSSI) = call_site_static_ptr(CSSI).
|
|
make_pdptr(PDI) = proc_dynamic_ptr(PDI).
|
|
make_psptr(PSI) = proc_static_ptr(PSI).
|
|
|
|
:- func make_dummy_pdptr = proc_dynamic_ptr.
|
|
:- func make_dummy_psptr = proc_static_ptr.
|
|
|
|
make_dummy_pdptr = proc_dynamic_ptr(-1).
|
|
make_dummy_psptr = proc_static_ptr(-1).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_decl("C", "#include ""mercury_deep_profiling.h""").
|
|
|
|
:- type next_deep_item
|
|
---> deep_item_end
|
|
; deep_item_call_site_static
|
|
; deep_item_call_site_dynamic
|
|
; deep_item_proc_static
|
|
; deep_item_proc_dynamic.
|
|
|
|
:- pragma foreign_enum("C", next_deep_item/0, [
|
|
deep_item_end - "MR_deep_item_end",
|
|
deep_item_call_site_static - "MR_deep_item_call_site_static",
|
|
deep_item_call_site_dynamic - "MR_deep_item_call_site_dynamic",
|
|
deep_item_proc_static - "MR_deep_item_proc_static",
|
|
deep_item_proc_dynamic - "MR_deep_item_proc_dynamic"
|
|
]).
|
|
|
|
:- pred is_next_item_token(int::in, next_deep_item::out) is semidet.
|
|
|
|
:- pragma foreign_proc("C",
|
|
is_next_item_token(Int::in, NextItem::out),
|
|
[promise_pure, will_not_call_mercury, thread_safe],
|
|
"
|
|
NextItem = (MR_DeepProfNextItem) Int;
|
|
|
|
switch (NextItem) {
|
|
case MR_deep_item_end:
|
|
case MR_deep_item_call_site_static:
|
|
case MR_deep_item_call_site_dynamic:
|
|
case MR_deep_item_proc_static:
|
|
case MR_deep_item_proc_dynamic:
|
|
SUCCESS_INDICATOR = MR_TRUE;
|
|
break;
|
|
|
|
default:
|
|
SUCCESS_INDICATOR = MR_FALSE;
|
|
break;
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module read_profile.
|
|
%---------------------------------------------------------------------------%
|