mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
Fix a problem that arises in the deep profiler if the program being profiled
was using both intermodule optimization and inlining. The issue was that
even though runtime/mercury_deep_profiling.c had access, for every call site
to the full context of that call site, containing both the file name and
the line number, it wrote out *only* the line number. The deep profiler
then got the file name from the file name stored in the proc_static structure
of the procedure containing the call site.
This works very close to 100% of the time, because
- user-written programs just about never use ":- pragma source_file", and
- in the absence of such pragmas, all the goals in a procedure will be
from the same file as the procedure's context.
However, if
- a call site calls a procedure in another module,
- the compiler has access to the code of that procedure from a .opt file, and
- the compiler decides to inline that call,
then the call, whose context is in the original source file, will be replaced
by the code of the procedure from the .opt file, whose context will NOT have
the same file name. Any description of this call site will list
- the code from the .opt file (such as the callee's callee),
- the file name from the original source file, and
- the line number from the .opt file.
This mismatch is very confusing, which is why this diff fixes it.
runtime/mercury_deep_profiling.c:
Fix this by writing out the file name part, as well as the line number
part, of each call site. The space impact is not as big as one might
expect, because compiler/deep_profiling.m already had an optimization
that set the filename part of each call site context to the empty string
if it was identical to the context of the procedure. Therefore for all
call sites that do NOT exhibit the bug that this diff fixes, the space
cost is only a single NULL character in the profiling data file.
Since this IS a change in the file format, bump the format version number
from 8 to 9.
deep_profiler/profile.m:
deep_profiler/read_profile.m:
Handle reading in both version 8 and version 9 profiling data files.
deep_profiler/create_report.m:
When creating descriptions of call sites, use the call site's filename
if it is not the empty string; if it is the empty string, then use
the containing procedure's file name, as we have done all along.
deep_profiler/display_report.m:
deep_profiler/dump.m:
deep_profiler/report.m:
Dump the new field in commands intended only for implementors.
deep_profiler/startup.m:
Conform to the changes above.
1758 lines
69 KiB
Mathematica
1758 lines
69 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2008-2012 The University of Melbourne.
|
|
% Copyright (C) 2014-2015, 2017, 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: create_report.m.
|
|
% Author: pbone.
|
|
%
|
|
% This module creates a report from a deep data structure and a query.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module create_report.
|
|
:- interface.
|
|
|
|
:- import_module measurements.
|
|
:- import_module profile.
|
|
:- import_module query.
|
|
:- import_module report.
|
|
|
|
:- import_module maybe.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred create_report(cmd::in, deep::in, deep_report::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Create a clique report for the specified clique.
|
|
%
|
|
:- pred create_clique_report(deep::in, clique_ptr::in,
|
|
maybe_error(clique_report)::out) is det.
|
|
|
|
% Create a proc report for the specified procedure.
|
|
%
|
|
:- pred create_proc_report(deep::in, proc_static_ptr::in,
|
|
maybe_error(proc_report)::out) is det.
|
|
|
|
% Create a static procrep coverage report.
|
|
%
|
|
:- pred create_static_procrep_coverage_report(deep::in,
|
|
proc_static_ptr::in, maybe_error(procrep_coverage_info)::out) is det.
|
|
|
|
% Create a dynamic procrep coverage report.
|
|
%
|
|
% This will fail if dynamic coverage information is not present in the
|
|
% profile.
|
|
%
|
|
:- pred create_dynamic_procrep_coverage_report(deep::in,
|
|
proc_dynamic_ptr::in, maybe_error(procrep_coverage_info)::out) is det.
|
|
|
|
% Create a CSD var use report.
|
|
%
|
|
:- pred create_call_site_dynamic_var_use_report(deep::in,
|
|
call_site_dynamic_ptr::in,
|
|
maybe_error(call_site_dynamic_var_use_info)::out) is det.
|
|
|
|
% Create a top procs report, from the given data with the specified
|
|
% parameters.
|
|
%
|
|
:- pred create_top_procs_report(deep::in, display_limit::in, cost_kind::in,
|
|
include_descendants::in, measurement_scope::in,
|
|
maybe_error(top_procs_report)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Create a proc_desc structure for a given proc static pointer.
|
|
%
|
|
:- func describe_proc(deep, proc_static_ptr) = proc_desc.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Convert own and inherit perf information to row data used for reports.
|
|
%
|
|
:- pred own_and_inherit_to_perf_row_data(deep::in, T::in,
|
|
own_prof_info::in, inherit_prof_info::in, perf_row_data(T)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module analysis_utils.
|
|
:- import_module apply_exclusion.
|
|
:- import_module coverage.
|
|
:- import_module exclude.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.goal_path.
|
|
:- import_module mdbcomp.program_representation.
|
|
:- import_module measurement_units.
|
|
:- import_module program_representation_utils.
|
|
:- import_module recursion_patterns.
|
|
:- import_module top_procs.
|
|
:- import_module var_use_analysis.
|
|
|
|
:- import_module array.
|
|
:- import_module char.
|
|
:- import_module cord.
|
|
:- import_module float.
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
:- import_module unit.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
create_report(Cmd, Deep, Report) :-
|
|
(
|
|
Cmd = deep_cmd_quit,
|
|
Msg = string.format("Shutting down deep profile server for %s.",
|
|
[s(Deep ^ data_file_name)]),
|
|
MessageReport = message_report(Msg),
|
|
Report = report_message(MessageReport)
|
|
;
|
|
Cmd = deep_cmd_restart,
|
|
unexpected($pred, "restart command")
|
|
;
|
|
Cmd = deep_cmd_timeout(Timeout),
|
|
Msg = string.format("Timeout set to %d minutes.", [i(Timeout)]),
|
|
MessageReport = message_report(Msg),
|
|
Report = report_message(MessageReport)
|
|
;
|
|
Cmd = deep_cmd_menu,
|
|
Deep ^ profile_stats = profile_stats(ProgramName,
|
|
NumCSD, NumCSS, NumPD, NumPS,
|
|
QuantaPerSec, InstrumentationQuanta, UserQuanta, NumCallseqs, _),
|
|
NumCliques = array.max(Deep ^ clique_members),
|
|
MenuReport = menu_report(ProgramName, QuantaPerSec,
|
|
UserQuanta, InstrumentationQuanta,
|
|
NumCallseqs, NumCSD, NumCSS, NumPD, NumPS, NumCliques),
|
|
Report = report_menu(ok(MenuReport))
|
|
;
|
|
Cmd = deep_cmd_root(MaybePercent),
|
|
create_root_report(Deep, MaybePercent, MaybeCliqueReport),
|
|
Report = report_clique(MaybeCliqueReport)
|
|
;
|
|
Cmd = deep_cmd_clique(CliquePtr),
|
|
create_clique_report(Deep, CliquePtr, MaybeCliqueReport),
|
|
Report = report_clique(MaybeCliqueReport)
|
|
;
|
|
Cmd = deep_cmd_clique_recursive_costs(CliquePtr),
|
|
create_clique_recursion_costs_report(Deep, CliquePtr,
|
|
MaybeCliqueRecursionReport),
|
|
Report = report_clique_recursion_costs(MaybeCliqueRecursionReport)
|
|
;
|
|
Cmd = deep_cmd_proc(PSPtr),
|
|
create_proc_report(Deep, PSPtr, MaybeProcReport),
|
|
Report = report_proc(MaybeProcReport)
|
|
;
|
|
Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum,
|
|
CallersPerBunch, Contour),
|
|
create_proc_callers_report(Deep, PSPtr, CallerGroups, BunchNum,
|
|
CallersPerBunch, Contour, MaybeProcCallersReport),
|
|
Report = report_proc_callers(MaybeProcCallersReport)
|
|
;
|
|
(
|
|
Cmd = deep_cmd_static_procrep_coverage(PSPtr),
|
|
create_static_procrep_coverage_report(Deep, PSPtr,
|
|
MaybeProcrepCoverageReport)
|
|
;
|
|
Cmd = deep_cmd_dynamic_procrep_coverage(PDPtr),
|
|
create_dynamic_procrep_coverage_report(Deep, PDPtr,
|
|
MaybeProcrepCoverageReport)
|
|
),
|
|
Report = report_procrep_coverage(MaybeProcrepCoverageReport)
|
|
;
|
|
Cmd = deep_cmd_call_site_dynamic_var_use(CSDPtr),
|
|
create_call_site_dynamic_var_use_report(Deep, CSDPtr, MaybeVarUse),
|
|
Report = report_call_site_dynamic_var_use(MaybeVarUse)
|
|
;
|
|
Cmd = deep_cmd_recursion_types_frequency,
|
|
create_recursion_types_frequency_report(Deep, MaybeRecTypesFreqReport),
|
|
Report = report_recursion_types_frequency(MaybeRecTypesFreqReport)
|
|
;
|
|
Cmd = deep_cmd_program_modules,
|
|
create_program_modules_report(Deep, MaybeProgramModulesReport),
|
|
Report = report_program_modules(MaybeProgramModulesReport)
|
|
;
|
|
Cmd = deep_cmd_module(ModuleName),
|
|
create_module_report(Deep, ModuleName, MaybeModuleReport),
|
|
Report = report_module(MaybeModuleReport)
|
|
;
|
|
Cmd = deep_cmd_module_getter_setters(ModuleName),
|
|
create_module_getter_setter_report(Deep, ModuleName,
|
|
MaybeModuleGetterSettersReport),
|
|
Report = report_module_getter_setters(MaybeModuleGetterSettersReport)
|
|
;
|
|
Cmd = deep_cmd_module_rep(ModuleName),
|
|
create_module_rep_report(Deep, ModuleName, MaybeModuleRepReport),
|
|
Report = report_module_rep(MaybeModuleRepReport)
|
|
;
|
|
Cmd = deep_cmd_top_procs(Limit, CostKind, InclDesc, Scope),
|
|
create_top_procs_report(Deep, Limit, CostKind, InclDesc, Scope,
|
|
MaybeTopProcsReport),
|
|
Report = report_top_procs(MaybeTopProcsReport)
|
|
;
|
|
Cmd = deep_cmd_dump_clique(CliquePtr),
|
|
create_clique_dump_report(Deep, CliquePtr, MaybeCliqueDump),
|
|
Report = report_clique_dump(MaybeCliqueDump)
|
|
;
|
|
Cmd = deep_cmd_dump_proc_static(PSPtr),
|
|
create_proc_static_dump_report(Deep, PSPtr, MaybeProcStaticDump),
|
|
Report = report_proc_static_dump(MaybeProcStaticDump)
|
|
;
|
|
Cmd = deep_cmd_dump_proc_dynamic(PDPtr),
|
|
create_proc_dynamic_dump_report(Deep, PDPtr, MaybeProcDynamicDump),
|
|
Report = report_proc_dynamic_dump(MaybeProcDynamicDump)
|
|
;
|
|
Cmd = deep_cmd_dump_call_site_static(CSSPtr),
|
|
create_call_site_static_dump_report(Deep, CSSPtr,
|
|
MaybeCallSiteStaticDump),
|
|
Report = report_call_site_static_dump(MaybeCallSiteStaticDump)
|
|
;
|
|
Cmd = deep_cmd_dump_call_site_dynamic(CSDPtr),
|
|
create_call_site_dynamic_dump_report(Deep, CSDPtr,
|
|
MaybeCallSiteStaticDump),
|
|
Report = report_call_site_dynamic_dump(MaybeCallSiteStaticDump)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code to build a clique report for the root clique, or the clique where
|
|
% the action begins.
|
|
%
|
|
|
|
:- pred create_root_report(deep::in, maybe(int)::in,
|
|
maybe_error(clique_report)::out) is det.
|
|
|
|
create_root_report(Deep, MaybePercent, MaybeReport) :-
|
|
deep_lookup_clique_index(Deep, Deep ^ root, RootCliquePtr),
|
|
create_clique_report(Deep, RootCliquePtr, MaybeRootCliqueReport),
|
|
(
|
|
MaybeRootCliqueReport = error(_),
|
|
MaybeReport = MaybeRootCliqueReport
|
|
;
|
|
MaybeRootCliqueReport = ok(RootCliqueReport),
|
|
(
|
|
MaybePercent = no,
|
|
MaybeReport = ok(RootCliqueReport)
|
|
;
|
|
MaybePercent = yes(Percent),
|
|
find_start_of_action(Deep, Percent, RootCliqueReport, Report),
|
|
MaybeReport = ok(Report)
|
|
)
|
|
).
|
|
|
|
:- pred find_start_of_action(deep::in, int::in,
|
|
clique_report::in, clique_report::out) is det.
|
|
|
|
find_start_of_action(Deep, Percent, CurrentReport, SelectedReport) :-
|
|
CurrentReport = clique_report(_, _, CliqueProcs),
|
|
list.foldl(find_start_of_action_clique_proc(Percent), CliqueProcs,
|
|
[], ActionCliquePtrs),
|
|
( if
|
|
ActionCliquePtrs = [ActionCliquePtr],
|
|
create_clique_report(Deep, ActionCliquePtr, MaybeActionCliqueReport),
|
|
MaybeActionCliqueReport = ok(ActionCliqueReport)
|
|
then
|
|
find_start_of_action(Deep, Percent, ActionCliqueReport, SelectedReport)
|
|
else
|
|
SelectedReport = CurrentReport
|
|
).
|
|
|
|
:- pred find_start_of_action_clique_proc(int::in, clique_proc_report::in,
|
|
list(clique_ptr)::in, list(clique_ptr)::out) is det.
|
|
|
|
find_start_of_action_clique_proc(Percent, CliqueProcReport,
|
|
!ActionCliquePtrs) :-
|
|
CliqueProcReport = clique_proc_report(_, FirstPDReport, LaterPDReports),
|
|
find_start_of_action_clique_proc_dynamic(Percent,
|
|
FirstPDReport, !ActionCliquePtrs),
|
|
list.foldl(find_start_of_action_clique_proc_dynamic(Percent),
|
|
LaterPDReports, !ActionCliquePtrs).
|
|
|
|
:- pred find_start_of_action_clique_proc_dynamic(int::in,
|
|
clique_proc_dynamic_report::in,
|
|
list(clique_ptr)::in, list(clique_ptr)::out) is det.
|
|
|
|
find_start_of_action_clique_proc_dynamic(Percent, CliquePDReport,
|
|
!ActionCliquePtrs) :-
|
|
CliquePDReport = clique_proc_dynamic_report(_, CallSites),
|
|
list.foldl(find_start_of_action_call_site(Percent), CallSites,
|
|
!ActionCliquePtrs).
|
|
|
|
:- pred find_start_of_action_call_site(int::in, clique_call_site_report::in,
|
|
list(clique_ptr)::in, list(clique_ptr)::out) is det.
|
|
|
|
find_start_of_action_call_site(Percent, CallSiteReport, !ActionCliquePtrs) :-
|
|
CallSiteReport = clique_call_site_report(_, _, CalleeRowDatas),
|
|
list.foldl(find_start_of_action_callee(Percent), CalleeRowDatas,
|
|
!ActionCliquePtrs).
|
|
|
|
:- pred find_start_of_action_callee(int::in, perf_row_data(clique_desc)::in,
|
|
list(clique_ptr)::in, list(clique_ptr)::out) is det.
|
|
|
|
find_start_of_action_callee(Percent, RowData, !ActionCliquePtrs) :-
|
|
MaybeTotalPerf = RowData ^ perf_row_maybe_total,
|
|
(
|
|
MaybeTotalPerf = no,
|
|
unexpected($pred, "no total perf")
|
|
;
|
|
MaybeTotalPerf = yes(TotalPerf),
|
|
CallSeqsPercent = TotalPerf ^ perf_row_callseqs_percent,
|
|
( if percent_at_or_above_threshold(Percent, CallSeqsPercent) then
|
|
CliqueDesc = RowData ^ perf_row_subject,
|
|
CliquePtr = CliqueDesc ^ cdesc_clique_ptr,
|
|
!:ActionCliquePtrs = [CliquePtr | !.ActionCliquePtrs]
|
|
else
|
|
true
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code to build a clique report.
|
|
%
|
|
|
|
create_clique_report(Deep, CliquePtr, MaybeCliqueReport) :-
|
|
AncestorRowDatas = find_clique_ancestors(Deep, CliquePtr),
|
|
|
|
deep_lookup_clique_members(Deep, CliquePtr, PDPtrs),
|
|
list.foldl(group_proc_dynamics_by_proc_static(Deep), PDPtrs,
|
|
map.init, PStoPDsMap),
|
|
map.to_assoc_list(PStoPDsMap, PStoPDsList0),
|
|
deep_lookup_clique_parents(Deep, CliquePtr, EntryCSDPtr),
|
|
( if valid_call_site_dynamic_ptr(Deep, EntryCSDPtr) then
|
|
deep_lookup_call_site_dynamics(Deep, EntryCSDPtr, EntryCSD),
|
|
EntryPDPtr = EntryCSD ^ csd_callee,
|
|
list.filter(proc_group_contains(EntryPDPtr), PStoPDsList0,
|
|
EntryGroup, RestGroup),
|
|
PStoPDsList = EntryGroup ++ RestGroup
|
|
else
|
|
PStoPDsList = PStoPDsList0
|
|
),
|
|
list.map(create_clique_proc_report(Deep, CliquePtr),
|
|
PStoPDsList, CliqueProcs),
|
|
|
|
CliqueReport = clique_report(CliquePtr, AncestorRowDatas, CliqueProcs),
|
|
MaybeCliqueReport = ok(CliqueReport).
|
|
|
|
:- func find_clique_ancestors(deep, clique_ptr) =
|
|
list(perf_row_data(ancestor_desc)).
|
|
|
|
find_clique_ancestors(Deep, CliquePtr) = Ancestors :-
|
|
deep_lookup_clique_parents(Deep, CliquePtr, EntryCSDPtr),
|
|
( if valid_call_site_dynamic_ptr(Deep, EntryCSDPtr) then
|
|
deep_lookup_call_site_dynamics(Deep, EntryCSDPtr, EntryCSD),
|
|
EntryPDPtr = EntryCSD ^ csd_caller,
|
|
( if EntryPDPtr = Deep ^ root then
|
|
% We could return the true root node, which is the Mercury runtime
|
|
% system, but that is of no interest to either users or programs.
|
|
Ancestors = []
|
|
else
|
|
deep_lookup_clique_index(Deep, EntryPDPtr, EntryCliquePtr),
|
|
CalleePDPtr = EntryCSD ^ csd_callee,
|
|
deep_lookup_proc_dynamics(Deep, CalleePDPtr, CalleePD),
|
|
CalleePSPtr = CalleePD ^ pd_proc_static,
|
|
CalleeDesc = describe_proc(Deep, CalleePSPtr),
|
|
deep_lookup_call_site_static_map(Deep, EntryCSDPtr, EntryCSSPtr),
|
|
EntryCallSiteDesc = describe_call_site(Deep, EntryCSSPtr),
|
|
AncestorDesc = ancestor_desc(EntryCliquePtr, CliquePtr,
|
|
CalleeDesc, EntryCallSiteDesc),
|
|
Own = EntryCSD ^ csd_own_prof,
|
|
deep_lookup_csd_desc(Deep, EntryCSDPtr, Desc),
|
|
own_and_inherit_to_perf_row_data(Deep, AncestorDesc, Own, Desc,
|
|
Parent),
|
|
MoreAncestors = find_clique_ancestors(Deep, EntryCliquePtr),
|
|
Ancestors = [Parent | MoreAncestors]
|
|
)
|
|
else
|
|
Ancestors = []
|
|
).
|
|
|
|
:- pred group_proc_dynamics_by_proc_static(deep::in, proc_dynamic_ptr::in,
|
|
map(proc_static_ptr, list(proc_dynamic_ptr))::in,
|
|
map(proc_static_ptr, list(proc_dynamic_ptr))::out) is det.
|
|
|
|
group_proc_dynamics_by_proc_static(Deep, PDPtr, !PStoPDsMap) :-
|
|
require(valid_proc_dynamic_ptr(Deep, PDPtr),
|
|
"group_proc_dynamics_by_proc_static: invalid PDPtr"),
|
|
deep_lookup_proc_dynamics(Deep, PDPtr, PD),
|
|
PSPtr = PD ^ pd_proc_static,
|
|
( if map.search(!.PStoPDsMap, PSPtr, PSPDs0) then
|
|
PSPDs = [PDPtr | PSPDs0],
|
|
map.det_update(PSPtr, PSPDs, !PStoPDsMap)
|
|
else
|
|
map.det_insert(PSPtr, [PDPtr], !PStoPDsMap)
|
|
).
|
|
|
|
:- pred proc_group_contains(proc_dynamic_ptr::in,
|
|
pair(proc_static_ptr, list(proc_dynamic_ptr))::in) is semidet.
|
|
|
|
proc_group_contains(EntryPDPtr, _ - PDPtrs) :-
|
|
list.member(EntryPDPtr, PDPtrs).
|
|
|
|
:- pred create_clique_proc_report(deep::in, clique_ptr::in,
|
|
pair(proc_static_ptr, list(proc_dynamic_ptr))::in,
|
|
clique_proc_report::out) is det.
|
|
|
|
create_clique_proc_report(Deep, CliquePtr, PSPtr - PDPtrs, CliqueProcReport) :-
|
|
(
|
|
PDPtrs = [],
|
|
unexpected($pred, "PDPtrs = []")
|
|
;
|
|
PDPtrs = [FirstPDPtr | LaterPDPtrs],
|
|
ProcDesc = describe_proc(Deep, PSPtr),
|
|
create_clique_proc_dynamic_report(Deep, CliquePtr, ProcDesc,
|
|
FirstPDPtr, FirstPDOwn, FirstPDDesc, FirstPDReport),
|
|
list.map3(create_clique_proc_dynamic_report(Deep, CliquePtr, ProcDesc),
|
|
LaterPDPtrs, LaterPDOwns, LaterPDDescs, LaterPDReports),
|
|
SummaryOwn = sum_own_infos([FirstPDOwn | LaterPDOwns]),
|
|
SummaryDesc = sum_inherit_infos([FirstPDDesc | LaterPDDescs]),
|
|
own_and_inherit_to_perf_row_data(Deep, ProcDesc,
|
|
SummaryOwn, SummaryDesc, ProcSummaryRowData),
|
|
CliqueProcReport = clique_proc_report(ProcSummaryRowData,
|
|
FirstPDReport, LaterPDReports)
|
|
).
|
|
|
|
:- pred create_clique_proc_dynamic_report(deep::in, clique_ptr::in,
|
|
proc_desc::in, proc_dynamic_ptr::in,
|
|
own_prof_info::out, inherit_prof_info::out,
|
|
clique_proc_dynamic_report::out) is det.
|
|
|
|
create_clique_proc_dynamic_report(Deep, _CliquePtr, ProcDesc, PDPtr,
|
|
Own, Desc, CliquePDReport) :-
|
|
( if valid_proc_dynamic_ptr(Deep, PDPtr) then
|
|
deep_lookup_pd_own(Deep, PDPtr, Own),
|
|
deep_lookup_pd_desc(Deep, PDPtr, Desc),
|
|
own_and_inherit_to_perf_row_data(Deep, ProcDesc, Own, Desc,
|
|
PDRowData),
|
|
deep_lookup_proc_dynamics(Deep, PDPtr, PD),
|
|
PSPtr = PD ^ pd_proc_static,
|
|
require(unify(PSPtr, ProcDesc ^ pdesc_ps_ptr),
|
|
"create_clique_proc_dynamic_report: psptr mismatch"),
|
|
create_child_call_site_reports(Deep, PDPtr, CliqueCallSiteReports),
|
|
CliquePDReport = clique_proc_dynamic_report(PDRowData,
|
|
CliqueCallSiteReports)
|
|
else
|
|
unexpected($pred, "invalid proc_dynamic index")
|
|
).
|
|
|
|
:- pred create_child_call_site_reports(deep::in, proc_dynamic_ptr::in,
|
|
list(clique_call_site_report)::out) is det.
|
|
|
|
create_child_call_site_reports(Deep, PDPtr, CliqueCallSiteReports) :-
|
|
proc_dynamic_paired_call_site_slots(Deep, PDPtr, PairedSlots),
|
|
list.map(create_child_call_site_report(Deep), PairedSlots,
|
|
CliqueCallSiteReports).
|
|
|
|
:- pred create_child_call_site_report(deep::in,
|
|
pair(call_site_static_ptr, call_site_array_slot)::in,
|
|
clique_call_site_report::out) is det.
|
|
|
|
create_child_call_site_report(Deep, Pair, CliqueCallSiteReport) :-
|
|
Pair = CSSPtr - CallSiteArraySlot,
|
|
deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
|
|
CallSiteDesc = describe_call_site(Deep, CSSPtr),
|
|
Kind = CSS ^ css_kind,
|
|
(
|
|
Kind = normal_call_and_callee(CalleePSPtr, TypeSubst),
|
|
KnownCalleeDesc = describe_proc(Deep, CalleePSPtr),
|
|
ProcDescKind = normal_call_and_callee(KnownCalleeDesc, TypeSubst),
|
|
(
|
|
CallSiteArraySlot = slot_normal(CSDPtr)
|
|
;
|
|
CallSiteArraySlot = slot_multi(_, _),
|
|
unexpected($pred, "normal_call is multi")
|
|
),
|
|
( if valid_call_site_dynamic_ptr(Deep, CSDPtr) then
|
|
create_callee_clique_perf_row_data(Deep, CSDPtr, Own, Desc,
|
|
CalleeCliqueRowData),
|
|
CalleeCliqueRowDatas = [CalleeCliqueRowData]
|
|
else
|
|
Own = zero_own_prof_info,
|
|
Desc = zero_inherit_prof_info,
|
|
CalleeCliqueRowDatas = []
|
|
)
|
|
;
|
|
(
|
|
Kind = special_call_and_no_callee,
|
|
ProcDescKind = special_call_and_no_callee
|
|
;
|
|
Kind = higher_order_call_and_no_callee,
|
|
ProcDescKind = higher_order_call_and_no_callee
|
|
;
|
|
Kind = method_call_and_no_callee,
|
|
ProcDescKind = method_call_and_no_callee
|
|
;
|
|
Kind = callback_and_no_callee,
|
|
ProcDescKind = callback_and_no_callee
|
|
),
|
|
(
|
|
CallSiteArraySlot = slot_normal(_),
|
|
unexpected($pred, "non-normal_call is normal")
|
|
;
|
|
CallSiteArraySlot = slot_multi(_IsZeroed, CSDPtrsArray),
|
|
array.to_list(CSDPtrsArray, CSDPtrs)
|
|
),
|
|
list.map3(create_callee_clique_perf_row_data(Deep), CSDPtrs,
|
|
Owns, Descs, CalleeCliqueRowDatas),
|
|
Own = sum_own_infos(Owns),
|
|
Desc = sum_inherit_infos(Descs)
|
|
),
|
|
own_and_inherit_to_perf_row_data(Deep, CallSiteDesc, Own, Desc,
|
|
SummaryRowData),
|
|
CliqueCallSiteReport = clique_call_site_report(SummaryRowData,
|
|
ProcDescKind, CalleeCliqueRowDatas).
|
|
|
|
:- pred create_callee_clique_perf_row_data(deep::in, call_site_dynamic_ptr::in,
|
|
own_prof_info::out, inherit_prof_info::out,
|
|
perf_row_data(clique_desc)::out) is det.
|
|
|
|
create_callee_clique_perf_row_data(Deep, CSDPtr, Own, Desc,
|
|
CalleeCliqueRowData) :-
|
|
require(valid_call_site_dynamic_ptr(Deep, CSDPtr),
|
|
"create_callee_clique_perf_row_data: invalid call_site_dynamic_ptr"),
|
|
deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
|
|
CalleePDPtr = CSD ^ csd_callee,
|
|
Own = CSD ^ csd_own_prof,
|
|
deep_lookup_csd_desc(Deep, CSDPtr, Desc),
|
|
deep_lookup_clique_index(Deep, CalleePDPtr, CalleeCliquePtr),
|
|
CliqueDesc = describe_clique(Deep, CalleeCliquePtr, yes(CalleePDPtr)),
|
|
own_and_inherit_to_perf_row_data(Deep, CliqueDesc, Own, Desc,
|
|
CalleeCliqueRowData).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code to build a proc report.
|
|
%
|
|
|
|
create_proc_report(Deep, PSPtr, MaybeProcReport) :-
|
|
( if valid_proc_static_ptr(Deep, PSPtr) then
|
|
ProcDesc = describe_proc(Deep, PSPtr),
|
|
deep_lookup_ps_own(Deep, PSPtr, Own),
|
|
deep_lookup_ps_desc(Deep, PSPtr, Desc),
|
|
own_and_inherit_to_perf_row_data(Deep, ProcDesc, Own, Desc,
|
|
ProcSummaryRowData),
|
|
|
|
deep_lookup_proc_statics(Deep, PSPtr, PS),
|
|
CallSitesArray = PS ^ ps_sites,
|
|
array.to_list(CallSitesArray, CallSites),
|
|
ProcCallSiteSummaryRowDatas = list.map(create_call_site_summary(Deep),
|
|
CallSites),
|
|
|
|
deep_lookup_proc_callers(Deep, PSPtr, CallerCSDPtrs0),
|
|
summarize_callers(Deep, CallerCSDPtrs0, PSPtr, set.init, SeenProcs,
|
|
0, NumDynamic, zero_own_prof_info, CallersOwn,
|
|
zero_inherit_prof_info, CallersInherit),
|
|
NumStatic = set.count(SeenProcs),
|
|
own_and_inherit_to_perf_row_data(Deep,
|
|
callers_counts(NumStatic, NumDynamic), CallersOwn,
|
|
CallersInherit, CallersSummaryRowData),
|
|
|
|
ProcReport = proc_report(CallersSummaryRowData, ProcSummaryRowData,
|
|
ProcCallSiteSummaryRowDatas),
|
|
MaybeProcReport = ok(ProcReport)
|
|
else
|
|
MaybeProcReport = error("invalid proc_static index")
|
|
).
|
|
|
|
:- func create_call_site_summary(deep, call_site_static_ptr) = call_site_perf.
|
|
|
|
create_call_site_summary(Deep, CSSPtr) = CallSitePerf :-
|
|
CallSiteDesc = describe_call_site(Deep, CSSPtr),
|
|
|
|
deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
|
|
KindAndCallee = CSS ^ css_kind,
|
|
CallerPSPtr = CSS ^ css_container,
|
|
|
|
deep_lookup_call_site_calls(Deep, CSSPtr, CallSiteCallMap),
|
|
map.to_assoc_list(CallSiteCallMap, CallSiteCalls),
|
|
|
|
(
|
|
KindAndCallee = normal_call_and_callee(CalleePSPtr, TypeSubstStr),
|
|
CalleeDesc = describe_proc(Deep, CalleePSPtr),
|
|
NormalCallId = normal_callee_id(CalleeDesc, TypeSubstStr),
|
|
KindAndInfo = normal_call_and_info(NormalCallId),
|
|
(
|
|
CallSiteCalls = [],
|
|
Own = zero_own_prof_info,
|
|
Desc = zero_inherit_prof_info
|
|
;
|
|
CallSiteCalls = [CallSiteCall],
|
|
CallSiteCall = CalleePSPtrFromCall - _,
|
|
require(unify(CalleePSPtr, CalleePSPtrFromCall),
|
|
"create_call_site_summary: callee mismatch"),
|
|
CallSiteCalleePerf = generate_call_site_callee_perf(Deep,
|
|
CallerPSPtr, CallSiteCall),
|
|
CallSiteCalleePerf = call_site_callee_perf(_, Own, Desc)
|
|
;
|
|
CallSiteCalls = [_, _ | _],
|
|
unexpected($pred, ">1 proc called at normal site")
|
|
),
|
|
own_and_inherit_to_perf_row_data(Deep, CallSiteDesc, Own, Desc,
|
|
SummaryRowData),
|
|
SubRowDatas = []
|
|
;
|
|
(
|
|
KindAndCallee = special_call_and_no_callee,
|
|
KindAndInfo = special_call_and_no_info
|
|
;
|
|
KindAndCallee = higher_order_call_and_no_callee,
|
|
KindAndInfo = higher_order_call_and_no_info
|
|
;
|
|
KindAndCallee = method_call_and_no_callee,
|
|
KindAndInfo = method_call_and_no_info
|
|
;
|
|
KindAndCallee = callback_and_no_callee,
|
|
KindAndInfo = callback_and_no_info
|
|
),
|
|
CallSiteCalleePerfs =
|
|
list.map(generate_call_site_callee_perf(Deep, CallerPSPtr),
|
|
CallSiteCalls),
|
|
list.map_foldl2(accumulate_call_site_callees(Deep),
|
|
CallSiteCalleePerfs, SubRowDatas,
|
|
zero_own_prof_info, SumOwn, zero_inherit_prof_info, SumDesc),
|
|
own_and_inherit_to_perf_row_data(Deep, CallSiteDesc, SumOwn, SumDesc,
|
|
SummaryRowData)
|
|
),
|
|
CallSitePerf = call_site_perf(KindAndInfo, SummaryRowData, SubRowDatas).
|
|
|
|
:- type call_site_callee_perf
|
|
---> call_site_callee_perf(
|
|
cscpi_callee :: proc_static_ptr,
|
|
cscpi_own_prof_info :: own_prof_info,
|
|
cscpi_inherit_prof_info :: inherit_prof_info
|
|
).
|
|
|
|
:- func generate_call_site_callee_perf(deep, proc_static_ptr,
|
|
pair(proc_static_ptr, list(call_site_dynamic_ptr)))
|
|
= call_site_callee_perf.
|
|
|
|
generate_call_site_callee_perf(Deep, CallerPSPtr, PSPtr - CSDPtrs)
|
|
= CalleeProf :-
|
|
list.foldl2(accumulate_csd_prof_info(Deep, CallerPSPtr), CSDPtrs,
|
|
zero_own_prof_info, Own, zero_inherit_prof_info, Desc),
|
|
CalleeProf = call_site_callee_perf(PSPtr, Own, Desc).
|
|
|
|
:- pred summarize_callers(deep::in, list(call_site_dynamic_ptr)::in,
|
|
proc_static_ptr::in, set(proc_static_ptr)::in, set(proc_static_ptr)::out,
|
|
int::in, int::out, own_prof_info::in, own_prof_info::out,
|
|
inherit_prof_info::in, inherit_prof_info::out) is det.
|
|
|
|
summarize_callers(Deep, CallerCSDPtrs0, CalleePSPtr, !PSSeen, !NumDynamic,
|
|
!Own, !Desc) :-
|
|
(
|
|
CallerCSDPtrs0 = []
|
|
;
|
|
CallerCSDPtrs0 = [CSDPtr | CallerCSDPtrs],
|
|
|
|
deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
|
|
CallerPDPtr = CSD ^ csd_caller,
|
|
deep_lookup_proc_dynamics(Deep, CallerPDPtr, CallerPD),
|
|
CallerPSPtr = CallerPD ^ pd_proc_static,
|
|
( if CallerPSPtr = CalleePSPtr then
|
|
% Exclude recursive calls.
|
|
true
|
|
else
|
|
!:NumDynamic = !.NumDynamic + 1,
|
|
set.insert(CallerPSPtr, !PSSeen),
|
|
CSDOwn = CSD ^ csd_own_prof,
|
|
!:Own = add_own_to_own(!.Own, CSDOwn),
|
|
deep_lookup_csd_desc(Deep, CSDPtr, CSDInherit),
|
|
!:Desc = add_inherit_to_inherit(!.Desc, CSDInherit)
|
|
),
|
|
|
|
summarize_callers(Deep, CallerCSDPtrs, CalleePSPtr, !PSSeen,
|
|
!NumDynamic, !Own, !Desc)
|
|
).
|
|
|
|
:- pred accumulate_csd_prof_info(deep::in, proc_static_ptr::in,
|
|
call_site_dynamic_ptr::in,
|
|
own_prof_info::in, own_prof_info::out,
|
|
inherit_prof_info::in, inherit_prof_info::out) is det.
|
|
|
|
accumulate_csd_prof_info(Deep, CallerPSPtr, CSDPtr, !Own, !Desc) :-
|
|
deep_lookup_csd_own(Deep, CSDPtr, CSDOwn),
|
|
deep_lookup_csd_desc(Deep, CSDPtr, CSDDesc),
|
|
!:Own = add_own_to_own(!.Own, CSDOwn),
|
|
!:Desc = add_inherit_to_inherit(!.Desc, CSDDesc),
|
|
deep_lookup_csd_comp_table(Deep, CSDPtr, CompTableArray),
|
|
( if map.search(CompTableArray, CallerPSPtr, InnerTotal) then
|
|
!:Desc = subtract_inherit_from_inherit(InnerTotal, !.Desc)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred accumulate_call_site_callees(deep::in,
|
|
call_site_callee_perf::in, perf_row_data(proc_desc)::out,
|
|
own_prof_info::in, own_prof_info::out,
|
|
inherit_prof_info::in, inherit_prof_info::out) is det.
|
|
|
|
accumulate_call_site_callees(Deep, CalleePerf, RowData, !Own, !Desc) :-
|
|
CalleePerf = call_site_callee_perf(CalleePSPtr, CalleeOwn, CalleeDesc),
|
|
CalleeProcDesc = describe_proc(Deep, CalleePSPtr),
|
|
own_and_inherit_to_perf_row_data(Deep, CalleeProcDesc,
|
|
CalleeOwn, CalleeDesc, RowData),
|
|
!:Own = add_own_to_own(!.Own, CalleeOwn),
|
|
!:Desc = add_inherit_to_inherit(!.Desc, CalleeDesc).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code to build a proc_callers report.
|
|
%
|
|
|
|
:- pred create_proc_callers_report(deep::in, proc_static_ptr::in,
|
|
caller_groups::in, int::in, int::in, contour_exclusion::in,
|
|
maybe_error(proc_callers_report)::out) is det.
|
|
|
|
create_proc_callers_report(Deep, PSPtr, CallerGroups, BunchNum,
|
|
CallersPerBunch, Contour, MaybeProcCallersReport) :-
|
|
( if valid_proc_static_ptr(Deep, PSPtr) then
|
|
ProcDesc = describe_proc(Deep, PSPtr),
|
|
|
|
deep_lookup_proc_callers(Deep, PSPtr, CallerCSDPtrs0),
|
|
(
|
|
Contour = do_not_apply_contour_exclusion,
|
|
CallerCSDPtrPairs0 = list.map(pair_self, CallerCSDPtrs0),
|
|
MaybeCallerCSDPtrPairs = ok(CallerCSDPtrPairs0),
|
|
MaybeWarnMessage = no
|
|
;
|
|
Contour = apply_contour_exclusion,
|
|
ExcludeFile = Deep ^ exclude_contour_file,
|
|
ExcludeFile = exclude_file(ExcludeFileName, ExcludeContents),
|
|
(
|
|
ExcludeContents = no_exclude_file,
|
|
% There is no contour exclusion file, so do the same as for
|
|
% do_not_apply_contour_exclusion, but add a message to the
|
|
% report.
|
|
string.format("Could not read contour exclusion file `%s'.",
|
|
[s(ExcludeFileName)], ErrorMessage0),
|
|
MaybeCallerCSDPtrPairs = error(ErrorMessage0),
|
|
MaybeWarnMessage = no
|
|
;
|
|
ExcludeContents = unreadable_exclude_file(ErrorMsg),
|
|
string.format(
|
|
"The contour exclusion file `%s' has an error: %s.",
|
|
[s(ExcludeFileName), s(ErrorMsg)], ErrorMessage0),
|
|
MaybeCallerCSDPtrPairs = error(ErrorMessage0),
|
|
MaybeWarnMessage = no
|
|
;
|
|
ExcludeContents = readable_exclude_file(ExcludeModules,
|
|
MaybeWarnMsg),
|
|
CallerCSDPtrPairs0 = list.map(
|
|
pair_contour(Deep, ExcludeModules), CallerCSDPtrs0),
|
|
MaybeCallerCSDPtrPairs = ok(CallerCSDPtrPairs0),
|
|
(
|
|
MaybeWarnMsg = no,
|
|
MaybeWarnMessage = no
|
|
;
|
|
MaybeWarnMsg = yes(WarnMessage),
|
|
MaybeWarnMessage = yes(WarnMessage)
|
|
)
|
|
)
|
|
),
|
|
(
|
|
MaybeCallerCSDPtrPairs = error(ErrorMessage),
|
|
MaybeProcCallersReport = error(ErrorMessage)
|
|
;
|
|
MaybeCallerCSDPtrPairs = ok(CallerCSDPtrPairs),
|
|
(
|
|
CallerGroups = group_by_call_site,
|
|
CallSiteCallerGroups = group_csds_by_call_site(Deep,
|
|
CallerCSDPtrPairs),
|
|
ProcCallerCallSites = list.map(
|
|
create_proc_caller_call_sites(Deep, PSPtr),
|
|
CallSiteCallerGroups),
|
|
Callers = proc_caller_call_sites(ProcCallerCallSites)
|
|
;
|
|
CallerGroups = group_by_proc,
|
|
ProcCallerGroups = group_csds_by_procedure(Deep,
|
|
CallerCSDPtrPairs),
|
|
ProcCallerProcs = list.map(
|
|
create_proc_caller_procedures(Deep, PSPtr),
|
|
ProcCallerGroups),
|
|
Callers = proc_caller_procedures(ProcCallerProcs)
|
|
;
|
|
CallerGroups = group_by_module,
|
|
ModuleCallerGroups = group_csds_by_module(Deep,
|
|
CallerCSDPtrPairs),
|
|
ProcCallerModules = list.map(
|
|
create_proc_caller_modules(Deep, PSPtr),
|
|
ModuleCallerGroups),
|
|
Callers = proc_caller_modules(ProcCallerModules)
|
|
;
|
|
CallerGroups = group_by_clique,
|
|
CliqueCallerGroups = group_csds_by_clique(Deep,
|
|
CallerCSDPtrPairs),
|
|
ProcCallerCliques = list.map(
|
|
create_proc_caller_cliques(Deep, PSPtr),
|
|
CliqueCallerGroups),
|
|
Callers = proc_caller_cliques(ProcCallerCliques)
|
|
),
|
|
ProcCallersReport = proc_callers_report(ProcDesc, Callers,
|
|
BunchNum, CallersPerBunch, Contour, MaybeWarnMessage),
|
|
MaybeProcCallersReport = ok(ProcCallersReport)
|
|
)
|
|
else
|
|
MaybeProcCallersReport = error("invalid proc_static index")
|
|
).
|
|
|
|
:- func create_proc_caller_call_sites(deep, proc_static_ptr,
|
|
pair(call_site_static_ptr, list(call_site_dynamic_ptr)))
|
|
= perf_row_data(call_site_desc).
|
|
|
|
create_proc_caller_call_sites(Deep, CalleePSPtr, CSSPtr - CSDPtrs) =
|
|
PerfRowData :-
|
|
CallSiteDesc = describe_call_site(Deep, CSSPtr),
|
|
compute_parent_csd_prof_info(Deep, CalleePSPtr, CSDPtrs, Own, Desc),
|
|
own_and_inherit_to_perf_row_data(Deep, CallSiteDesc, Own, Desc,
|
|
PerfRowData).
|
|
|
|
:- func create_proc_caller_procedures(deep, proc_static_ptr,
|
|
pair(proc_static_ptr, list(call_site_dynamic_ptr)))
|
|
= perf_row_data(proc_desc).
|
|
|
|
create_proc_caller_procedures(Deep, CalleePSPtr, PSSPtr - CSDPtrs) =
|
|
PerfRowData :-
|
|
ProcDesc = describe_proc(Deep, PSSPtr),
|
|
compute_parent_csd_prof_info(Deep, CalleePSPtr, CSDPtrs, Own, Desc),
|
|
own_and_inherit_to_perf_row_data(Deep, ProcDesc, Own, Desc,
|
|
PerfRowData).
|
|
|
|
:- func create_proc_caller_modules(deep, proc_static_ptr,
|
|
pair(string, list(call_site_dynamic_ptr)))
|
|
= perf_row_data(string).
|
|
|
|
create_proc_caller_modules(Deep, CalleePSPtr, ModuleName - CSDPtrs) =
|
|
PerfRowData :-
|
|
compute_parent_csd_prof_info(Deep, CalleePSPtr, CSDPtrs, Own, Desc),
|
|
own_and_inherit_to_perf_row_data(Deep, ModuleName, Own, Desc,
|
|
PerfRowData).
|
|
|
|
:- func create_proc_caller_cliques(deep, proc_static_ptr,
|
|
pair(clique_ptr, list(call_site_dynamic_ptr)))
|
|
= perf_row_data(clique_desc).
|
|
|
|
create_proc_caller_cliques(Deep, CalleePSPtr, CliquePtr - CSDPtrs) =
|
|
PerfRowData :-
|
|
CliqueDesc = describe_clique(Deep, CliquePtr, no),
|
|
compute_parent_csd_prof_info(Deep, CalleePSPtr, CSDPtrs, Own, Desc),
|
|
own_and_inherit_to_perf_row_data(Deep, CliqueDesc, Own, Desc,
|
|
PerfRowData).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code to create the coverage annotated procedure representation report.
|
|
%
|
|
|
|
create_static_procrep_coverage_report(Deep, PSPtr, MaybeReport) :-
|
|
( if valid_proc_static_ptr(Deep, PSPtr) then
|
|
deep_lookup_ps_coverage(Deep, PSPtr, StaticCoverage),
|
|
MaybeCoveragePoints =
|
|
static_coverage_maybe_get_coverage_points(StaticCoverage),
|
|
|
|
% Gather call site information.
|
|
deep_lookup_proc_statics(Deep, PSPtr, PS),
|
|
CallSitesArray = PS ^ ps_sites,
|
|
array.foldl(build_static_call_site_cost_and_callee_map(Deep),
|
|
CallSitesArray, map.init, CallSitesMap),
|
|
|
|
% Gather information about the procedure.
|
|
deep_lookup_ps_own(Deep, PSPtr, Own),
|
|
|
|
maybe_create_procrep_coverage_report(Deep, PSPtr, Own,
|
|
MaybeCoveragePoints, CallSitesMap, MaybeReport)
|
|
else
|
|
PSPtr = proc_static_ptr(PSId),
|
|
MaybeReport = error(
|
|
string.format("Proc static pointer is invalid %d", [i(PSId)]))
|
|
).
|
|
|
|
create_dynamic_procrep_coverage_report(Deep, PDPtr, MaybeReport) :-
|
|
( if valid_proc_dynamic_ptr(Deep, PDPtr) then
|
|
deep_lookup_proc_dynamics(Deep, PDPtr, PD),
|
|
PSPtr = PD ^ pd_proc_static,
|
|
MaybeCoveragePoints = PD ^ pd_maybe_coverage_points,
|
|
|
|
% Gather call site information.
|
|
proc_dynamic_paired_call_site_slots(Deep, PDPtr, Slots),
|
|
foldl(build_dynamic_call_site_cost_and_callee_map(Deep), Slots,
|
|
map.init, CallSitesMap),
|
|
|
|
% Gather information about the procedure.
|
|
deep_lookup_pd_own(Deep, PDPtr, Own),
|
|
|
|
maybe_create_procrep_coverage_report(Deep, PSPtr, Own,
|
|
MaybeCoveragePoints, CallSitesMap, MaybeReport)
|
|
else
|
|
PDPtr = proc_dynamic_ptr(PDId),
|
|
MaybeReport = error(
|
|
string.format("Proc dynamic pointer is invalid %d", [i(PDId)]))
|
|
).
|
|
|
|
:- pred maybe_create_procrep_coverage_report(deep::in, proc_static_ptr::in,
|
|
own_prof_info::in, maybe(array(int))::in,
|
|
map(reverse_goal_path, cost_and_callees(Callee))::in,
|
|
maybe_error(procrep_coverage_info)::out) is det.
|
|
|
|
maybe_create_procrep_coverage_report(_, _, _, no, _, error(Error)) :-
|
|
Error = "No coverage information available".
|
|
maybe_create_procrep_coverage_report(Deep, PSPtr, Own,
|
|
yes(CoveragePointsArray), CallSitesMap, MaybeReport) :-
|
|
deep_lookup_proc_statics(Deep, PSPtr, PS),
|
|
coverage_point_arrays_to_list(PS ^ ps_coverage_point_infos,
|
|
CoveragePointsArray, CoveragePoints),
|
|
deep_get_maybe_procrep(Deep, PSPtr, MaybeProcRep0),
|
|
(
|
|
MaybeProcRep0 = error(Error),
|
|
MaybeReport = error(Error)
|
|
;
|
|
MaybeProcRep0 = ok(ProcRep0),
|
|
|
|
foldl2(add_coverage_point_to_map,
|
|
CoveragePoints, map.init, SolnsCoveragePointMap,
|
|
map.init, BranchCoveragePointMap),
|
|
Goal0 = ProcRep0 ^ pr_defn ^ pdr_goal,
|
|
label_goals(LastGoalId, ContainingGoalMap, Goal0, Goal),
|
|
ProcRep = ProcRep0 ^ pr_defn ^ pdr_goal := Goal,
|
|
procrep_annotate_with_coverage(ProcRep, Own, CallSitesMap,
|
|
SolnsCoveragePointMap, BranchCoveragePointMap,
|
|
ContainingGoalMap, LastGoalId, CoverageArray),
|
|
MaybeReport = ok(procrep_coverage_info(PSPtr, ProcRep, CoverageArray))
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code to build a var use report for a call site.
|
|
%
|
|
|
|
create_call_site_dynamic_var_use_report(Deep, CSDPtr, MaybeVarUseInfo) :-
|
|
( if valid_call_site_dynamic_ptr(Deep, CSDPtr) then
|
|
deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
|
|
CalleePDPtr = CSD ^ csd_callee,
|
|
CallerPDPtr = CSD ^ csd_caller,
|
|
deep_lookup_proc_dynamics(Deep, CalleePDPtr, CalleePD),
|
|
CalleePSPtr = CalleePD ^ pd_proc_static,
|
|
deep_get_maybe_procrep(Deep, CalleePSPtr, MaybeProcrep),
|
|
(
|
|
MaybeProcrep = ok(Procrep),
|
|
HeadVars = Procrep ^ pr_defn ^ pdr_head_vars,
|
|
VarNameTable = Procrep ^ pr_defn ^ pdr_var_name_table,
|
|
deep_lookup_clique_index(Deep, CallerPDPtr, ParentCliquePtr),
|
|
deep_lookup_clique_index(Deep, CalleePDPtr, CalleeCliquePtr),
|
|
create_clique_recursion_costs_report(Deep, ParentCliquePtr,
|
|
MaybeRecursiveCostsReport),
|
|
(
|
|
MaybeRecursiveCostsReport = ok(RecursiveCostsReport),
|
|
RecursionType = RecursiveCostsReport ^ crr_recursion_type,
|
|
( if ParentCliquePtr = CalleeCliquePtr then
|
|
get_recursive_csd_cost(Deep, CSDPtr, RecursionType,
|
|
MaybeCost)
|
|
else
|
|
deep_lookup_csd_desc(Deep, CSDPtr, Desc),
|
|
deep_lookup_csd_own(Deep, CSDPtr, Own),
|
|
Cost0 = callseqs(Own) + inherit_callseqs(Desc),
|
|
MaybeCost = ok(float(Cost0))
|
|
),
|
|
(
|
|
MaybeCost = ok(Cost),
|
|
map_foldl(call_site_dynamic_var_use_arg(Deep, CSDPtr,
|
|
RecursionType, Cost, VarNameTable),
|
|
HeadVars, Uses0, 0, _),
|
|
list_maybe_error_to_maybe_error_list(Uses0, MaybeUses),
|
|
(
|
|
MaybeUses = ok(Uses),
|
|
VarUseInfo =
|
|
call_site_dynamic_var_use_info(Cost, Uses),
|
|
MaybeVarUseInfo = ok(VarUseInfo)
|
|
;
|
|
MaybeUses = error(Error),
|
|
MaybeVarUseInfo = error(Error)
|
|
)
|
|
;
|
|
MaybeCost = error(Error),
|
|
MaybeVarUseInfo = error(Error)
|
|
)
|
|
;
|
|
MaybeRecursiveCostsReport = error(Error),
|
|
MaybeVarUseInfo = error(Error)
|
|
)
|
|
;
|
|
MaybeProcrep = error(Error),
|
|
MaybeVarUseInfo = error(Error)
|
|
)
|
|
else
|
|
CSDPtr = call_site_dynamic_ptr(CSDNum),
|
|
MaybeVarUseInfo = error(
|
|
string.format("Invalid call site dynamic %d", [i(CSDNum)]))
|
|
).
|
|
|
|
:- pred call_site_dynamic_var_use_arg(deep::in, call_site_dynamic_ptr::in,
|
|
recursion_type::in, float::in, var_name_table::in, head_var_rep::in,
|
|
maybe_error(var_use_and_name)::out, int::in, int::out) is det.
|
|
|
|
call_site_dynamic_var_use_arg(Deep, CSDPtr, RecursionType, Cost, VarNameTable,
|
|
HeadVar, MaybeUseAndName, !ArgNum) :-
|
|
HeadVar = head_var_rep(Var, Mode),
|
|
var_mode_to_var_use_type(Mode, UseType),
|
|
% XXX: Allow user to configure var use options.
|
|
UseOptions = var_use_options(Deep, follow_any_call, UseType),
|
|
get_call_site_dynamic_var_use_info(CSDPtr, !.ArgNum, RecursionType,
|
|
Cost, UseOptions, MaybeUse),
|
|
(
|
|
MaybeUse = ok(Use),
|
|
lookup_var_name(VarNameTable, Var, Name),
|
|
MaybeUseAndName = ok(var_use_and_name(Name, Use))
|
|
;
|
|
MaybeUse = error(Error),
|
|
MaybeUseAndName = error(Error)
|
|
),
|
|
!:ArgNum = !.ArgNum + 1.
|
|
|
|
:- pred list_maybe_error_to_maybe_error_list(list(maybe_error(T))::in,
|
|
maybe_error(list(T))::out) is det.
|
|
|
|
list_maybe_error_to_maybe_error_list([], ok([])).
|
|
list_maybe_error_to_maybe_error_list([error(E) | _], error(E)).
|
|
list_maybe_error_to_maybe_error_list([ok(X) | MaybeXs0], MaybeXs) :-
|
|
list_maybe_error_to_maybe_error_list(MaybeXs0, MaybeXs1),
|
|
(
|
|
MaybeXs1 = ok(Xs1),
|
|
MaybeXs = ok([X | Xs1])
|
|
;
|
|
MaybeXs1 = error(E),
|
|
MaybeXs = error(E)
|
|
).
|
|
|
|
:- pred get_recursive_csd_cost(deep::in, call_site_dynamic_ptr::in,
|
|
recursion_type::in, maybe_error(float)::out) is det.
|
|
|
|
get_recursive_csd_cost(Deep, CSDPtr, RecursionType, MaybeCost) :-
|
|
(
|
|
RecursionType = rt_not_recursive,
|
|
MaybeCost =
|
|
error("get_recursive_csd_cost called for non-recursive clique")
|
|
;
|
|
RecursionType = rt_single(_, _, AvgMaxDepth, _, CostFn),
|
|
deep_lookup_csd_own(Deep, CSDPtr, Own),
|
|
Calls = float(calls(Own)),
|
|
MaybeCost = ok(CostFn(round_to_int(AvgMaxDepth) - 1) * Calls)
|
|
;
|
|
( RecursionType = rt_divide_and_conquer(_, _)
|
|
; RecursionType = rt_mutual_recursion(_)
|
|
; RecursionType = rt_other(_)
|
|
),
|
|
MaybeCost = error("get_recursive_csd_cost: unhandled recursion type")
|
|
;
|
|
RecursionType = rt_errors(Errors),
|
|
MaybeCost = error(join_list("\n", Errors))
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code to build a program_modules report.
|
|
%
|
|
|
|
% Create a program_modules report, from the given data with the specified
|
|
% parameters.
|
|
%
|
|
:- pred create_program_modules_report(deep::in,
|
|
maybe_error(program_modules_report)::out) is det.
|
|
|
|
create_program_modules_report(Deep, MaybeProgramModulesReport) :-
|
|
map.to_assoc_list(Deep ^ module_data, ModulePairs0),
|
|
list.filter(not_mercury_runtime, ModulePairs0, ModulePairs),
|
|
ModuleRowDatas = list.map(module_pair_to_row_data(Deep), ModulePairs),
|
|
ProgramModulesReport = program_modules_report(ModuleRowDatas),
|
|
MaybeProgramModulesReport = ok(ProgramModulesReport).
|
|
|
|
:- pred not_mercury_runtime(pair(string, module_data)::in) is semidet.
|
|
|
|
not_mercury_runtime(ModuleName - _) :-
|
|
ModuleName \= "Mercury runtime".
|
|
|
|
:- func module_pair_to_row_data(deep, pair(string, module_data))
|
|
= perf_row_data(module_active).
|
|
|
|
module_pair_to_row_data(Deep, ModuleName - ModuleData) = ModuleRowData :-
|
|
Own = ModuleData ^ module_own,
|
|
IsActive = compute_is_active(Own),
|
|
(
|
|
IsActive = is_active,
|
|
ModuleIsActive = module_is_active
|
|
;
|
|
IsActive = is_not_active,
|
|
ModuleIsActive = module_is_not_active
|
|
),
|
|
ModuleActive = module_active(ModuleName, ModuleIsActive),
|
|
own_and_maybe_inherit_to_perf_row_data(Deep, ModuleActive, Own, no,
|
|
ModuleRowData).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code to build a module report.
|
|
%
|
|
|
|
% Create a module report, from the given data with the specified
|
|
% parameters.
|
|
%
|
|
:- pred create_module_report(deep::in, string::in,
|
|
maybe_error(module_report)::out) is det.
|
|
|
|
create_module_report(Deep, ModuleName, MaybeModuleReport) :-
|
|
( if map.search(Deep ^ module_data, ModuleName, ModuleData) then
|
|
deep_get_maybe_progrep(Deep, MaybeProgRep),
|
|
( if
|
|
MaybeProgRep = ok(ProgRep),
|
|
ProgRep = prog_rep(ModuleMap),
|
|
map.search(ModuleMap, ModuleName, _)
|
|
then
|
|
HaveModuleRep = have_module_rep
|
|
else
|
|
HaveModuleRep = do_not_have_module_rep
|
|
),
|
|
PSPtrs = ModuleData ^ module_procs,
|
|
ProcRowDatas = list.map(proc_to_active_row_data(Deep), PSPtrs),
|
|
ModuleReport = module_report(ModuleName, HaveModuleRep, ProcRowDatas),
|
|
MaybeModuleReport = ok(ModuleReport)
|
|
else
|
|
Msg = string.format("There is no module named `%s'.\n",
|
|
[s(ModuleName)]),
|
|
MaybeModuleReport = error(Msg)
|
|
).
|
|
|
|
:- func proc_to_active_row_data(deep, proc_static_ptr)
|
|
= perf_row_data(proc_active).
|
|
|
|
proc_to_active_row_data(Deep, PSPtr) = ProcRowData :-
|
|
deep_lookup_ps_own(Deep, PSPtr, Own),
|
|
deep_lookup_ps_desc(Deep, PSPtr, Desc),
|
|
IsActive = compute_is_active(Own),
|
|
(
|
|
IsActive = is_active,
|
|
ProcIsActive = proc_is_active
|
|
;
|
|
IsActive = is_not_active,
|
|
ProcIsActive = proc_is_not_active
|
|
),
|
|
ProcDesc = describe_proc(Deep, PSPtr),
|
|
ProcActive = proc_active(ProcDesc, ProcIsActive),
|
|
own_and_inherit_to_perf_row_data(Deep, ProcActive, Own, Desc, ProcRowData).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code to build a module_getter_setters report.
|
|
%
|
|
|
|
:- type gs_field_raw_data
|
|
---> gs_field_raw_data(
|
|
gs_raw_proc :: proc_desc,
|
|
gs_raw_own :: own_prof_info,
|
|
gs_raw_desc :: inherit_prof_info
|
|
).
|
|
|
|
:- type raw_gs_field_info == gs_field_info(gs_field_raw_data, unit).
|
|
:- type raw_gs_field_map == gs_field_map(raw_gs_field_info).
|
|
:- type raw_gs_ds_map == gs_ds_map(raw_gs_field_info).
|
|
|
|
% Create a module_getter_setters report, from the given data
|
|
% with the specified parameters.
|
|
%
|
|
:- pred create_module_getter_setter_report(deep::in, string::in,
|
|
maybe_error(module_getter_setters_report)::out) is det.
|
|
|
|
create_module_getter_setter_report(Deep, ModuleName,
|
|
MaybeModuleGetterSettersReport) :-
|
|
( if map.search(Deep ^ module_data, ModuleName, ModuleData) then
|
|
PSPtrs = ModuleData ^ module_procs,
|
|
list.foldl(gather_getters_setters(Deep), PSPtrs,
|
|
map.init, GetterSetterDataMap),
|
|
map.map_values(getter_setter_raw_map_to_info_map(Deep),
|
|
GetterSetterDataMap, GetterSetterInfoMap),
|
|
ModuleGetterSettersReport = module_getter_setters_report(ModuleName,
|
|
GetterSetterInfoMap),
|
|
MaybeModuleGetterSettersReport = ok(ModuleGetterSettersReport)
|
|
else
|
|
Msg = string.format("There is no module named `%s'.\n",
|
|
[s(ModuleName)]),
|
|
MaybeModuleGetterSettersReport = error(Msg)
|
|
).
|
|
|
|
:- pred getter_setter_raw_map_to_info_map(deep::in, data_struct_name::in,
|
|
raw_gs_field_map::in, gs_field_map::out) is det.
|
|
|
|
getter_setter_raw_map_to_info_map(Deep, _DataStructName, RawMap, Map) :-
|
|
map.map_values(getter_setter_raw_data_to_info(Deep), RawMap, Map).
|
|
|
|
:- pred getter_setter_raw_data_to_info(deep::in, field_name::in,
|
|
raw_gs_field_info::in, gs_field_info::out) is det.
|
|
|
|
getter_setter_raw_data_to_info(Deep, _FieldName, RawData, Data) :-
|
|
(
|
|
RawData = gs_field_both(RawGetter, RawSetter, _),
|
|
RawGetter = gs_field_raw_data(GetterProcDesc, GetterOwn, GetterDesc),
|
|
RawSetter = gs_field_raw_data(SetterProcDesc, SetterOwn, SetterDesc),
|
|
own_and_inherit_to_perf_row_data(Deep, GetterProcDesc,
|
|
GetterOwn, GetterDesc, GetterRowData),
|
|
own_and_inherit_to_perf_row_data(Deep, SetterProcDesc,
|
|
SetterOwn, SetterDesc, SetterRowData),
|
|
SumOwn = add_own_to_own(GetterOwn, SetterOwn),
|
|
SumDesc = add_inherit_to_inherit(GetterDesc, SetterDesc),
|
|
own_and_inherit_to_perf_row_data(Deep, unit, SumOwn, SumDesc,
|
|
SumRowData),
|
|
Data = gs_field_both(GetterRowData, SetterRowData, SumRowData)
|
|
;
|
|
RawData = gs_field_getter(RawGetter),
|
|
RawGetter = gs_field_raw_data(GetterProcDesc, GetterOwn, GetterDesc),
|
|
own_and_inherit_to_perf_row_data(Deep, GetterProcDesc, GetterOwn,
|
|
GetterDesc, GetterRowData),
|
|
Data = gs_field_getter(GetterRowData)
|
|
;
|
|
RawData = gs_field_setter(RawSetter),
|
|
RawSetter = gs_field_raw_data(SetterProcDesc, SetterOwn, SetterDesc),
|
|
own_and_inherit_to_perf_row_data(Deep, SetterProcDesc, SetterOwn,
|
|
SetterDesc, SetterRowData),
|
|
Data = gs_field_setter(SetterRowData)
|
|
).
|
|
|
|
:- pred gather_getters_setters(deep::in, proc_static_ptr::in,
|
|
raw_gs_ds_map::in, raw_gs_ds_map::out) is det.
|
|
|
|
gather_getters_setters(Deep, PSPtr, !GSDSRawMap) :-
|
|
( if valid_proc_static_ptr(Deep, PSPtr) then
|
|
deep_lookup_proc_statics(Deep, PSPtr, PS),
|
|
Id = PS ^ ps_id,
|
|
( if
|
|
is_getter_or_setter(Id, GetterSetter, DataStructName, FieldName)
|
|
then
|
|
deep_lookup_ps_own(Deep, PSPtr, Own),
|
|
deep_lookup_ps_desc(Deep, PSPtr, Desc),
|
|
ProcDesc = describe_proc(Deep, PSPtr),
|
|
RawData = gs_field_raw_data(ProcDesc, Own, Desc),
|
|
( if map.search(!.GSDSRawMap, DataStructName, FieldMap0Prime) then
|
|
FieldMap0 = FieldMap0Prime
|
|
else
|
|
map.init(FieldMap0)
|
|
),
|
|
( if map.search(FieldMap0, FieldName, FieldData0) then
|
|
(
|
|
GetterSetter = getter,
|
|
(
|
|
( FieldData0 = gs_field_both(_, _, _)
|
|
; FieldData0 = gs_field_getter(_)
|
|
),
|
|
unexpected($pred, "redundant getter")
|
|
;
|
|
FieldData0 = gs_field_setter(SetterRawData),
|
|
FieldData = gs_field_both(RawData, SetterRawData, unit)
|
|
)
|
|
;
|
|
GetterSetter = setter,
|
|
(
|
|
( FieldData0 = gs_field_both(_, _, _)
|
|
; FieldData0 = gs_field_setter(_)
|
|
),
|
|
unexpected($pred, "redundant setter")
|
|
;
|
|
FieldData0 = gs_field_getter(GetterRawData),
|
|
FieldData = gs_field_both(GetterRawData, RawData, unit)
|
|
)
|
|
),
|
|
map.det_update(FieldName, FieldData, FieldMap0, FieldMap)
|
|
else
|
|
(
|
|
GetterSetter = getter,
|
|
FieldData = gs_field_getter(RawData)
|
|
;
|
|
GetterSetter = setter,
|
|
FieldData = gs_field_setter(RawData)
|
|
),
|
|
map.det_insert(FieldName, FieldData, FieldMap0, FieldMap)
|
|
),
|
|
map.set(DataStructName, FieldMap, !GSDSRawMap)
|
|
else
|
|
true
|
|
)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred is_getter_or_setter(string_proc_label::in, getter_or_setter::out,
|
|
data_struct_name::out, field_name::out) is semidet.
|
|
|
|
is_getter_or_setter(StringProcLabel, GetterSetter, DataStructName,
|
|
FieldName) :-
|
|
StringProcLabel = str_ordinary_proc_label(_PorF, DeclModule, DefModule,
|
|
Name, Arity, _Mode),
|
|
DeclModule = DefModule,
|
|
string.to_char_list(Name, NameChars),
|
|
is_getter_or_setter_2(NameChars, GetterSetter, DataStructNameChars,
|
|
FieldNameChars),
|
|
(
|
|
GetterSetter = getter,
|
|
Arity = 2
|
|
;
|
|
GetterSetter = setter,
|
|
Arity = 3
|
|
),
|
|
string.from_char_list(DataStructNameChars, DataStructNameStr),
|
|
string.from_char_list(FieldNameChars, FieldNameStr),
|
|
DataStructName = data_struct_name(DataStructNameStr),
|
|
FieldName = field_name(FieldNameStr).
|
|
|
|
:- pred is_getter_or_setter_2(list(char)::in, getter_or_setter::out,
|
|
list(char)::out, list(char)::out) is semidet.
|
|
|
|
is_getter_or_setter_2(NameChars, GetterSetter, DataStructNameChars,
|
|
FieldNameChars) :-
|
|
( if NameChars = ['_', 'g', 'e', 't', '_' | FieldNameCharsPrime] then
|
|
GetterSetter = getter,
|
|
DataStructNameChars = [],
|
|
FieldNameChars = FieldNameCharsPrime
|
|
else if NameChars = ['_', 's', 'e', 't', '_' | FieldNameCharsPrime] then
|
|
GetterSetter = setter,
|
|
DataStructNameChars = [],
|
|
FieldNameChars = FieldNameCharsPrime
|
|
else
|
|
NameChars = [FirstNameChar | LaterNameChars],
|
|
is_getter_or_setter_2(LaterNameChars, GetterSetter,
|
|
LaterDataStructNameChars, FieldNameChars),
|
|
DataStructNameChars = [FirstNameChar | LaterDataStructNameChars]
|
|
).
|
|
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code to build a module_rep report.
|
|
%
|
|
|
|
:- pred create_module_rep_report(deep::in, string::in,
|
|
maybe_error(module_rep_report)::out) is det.
|
|
|
|
create_module_rep_report(Deep, ModuleName, MaybeModuleRepReport) :-
|
|
MaybeProgRep = Deep ^ procrep_file,
|
|
(
|
|
MaybeProgRep = yes(MaybeErrorProgRep),
|
|
(
|
|
MaybeErrorProgRep = ok(ProgRep),
|
|
ProgRep = prog_rep(ModuleRepMap),
|
|
( if map.search(ModuleRepMap, ModuleName, ModuleRep) then
|
|
print_module_to_strings(ModuleRep, CordStrs),
|
|
Str = string.append_list(cord.list(CordStrs)),
|
|
ModuleRepReport = module_rep_report(ModuleName, Str),
|
|
MaybeModuleRepReport = ok(ModuleRepReport)
|
|
else
|
|
Msg = string.format("There is no module named %s.\n",
|
|
[s(ModuleName)]),
|
|
MaybeModuleRepReport = error(Msg)
|
|
)
|
|
;
|
|
MaybeErrorProgRep = error(Msg),
|
|
MaybeModuleRepReport = error(Msg)
|
|
)
|
|
;
|
|
MaybeProgRep = no,
|
|
Msg = "Information about module representations is not available.\n",
|
|
MaybeModuleRepReport = error(Msg)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code to build a top_procs report.
|
|
%
|
|
|
|
create_top_procs_report(Deep, Limit, CostKind, InclDesc0, Scope0,
|
|
MaybeTopProcsReport) :-
|
|
(
|
|
CostKind = cost_calls,
|
|
% Counting calls is incompatible both with self_and_desc
|
|
% and per_call.
|
|
InclDesc = self,
|
|
Scope = overall
|
|
;
|
|
( CostKind = cost_redos
|
|
; CostKind = cost_time
|
|
; CostKind = cost_callseqs
|
|
; CostKind = cost_allocs
|
|
; CostKind = cost_words
|
|
),
|
|
InclDesc = InclDesc0,
|
|
Scope = Scope0
|
|
),
|
|
MaybeTopPSIs = find_top_procs(CostKind, InclDesc, Scope, Limit, Deep),
|
|
(
|
|
MaybeTopPSIs = error(ErrorMessage),
|
|
MaybeTopProcsReport = error("Internal error: " ++ ErrorMessage)
|
|
;
|
|
MaybeTopPSIs = ok(TopPSIs),
|
|
Ordering = report_ordering(Limit, CostKind, InclDesc, Scope),
|
|
list.map(psi_to_perf_row_data(Deep), TopPSIs, ProcRowDatas),
|
|
TopProcsReport = top_procs_report(Ordering, ProcRowDatas),
|
|
MaybeTopProcsReport = ok(TopProcsReport)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code to build the reports that just dump the contents of the main
|
|
% data structures.
|
|
%
|
|
|
|
:- pred create_clique_dump_report(deep::in, clique_ptr::in,
|
|
maybe_error(clique_dump_info)::out) is det.
|
|
|
|
create_clique_dump_report(Deep, CliquePtr, MaybeCliqueDumpInfo) :-
|
|
( if valid_clique_ptr(Deep, CliquePtr) then
|
|
CliqueDesc = describe_clique(Deep, CliquePtr, no),
|
|
deep_lookup_clique_parents(Deep, CliquePtr, ParentCSDPtr),
|
|
deep_lookup_clique_members(Deep, CliquePtr, MemberPDPtrs),
|
|
CliqueDumpInfo = clique_dump_info(CliqueDesc, ParentCSDPtr,
|
|
MemberPDPtrs),
|
|
MaybeCliqueDumpInfo = ok(CliqueDumpInfo)
|
|
else
|
|
MaybeCliqueDumpInfo = error("invalid clique_ptr")
|
|
).
|
|
|
|
:- pred create_proc_static_dump_report(deep::in, proc_static_ptr::in,
|
|
maybe_error(proc_static_dump_info)::out) is det.
|
|
|
|
create_proc_static_dump_report(Deep, PSPtr, MaybeProcStaticDumpInfo) :-
|
|
( if valid_proc_static_ptr(Deep, PSPtr) then
|
|
deep_lookup_proc_statics(Deep, PSPtr, PS),
|
|
% Should we dump some other fields?
|
|
PS = proc_static(_ProcId, _DeclModule,
|
|
UnQualRefinedName, QualRefinedName, RawName, FileName, LineNumber,
|
|
_InInterface, CallSites, CoveragePointInfos, _MaybeCoveragePoints,
|
|
_IsZeroed),
|
|
array.max(CallSites, MaxCallSiteIdx),
|
|
NumCallSites = MaxCallSiteIdx + 1,
|
|
array.max(CoveragePointInfos, MaxCoveragePointIdx),
|
|
NumCoveragePoints = MaxCoveragePointIdx + 1,
|
|
ProcStaticDumpInfo = proc_static_dump_info(PSPtr, RawName,
|
|
UnQualRefinedName, QualRefinedName,
|
|
FileName, LineNumber, NumCallSites, NumCoveragePoints),
|
|
MaybeProcStaticDumpInfo = ok(ProcStaticDumpInfo)
|
|
else
|
|
MaybeProcStaticDumpInfo = error("invalid proc_static index")
|
|
).
|
|
|
|
:- pred create_proc_dynamic_dump_report(deep::in, proc_dynamic_ptr::in,
|
|
maybe_error(proc_dynamic_dump_info)::out) is det.
|
|
|
|
create_proc_dynamic_dump_report(Deep, PDPtr, MaybeProcDynamicDumpInfo) :-
|
|
( if valid_proc_dynamic_ptr(Deep, PDPtr) then
|
|
deep_lookup_proc_dynamics(Deep, PDPtr, PD),
|
|
PD = proc_dynamic(PSPtr, CallSiteArray, MaybeCPCounts),
|
|
deep_lookup_proc_statics(Deep, PSPtr, PS),
|
|
RawName = PS ^ ps_raw_id,
|
|
ModuleName = PS ^ ps_decl_module,
|
|
UnQualRefinedName = PS ^ ps_uq_refined_id,
|
|
QualRefinedName = PS ^ ps_q_refined_id,
|
|
array.to_list(CallSiteArray, CallSites),
|
|
(
|
|
MaybeCPCounts = yes(CPCounts),
|
|
CPInfos = PS ^ ps_coverage_point_infos,
|
|
coverage_point_arrays_to_list(CPInfos, CPCounts, CPs),
|
|
MaybeCPs = yes(CPs)
|
|
;
|
|
MaybeCPCounts = no,
|
|
MaybeCPs = no
|
|
),
|
|
ProcDynamicDumpInfo = proc_dynamic_dump_info(PDPtr, PSPtr,
|
|
RawName, ModuleName, UnQualRefinedName, QualRefinedName,
|
|
CallSites, MaybeCPs),
|
|
MaybeProcDynamicDumpInfo = ok(ProcDynamicDumpInfo)
|
|
else
|
|
MaybeProcDynamicDumpInfo = error("invalid proc_dynamic index")
|
|
).
|
|
|
|
:- pred create_call_site_static_dump_report(deep::in, call_site_static_ptr::in,
|
|
maybe_error(call_site_static_dump_info)::out) is det.
|
|
|
|
create_call_site_static_dump_report(Deep, CSSPtr,
|
|
MaybeCallSiteStaticDumpInfo) :-
|
|
( if valid_call_site_static_ptr(Deep, CSSPtr) then
|
|
deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
|
|
CSS = call_site_static(ContainingPSPtr, SlotNumber, CallSiteKind,
|
|
FileName, LineNumber, GoalPath),
|
|
CallSiteStaticDumpInfo = call_site_static_dump_info(CSSPtr,
|
|
ContainingPSPtr, SlotNumber, FileName, LineNumber, GoalPath,
|
|
CallSiteKind),
|
|
MaybeCallSiteStaticDumpInfo = ok(CallSiteStaticDumpInfo)
|
|
else
|
|
MaybeCallSiteStaticDumpInfo = error("invalid call_site_static index")
|
|
).
|
|
|
|
:- pred create_call_site_dynamic_dump_report(deep::in,
|
|
call_site_dynamic_ptr::in,
|
|
maybe_error(call_site_dynamic_dump_info)::out) is det.
|
|
|
|
create_call_site_dynamic_dump_report(Deep, CSDPtr,
|
|
MaybeCallSiteDynamicDumpInfo) :-
|
|
( if valid_call_site_dynamic_ptr(Deep, CSDPtr) then
|
|
deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
|
|
CSD = call_site_dynamic(CallerPSPtr, CalleePSDPtr, Own),
|
|
Desc = zero_inherit_prof_info,
|
|
deep_lookup_call_site_static_map(Deep, CSDPtr, CSSPtr),
|
|
CallSiteDesc = describe_call_site(Deep, CSSPtr),
|
|
own_and_inherit_to_perf_row_data(Deep, CallSiteDesc, Own, Desc,
|
|
PerfRowData),
|
|
CallSiteDynamicDumpInfo = call_site_dynamic_dump_info(CSDPtr,
|
|
CallerPSPtr, CalleePSDPtr, PerfRowData),
|
|
MaybeCallSiteDynamicDumpInfo = ok(CallSiteDynamicDumpInfo)
|
|
else
|
|
MaybeCallSiteDynamicDumpInfo = error("invalid call_site_dynamic index")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
describe_proc(Deep, PSPtr) = ProcDesc :-
|
|
( if valid_proc_static_ptr(Deep, PSPtr) then
|
|
deep_lookup_proc_statics(Deep, PSPtr, PS),
|
|
FileName = PS ^ ps_file_name,
|
|
LineNumber = PS ^ ps_line_number,
|
|
ModuleName = PS ^ ps_decl_module,
|
|
UnQualRefinedName = PS ^ ps_uq_refined_id,
|
|
QualRefinedName = PS ^ ps_q_refined_id
|
|
else
|
|
FileName = "",
|
|
LineNumber = 0,
|
|
ModuleName = "",
|
|
UnQualRefinedName = "mercury_runtime",
|
|
QualRefinedName = "mercury_runtime"
|
|
),
|
|
ProcDesc = proc_desc(PSPtr, FileName, LineNumber, ModuleName,
|
|
UnQualRefinedName, QualRefinedName).
|
|
|
|
% Create a call_site_desc structure for a given call site static pointer.
|
|
%
|
|
:- func describe_call_site(deep, call_site_static_ptr) = call_site_desc.
|
|
|
|
describe_call_site(Deep, CSSPtr) = CallSiteDesc :-
|
|
( if valid_call_site_static_ptr(Deep, CSSPtr) then
|
|
deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
|
|
CSS = call_site_static(ContainingPSPtr, SlotNumber, Kind,
|
|
FileNameCSS, LineNumber, RevGoalPath),
|
|
deep_lookup_proc_statics(Deep, ContainingPSPtr, ContainingPS),
|
|
( if FileNameCSS = "" then
|
|
FileName = ContainingPS ^ ps_file_name
|
|
else
|
|
FileName = FileNameCSS
|
|
),
|
|
ModuleName = ContainingPS ^ ps_decl_module,
|
|
UnQualRefinedName = ContainingPS ^ ps_uq_refined_id,
|
|
QualRefinedName = ContainingPS ^ ps_q_refined_id,
|
|
(
|
|
Kind = normal_call_and_callee(CalleePSPtr, _TypeSubst),
|
|
CalleeDesc = describe_proc(Deep, CalleePSPtr),
|
|
MaybeCalleeDesc = yes(CalleeDesc)
|
|
;
|
|
( Kind = special_call_and_no_callee
|
|
; Kind = higher_order_call_and_no_callee
|
|
; Kind = method_call_and_no_callee
|
|
; Kind = callback_and_no_callee
|
|
),
|
|
MaybeCalleeDesc = no
|
|
)
|
|
else
|
|
ContainingPSPtr = dummy_proc_static_ptr,
|
|
FileName = "",
|
|
LineNumber = 0,
|
|
ModuleName = "",
|
|
UnQualRefinedName = "mercury_runtime",
|
|
QualRefinedName = "mercury_runtime",
|
|
SlotNumber = -1,
|
|
RevGoalPath = rgp_nil,
|
|
MaybeCalleeDesc = no
|
|
),
|
|
CallSiteDesc = call_site_desc(CSSPtr, ContainingPSPtr,
|
|
FileName, LineNumber, ModuleName, UnQualRefinedName, QualRefinedName,
|
|
SlotNumber, RevGoalPath, MaybeCalleeDesc).
|
|
|
|
% describe_clique(Deep, CliquePtr, MaybeEntryPDPtr) = CliqueDesc
|
|
%
|
|
% Create a clique_desc structure for a given clique. The calculation for
|
|
% the entry procedure into the clique can be overridden by supplying an
|
|
% EntryPDPtr in MaybeEntryPDPtr. This is useful when referring to a clique
|
|
% from itself.
|
|
%
|
|
:- func describe_clique(deep, clique_ptr, maybe(proc_dynamic_ptr)) =
|
|
clique_desc.
|
|
|
|
describe_clique(Deep, CliquePtr, MaybeEntryPDPtr) = CliqueDesc :-
|
|
( if valid_clique_ptr(Deep, CliquePtr) then
|
|
deep_lookup_clique_members(Deep, CliquePtr, MemberPDPtrs),
|
|
deep_lookup_clique_parents(Deep, CliquePtr, ParentCSDPtr),
|
|
deep_lookup_call_site_dynamics(Deep, ParentCSDPtr, ParentCSD),
|
|
(
|
|
MaybeEntryPDPtr = yes(EntryPDPtr)
|
|
;
|
|
MaybeEntryPDPtr = no,
|
|
EntryPDPtr = ParentCSD ^ csd_callee
|
|
),
|
|
( if list.delete_first(MemberPDPtrs, EntryPDPtr, OtherPDPtrs) then
|
|
EntryProcDesc = describe_clique_member(Deep, EntryPDPtr),
|
|
OtherProcDescs =
|
|
list.map(describe_clique_member(Deep), OtherPDPtrs),
|
|
CliqueDesc = clique_desc(CliquePtr, EntryProcDesc, OtherProcDescs)
|
|
else
|
|
unexpected($pred, "entry pdptr not a member")
|
|
)
|
|
else
|
|
unexpected($pred, "invalid clique_ptr")
|
|
).
|
|
|
|
:- func describe_clique_member(deep, proc_dynamic_ptr) = proc_desc.
|
|
|
|
describe_clique_member(Deep, PDPtr) = ProcDesc :-
|
|
deep_lookup_proc_dynamics(Deep, PDPtr, PD),
|
|
ProcDesc = describe_proc(Deep, PD ^ pd_proc_static).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Lookup the proc_static structure with the given PSI index number
|
|
% and return performance information about it.
|
|
%
|
|
:- pred psi_to_perf_row_data(deep::in, int::in, perf_row_data(proc_desc)::out)
|
|
is det.
|
|
|
|
psi_to_perf_row_data(Deep, PSI, RowData) :-
|
|
PSPtr = proc_static_ptr(PSI),
|
|
ProcDesc = describe_proc(Deep, PSPtr),
|
|
deep_lookup_ps_own(Deep, PSPtr, Own),
|
|
deep_lookup_ps_desc(Deep, PSPtr, Desc),
|
|
own_and_inherit_to_perf_row_data(Deep, ProcDesc, Own, Desc, RowData).
|
|
|
|
own_and_inherit_to_perf_row_data(Deep, Subject, Own, Desc, RowData) :-
|
|
own_and_maybe_inherit_to_perf_row_data(Deep, Subject, Own, yes(Desc),
|
|
RowData).
|
|
|
|
:- pred own_and_maybe_inherit_to_perf_row_data(deep::in, T::in,
|
|
own_prof_info::in, maybe(inherit_prof_info)::in, perf_row_data(T)::out)
|
|
is det.
|
|
|
|
own_and_maybe_inherit_to_perf_row_data(Deep, Subject, Own, MaybeDesc,
|
|
RowData) :-
|
|
% Look up global parameters and totals.
|
|
ProfileStats = Deep ^ profile_stats,
|
|
TicksPerSec = ProfileStats ^ prs_ticks_per_sec,
|
|
WordSize = ProfileStats ^ prs_deep_flags ^ df_bytes_per_int,
|
|
|
|
Root = root_total_info(Deep),
|
|
RootQuanta = inherit_quanta(Root),
|
|
RootCallseqs = inherit_callseqs(Root),
|
|
RootAllocs = inherit_allocs(Root),
|
|
RootWords = inherit_words(Root),
|
|
|
|
% Port counts.
|
|
Calls = calls(Own),
|
|
Exits = exits(Own),
|
|
Fails = fails(Own),
|
|
Redos = redos(Own),
|
|
Excps = excps(Own),
|
|
|
|
% Self times.
|
|
SelfTicks = quanta(Own),
|
|
SelfTime = ticks_to_time(SelfTicks, TicksPerSec),
|
|
SelfTimePercent = percent_from_ints(SelfTicks, RootQuanta),
|
|
SelfTimePerCall = time_percall(SelfTime, Calls),
|
|
|
|
% Self call sequence counts.
|
|
SelfCallseqs = callseqs(Own),
|
|
SelfCallseqsPercent = percent_from_ints(SelfCallseqs, RootCallseqs),
|
|
SelfCallseqsPerCall = int_per_call(SelfCallseqs, Calls),
|
|
|
|
% Self memory allocations.
|
|
SelfAllocs = allocs(Own),
|
|
SelfAllocsPercent = percent_from_ints(SelfAllocs, RootAllocs),
|
|
SelfAllocsPerCall = int_per_call(SelfAllocs, Calls),
|
|
|
|
% Self memory words.
|
|
SelfWords = words(Own),
|
|
SelfMemory = memory_words(SelfWords, WordSize),
|
|
SelfMemoryPercent = percent_from_ints(SelfWords, RootWords),
|
|
SelfMemoryPerCall = SelfMemory / Calls,
|
|
|
|
SelfPerf = inheritable_perf(
|
|
SelfTicks, SelfTime, SelfTimePercent, SelfTimePerCall,
|
|
SelfCallseqs, SelfCallseqsPercent, SelfCallseqsPerCall,
|
|
SelfAllocs, SelfAllocsPercent, SelfAllocsPerCall,
|
|
SelfMemory, SelfMemoryPercent, SelfMemoryPerCall),
|
|
|
|
(
|
|
MaybeDesc = no,
|
|
MaybeTotalPerf = no
|
|
;
|
|
MaybeDesc = yes(Desc),
|
|
|
|
% Self + descendants times.
|
|
TotalTicks = SelfTicks + inherit_quanta(Desc),
|
|
TotalTime = ticks_to_time(TotalTicks, TicksPerSec),
|
|
TotalTimePercent = percent_from_ints(TotalTicks, RootQuanta),
|
|
TotalTimePerCall = time_percall(TotalTime, Calls),
|
|
|
|
% Self + descendants call sequence counts.
|
|
TotalCallseqs = callseqs(Own) + inherit_callseqs(Desc),
|
|
TotalCallseqsPercent = percent_from_ints(TotalCallseqs, RootCallseqs),
|
|
TotalCallseqsPerCall = int_per_call(TotalCallseqs, Calls),
|
|
|
|
% Self + descendants memory allocations.
|
|
TotalAllocs = SelfAllocs + inherit_allocs(Desc),
|
|
TotalAllocsPercent = percent_from_ints(TotalAllocs, RootAllocs),
|
|
TotalAllocsPerCall = int_per_call(TotalAllocs, Calls),
|
|
|
|
% Self + descendants memory words.
|
|
TotalWords = SelfWords + inherit_words(Desc),
|
|
TotalMemory = memory_words(TotalWords, WordSize),
|
|
TotalMemoryPercent = percent_from_ints(TotalWords, RootWords),
|
|
TotalMemoryPerCall = TotalMemory / Calls,
|
|
|
|
TotalPerf = inheritable_perf(
|
|
TotalTicks, TotalTime, TotalTimePercent, TotalTimePerCall,
|
|
TotalCallseqs, TotalCallseqsPercent, TotalCallseqsPerCall,
|
|
TotalAllocs, TotalAllocsPercent, TotalAllocsPerCall,
|
|
TotalMemory, TotalMemoryPercent, TotalMemoryPerCall),
|
|
MaybeTotalPerf = yes(TotalPerf)
|
|
),
|
|
|
|
RowData = perf_row_data(Subject, Calls, Exits, Fails, Redos, Excps,
|
|
WordSize, SelfPerf, MaybeTotalPerf).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% int_per_call(Num, Calls) is the quotient of Nom and Calls, after they've
|
|
% both been cast to float.
|
|
%
|
|
:- func int_per_call(int, int) = float.
|
|
|
|
int_per_call(Num, Calls) =
|
|
( if Calls = 0 then
|
|
0.0
|
|
else
|
|
float(Num) / float(Calls)
|
|
).
|
|
|
|
% Give the percentage of two 'counts'.
|
|
%
|
|
:- func percent_from_ints(int, int) = percent.
|
|
|
|
percent_from_ints(Nom, Denom) = Percent :-
|
|
( if Denom = 0 then
|
|
Percent = percent(0.0)
|
|
else
|
|
Percent = percent(float(Nom) / float(Denom))
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module create_report.
|
|
%---------------------------------------------------------------------------%
|