Files
mercury/deep_profiler/display_report.m
Zoltan Somogyi 1eb4f55834 Include file names in call site static structures.
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.
2025-03-20 20:00:15 +11:00

5393 lines
208 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2008-2012 The University of Melbourne.
% Copyright (C) 2015, 2017-2019, 2021-2022, 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: display_report.m.
% Author: pbone.
%
% This module contains code to create a display data structure from a deep
% profiling report.
%
%---------------------------------------------------------------------------%
:- module display_report.
:- interface.
:- import_module display.
:- import_module profile.
:- import_module report.
% XXX: This include should be removed or replaced. Some data structures
% such as preferences are currently defined in query; they should be moved
% into a different module so that this module doesn't need to include
% the whole of query.
:- import_module query.
%---------------------------------------------------------------------------%
:- func report_to_display(deep, preferences, deep_report) = display.
%---------------------------------------------------------------------------%
:- implementation.
:- 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 var_use_analysis.
:- import_module array.
:- import_module assoc_list.
:- import_module bool.
:- import_module cord.
:- import_module counter.
:- import_module float.
:- import_module int.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module pair.
:- import_module require.
:- import_module set.
:- import_module string.
:- import_module unit.
%---------------------------------------------------------------------------%
report_to_display(Deep, Prefs, Report) = Display :-
(
Report = report_message(message_report(Msg)),
Display = display(no, [display_heading(Msg)])
;
Report = report_menu(MaybeMenuReport),
(
MaybeMenuReport = ok(MenuReport),
display_report_menu(Deep, Prefs, MenuReport, Display)
;
MaybeMenuReport = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_clique(MaybeCliqueReport),
(
MaybeCliqueReport = ok(CliqueReport),
display_report_clique(Prefs, CliqueReport, Display)
;
MaybeCliqueReport = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_clique_recursion_costs(MaybeCliqueRecursionReport),
(
MaybeCliqueRecursionReport = ok(CliqueRecursionReport),
display_report_clique_recursion(Prefs, CliqueRecursionReport,
Display)
;
MaybeCliqueRecursionReport = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_recursion_types_frequency(MaybeRecTypesFreqReport),
(
MaybeRecTypesFreqReport = ok(RecTypesFreqReport),
display_report_recursion_types_frequency(Prefs, RecTypesFreqReport,
Display)
;
MaybeRecTypesFreqReport = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_program_modules(MaybeProgramModulesReport),
(
MaybeProgramModulesReport = ok(ProgramModulesReport),
display_report_program_modules(Prefs, ProgramModulesReport,
Display)
;
MaybeProgramModulesReport = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_module(MaybeModuleReport),
(
MaybeModuleReport = ok(ModuleReport),
display_report_module(Prefs, ModuleReport, Display)
;
MaybeModuleReport = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_module_getter_setters(MaybeModuleGetterSettersReport),
(
MaybeModuleGetterSettersReport = ok(ModuleGetterSettersReport),
display_report_module_getter_setters(Prefs,
ModuleGetterSettersReport, Display)
;
MaybeModuleGetterSettersReport = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_module_rep(MaybeModuleRepReport),
(
MaybeModuleRepReport = ok(ModuleRepReport),
display_report_module_rep(Prefs, ModuleRepReport, Display)
;
MaybeModuleRepReport = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_top_procs(MaybeTopProcsReport),
(
MaybeTopProcsReport = ok(TopProcsReport),
display_report_top_procs(Prefs, TopProcsReport, Display)
;
MaybeTopProcsReport = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_proc(MaybeProcReport),
(
MaybeProcReport = ok(ProcReport),
display_report_proc(Deep, Prefs, ProcReport, Display)
;
MaybeProcReport = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_procrep_coverage(MaybeProcrepCoverageInfo),
(
MaybeProcrepCoverageInfo = ok(ProcrepCoverageInfo),
display_report_procrep_coverage_info(Prefs, ProcrepCoverageInfo,
Display)
;
MaybeProcrepCoverageInfo = error(Msg),
Display = display(no, [display_text(Msg)])
)
;
Report = report_proc_callers(MaybeProcCallersReport),
(
MaybeProcCallersReport = ok(ProcCallersReport),
display_report_proc_callers(Deep, Prefs, ProcCallersReport,
Display)
;
MaybeProcCallersReport = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_proc_static_dump(MaybeProcStaticDumpInfo),
(
MaybeProcStaticDumpInfo = ok(ProcStaticDumpInfo),
display_report_proc_static_dump(ProcStaticDumpInfo, Display)
;
MaybeProcStaticDumpInfo = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_proc_dynamic_dump(MaybeProcDynamicDumpInfo),
(
MaybeProcDynamicDumpInfo = ok(ProcDynamicDumpInfo),
display_report_proc_dynamic_dump(Deep, Prefs, ProcDynamicDumpInfo,
Display)
;
MaybeProcDynamicDumpInfo = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_call_site_static_dump(MaybeCallSiteStaticDumpInfo),
(
MaybeCallSiteStaticDumpInfo = ok(CallSiteStaticDumpInfo),
display_report_call_site_static_dump(Prefs, CallSiteStaticDumpInfo,
Display)
;
MaybeCallSiteStaticDumpInfo = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_call_site_dynamic_dump(MaybeCallSiteDynamicDumpInfo),
(
MaybeCallSiteDynamicDumpInfo = ok(CallSiteDynamicDumpInfo),
display_report_call_site_dynamic_dump(Prefs,
CallSiteDynamicDumpInfo, Display)
;
MaybeCallSiteDynamicDumpInfo = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_clique_dump(MaybeCliqueDumpInfo),
(
MaybeCliqueDumpInfo = ok(CliqueDumpInfo),
display_report_clique_dump(Prefs, CliqueDumpInfo, Display)
;
MaybeCliqueDumpInfo = error(Msg),
Display = display(no, [display_heading(Msg)])
)
;
Report = report_call_site_dynamic_var_use(MaybeVarUseInfo),
(
MaybeVarUseInfo = ok(VarUseInfo),
display_report_call_site_dynamic_var_use(Prefs, VarUseInfo,
Display)
;
MaybeVarUseInfo = error(Msg),
Display = display(no, [display_heading(Msg)])
)
).
%---------------------------------------------------------------------------%
%
% Code to display menu report.
%
:- pred display_report_menu(deep::in, preferences::in, menu_report::in,
display::out) is det.
display_report_menu(Deep, Prefs, MenuReport, Display) :-
MenuReport = menu_report(ProgramName, QuantaPerSec, UserQuanta, InstQuanta,
NumCallseqs, NumCSD, NumCSS, NumPD, NumPS, NumClique),
TotalQuanta = UserQuanta + InstQuanta,
TotalTime = ticks_to_time(TotalQuanta, QuantaPerSec),
ShouldDisplayTimes = should_display_times(Deep),
% Display the links section of the report.
ActionPrefs0 = Prefs,
ActionPrefs1 = ActionPrefs0 ^ pref_inactive :=
inactive_items(inactive_hide, inactive_hide, inactive_hide),
ActionPrefs = ActionPrefs1 ^ pref_criteria :=
by_cost(cost_time, self_and_desc, overall),
LinksExploration = [
link_base(deep_cmd_root(no), yes(ActionPrefs),
"Exploring the call graph, starting at the root."),
link_base(deep_cmd_root(yes(90)), yes(ActionPrefs),
"Exploring the call graph, starting at the action."),
link_base(deep_cmd_program_modules, yes(Prefs),
"Exploring the program module by module.")
],
(
ShouldDisplayTimes = yes,
Top100SelfCmd = deep_cmd_top_procs(rank_range(1, 100),
cost_time, self, overall),
Top100SelfAndDescCmd = deep_cmd_top_procs(rank_range(1, 100),
cost_time, self_and_desc, overall),
LinksTopProcsByLimitTime = [
link_base(Top100SelfCmd, yes(Prefs),
"Top 100 most expensive procedures: time, self."),
link_base(Top100SelfAndDescCmd, yes(Prefs),
"Top 100 most expensive procedures: time, self+descendants.")
]
;
ShouldDisplayTimes = no,
LinksTopProcsByLimitTime = []
),
TopLimitCallSeqsSelf = deep_cmd_top_procs(rank_range(1, 100),
cost_callseqs, self, overall),
TopLimitCallSeqsSelfAndDesc = deep_cmd_top_procs(rank_range(1, 100),
cost_callseqs, self_and_desc, overall),
TopLimitWordsSelf = deep_cmd_top_procs(rank_range(1, 100),
cost_words, self, overall),
TopLimitWordsSelfAndDesc = deep_cmd_top_procs(rank_range(1, 100),
cost_words, self_and_desc, overall),
LinksTopProcsByLimit = [
link_base(TopLimitCallSeqsSelf, yes(Prefs),
"Top 100 most expensive procedures: callseqs, self."),
link_base(TopLimitCallSeqsSelfAndDesc, yes(Prefs),
"Top 100 most expensive procedures: callseqs, self+descendants."),
link_base(TopLimitWordsSelf, yes(Prefs),
"Top 100 most expensive procedures: words, self."),
link_base(TopLimitWordsSelfAndDesc, yes(Prefs),
"Top 100 most expensive procedures: words, self+descendants.")
],
(
ShouldDisplayTimes = yes,
TimeAbove01PercentCmd = deep_cmd_top_procs(threshold_percent(0.1),
cost_time, self, overall),
TimeAbove1PercentCmd = deep_cmd_top_procs(threshold_percent(1.0),
cost_time, self, overall),
TimeAbove1SecondCmd = deep_cmd_top_procs(threshold_value(100.0),
cost_time, self_and_desc, overall),
LinksTopProcsByPercentTime = [
link_base(TimeAbove01PercentCmd, yes(Prefs),
"Procedures above 0.1% threshold: time, self."),
link_base(TimeAbove1PercentCmd, yes(Prefs),
"Procedures above 1% threshold: time, self+descendants."),
link_base(TimeAbove1SecondCmd, yes(Prefs),
"Procedures above 1 second threshold: time, self+descendants.")
]
;
ShouldDisplayTimes = no,
LinksTopProcsByPercentTime = []
),
CallSeqsAbove01PercentCmd = deep_cmd_top_procs(threshold_percent(0.1),
cost_callseqs, self, overall),
CallSeqsAbove1PercentCmd = deep_cmd_top_procs(threshold_percent(1.0),
cost_callseqs, self_and_desc, overall),
CallSeqsAboveMillionCmd = deep_cmd_top_procs(threshold_value(1000000.0),
cost_callseqs, self_and_desc, overall),
WordsAbove01PercentCmd = deep_cmd_top_procs(threshold_percent(0.1),
cost_words, self, overall),
WordsAbove1PercentCmd = deep_cmd_top_procs(threshold_percent(1.0),
cost_words, self_and_desc, overall),
% 2M words is 8MB on ia32.
WordsAbove2Megawords = deep_cmd_top_procs(
threshold_value(float(1024 * 1024 * 2)),
cost_words, self_and_desc, overall),
LinksTopProcsByPercent = [
link_base(CallSeqsAbove01PercentCmd, yes(Prefs),
"Procedures above 0.1% threshold: callseqs, self."),
link_base(CallSeqsAbove1PercentCmd, yes(Prefs),
"Procedures above 1% threshold: callseqs, self+descendants."),
link_base(CallSeqsAboveMillionCmd, yes(Prefs),
("Procedures above 1,000,000 callseqs threshold: callseqs, " ++
"self+descendants.")),
link_base(WordsAbove01PercentCmd, yes(Prefs),
"Procedures above 0.1% threshold: words, self."),
link_base(WordsAbove1PercentCmd, yes(Prefs),
"Procedures above 1% threshold: words, self+descendants."),
link_base(WordsAbove2Megawords, yes(Prefs),
"Procedures above 2M words threshold: words, self+descendants.")
],
LinkCmds = LinksExploration ++
LinksTopProcsByLimitTime ++ LinksTopProcsByLimit ++
LinksTopProcsByPercentTime ++ LinksTopProcsByPercent,
list.map(make_link, LinkCmds, LinksList),
Links = display_list(list_class_vertical_bullets,
yes("You can start exploring the deep profile at the following" ++
" points."), LinksList),
% Produce the developer-only options list.
RecursionTypeFrequenciesCmd = deep_cmd_recursion_types_frequency,
LinksDeveloperCmds = [
link_base(RecursionTypeFrequenciesCmd, yes(Prefs),
"Frequencies of different types of recursion used in the program.")
],
list.map(make_link, LinksDeveloperCmds, DeveloperLinksList),
DeveloperLinks = display_developer(
display_list(list_class_vertical_bullets,
yes("Options that are only useful to Mercury developers"),
DeveloperLinksList)),
% Display the table section of the report.
ProfilingStatistics =
[("Profile generated for:" - td_s(ProgramName)),
("Quanta per second:" - td_i(QuantaPerSec)),
("Quanta in user code:" - td_i(UserQuanta)),
("Quanta in instrumentation:" - td_i(InstQuanta)),
("Total quanta:" - td_i(TotalQuanta)),
("Total time:" - td_t(TotalTime)),
("Call sequence numbers:" - td_i(NumCallseqs)),
("CallSiteDynamic structures:" - td_i(NumCSD)),
("ProcDynamic structures:" - td_i(NumPD)),
("CallSiteStatic structures:" - td_i(NumCSS)),
("ProcStatic structures:" - td_i(NumPS)),
("Cliques:" - td_i(NumClique))],
Rows = list.map(make_labelled_table_row, ProfilingStatistics),
Table = table(table_class_do_not_box, 2, no, Rows),
OptionsControls = general_options_controls(deep_cmd_menu, Prefs),
MenuRestartQuitControls = cmds_menu_restart_quit(yes(Prefs)),
% Construct the complete representation of what to display.
Display = display(yes("Deep profiler menu"),
[Links, DeveloperLinks, display_table(Table),
display_paragraph_break, MenuRestartQuitControls,
display_paragraph_break, OptionsControls]).
%---------------------------------------------------------------------------%
%
% Code to display a clique report.
%
% Create a display_report structure for a clique report.
%
:- pred display_report_clique(preferences::in, clique_report::in,
display::out) is det.
display_report_clique(Prefs, CliqueReport, Display) :-
CliqueReport = clique_report(CliquePtr, InnerToOuterAncestorCallSites0,
CliqueProcs0),
Cmd = deep_cmd_clique(CliquePtr),
CliquePtr = clique_ptr(CliqueNum),
Title = string.format("Clique %d:", [i(CliqueNum)]),
% Build the table of all modules.
SortByContextPrefs = Prefs ^ pref_criteria := by_context,
SortByNamePrefs = Prefs ^ pref_criteria := by_name,
SourceHeaderCell = td_l(deep_link(Cmd, yes(SortByContextPrefs),
attr_str([], "Source"), link_class_link)),
ProcHeaderCell = td_l(deep_link(Cmd, yes(SortByNamePrefs),
attr_str([], "Procedure"), link_class_link)),
SourceHeaderGroup =
make_single_table_header_group(SourceHeaderCell,
table_column_class_source_context, column_do_not_colour),
ProcHeaderGroup =
make_single_table_header_group(ProcHeaderCell,
table_column_class_proc, column_do_not_colour),
MakeHeaderData = override_order_criteria_header_data(Cmd),
perf_table_header(total_columns_meaningful, Prefs, MakeHeaderData,
PerfHeaderGroups),
AllHeaderGroups = [SourceHeaderGroup, ProcHeaderGroup] ++ PerfHeaderGroups,
header_groups_to_header(AllHeaderGroups, NumColumns, Header),
list.length(InnerToOuterAncestorCallSites0, NumAncestors),
MaybeAncestorLimit = Prefs ^ pref_anc,
( if
MaybeAncestorLimit = yes(AncestorLimit),
NumAncestors > AncestorLimit
then
AncestorTitle = string.format("The %d closest ancestor call sites:",
[i(AncestorLimit)]),
list.take_upto(AncestorLimit,
InnerToOuterAncestorCallSites0, InnerToOuterAncestorCallSites)
else
AncestorTitle = "Ancestor call sites:",
InnerToOuterAncestorCallSites = InnerToOuterAncestorCallSites0
),
list.reverse(InnerToOuterAncestorCallSites, AncestorCallSites),
ModuleQual = Prefs ^ pref_module_qual,
CliqueModuleNames = list.map(clique_proc_report_module_name, CliqueProcs0),
( if
CliqueModuleNames = [FirstModuleName | _],
list.all_same(CliqueModuleNames)
then
MaybeCurModuleName = yes(FirstModuleName)
else
MaybeCurModuleName = no
),
AncestorDataRows = list.map(
clique_ancestor_to_row(MaybeCurModuleName, ModuleQual, Prefs),
AncestorCallSites),
AncestorSectionHeaderRow = table_section_header(td_s(AncestorTitle)),
AncestorRows = [AncestorSectionHeaderRow, table_separator_row] ++
AncestorDataRows,
list.length(CliqueProcs0, NumCliqueprocs),
( if NumCliqueprocs > 1 then
ProcsTitle = "Procedures of the clique:"
else
ProcsTitle = "The clique has one procedure:"
),
CliqueProcsHeaderRow = table_section_header(td_s(ProcsTitle)),
sort_clique_procs_by_preferences(MaybeCurModuleName, ModuleQual, Prefs,
CliqueProcs0, CliqueProcs),
ProcRowLists0 = list.map(
clique_proc_to_table_rows(MaybeCurModuleName, ModuleQual, Prefs,
CliquePtr),
CliqueProcs),
ProcRowLists = list.map(add_front_separator_row, ProcRowLists0),
list.condense(ProcRowLists, ProcRows),
AllRows = AncestorRows ++
[table_separator_row, CliqueProcsHeaderRow] ++ ProcRows,
Table = table(table_class_box_if_pref, NumColumns, yes(Header), AllRows),
DisplayTable = display_table(Table),
% Build controls at the bottom of the page.
AncestorControls = ancestor_controls(Prefs, Cmd),
InactiveCallSiteControls = inactive_call_site_controls(Prefs, Cmd),
ModuleQualControls = module_qual_controls(Prefs, Cmd),
FieldControls = field_controls(Prefs, Cmd),
FormatControls = format_controls(Prefs, Cmd),
CliqueReportControls = clique_reports_controls(Prefs, CliquePtr, Cmd),
MenuRestartQuitControls = cmds_menu_restart_quit(yes(Prefs)),
Display = display(yes(Title),
[DisplayTable,
display_paragraph_break, AncestorControls,
display_paragraph_break, InactiveCallSiteControls,
display_paragraph_break, ModuleQualControls,
display_paragraph_break, FieldControls,
display_paragraph_break, FormatControls,
display_paragraph_break, CliqueReportControls,
display_paragraph_break, MenuRestartQuitControls]).
:- func clique_proc_report_module_name(clique_proc_report) = string.
clique_proc_report_module_name(CliqueProc) =
CliqueProc ^ cpr_proc_summary ^ perf_row_subject ^ pdesc_module_name.
:- func add_front_separator_row(list(table_row)) = list(table_row).
add_front_separator_row(Rows) = [table_separator_row | Rows].
:- func clique_ancestor_to_row(maybe(string), module_qual, preferences,
perf_row_data(ancestor_desc)) = table_row.
clique_ancestor_to_row(MaybeCurModuleName, ModuleQual, Prefs, AncestorRowData)
= Row :-
AncestorDesc = AncestorRowData ^ perf_row_subject,
CallSiteDesc = AncestorDesc ^ ad_call_site_desc,
SourceCell = call_site_desc_source_cell(CallSiteDesc),
CliqueProcCell = call_site_desc_clique_proc_cell(MaybeCurModuleName,
ModuleQual, Prefs, AncestorDesc),
Fields = Prefs ^ pref_fields,
perf_table_row(total_columns_meaningful, Fields, AncestorRowData,
PerfCells),
AllCells = [SourceCell, CliqueProcCell] ++ PerfCells,
Row = table_row(AllCells).
:- func clique_proc_to_table_rows(maybe(string), module_qual, preferences,
clique_ptr, clique_proc_report) = list(table_row).
clique_proc_to_table_rows(MaybeCurModuleName, ModuleQual, Prefs, CliquePtr,
CliqueProcReport) = ProcRows :-
CliqueProcReport = clique_proc_report(SummaryRowData,
FirstPDReport, LaterPDReports),
(
LaterPDReports = [],
ProcRows = clique_proc_dynamic_to_table_rows(MaybeCurModuleName,
ModuleQual, Prefs, CliquePtr, FirstPDReport)
;
LaterPDReports = [_ | _],
AllPDReports = [FirstPDReport | LaterPDReports],
sort_clique_proc_dynamics_by_preferences(Prefs, AllPDReports,
SortedAllPDReports),
PDProcRowLists =
list.map(
clique_proc_dynamic_to_table_rows(MaybeCurModuleName,
ModuleQual, Prefs, CliquePtr),
SortedAllPDReports),
% Do we want separators between the rows of different proc dynamics?
list.condense(PDProcRowLists, PDProcRows),
Fields = Prefs ^ pref_fields,
perf_table_row(total_columns_meaningful, Fields, SummaryRowData,
SummaryPerfCells),
ProcDesc = SummaryRowData ^ perf_row_subject,
SourceCell = proc_desc_to_source_cell(ProcDesc),
ProcCell = proc_desc_to_prefix_proc_name_cell(no,
ModuleQual, Prefs, [attr_bold], ProcDesc, "summary "),
SummaryRowCells = [SourceCell, ProcCell] ++ SummaryPerfCells,
SummaryRow = table_row(SummaryRowCells),
ProcRows = [SummaryRow, table_separator_row] ++ PDProcRows
).
:- func clique_proc_dynamic_to_table_rows(maybe(string), module_qual,
preferences, clique_ptr, clique_proc_dynamic_report) = list(table_row).
clique_proc_dynamic_to_table_rows(MaybeCurModuleName, ModuleQual,
Prefs, CliquePtr, CliqueProcDynamicReport) = ProcRows :-
CliqueProcDynamicReport = clique_proc_dynamic_report(SummaryRowData,
CallSiteReports0),
ProcDesc = SummaryRowData ^ perf_row_subject,
ProcCell = proc_desc_to_proc_name_cell_span(no, ModuleQual, Prefs,
[attr_bold], ProcDesc, 2),
Fields = Prefs ^ pref_fields,
perf_table_row(total_columns_meaningful, Fields, SummaryRowData,
SummaryPerfCells),
SummaryRowCells = [ProcCell] ++ SummaryPerfCells,
SummaryRow = table_row(SummaryRowCells),
sort_clique_call_site_reports_by_preferences(MaybeCurModuleName,
ModuleQual, Prefs, CallSiteReports0, CallSiteReports),
CallSiteRowLists =
list.map(
clique_call_site_to_rows(MaybeCurModuleName, ModuleQual, Prefs,
CliquePtr),
CallSiteReports),
list.condense(CallSiteRowLists, CallSiteRows),
ProcRows = [SummaryRow, table_separator_row] ++ CallSiteRows.
:- func clique_call_site_to_rows(maybe(string), module_qual, preferences,
clique_ptr, clique_call_site_report) = list(table_row).
clique_call_site_to_rows(MaybeCurModuleName, ModuleQual, Prefs,
CliquePtr, CallSiteReport) = Rows :-
CallSiteReport = clique_call_site_report(CallSiteRowData, Kind,
CalleePerfs),
CallSiteDesc = CallSiteRowData ^ perf_row_subject,
SourceCell = call_site_desc_source_cell(CallSiteDesc),
Fields = Prefs ^ pref_fields,
perf_table_row(total_columns_meaningful, Fields, CallSiteRowData,
SummaryPerfCells),
(
Kind = normal_call_and_callee(CalleeProcDesc, _TypeSubst),
(
CalleePerfs = [],
Inactive = Prefs ^ pref_inactive,
Inactive = inactive_items(InactiveCallSites, _, _),
(
InactiveCallSites = inactive_show,
CalleeProcCell = proc_desc_to_prefix_proc_name_cell(
MaybeCurModuleName, ModuleQual, Prefs,
[], CalleeProcDesc, "no calls made to "),
RowCells = [SourceCell, CalleeProcCell] ++ SummaryPerfCells,
Row = table_row(RowCells),
Rows = [Row]
;
InactiveCallSites = inactive_hide,
Rows = []
)
;
CalleePerfs = [CalleePerf],
CalleeCliqueDesc = CalleePerf ^ perf_row_subject,
CalleeProcCell = clique_desc_to_non_self_link_proc_name_cell(
MaybeCurModuleName, ModuleQual, Prefs,
CalleeCliqueDesc, CliquePtr),
RowCells = [SourceCell, CalleeProcCell] ++ SummaryPerfCells,
Row = table_row(RowCells),
Rows = [Row]
;
CalleePerfs = [_, _ | _],
unexpected($pred, "more than one callee at normal")
)
;
(
Kind = special_call_and_no_callee,
CalleeCellStr0 = "special call"
;
Kind = higher_order_call_and_no_callee,
CalleeCellStr0 = "higher order call"
;
Kind = method_call_and_no_callee,
CalleeCellStr0 = "method call"
;
Kind = callback_and_no_callee,
CalleeCellStr0 = "callback"
),
(
CalleePerfs = [],
CalleeCellStr = CalleeCellStr0 ++ " (no calls made)"
;
CalleePerfs = [_ | _],
CalleeCellStr = CalleeCellStr0 ++ " (summary)"
),
CalleeCell = table_cell(td_s(CalleeCellStr)),
SummaryRowCells = [SourceCell, CalleeCell] ++ SummaryPerfCells,
SummaryRow = table_row(SummaryRowCells),
Summarize = Prefs ^ pref_summarize,
(
Summarize = summarize_ho_call_sites,
Rows = [SummaryRow]
;
Summarize = do_not_summarize_ho_call_sites,
sort_clique_rows_by_preferences(Prefs, CalleePerfs,
SortedCalleePerfs),
CalleeRows =
list.map(
clique_call_site_callee_to_row(MaybeCurModuleName,
ModuleQual, Prefs, CliquePtr),
SortedCalleePerfs),
Rows = [SummaryRow] ++ CalleeRows
)
).
:- func clique_call_site_callee_to_row(maybe(string), module_qual,
preferences, clique_ptr, perf_row_data(clique_desc)) = table_row.
clique_call_site_callee_to_row(MaybeCurModuleName, ModuleQual, Prefs,
CliquePtr, CalleeRowData) = Row :-
CalleeCliqueDesc = CalleeRowData ^ perf_row_subject,
CalleeProcCell = clique_desc_to_non_self_link_proc_name_cell(
MaybeCurModuleName, ModuleQual, Prefs, CalleeCliqueDesc, CliquePtr),
Fields = Prefs ^ pref_fields,
perf_table_row(total_columns_meaningful, Fields, CalleeRowData, PerfCells),
EmptyCell = table_cell(td_s("")),
Cells = [EmptyCell, CalleeProcCell] ++ PerfCells,
Row = table_row(Cells).
%---------------------------------------------------------------------------%
%
% Code to display a clique recursion report.
%
:- pred display_report_clique_recursion(preferences::in,
clique_recursion_report::in, display::out) is det.
display_report_clique_recursion(Prefs, CliqueRecursionReport, Display) :-
CliqueRecursionReport = clique_recursion_report(CliquePtr, RecursionType,
_NumProcs),
Cmd = deep_cmd_clique_recursive_costs(CliquePtr),
CliquePtr = clique_ptr(CliqueNum),
Title = string.format("The recursion information for clique %d:",
[i(CliqueNum)]),
display_recursion_type(RecursionType, DisplayRecursionType),
CliqueReportsControls = clique_reports_controls(Prefs, CliquePtr, Cmd),
MenuRestartQuitControls = cmds_menu_restart_quit(yes(Prefs)),
Display = display(yes(Title), DisplayRecursionType ++
[display_paragraph_break, CliqueReportsControls,
display_paragraph_break, MenuRestartQuitControls]).
:- pred display_recursion_type(recursion_type::in, list(display_item)::out)
is det.
display_recursion_type(RecursionType, Items) :-
(
(
RecursionType = rt_not_recursive,
Text = "Clique is non-recursive"
;
RecursionType = rt_mutual_recursion(NumProcs),
Text = string.format("Mutual recursion between %d procedures",
[i(NumProcs)])
),
Items = [display_text(Text)]
;
RecursionType = rt_errors(Errors),
ErrorItems = list.map((func(Text) = display_text(Text)), Errors),
Items = [display_list(list_class_vertical_no_bullets,
yes("Unknown, error(s) occured"), ErrorItems)]
;
(
RecursionType = rt_single(BaseLevel, RecLevel, AvgDepth,
AvgRecCost, AnyRecCost),
RowData =
[{"Base case", BaseLevel},
{"Recursive case", RecLevel}],
Text = "Single-recursion:",
MaxDepthI = float.round_to_int(AvgDepth),
CostAtDepthFunc =
( func(Level) =
{string.format("Cost at depth %d:", [i(Level)]),
AnyRecCost(Level)}
),
CostAtDepths = [0, 1, 2, float.round_to_int(AvgDepth / 2.0),
MaxDepthI - 2, MaxDepthI - 1, MaxDepthI],
ExtraTableRows0 =
[{"Average recursion depth:", AvgDepth},
{"Average recursive call cost (excluding the call itself):",
AvgRecCost}] ++
list.map(CostAtDepthFunc, CostAtDepths),
ExtraTableRows = list.map(
( func({Label, Value}) = table_row(
[table_cell(td_s(Label)), table_cell(td_f(Value))])
), ExtraTableRows0)
;
RecursionType = rt_divide_and_conquer(BaseLevel, RecLevel),
RowData =
[{"Base case", BaseLevel},
{"Doubly-recursive case", RecLevel}],
Text = "Double-recursion (Probably Divide and Conquer):",
ExtraTableRows = []
;
RecursionType = rt_other(Levels),
RowData = list.map(
( func(Level) = {Label, Level} :-
Label = string.format("Case for %d recursive calls",
[i(Level ^ rlr_level)])
), Levels),
Text = "Unknown recursion type:",
ExtraTableRows = []
),
Rows = list.map(make_recursion_table_row, RowData),
ExtraTable = display_table(
table(table_class_do_not_box, 2, no, ExtraTableRows)),
NameClassGroupFunc =
( func({Name, Class}) =
table_header_group(table_header_group_single(td_s(Name)),
Class, column_do_not_colour)
),
NamesClasses =
[{"Recursion type", table_column_class_field_name},
{"Exec count", table_column_class_number},
{"Non recursive calls per-call cost",
table_column_class_callseqs},
{"Recursive calls per-call cost (ex children)",
table_column_class_callseqs}],
Header = table_header(list.map(NameClassGroupFunc, NamesClasses)),
Table = display_table(
table(table_class_box_if_pref, 4, yes(Header), Rows)),
Description = display_text(Text),
Items = [Description,
display_paragraph_break, Table,
display_paragraph_break, ExtraTable]
).
:- func make_recursion_table_row({string, recursion_level_report}) = table_row.
make_recursion_table_row({Label, Report}) =
table_row([table_cell(td_s(Label)),
table_cell(td_i(Report ^ rlr_calls)),
table_cell(td_f(Report ^ rlr_non_rec_calls_cost)),
table_cell(td_f(Report ^ rlr_rec_calls_ex_chld_cost))]).
:- pred display_report_recursion_types_frequency(preferences::in,
recursion_types_frequency_report::in, display::out) is det.
display_report_recursion_types_frequency(Prefs, Report, Display) :-
Cmd = deep_cmd_recursion_types_frequency,
Report = recursion_types_frequency_report(Histogram0),
Title = "Frequencies of recognized recursion types",
% Build the table.
RecursionTypeLink = deep_link(Cmd,
yes(Prefs ^ pref_criteria := by_context),
attr_str([], "Recursion Type"), link_class_link),
RecursionTypeHeaderGroup = make_single_table_header_group(
td_l(RecursionTypeLink), table_column_class_no_class,
column_do_not_colour),
FreqHeaderGroup = make_single_table_header_group(
td_s("Frequency"), table_column_class_no_class,
column_do_not_colour),
PercentageHeaderGroup = make_single_table_header_group(
td_s("Percentage"), table_column_class_no_class,
column_do_not_colour),
perf_table_header(total_columns_meaningful, Prefs,
override_order_criteria_header_data(Cmd), PerfHeaderGroups),
AllHeaderGroups = [RecursionTypeHeaderGroup, FreqHeaderGroup,
PercentageHeaderGroup] ++ PerfHeaderGroups,
header_groups_to_header(AllHeaderGroups, NumColumns, Header),
Histogram1 = map.to_assoc_list(Histogram0),
sort_recursion_types_by_preferences(Prefs, Histogram1, Histogram),
list.map(display_report_rec_type_freq_rows(Prefs, NumColumns),
Histogram, Rowss),
list.condense(Rowss, Rows),
RecursionTypesTable = display_table(table(table_class_box_if_pref,
NumColumns, yes(Header), Rows)),
ModuleQualControls = module_qual_controls(Prefs, Cmd),
FieldControls = field_controls(Prefs, Cmd),
FormatControls = format_controls(Prefs, Cmd),
MenuRestartQuitControls = cmds_menu_restart_quit(yes(Prefs)),
Display = display(yes(Title), [RecursionTypesTable,
display_paragraph_break, ModuleQualControls,
display_paragraph_break, FieldControls,
display_paragraph_break, FormatControls,
display_paragraph_break, MenuRestartQuitControls]).
:- pred sort_recursion_types_by_preferences(preferences::in,
assoc_list(recursion_type_simple, recursion_type_freq_data)::in,
assoc_list(recursion_type_simple, recursion_type_freq_data)::out) is det.
sort_recursion_types_by_preferences(Prefs, !RecursionTypes) :-
OrderCriteria = Prefs ^ pref_criteria,
(
( OrderCriteria = by_context
; OrderCriteria = by_name
),
% Sort by the type of recursion.
list.sort(compare_recursion_type_row_by_rec_type, !RecursionTypes)
;
OrderCriteria = by_cost(CostKind, InclDesc, Scope),
list.sort(
compare_rec_type_row_datas_by_cost(CostKind, InclDesc, Scope),
!RecursionTypes),
% We want the most expensive rows to appear first.
list.reverse(!RecursionTypes)
).
:- pred sort_recursion_type_procs_by_preferences(preferences::in,
list(recursion_type_proc_freq_data)::in,
list(recursion_type_proc_freq_data)::out) is det.
sort_recursion_type_procs_by_preferences(Prefs, !RecursionTypeProcs) :-
OrderCriteria = Prefs ^ pref_criteria,
(
( OrderCriteria = by_context
; OrderCriteria = by_name
),
% Since in this case we sort recursion type rows by their recursion
% type It makes sense to sort the procs within each type by their
% frequency.
sort(compare_recursion_proc_row_by_frequency_rev, !RecursionTypeProcs)
;
OrderCriteria = by_cost(CostKind, InclDesc, Scope),
sort(compare_rec_proc_row_datas_by_cost(CostKind, InclDesc, Scope),
!RecursionTypeProcs),
% We want the most expensive rows to appear first.
reverse(!RecursionTypeProcs)
).
:- pred compare_recursion_type_row_by_rec_type(
pair(recursion_type_simple, T)::in,
pair(recursion_type_simple, T)::in, comparison_result::out) is det.
compare_recursion_type_row_by_rec_type(RTA - _, RTB - _, Result) :-
compare(Result, RTA, RTB).
:- pred compare_recursion_proc_row_by_frequency_rev(
recursion_type_proc_freq_data::in, recursion_type_proc_freq_data::in,
comparison_result::out) is det.
compare_recursion_proc_row_by_frequency_rev(
recursion_type_proc_freq_data(FreqA, _, _),
recursion_type_proc_freq_data(FreqB, _, _), Result) :-
% The reverse is so that entries with larger frequencies are listed first.
compare(Result, FreqB, FreqA).
:- pred compare_rec_type_row_datas_by_cost(cost_kind::in,
include_descendants::in, measurement_scope::in,
pair(T, recursion_type_freq_data)::in,
pair(T, recursion_type_freq_data)::in,
comparison_result::out) is det.
compare_rec_type_row_datas_by_cost(CostKind, IncDesc, Scope, _ - DataA,
_ - DataB, Result) :-
MaybePerfA = DataA ^ rtfd_maybe_summary,
MaybePerfB = DataB ^ rtfd_maybe_summary,
(
MaybePerfA = yes(PerfA),
MaybePerfB = yes(PerfB),
compare_perf_row_datas_by_cost(CostKind, IncDesc, Scope,
PerfA, PerfB, Result)
;
MaybePerfA = yes(_),
MaybePerfB = no,
Result = (>)
;
MaybePerfA = no,
MaybePerfB = yes(_),
Result = (=)
;
MaybePerfA = no,
MaybePerfB = no,
Result = (=)
).
:- pred compare_rec_proc_row_datas_by_cost(cost_kind::in,
include_descendants::in, measurement_scope::in,
recursion_type_proc_freq_data::in, recursion_type_proc_freq_data::in,
comparison_result::out) is det.
compare_rec_proc_row_datas_by_cost(CostKind, InclDesc, Scope,
recursion_type_proc_freq_data(_, _, PerfA),
recursion_type_proc_freq_data(_, _, PerfB), Result) :-
compare_perf_row_datas_by_cost(CostKind, InclDesc, Scope, PerfA, PerfB,
Result).
:- pred display_report_rec_type_freq_rows(preferences::in, int::in,
pair(recursion_type_simple, recursion_type_freq_data)::in,
list(table_row)::out) is det.
display_report_rec_type_freq_rows(Prefs, NumColumns, Type - FreqData, Rows) :-
FreqData = recursion_type_freq_data(Frequency, Percent, MaybeSummary,
ProcsMap),
% Make the header row.
display_report_recursion_type_simple(Type, TypeStr),
HeaderRow = table_section_header(td_s(TypeStr)),
% Make the row summarising the recursion type.
(
MaybeSummary = yes(Summary),
perf_table_row(total_columns_meaningful, Prefs ^ pref_fields, Summary,
SummaryCells)
;
MaybeSummary = no,
duplicate(NumColumns - 3, table_empty_cell, SummaryCells)
),
Row = table_row([table_cell(td_s("Totals")), table_cell(td_i(Frequency)),
table_cell(td_p(Percent))] ++ SummaryCells),
% Make rows for the top proc statics.
Procs0 = values(ProcsMap),
sort_recursion_type_procs_by_preferences(Prefs, Procs0, Procs1),
take_upto(Prefs ^ pref_proc_statics_per_rec_type, Procs1, Procs),
list.map(display_report_rec_type_proc_rows(Prefs), Procs, ProcRows),
Rows = [HeaderRow | [Row | ProcRows ++
[table_separator_row]]].
:- pred display_report_rec_type_proc_rows(preferences::in,
recursion_type_proc_freq_data::in, table_row::out) is det.
display_report_rec_type_proc_rows(Prefs,
recursion_type_proc_freq_data(Freq, Percent, Summary), Row) :-
perf_table_row(total_columns_meaningful, Prefs ^ pref_fields, Summary,
SummaryCells),
ProcDesc = Summary ^ perf_row_subject,
ProcCell = proc_desc_to_proc_name_cell(no, Prefs ^ pref_module_qual, Prefs,
ProcDesc),
Row = table_row([ProcCell, table_cell(td_i(Freq)),
table_cell(td_p(Percent))] ++ SummaryCells).
:- pred display_report_recursion_type_simple(recursion_type_simple::in,
string::out) is det.
display_report_recursion_type_simple(rts_not_recursive, "Not recursive").
display_report_recursion_type_simple(rts_single, "Single-recursion").
display_report_recursion_type_simple(rts_divide_and_conquer,
"Divide and conquer").
display_report_recursion_type_simple(rts_mutual_recursion(NumProcs), String) :-
format("Mutual recursion between %d procs", [i(NumProcs)], String).
display_report_recursion_type_simple(rts_other(Levels), String) :-
LevelsStr = join_list(", ", list.map(string, set.to_sorted_list(Levels))),
format("Other recursion with levels: %s", [s(LevelsStr)], String).
display_report_recursion_type_simple(rts_total_error_instances,
"Total errors").
display_report_recursion_type_simple(rts_error(Error), "Error: " ++ Error).
%---------------------------------------------------------------------------%
%
% Code to display a program_modules report.
%
% Create a display_report structure for a program_modules report.
%
:- pred display_report_program_modules(preferences::in,
program_modules_report::in, display::out) is det.
display_report_program_modules(Prefs, ProgramModulesReport, Display) :-
ProgramModulesReport = program_modules_report(ModuleRowDatas0),
Cmd = deep_cmd_program_modules,
Title = "The modules of the program:",
ShowInactiveModules = Prefs ^ pref_inactive ^ inactive_modules,
(
ShowInactiveModules = inactive_show,
ModuleRowDatas1 = ModuleRowDatas0
;
ShowInactiveModules = inactive_hide,
list.filter(active_module, ModuleRowDatas0, ModuleRowDatas1)
),
SortPrefs = avoid_sort_self_and_desc(Prefs),
sort_module_active_rows_by_preferences(SortPrefs,
ModuleRowDatas1, ModuleRowDatas),
% Build the table of all modules.
SortByNamePrefs = Prefs ^ pref_criteria := by_name,
ModuleHeaderCell = td_l(deep_link(Cmd, yes(SortByNamePrefs),
attr_str([], "Module"), link_class_link)),
RankHeaderGroup =
make_single_table_header_group(td_s("Rank"),
table_column_class_ordinal_rank, column_do_not_colour),
ModuleHeaderGroup =
make_single_table_header_group(ModuleHeaderCell,
table_column_class_module_name, column_do_not_colour),
MakeHeaderData = override_order_criteria_header_data(Cmd),
perf_table_header(total_columns_not_meaningful, Prefs, MakeHeaderData,
PerfHeaderGroups),
AllHeaderGroups =
[RankHeaderGroup, ModuleHeaderGroup] ++ PerfHeaderGroups,
header_groups_to_header(AllHeaderGroups, NumColumns, Header),
list.map_foldl(
maybe_ranked_subject_perf_table_row(Prefs, ranked,
total_columns_not_meaningful, module_active_to_module_name_cell),
ModuleRowDatas, Rows, 1, _),
Table = table(table_class_box_if_pref, NumColumns, yes(Header), Rows),
DisplayTable = display_table(Table),
% Build controls at the bottom of the page.
InactiveControls = inactive_module_controls(Prefs, Cmd),
FieldControls = field_controls(Prefs, Cmd),
FormatControls = format_controls(Prefs, Cmd),
MenuRestartQuitControls = cmds_menu_restart_quit(yes(Prefs)),
Display = display(yes(Title),
[DisplayTable,
display_paragraph_break, InactiveControls,
display_paragraph_break, FieldControls,
display_paragraph_break, FormatControls,
display_paragraph_break, MenuRestartQuitControls]).
:- pred active_module(perf_row_data(module_active)::in) is semidet.
active_module(ModuleRowData) :-
ModuleActive = ModuleRowData ^ perf_row_subject,
ModuleActive ^ ma_is_active = module_is_active.
:- func avoid_sort_self_and_desc(preferences) = preferences.
avoid_sort_self_and_desc(Prefs) = SortPrefs :-
( if Prefs ^ pref_criteria = by_cost(CostKind, self_and_desc, Scope) then
SortPrefs = Prefs ^ pref_criteria := by_cost(CostKind, self, Scope)
else
SortPrefs = Prefs
).
%---------------------------------------------------------------------------%
%
% Code to display a module report.
%
% Create a display_report structure for a module report.
%
:- pred display_report_module(preferences::in, module_report::in, display::out)
is det.
display_report_module(Prefs, ModuleReport, Display) :-
ModuleReport = module_report(ModuleName, HaveModuleRep, ProcRowDatas0),
Cmd = deep_cmd_module(ModuleName),
Title = string.format("The procedures of module %s:", [s(ModuleName)]),
ShowInactiveProcs = Prefs ^ pref_inactive ^ inactive_procs,
(
ShowInactiveProcs = inactive_show,
ProcRowDatas1 = ProcRowDatas0
;
ShowInactiveProcs = inactive_hide,
list.filter(active_proc, ProcRowDatas0, ProcRowDatas1)
),
MaybeCurModuleName = yes(ModuleName),
ModuleQual = Prefs ^ pref_module_qual,
sort_proc_active_rows_by_preferences(MaybeCurModuleName, ModuleQual,
Prefs, ProcRowDatas1, ProcRowDatas),
% Build the table of all modules.
SortByNamePrefs = Prefs ^ pref_criteria := by_name,
ProcHeaderCell = td_l(deep_link(Cmd, yes(SortByNamePrefs),
attr_str([], "Procedure"), link_class_link)),
RankHeaderGroup =
make_single_table_header_group(td_s("Rank"),
table_column_class_ordinal_rank, column_do_not_colour),
ProcHeaderGroup =
make_single_table_header_group(ProcHeaderCell,
table_column_class_module_name, column_do_not_colour),
MakeHeaderData = override_order_criteria_header_data(Cmd),
perf_table_header(total_columns_meaningful, Prefs, MakeHeaderData,
PerfHeaderGroups),
AllHeaderGroups =
[RankHeaderGroup, ProcHeaderGroup] ++ PerfHeaderGroups,
header_groups_to_header(AllHeaderGroups, NumColumns, Header),
list.map_foldl(
maybe_ranked_subject_perf_table_row(Prefs, ranked,
total_columns_meaningful,
proc_active_to_proc_name_cell(MaybeCurModuleName, ModuleQual)),
ProcRowDatas, Rows, 1, _),
Table = table(table_class_box_if_pref, NumColumns, yes(Header), Rows),
DisplayTable = display_table(Table),
% Build controls at the bottom of the page.
GetterSetterCmd = deep_cmd_module_getter_setters(ModuleName),
GetterSetterControl = display_link(deep_link(GetterSetterCmd, yes(Prefs),
attr_str([], "Show field getters and setters"), link_class_link)),
(
HaveModuleRep = do_not_have_module_rep,
ModuleControls =
[display_paragraph_break, GetterSetterControl]
;
HaveModuleRep = have_module_rep,
RepCmd = deep_cmd_module_rep(ModuleName),
RepControl = display_link(deep_link(RepCmd, yes(Prefs),
attr_str([], "Show module representation"), link_class_link)),
ModuleControls =
[display_paragraph_break, GetterSetterControl,
display_paragraph_break, RepControl]
),
InactiveControls = inactive_proc_controls(Prefs, Cmd),
FieldControls = field_controls(Prefs, Cmd),
FormatControls = format_controls(Prefs, Cmd),
MenuRestartQuitControls = cmds_menu_restart_quit(yes(Prefs)),
GeneralControls =
[display_paragraph_break, InactiveControls,
display_paragraph_break, FieldControls,
display_paragraph_break, FormatControls,
display_paragraph_break, MenuRestartQuitControls],
Display = display(yes(Title),
[DisplayTable] ++ ModuleControls ++ GeneralControls).
:- pred active_proc(perf_row_data(proc_active)::in) is semidet.
active_proc(ProcRowData) :-
ProcActive = ProcRowData ^ perf_row_subject,
ProcActive ^ pa_is_active = proc_is_active.
%---------------------------------------------------------------------------%
%
% Code to display a module_getter_setters report.
%
% Create a display_report structure for a module_getter_setters report.
%
:- pred display_report_module_getter_setters(preferences::in,
module_getter_setters_report::in, display::out) is det.
display_report_module_getter_setters(Prefs, Report, Display) :-
Report = module_getter_setters_report(ModuleName, GSMap),
Title = string.format("The getters and setters of module %s:",
[s(ModuleName)]),
map.to_assoc_list(GSMap, GSPairs),
RowLists = list.map(display_data_struct_getter_setters(Prefs, ModuleName),
GSPairs),
list.condense(RowLists, Rows),
SortByNamePrefs = Prefs ^ pref_criteria := by_name,
FieldNameHeaderCell = td_l(deep_link(Cmd, yes(SortByNamePrefs),
attr_str([], "FieldName"), link_class_link)),
RankHeaderGroup = make_single_table_header_group(td_s("Rank"),
table_column_class_ordinal_rank, column_do_not_colour),
FieldNameHeaderGroup = make_single_table_header_group(FieldNameHeaderCell,
table_column_class_field_name, column_do_not_colour),
Cmd = deep_cmd_module_getter_setters(ModuleName),
MakeHeaderData = override_order_criteria_header_data(Cmd),
perf_table_header(total_columns_meaningful, Prefs, MakeHeaderData,
PerfHeaderGroups),
header_groups_to_header([RankHeaderGroup, FieldNameHeaderGroup
| PerfHeaderGroups], NumColumns, Header),
Table = table(table_class_box_if_pref, NumColumns, yes(Header), Rows),
TableItem = display_table(Table),
ModuleCmd = deep_cmd_module(ModuleName),
ModuleControl = display_link(deep_link(ModuleCmd, yes(Prefs),
attr_str([], "Show all the procedures of the module"),
link_class_link)),
MenuResetQuitControls = cmds_menu_restart_quit(yes(Prefs)),
Controls =
[display_paragraph_break, ModuleControl,
display_paragraph_break, MenuResetQuitControls],
Display = display(yes(Title), [TableItem] ++ Controls).
:- func display_data_struct_getter_setters(preferences, string,
pair(data_struct_name, gs_field_map)) = list(table_row).
display_data_struct_getter_setters(Prefs, ModuleName,
DataStructName - FieldMap) = Rows :-
DataStructName = data_struct_name(Name),
Title = string.format("The getters and setters of %s:", [s(Name)]),
TitleHeader = table_section_header(td_as(attr_str([attr_bold], Title))),
map.to_assoc_list(FieldMap, FieldPairs0),
sort_getter_setter_fields(Prefs, FieldPairs0, FieldPairs),
list.map_foldl(display_field_getter_setters(Prefs, ModuleName),
FieldPairs, DataRowLists, 1, _),
list.condense(DataRowLists, DataRows),
Rows = [table_separator_row, TitleHeader, table_separator_row | DataRows].
:- pred display_field_getter_setters(preferences::in, string::in,
pair(field_name, gs_field_info)::in, list(table_row)::out,
int::in, int::out) is det.
display_field_getter_setters(Prefs, ModuleName, FieldName - FieldInfo, Rows,
!Rank):-
Fields = Prefs ^ pref_fields,
FieldName = field_name(Name),
RankCell = table_cell(td_i(!.Rank)),
MaybeCurModuleName = yes(ModuleName),
ModuleQual = Prefs ^ pref_module_qual,
(
FieldInfo = gs_field_getter(GetterRowData),
perf_table_row(total_columns_meaningful, Fields, GetterRowData,
GetterPerfCells),
GetterProcDesc = GetterRowData ^ perf_row_subject,
GetterFieldNameCell = proc_desc_to_proc_name_cell(MaybeCurModuleName,
ModuleQual, Prefs, GetterProcDesc),
GetterRow =
table_row([RankCell, GetterFieldNameCell | GetterPerfCells]),
Rows = [table_separator_row, GetterRow]
;
FieldInfo = gs_field_setter(SetterRowData),
perf_table_row(total_columns_meaningful, Fields, SetterRowData,
SetterPerfCells),
SetterProcDesc = SetterRowData ^ perf_row_subject,
SetterFieldNameCell = proc_desc_to_proc_name_cell(MaybeCurModuleName,
ModuleQual, Prefs, SetterProcDesc),
SetterRow =
table_row([RankCell, SetterFieldNameCell | SetterPerfCells]),
Rows = [table_separator_row, SetterRow]
;
FieldInfo = gs_field_both(GetterRowData, SetterRowData, SumRowData),
EmptyCell = table_cell(td_s("")),
perf_table_row(total_columns_meaningful, Fields, SumRowData,
SumPerfCells),
SummaryName = string.format("%s summary", [s(Name)]),
SumFieldNameCell = table_cell(td_s(SummaryName)),
SumRow = table_row([RankCell, SumFieldNameCell | SumPerfCells]),
perf_table_row(total_columns_meaningful, Fields, GetterRowData,
GetterPerfCells),
GetterProcDesc = GetterRowData ^ perf_row_subject,
GetterFieldNameCell = proc_desc_to_proc_name_cell(MaybeCurModuleName,
ModuleQual, Prefs, GetterProcDesc),
GetterRow =
table_row([EmptyCell, GetterFieldNameCell | GetterPerfCells]),
perf_table_row(total_columns_meaningful, Fields, SetterRowData,
SetterPerfCells),
SetterProcDesc = SetterRowData ^ perf_row_subject,
SetterFieldNameCell = proc_desc_to_proc_name_cell(MaybeCurModuleName,
ModuleQual, Prefs, SetterProcDesc),
SetterRow =
table_row([EmptyCell, SetterFieldNameCell | SetterPerfCells]),
Rows = [table_separator_row, SumRow, GetterRow, SetterRow]
),
!:Rank = !.Rank + 1.
%---------------------------------------------------------------------------%
%
% Code to display a module_rep report.
%
% Create a display_report structure for a module_rep report.
%
:- pred display_report_module_rep(preferences::in, module_rep_report::in,
display::out) is det.
display_report_module_rep(Prefs, Report, Display) :-
Report = module_rep_report(ModuleName, ModuleRepStr),
Title = string.format("The representation of module %s:",
[s(ModuleName)]),
BodyItem = display_verbatim(ModuleRepStr),
ModuleCmd = deep_cmd_module(ModuleName),
ModuleControl = display_link(deep_link(ModuleCmd, yes(Prefs),
attr_str([], "Show all the procedures of the module"),
link_class_link)),
MenuResetQuitControls = cmds_menu_restart_quit(yes(Prefs)),
Controls =
[display_paragraph_break, ModuleControl,
display_paragraph_break, MenuResetQuitControls],
Display = display(yes(Title), [BodyItem | Controls]).
%---------------------------------------------------------------------------%
%
% Code to display a top procedures report.
%
% Create a display_report structure for a top_procedures report.
%
:- pred display_report_top_procs(preferences::in, top_procs_report::in,
display::out) is det.
display_report_top_procs(Prefs, TopProcsReport, Display) :-
TopProcsReport = top_procs_report(Ordering, TopProcRowDatas),
Ordering = report_ordering(DisplayLimit, CostKind, InclDesc, Scope),
Desc = cost_criteria_to_description(CostKind, InclDesc, Scope),
Title = "Top procedures " ++ Desc,
% Build the table of the top procedures.
Cmd = deep_cmd_top_procs(DisplayLimit, CostKind, InclDesc, Scope),
MakeHeaderData = override_order_criteria_header_data(Cmd),
maybe_ranked_proc_table_header(Prefs, ranked, MakeHeaderData,
NumColumns, Header),
MaybeCurModuleName = no,
ModuleQual = Prefs ^ pref_module_qual,
sort_proc_desc_rows_by_preferences(MaybeCurModuleName, ModuleQual,
Prefs, TopProcRowDatas, OrderedTopProcRowDatas),
list.map_foldl(
maybe_ranked_subject_perf_table_row(Prefs, ranked,
total_columns_meaningful,
proc_desc_to_proc_name_cell(no, ModuleQual)),
OrderedTopProcRowDatas, Rows, 1, _),
Table = table(table_class_box_if_pref, NumColumns, yes(Header), Rows),
DisplayTable = display_table(Table),
% Build controls at the bottom of the page.
TopProcsControls = top_procs_controls(Prefs,
DisplayLimit, CostKind, InclDesc, Scope),
FieldControls = field_controls(Prefs, Cmd),
FormatControls = format_controls(Prefs, Cmd),
MenuRestartQuitControls = cmds_menu_restart_quit(yes(Prefs)),
Display = display(yes(Title),
[DisplayTable,
display_paragraph_break, TopProcsControls,
display_paragraph_break, FieldControls,
display_paragraph_break, FormatControls,
display_paragraph_break, MenuRestartQuitControls]).
% Create a phrase describing how the top procedures may be sorted.
%
:- func cost_criteria_to_description(cost_kind, include_descendants,
measurement_scope) = string.
cost_criteria_to_description(CostKind, InclDesc, Scope) = Desc :-
Desc =
"ordered by " ++
incl_desc_to_description(InclDesc) ++ " " ++
cost_kind_to_description(CostKind) ++ " " ++
scope_to_description(Scope).
% Give the short name for what profiling data a field may be measuring.
%
:- func incl_desc_to_description(include_descendants) = string.
incl_desc_to_description(self) = "self".
incl_desc_to_description(self_and_desc) = "total".
% Describe the a measurement used by the deep profiler.
%
:- func cost_kind_to_description(cost_kind) = string.
cost_kind_to_description(cost_calls) = "number of calls".
cost_kind_to_description(cost_redos) = "number of redos".
cost_kind_to_description(cost_time) = "time".
cost_kind_to_description(cost_callseqs) = "call sequence numbers".
cost_kind_to_description(cost_allocs) = "memory allocations".
cost_kind_to_description(cost_words) = "words allocated".
% Describe a scope of profiling data.
%
:- func scope_to_description(measurement_scope) = string.
scope_to_description(per_call) = "per call".
scope_to_description(overall) = "overall".
%---------------------------------------------------------------------------%
%
% Code to display a procedure report.
%
% Create a display_report structure for a proc report.
%
:- pred display_report_proc(deep::in, preferences::in, proc_report::in,
display::out) is det.
display_report_proc(Deep, Prefs, ProcReport, Display) :-
ProcReport = proc_report(CallersSummaryRowData, ProcSummaryRowData,
CallSitePerfs0),
ProcDesc = ProcSummaryRowData ^ perf_row_subject,
RefinedName = ProcDesc ^ pdesc_q_refined_name,
Title = "Summary of procedure " ++ RefinedName,
PSPtr = ProcDesc ^ pdesc_ps_ptr,
Cmd = deep_cmd_proc(PSPtr),
SortByContextPrefs = Prefs ^ pref_criteria := by_context,
SourceHeaderCell = td_l(deep_link(Cmd, yes(SortByContextPrefs),
attr_str([], "Source"), link_class_link)),
SortByNamePrefs = Prefs ^ pref_criteria := by_name,
ProcHeaderCell = td_l(deep_link(Cmd, yes(SortByNamePrefs),
attr_str([], "Procedure"), link_class_link)),
SourceHeaderGroup = make_single_table_header_group(SourceHeaderCell,
table_column_class_source_context, column_do_not_colour),
ProcHeaderGroup = make_single_table_header_group(ProcHeaderCell,
table_column_class_proc, column_do_not_colour),
MakeHeaderData = override_order_criteria_header_data(Cmd),
perf_table_header(total_columns_meaningful, Prefs, MakeHeaderData,
PerfHeaderGroups),
AllHeaderGroups =
[SourceHeaderGroup, ProcHeaderGroup] ++ PerfHeaderGroups,
header_groups_to_header(AllHeaderGroups, NumColumns, Header),
Fields = Prefs ^ pref_fields,
CallersCounts = CallersSummaryRowData ^ perf_row_subject,
CallersSummaryText = format("%d static & %d dynamic call sites",
[i(CallersCounts ^ cc_static), i(CallersCounts ^ cc_dynamic)]),
CallersSummaryCell = table_multi_cell(td_s(CallersSummaryText), 2),
perf_table_row(total_columns_meaningful, Fields, CallersSummaryRowData,
CallersPerfCells),
CallersCells = [CallersSummaryCell] ++ CallersPerfCells,
CallersRow = table_row(CallersCells),
% We could make SummaryProcCell a link, but the only link that would make
% sense (and the link that pre-display versions of the deep profiler
% generated) point back to this page itself, which is not useful and
% could be considered misleading.
%
% SummaryProcCell spans two columns: the ones that contain (1) the context
% and (2) the callee of each call site in the rows below.
SummaryProcCell = table_multi_cell(td_s(RefinedName), 2),
perf_table_row(total_columns_meaningful, Fields, ProcSummaryRowData,
SummaryPerfCells),
SummaryCells = [SummaryProcCell] ++ SummaryPerfCells,
SummaryRow = table_row(SummaryCells),
MaybeCurModuleName = yes(ProcDesc ^ pdesc_module_name),
ModuleQual = Prefs ^ pref_module_qual,
sort_call_sites_by_preferences(MaybeCurModuleName, ModuleQual, Prefs,
CallSitePerfs0, CallSitePerfs),
CallSiteRowLists = list.map(
report_proc_call_site(MaybeCurModuleName, ModuleQual, Prefs),
CallSitePerfs),
list.condense(CallSiteRowLists, CallSiteRows),
DeveloperRows = list.map((func(X) = table_developer_row(X)),
[table_section_header(td_s(
"Callers excluding directly-recursive calls")),
CallersRow, table_separator_row]),
CommonRows = [SummaryRow, table_separator_row] ++ CallSiteRows,
AllRows = DeveloperRows ++ CommonRows,
Table = table(table_class_box_if_pref, NumColumns, yes(Header), AllRows),
DisplayTable = display_table(Table),
% Build the controls at the bottom of the page.
ProcCallersControls = proc_callers_group_controls(Deep, Prefs, Cmd,
PSPtr, group_by_call_site, default_callers_per_bunch,
Prefs ^ pref_contour),
SummarizeControls = summarize_controls(Prefs, Cmd),
InactiveCallSitesControls = inactive_call_site_controls(Prefs, Cmd),
ModuleQualControls = module_qual_controls(Prefs, Cmd),
SortControls = sort_controls(Prefs, Cmd),
FieldControls = field_controls(Prefs, Cmd),
FormatControls = format_controls(Prefs, Cmd),
ProcReportControls = proc_reports_controls(Prefs, PSPtr, Cmd),
MenuRestartQuitControls = cmds_menu_restart_quit(yes(Prefs)),
Display = display(yes(Title),
[DisplayTable,
display_paragraph_break, ProcCallersControls,
display_paragraph_break, SummarizeControls,
display_paragraph_break, InactiveCallSitesControls,
display_paragraph_break, ModuleQualControls,
display_paragraph_break, SortControls,
display_paragraph_break, FieldControls,
display_paragraph_break, FormatControls,
display_paragraph_break, ProcReportControls,
display_paragraph_break, MenuRestartQuitControls]).
:- func default_callers_per_bunch = int.
default_callers_per_bunch = 20.
:- func report_proc_call_site(maybe(string), module_qual, preferences,
call_site_perf) = list(table_row).
report_proc_call_site(MaybeCurModuleName, ModuleQual, Prefs, CallSitePerf)
= Rows :-
CallSitePerf =
call_site_perf(KindAndCallee, SummaryPerfRowData, SubPerfs0),
CallSiteDesc = SummaryPerfRowData ^ perf_row_subject,
ContextCell = call_site_desc_source_cell(CallSiteDesc),
(
KindAndCallee = normal_call_and_info(NormalCalleeId),
NormalCalleeId = normal_callee_id(CalleeDesc, TypeSubstStr),
CalleeRefinedName = proc_desc_get_refined_id(MaybeCurModuleName,
ModuleQual, CalleeDesc),
( if TypeSubstStr = "" then
CallSiteStr = CalleeRefinedName
else
CallSiteStr = string.format("%s [%s]",
[s(CalleeRefinedName), s(TypeSubstStr)])
),
CalleePSPtr = CalleeDesc ^ pdesc_ps_ptr,
CallSiteLinkCmd = deep_cmd_proc(CalleePSPtr),
CallSiteLink = deep_link(CallSiteLinkCmd, yes(Prefs),
attr_str([], CallSiteStr), link_class_link),
CallSiteCell = table_cell(td_l(CallSiteLink)),
require(unify(SubPerfs0, []),
"report_proc_call_site: SubPerfs0 != [] for normal call site")
;
(
KindAndCallee = special_call_and_no_info,
CallSiteStr = "special call"
;
KindAndCallee = higher_order_call_and_no_info,
CallSiteStr = "higher order call"
;
KindAndCallee = method_call_and_no_info,
CallSiteStr = "method call"
;
KindAndCallee = callback_and_no_info,
CallSiteStr = "callback"
),
CallSiteCell = table_cell(td_s(CallSiteStr))
),
Fields = Prefs ^ pref_fields,
perf_table_row(total_columns_meaningful, Fields, SummaryPerfRowData,
SummaryPerfCells),
SummaryCells = [ContextCell, CallSiteCell] ++ SummaryPerfCells,
SummaryRow = table_row(SummaryCells),
Summarize = Prefs ^ pref_summarize,
(
Summarize = summarize_ho_call_sites,
Rows = [SummaryRow]
;
Summarize = do_not_summarize_ho_call_sites,
sort_proc_desc_rows_by_preferences(MaybeCurModuleName, ModuleQual,
Prefs, SubPerfs0, SubPerfs),
SubRows = list.map(
report_proc_call_site_callee(MaybeCurModuleName, ModuleQual,
Prefs),
SubPerfs),
Rows = [SummaryRow] ++ SubRows
).
:- func report_proc_call_site_callee(maybe(string), module_qual,
preferences, perf_row_data(proc_desc)) = table_row.
report_proc_call_site_callee(MaybeCurModuleName, ModuleQual,
Prefs, RowData) = Row :-
Fields = Prefs ^ pref_fields,
EmptyCell = table_empty_cell,
ProcDesc = RowData ^ perf_row_subject,
ProcCell = proc_desc_to_proc_name_cell(MaybeCurModuleName, ModuleQual,
Prefs, ProcDesc),
perf_table_row(total_columns_meaningful, Fields, RowData, PerfCells),
Cells = [EmptyCell, ProcCell] ++ PerfCells,
Row = table_row(Cells).
%---------------------------------------------------------------------------%
%
% Code to display a procedure callers report.
%
% Create a display_report structure for a proc_callers report.
%
:- pred display_report_proc_callers(deep::in, preferences::in,
proc_callers_report::in, display::out) is det.
display_report_proc_callers(Deep, Prefs0, ProcCallersReport, Display) :-
ProcCallersReport = proc_callers_report(ProcDesc, CallerRowDatas,
BunchNum, BunchSize, ContourExcl, MaybeContourWarnMessage),
(
MaybeContourWarnMessage = no,
WarnItems = []
;
MaybeContourWarnMessage = yes(ContourWarnMessage),
ContourWarnItem = display_text(ContourWarnMessage),
WarnItems = [display_paragraph_break, ContourWarnItem]
),
RefinedName = ProcDesc ^ pdesc_q_refined_name,
MaybeCurModuleName = yes(ProcDesc ^ pdesc_module_name),
ModuleQual = Prefs ^ pref_module_qual,
% Remember the selected value of contour exclusion.
Prefs = Prefs0 ^ pref_contour := ContourExcl,
PSPtr = ProcDesc ^ pdesc_ps_ptr,
(
CallerRowDatas = proc_caller_call_sites(CallSiteRowDatas),
CallerGroups = group_by_call_site,
Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, BunchSize,
ContourExcl),
Title = "The call sites calling " ++ RefinedName,
sort_call_site_desc_rows_by_preferences(MaybeCurModuleName, ModuleQual,
Prefs, CallSiteRowDatas, SortedCallSiteRowDatas),
select_displayed_rows(SortedCallSiteRowDatas, BunchNum, BunchSize,
DisplayedCallSiteRowDatas, TotalNumRows, FirstRowNum, LastRowNum,
DisplayedBunchNum, MaybeFirstAndLastBunchNum),
list.map_foldl(
display_caller_call_site(MaybeCurModuleName, ModuleQual, Prefs),
DisplayedCallSiteRowDatas, Rows, FirstRowNum, AfterLastRowNum),
SortByContextPrefs = Prefs ^ pref_criteria := by_context,
SourceHeaderCell = td_l(deep_link(Cmd, yes(SortByContextPrefs),
attr_str([], "Source"), link_class_link)),
SortByNamePrefs = Prefs ^ pref_criteria := by_context,
ProcHeaderCell = td_l(deep_link(Cmd, yes(SortByNamePrefs),
attr_str([], "In procedure"), link_class_link)),
SourceHeaderGroup = make_single_table_header_group(SourceHeaderCell,
table_column_class_source_context, column_do_not_colour),
ProcHeaderGroup = make_single_table_header_group(ProcHeaderCell,
table_column_class_proc, column_do_not_colour),
IdHeaderGroups = [SourceHeaderGroup, ProcHeaderGroup]
;
CallerRowDatas = proc_caller_procedures(ProcRowDatas),
CallerGroups = group_by_proc,
Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, BunchSize,
ContourExcl),
Title = "The procedures calling " ++ RefinedName,
sort_proc_desc_rows_by_preferences(MaybeCurModuleName, ModuleQual,
Prefs, ProcRowDatas, SortedProcRowDatas),
select_displayed_rows(SortedProcRowDatas, BunchNum, BunchSize,
DisplayedProcRowDatas, TotalNumRows, FirstRowNum, LastRowNum,
DisplayedBunchNum, MaybeFirstAndLastBunchNum),
list.map_foldl(
display_caller_proc(MaybeCurModuleName, ModuleQual, Prefs),
DisplayedProcRowDatas, Rows, FirstRowNum, AfterLastRowNum),
SortByContextPrefs = Prefs ^ pref_criteria := by_context,
SourceHeaderCell = td_l(deep_link(Cmd, yes(SortByContextPrefs),
attr_str([], "Source"), link_class_link)),
SortByNamePrefs = Prefs ^ pref_criteria := by_context,
ProcHeaderCell = td_l(deep_link(Cmd, yes(SortByNamePrefs),
attr_str([], "Procedure"), link_class_link)),
SourceHeaderGroup = make_single_table_header_group(SourceHeaderCell,
table_column_class_source_context, column_do_not_colour),
ProcHeaderGroup = make_single_table_header_group(ProcHeaderCell,
table_column_class_proc, column_do_not_colour),
IdHeaderGroups = [SourceHeaderGroup, ProcHeaderGroup]
;
CallerRowDatas = proc_caller_modules(ModuleRowDatas),
CallerGroups = group_by_module,
Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, BunchSize,
ContourExcl),
Title = "The modules calling " ++ RefinedName,
sort_module_name_rows_by_preferences(Prefs, ModuleRowDatas,
SortedModuleRowDatas),
select_displayed_rows(SortedModuleRowDatas, BunchNum, BunchSize,
DisplayedModuleRowDatas, TotalNumRows, FirstRowNum, LastRowNum,
DisplayedBunchNum, MaybeFirstAndLastBunchNum),
list.map_foldl(display_caller_module(Prefs),
DisplayedModuleRowDatas, Rows, FirstRowNum, AfterLastRowNum),
SortByNamePrefs = Prefs ^ pref_criteria := by_name,
ModuleHeaderCell = td_l(deep_link(Cmd, yes(SortByNamePrefs),
attr_str([], "Module"), link_class_link)),
ModuleHeaderGroup = make_single_table_header_group(ModuleHeaderCell,
table_column_class_source_context, column_do_not_colour),
IdHeaderGroups = [ModuleHeaderGroup]
;
CallerRowDatas = proc_caller_cliques(CliqueRowDatas),
CallerGroups = group_by_clique,
Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, BunchSize,
ContourExcl),
Title = "The cliques calling " ++ RefinedName,
sort_clique_rows_by_preferences(Prefs, CliqueRowDatas,
SortedCliqueRowDatas),
select_displayed_rows(SortedCliqueRowDatas, BunchNum, BunchSize,
DisplayedCliqueRowDatas, TotalNumRows, FirstRowNum, LastRowNum,
DisplayedBunchNum, MaybeFirstAndLastBunchNum),
list.map_foldl(
display_caller_clique(MaybeCurModuleName, ModuleQual, Prefs),
DisplayedCliqueRowDatas, Rows, FirstRowNum, AfterLastRowNum),
SortByNamePrefs = Prefs ^ pref_criteria := by_name,
CliqueHeaderCell = td_l(deep_link(Cmd, yes(SortByNamePrefs),
attr_str([], "Clique"), link_class_link)),
CliqueHeaderGroup = make_single_table_header_group(CliqueHeaderCell,
table_column_class_clique, column_do_not_colour),
MembersHeaderGroup = make_single_table_header_group(td_s("Members"),
table_column_class_clique, column_do_not_colour),
IdHeaderGroups = [CliqueHeaderGroup, MembersHeaderGroup]
),
RankHeaderGroup = make_single_table_header_group(td_s("Rank"),
table_column_class_ordinal_rank, column_do_not_colour),
MakeHeaderData = override_order_criteria_header_data(Cmd),
perf_table_header(total_columns_meaningful, Prefs, MakeHeaderData,
PerfHeaderGroups),
AllHeaderGroups = [RankHeaderGroup] ++ IdHeaderGroups ++ PerfHeaderGroups,
header_groups_to_header(AllHeaderGroups, NumColumns, Header),
(
MaybeFirstAndLastBunchNum = no,
Message = "There are none.",
Display = display(yes(Title), [display_text(Message)])
;
MaybeFirstAndLastBunchNum = yes({FirstBunchNum, LastBunchNum}),
require(unify(LastRowNum + 1, AfterLastRowNum),
"display_report_proc_callers: row number mismatch"),
require((FirstBunchNum =< DisplayedBunchNum),
"display_report_proc_callers: display bunch number mismatch"),
require((DisplayedBunchNum =< LastBunchNum),
"display_report_proc_callers: display bunch number mismatch"),
( if FirstBunchNum = LastBunchNum then
Message = string.format("There are %d:",
[i(TotalNumRows)]),
BunchControls = []
else
Message = string.format("There are %d, showing %d to %d:",
[i(TotalNumRows), i(FirstRowNum), i(LastRowNum)]),
( if BunchNum > FirstBunchNum then
BunchControlsFirst = [make_proc_callers_link(Prefs,
"First group", PSPtr, CallerGroups, FirstBunchNum,
BunchSize, ContourExcl)]
else
BunchControlsFirst = []
),
( if BunchNum - 1 > FirstBunchNum then
BunchControlsPrev = [make_proc_callers_link(Prefs,
"Previous group", PSPtr, CallerGroups, BunchNum - 1,
BunchSize, ContourExcl)]
else
BunchControlsPrev = []
),
( if BunchNum + 1 < LastBunchNum then
BunchControlsNext = [make_proc_callers_link(Prefs,
"Next group", PSPtr, CallerGroups, BunchNum + 1,
BunchSize, ContourExcl)]
else
BunchControlsNext = []
),
( if BunchNum < LastBunchNum then
BunchControlsLast = [make_proc_callers_link(Prefs,
"Last group", PSPtr, CallerGroups, LastBunchNum,
BunchSize, ContourExcl)]
else
BunchControlsLast = []
),
BunchControlList = BunchControlsFirst ++ BunchControlsPrev ++
BunchControlsNext ++ BunchControlsLast,
BunchControls = [display_paragraph_break,
display_list(list_class_horizontal_except_title,
yes("Show other groups:"), BunchControlList)]
),
( if
BunchSize > 5,
HalfBunchSize = BunchSize / 2,
HalfBunchSize \= 10,
HalfBunchSize \= 20,
HalfBunchSize \= 50,
HalfBunchSize \= 100
then
BunchSizeControlPairsHalf = [(BunchSize / 2) -
make_proc_callers_link(Prefs,
"Halve group size", PSPtr, CallerGroups, BunchNum * 2,
BunchSize / 2, ContourExcl)]
else
BunchSizeControlPairsHalf = []
),
( if
DoubleBunchSize = BunchSize * 2,
DoubleBunchSize \= 10,
DoubleBunchSize \= 20,
DoubleBunchSize \= 50,
DoubleBunchSize \= 100
then
BunchSizeControlPairsDouble = [(BunchSize * 2) -
make_proc_callers_link(Prefs,
"Double group size", PSPtr, CallerGroups, BunchNum / 2,
BunchSize * 2, ContourExcl)]
else
BunchSizeControlPairsDouble = []
),
( if
TotalNumRows > 10,
BunchSize \= 10
then
BunchSizeControlPairs10 = [10 -
make_proc_callers_link(Prefs,
"Set group size to 10", PSPtr, CallerGroups,
(BunchNum * BunchSize) / 10, 10, ContourExcl)]
else
BunchSizeControlPairs10 = []
),
( if
TotalNumRows > 20,
BunchSize \= 20
then
BunchSizeControlPairs20 = [20 -
make_proc_callers_link(Prefs,
"Set group size to 20", PSPtr, CallerGroups,
(BunchNum * BunchSize) / 20, 20, ContourExcl)]
else
BunchSizeControlPairs20 = []
),
( if
TotalNumRows > 50,
BunchSize \= 50
then
BunchSizeControlPairs50 = [50 -
make_proc_callers_link(Prefs,
"Set group size to 50", PSPtr, CallerGroups,
(BunchNum * BunchSize) / 50, 50, ContourExcl)]
else
BunchSizeControlPairs50 = []
),
( if
TotalNumRows > 100,
BunchSize \= 100
then
BunchSizeControlPairs100 = [100 -
make_proc_callers_link(Prefs,
"Set group size to 100", PSPtr, CallerGroups,
(BunchNum * BunchSize) / 100, 100, ContourExcl)]
else
BunchSizeControlPairs100 = []
),
BunchSizeControlPairs =
BunchSizeControlPairsHalf ++ BunchSizeControlPairsDouble ++
BunchSizeControlPairs10 ++ BunchSizeControlPairs20 ++
BunchSizeControlPairs50 ++ BunchSizeControlPairs100,
list.sort(BunchSizeControlPairs, SortedBunchSizeControlPairs),
assoc_list.values(SortedBunchSizeControlPairs, BunchSizeControlList),
BunchSizeControls = [display_paragraph_break,
display_list(list_class_horizontal_except_title,
yes("Change group size:"), BunchSizeControlList)],
Table = table(table_class_box_if_pref, NumColumns, yes(Header), Rows),
DisplayTable = display_table(Table),
% Build the controls at the bottom of the page.
FirstCmd = deep_cmd_proc_callers(PSPtr, CallerGroups, 1, BunchSize,
ContourExcl),
CallerGroupControls = proc_callers_group_controls(Deep, Prefs,
FirstCmd, PSPtr, CallerGroups, BunchSize, ContourExcl),
FieldControls = field_controls(Prefs, Cmd),
FormatControls = format_controls(Prefs, Cmd),
MenuRestartQuitControls = cmds_menu_restart_quit(yes(Prefs)),
Display = display(yes(Title),
[display_text(Message)] ++
WarnItems ++
[display_paragraph_break, DisplayTable] ++
BunchControls ++
BunchSizeControls ++
[display_paragraph_break, CallerGroupControls,
display_paragraph_break, FieldControls,
display_paragraph_break, FormatControls,
display_paragraph_break, MenuRestartQuitControls])
).
:- pred select_displayed_rows(list(perf_row_data(T))::in, int::in, int::in,
list(perf_row_data(T))::out, int::out, int::out, int::out,
int::out, maybe({int, int})::out) is det.
select_displayed_rows(RowDatas, BunchNum, BunchSize, DisplayRowDatas,
TotalNumRows, FirstRowNum, LastRowNum,
DisplayedBunchNum, MaybeFirstAndLastBunchNum) :-
list.length(RowDatas, TotalNumRows),
NumRowsToDelete = (BunchNum - 1) * BunchSize,
( if
list.drop(NumRowsToDelete, RowDatas, RemainingRowDatasPrime),
RemainingRowDatasPrime = [_ | _]
then
DisplayedBunchNum = BunchNum,
% We start counting rows at 1, not 0.
FirstRowNum = NumRowsToDelete + 1,
RemainingRowDatas = RemainingRowDatasPrime,
NumRemainingRows = TotalNumRows - NumRowsToDelete
else
DisplayedBunchNum = 1,
FirstRowNum = 1,
RemainingRowDatas = RowDatas,
NumRemainingRows = TotalNumRows
),
( if NumRemainingRows > BunchSize then
list.take_upto(BunchSize, RemainingRowDatas, DisplayRowDatas)
else
DisplayRowDatas = RemainingRowDatas
),
LastRowNum = FirstRowNum - 1 + list.length(DisplayRowDatas),
( if TotalNumRows > 0 then
FirstBunchNum = 1,
LastBunchNum = (TotalNumRows + BunchSize - 1) / BunchSize,
MaybeFirstAndLastBunchNum = yes({FirstBunchNum, LastBunchNum})
else
MaybeFirstAndLastBunchNum = no
).
:- pred display_caller_call_site(maybe(string)::in, module_qual::in,
preferences::in, perf_row_data(call_site_desc)::in, table_row::out,
int::in, int::out) is det.
display_caller_call_site(MaybeCurModuleName, ModuleQual, Prefs,
CallSiteRowData, Row, !RowNum) :-
RankCell = table_cell(td_i(!.RowNum)),
!:RowNum = !.RowNum + 1,
CallSiteDesc = CallSiteRowData ^ perf_row_subject,
SourceCell = call_site_desc_to_source_cell(CallSiteDesc),
ProcCell = call_site_desc_to_caller_proc_name_cell(MaybeCurModuleName,
ModuleQual, Prefs, CallSiteDesc),
Fields = Prefs ^ pref_fields,
perf_table_row(total_columns_meaningful, Fields, CallSiteRowData,
PerfCells),
Cells = [RankCell, SourceCell, ProcCell] ++ PerfCells,
Row = table_row(Cells).
:- pred display_caller_proc(maybe(string)::in, module_qual::in,
preferences::in, perf_row_data(proc_desc)::in, table_row::out,
int::in, int::out) is det.
display_caller_proc(MaybeCurModuleName, ModuleQual, Prefs, ProcRowData, Row,
!RowNum) :-
RankCell = table_cell(td_i(!.RowNum)),
!:RowNum = !.RowNum + 1,
ProcDesc = ProcRowData ^ perf_row_subject,
SourceCell = proc_desc_to_source_cell(ProcDesc),
ProcCell = proc_desc_to_proc_name_cell(MaybeCurModuleName, ModuleQual,
Prefs, ProcDesc),
Fields = Prefs ^ pref_fields,
perf_table_row(total_columns_meaningful, Fields, ProcRowData, PerfCells),
Cells = [RankCell, SourceCell, ProcCell] ++ PerfCells,
Row = table_row(Cells).
:- pred display_caller_module(preferences::in, perf_row_data(string)::in,
table_row::out, int::in, int::out) is det.
display_caller_module(Prefs, ModuleRowData, Row, !RowNum) :-
RankCell = table_cell(td_i(!.RowNum)),
!:RowNum = !.RowNum + 1,
ModuleName = ModuleRowData ^ perf_row_subject,
ModuleCell = table_cell(td_s(ModuleName)),
Fields = Prefs ^ pref_fields,
perf_table_row(total_columns_meaningful, Fields, ModuleRowData, PerfCells),
Cells = [RankCell, ModuleCell] ++ PerfCells,
Row = table_row(Cells).
:- pred display_caller_clique(maybe(string)::in, module_qual::in,
preferences::in, perf_row_data(clique_desc)::in, table_row::out,
int::in, int::out) is det.
display_caller_clique(MaybeCurModuleName, ModuleQual, Prefs,
CliqueRowData, Row, !RowNum) :-
RankCell = table_cell(td_i(!.RowNum)),
!:RowNum = !.RowNum + 1,
CliqueDesc = CliqueRowData ^ perf_row_subject,
CliqueDesc = clique_desc(CliquePtr, EntryProcDesc, OtherProcDescs),
CliquePtr = clique_ptr(CliqueNum),
CliqueLinkCmd = deep_cmd_clique(CliquePtr),
CliqueText = string.format("clique %d", [i(CliqueNum)]),
CliqueLink = deep_link(CliqueLinkCmd, yes(Prefs), attr_str([], CliqueText),
link_class_link),
CliqueCell = table_cell(td_l(CliqueLink)),
MembersStrs = list.map(
proc_desc_get_refined_id(MaybeCurModuleName, ModuleQual),
[EntryProcDesc | OtherProcDescs]),
MembersStr = string.join_list(", ", MembersStrs),
MembersCell = table_cell(td_s(MembersStr)),
Fields = Prefs ^ pref_fields,
perf_table_row(total_columns_meaningful, Fields, CliqueRowData, PerfCells),
Cells = [RankCell, CliqueCell, MembersCell] ++ PerfCells,
Row = table_row(Cells).
:- func make_proc_callers_link(preferences, string, proc_static_ptr,
caller_groups, int, int, contour_exclusion) = display_item.
make_proc_callers_link(Prefs, Label, PSPtr, CallerGroups, BunchNum, BunchSize,
ContourExcl) = Item :-
Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, BunchSize,
ContourExcl),
Link = deep_link(Cmd, yes(Prefs), attr_str([], Label), link_class_control),
Item = display_link(Link).
%---------------------------------------------------------------------------%
%
% Code to display procrep_coverage information.
%
:- pred display_report_procrep_coverage_info(preferences::in,
procrep_coverage_info::in, display::out) is det.
display_report_procrep_coverage_info(Prefs, ProcrepCoverageReport, Display) :-
ProcrepCoverageReport =
procrep_coverage_info(PSPtr, Procrep, CoverageArray),
print_proc_to_strings(get_goal_attribute_det(CoverageArray), Procrep,
ProcRepStrings),
string.append_list(list(ProcRepStrings), ProcRepString),
CoverageInfoItem = display_verbatim(ProcRepString),
Cmd = deep_cmd_static_procrep_coverage(PSPtr),
ProcReportControls = proc_reports_controls(Prefs, PSPtr, Cmd),
MenuResetQuitControls = cmds_menu_restart_quit(yes(Prefs)),
Controls =
[display_paragraph_break, ProcReportControls,
display_paragraph_break, MenuResetQuitControls],
Title = "Procedure coverage:",
Display = display(yes(Title), [CoverageInfoItem] ++ Controls).
:- instance goal_annotation(coverage_info) where [
pred(print_goal_annotation_to_strings/3) is coverage_to_cord_string
].
% Print the coverage information for a goal, this is used by
% print_proc_to_strings.
%
:- pred coverage_to_cord_string(var_name_table::in, coverage_info::in,
cord(cord(string))::out) is det.
coverage_to_cord_string(_, Coverage, singleton(singleton(String))) :-
(
Coverage = coverage_unknown,
String0 = "_ - _"
;
Coverage = coverage_known(Before, After),
String0 = string.format("%d - %d", [i(Before), i(After)])
;
(
Coverage = coverage_known_same(Count)
;
Coverage = coverage_known_zero,
Count = 0
),
String0 = string.format("%d - %d", [i(Count), i(Count)])
;
Coverage = coverage_known_before(Before),
String0 = string.format("%d - _", [i(Before)])
;
Coverage = coverage_known_after(After),
String0 = string.format("_ - %d", [i(After)])
),
String = "Coverage: " ++ String0.
%---------------------------------------------------------------------------%
%
% Code to display proc_static and proc_dynamic dumps.
%
% Create a display_report structure for a proc_static_dump report.
%
:- pred display_report_proc_static_dump(proc_static_dump_info::in,
display::out) is det.
display_report_proc_static_dump(ProcStaticDumpInfo, Display) :-
ProcStaticDumpInfo = proc_static_dump_info(PSPtr, RawName,
UnQualRefinedName, QualRefinedName, FileName, LineNumber,
NumCallSites, NumCoveragePoints),
PSPtr = proc_static_ptr(PSI),
string.format("Dump of proc_static %d", [i(PSI)], Title),
Values =
[("Raw name:" - td_s(RawName)),
("Unqualified refined name:" - td_s(UnQualRefinedName)),
("Qualified refined name:" - td_s(QualRefinedName)),
("File name:" - td_s(FileName)),
("Line number:" - td_i(LineNumber)),
("Number of call sites:" - td_i(NumCallSites)),
("Number of coverage points:" - td_i(NumCoveragePoints))],
Rows = list.map(make_labelled_table_row, Values),
Table = table(table_class_do_not_box, 2, no, Rows),
Display = display(yes(Title), [display_table(Table)]).
% Create a display_report structure for a proc_dynamic_dump report.
%
:- pred display_report_proc_dynamic_dump(deep::in, preferences::in,
proc_dynamic_dump_info::in, display::out) is det.
display_report_proc_dynamic_dump(_Deep, Prefs, ProcDynamicDumpInfo, Display) :-
ProcDynamicDumpInfo = proc_dynamic_dump_info(PDPtr, PSPtr, RawName,
ModuleName, UnQualRefinedName, QualRefinedName, CallSites,
MaybeCoveragePoints),
PDPtr = proc_dynamic_ptr(PDI),
PSPtr = proc_static_ptr(PSI),
string.format("Dump of proc_dynamic %d", [i(PDI)], Title),
ProcStaticLink = deep_link(deep_cmd_dump_proc_static(PSPtr), yes(Prefs),
attr_str([], string.int_to_string(PSI)), link_class_link),
MainValues =
[("Proc static:" - td_l(ProcStaticLink)),
("Raw name:" - td_s(RawName)),
("Module name:" - td_s(ModuleName)),
("Unqualified refined name:" - td_s(UnQualRefinedName)),
("Qualified refined name:" - td_s(QualRefinedName))],
MainRows = list.map(make_labelled_table_row, MainValues),
MainTable = table(table_class_do_not_box, 2, no, MainRows),
MainTableItem = display_table(MainTable),
CallSitesTitle = "Call site dynamics:",
CallSitesTitleItem = display_heading(CallSitesTitle),
list.map_foldl(dump_psd_call_site(Prefs), CallSites, CallSitesRowsList,
counter.init(1), _),
list.condense(CallSitesRowsList, CallSitesRows),
CallSitesTable = table(table_class_box_if_pref, 2, no, CallSitesRows),
CallSitesTableItem = display_table(CallSitesTable),
(
MaybeCoveragePoints = yes(CoveragePoints),
CoveragePointsTitle = "Coverage points:",
CoveragePointsTitleItem = display_heading(CoveragePointsTitle),
list.map(format_coverage_point_row, CoveragePoints,
CoveragePointsRows),
CoveragePointsTableHeader = table_header([
table_header_group(table_header_group_single(td_s("Goal Path")),
table_column_class_no_class, column_do_not_colour),
table_header_group(table_header_group_single(td_s("Type")),
table_column_class_no_class, column_do_not_colour),
table_header_group(table_header_group_single(td_s("Count")),
table_column_class_number, column_do_not_colour)]),
CoveragePointsTable = table(table_class_box_if_pref, 3,
yes(CoveragePointsTableHeader), CoveragePointsRows),
CoveragePointsTableItem = display_table(CoveragePointsTable),
CoveragePointsItems =
[CoveragePointsTitleItem, CoveragePointsTableItem]
;
MaybeCoveragePoints = no,
CoveragePointsItems = []
),
make_link(link_base(deep_cmd_dynamic_procrep_coverage(PDPtr), yes(Prefs),
"Dynamic coverage annotated procedure representation"),
CoverageAnnotatedProcrepItem),
RelatedReportsList = display_list(list_class_horizontal_except_title,
yes("Related reports:"), [CoverageAnnotatedProcrepItem]),
Display = display(yes(Title),
[MainTableItem, CallSitesTitleItem, CallSitesTableItem] ++
CoveragePointsItems ++
[display_paragraph_break, RelatedReportsList]).
:- pred dump_psd_call_site(preferences::in,
call_site_array_slot::in, list(table_row)::out,
counter::in, counter::out) is det.
dump_psd_call_site(Prefs, CallSite, Rows, !CallSiteCounter) :-
counter.allocate(CallSiteNum, !CallSiteCounter),
CallSiteNumCell = table_cell(td_i(CallSiteNum)),
(
CallSite = slot_normal(CSDPtr),
CSDPtr = call_site_dynamic_ptr(CSDI),
CSDLink = deep_link(deep_cmd_dump_call_site_dynamic(CSDPtr),
yes(Prefs), attr_str([], string.int_to_string(CSDI)),
link_class_link),
CSDCell = table_cell(td_l(CSDLink)),
FirstRow = table_row([CallSiteNumCell, CSDCell]),
Rows = [FirstRow]
;
CallSite = slot_multi(IsZeroed, CSDPtrArray),
(
IsZeroed = zeroed,
IsZeroedStr = "zeroed"
;
IsZeroed = not_zeroed,
IsZeroedStr = "not_zeroed"
),
array.to_list(CSDPtrArray, CSDPtrs),
NumCSDPtrs = list.length(CSDPtrs),
string.format("multi, %d csds (%s)", [i(NumCSDPtrs), s(IsZeroedStr)],
MultiCellStr),
MultiCell = table_cell(td_s(MultiCellStr)),
FirstRow = table_row([CallSiteNumCell, MultiCell]),
list.map(dump_psd_call_site_multi_entry(Prefs), CSDPtrs, LaterRows),
Rows = [FirstRow | LaterRows]
).
:- pred dump_psd_call_site_multi_entry(preferences::in,
call_site_dynamic_ptr::in, table_row::out) is det.
dump_psd_call_site_multi_entry(Prefs, CSDPtr, Row) :-
CSDPtr = call_site_dynamic_ptr(CSDI),
CSDLink = deep_link(deep_cmd_dump_call_site_dynamic(CSDPtr), yes(Prefs),
attr_str([], string.int_to_string(CSDI)), link_class_link),
CSDCell = table_cell(td_l(CSDLink)),
EmptyCell = table_cell(td_s("")),
Row = table_row([EmptyCell, CSDCell]).
:- pred format_coverage_point_row(coverage_point::in, table_row::out) is det.
format_coverage_point_row(CoveragePoint, Row) :-
CoveragePoint = coverage_point(Count, RevGoalPath, CPType),
GoalPathString = rev_goal_path_to_string(RevGoalPath),
GoalPathCell = table_cell(td_s(GoalPathString)),
TypeCell = table_cell(td_s(string(CPType))),
CountCell = table_cell(td_i(Count)),
Row = table_row([GoalPathCell, TypeCell, CountCell]).
% Create a display_report structure for a call_site_static_dump report.
%
:- pred display_report_call_site_static_dump(preferences::in,
call_site_static_dump_info::in, display::out) is det.
display_report_call_site_static_dump(Prefs, CallSiteStaticDumpInfo, Display) :-
CallSiteStaticDumpInfo = call_site_static_dump_info(CSSPtr,
ContainingPSPtr, SlotNumber, FileName, LineNumber, GoalPath,
CallSiteKind),
CSSPtr = call_site_static_ptr(CSSI),
string.format("Dump of call_site_static %d", [i(CSSI)], Title),
ContainingPSPtr = proc_static_ptr(ContainingPSI),
ContainingProcStaticLink = deep_link(
deep_cmd_dump_proc_static(ContainingPSPtr), yes(Prefs),
attr_str([], string.int_to_string(ContainingPSI)), link_class_link),
(
CallSiteKind = normal_call_and_callee(CalleePSPtr, TypeSpecDesc),
CalleePSPtr = proc_static_ptr(CalleePSI),
CalleeDesc0 = "normal, callee " ++ string.int_to_string(CalleePSI),
( if TypeSpecDesc = "" then
CalleeDesc = CalleeDesc0
else
CalleeDesc = CalleeDesc0 ++ " typespec " ++ TypeSpecDesc
),
CalleeProcStaticLink = deep_link(
deep_cmd_dump_proc_static(CalleePSPtr), yes(Prefs),
attr_str([], CalleeDesc), link_class_link),
CallSiteKindData = td_l(CalleeProcStaticLink)
;
CallSiteKind = special_call_and_no_callee,
CallSiteKindData = td_s("special_call")
;
CallSiteKind = higher_order_call_and_no_callee,
CallSiteKindData = td_s("higher_order_call")
;
CallSiteKind = method_call_and_no_callee,
CallSiteKindData = td_s("method_call")
;
CallSiteKind = callback_and_no_callee,
CallSiteKindData = td_s("callback")
),
Values =
[("Containing proc_static:" - td_l(ContainingProcStaticLink)),
("Slot number:" - td_i(SlotNumber)),
("File name:" - td_s(FileName)),
("Line number:" - td_i(LineNumber)),
("Goal path:" - td_s(rev_goal_path_to_string(GoalPath))),
("Call site kind:" - CallSiteKindData)],
Rows = list.map(make_labelled_table_row, Values),
Table = table(table_class_do_not_box, 2, no, Rows),
Display = display(yes(Title), [display_table(Table)]).
% Create a display_report structure for a call_site_dynamic_dump report.
%
:- pred display_report_call_site_dynamic_dump(preferences::in,
call_site_dynamic_dump_info::in, display::out) is det.
display_report_call_site_dynamic_dump(Prefs, CallSiteDynamiccDumpInfo,
Display) :-
CallSiteDynamiccDumpInfo = call_site_dynamic_dump_info(CSDPtr,
CallerPDPtr, CalleePDPtr, RowData),
CSDPtr = call_site_dynamic_ptr(CSDI),
string.format("Dump of call_site_dynamic %d", [i(CSDI)], Title),
CallerPDPtr = proc_dynamic_ptr(CallerPDI),
CallerProcDynamicLink = deep_link(deep_cmd_dump_proc_dynamic(CallerPDPtr),
yes(Prefs), attr_str([], string.int_to_string(CallerPDI)),
link_class_link),
CalleePDPtr = proc_dynamic_ptr(CalleePDI),
CalleeProcDynamicLink = deep_link(deep_cmd_dump_proc_dynamic(CalleePDPtr),
yes(Prefs), attr_str([], string.int_to_string(CalleePDI)),
link_class_link),
FirstValues =
[("Caller proc_dynamic:" - td_l(CallerProcDynamicLink)),
("Callee proc_dynamic:" - td_l(CalleeProcDynamicLink))],
FirstRows = list.map(make_labelled_table_row, FirstValues),
FirstTable = table(table_class_do_not_box, 2, no, FirstRows),
MakeHeaderData = dummy_order_criteria_header_data,
maybe_ranked_proc_table_header(Prefs, non_ranked, MakeHeaderData,
NumColumns, Header),
maybe_ranked_subject_perf_table_row(Prefs, non_ranked,
total_columns_meaningful,
call_site_desc_to_name_path_slot_cell(no, module_qual_always),
RowData, PerfRow, 1, _),
PerfTable = table(table_class_box, NumColumns, yes(Header), [PerfRow]),
Display = display(yes(Title),
[display_table(FirstTable), display_table(PerfTable)]).
% Create a display_report structure for a clique_dump report.
%
:- pred display_report_clique_dump(preferences::in, clique_dump_info::in,
display::out) is det.
display_report_clique_dump(Prefs, CliqueDumpInfo, Display) :-
CliqueDumpInfo = clique_dump_info(CliqueDesc, EntryCSDPtr, MemberPDPtrs),
CliquePtr = CliqueDesc ^ cdesc_clique_ptr,
CliquePtr = clique_ptr(CliqueNum),
string.format("Dump of clique %d", [i(CliqueNum)], Title),
EntryCSDPtr = call_site_dynamic_ptr(EntryCSDI),
EntryLink = deep_link(deep_cmd_dump_call_site_dynamic(EntryCSDPtr),
yes(Prefs), attr_str([], string.int_to_string(EntryCSDI)),
link_class_link),
list.map_foldl(display_report_clique_dump_member(Prefs), MemberPDPtrs,
MemberPDLinks, "Member proc_dynamics:", _),
Values =
[("Caller call_site_dynamic:" - td_l(EntryLink))] ++
MemberPDLinks,
Rows = list.map(make_labelled_table_row, Values),
Table = table(table_class_do_not_box, 2, no, Rows),
Display = display(yes(Title), [display_table(Table)]).
:- pred display_report_clique_dump_member(preferences::in,
proc_dynamic_ptr::in, pair(string, table_data)::out,
string::in, string::out) is det.
display_report_clique_dump_member(Prefs, PDPtr, Pair, !Label) :-
PDLabel = !.Label,
!:Label = "",
PDPtr = proc_dynamic_ptr(PDI),
Link = deep_link(deep_cmd_dump_proc_dynamic(PDPtr),
yes(Prefs), attr_str([], string.int_to_string(PDI)),
link_class_link),
PDData = td_l(Link),
Pair = PDLabel - PDData.
:- pred display_report_call_site_dynamic_var_use(preferences::in,
call_site_dynamic_var_use_info::in, display::out) is det.
display_report_call_site_dynamic_var_use(_Prefs, CSDVarUseInfo, Display) :-
CSDVarUseInfo = call_site_dynamic_var_use_info(AverageCost, VarUses),
AverageCostItem = display_text(
format("Average Cost: %f", [f(AverageCost)])),
format_var_uses(VarUses, 1, VarUseRows),
TextClassHdrGroupFunc =
( func({Text, Class}) = table_header_group(
table_header_group_single(td_s(Text)),
Class, column_do_not_colour)
),
TextsClasses =
[{"Argument", table_column_class_ordinal_rank},
{"Name", table_column_class_no_class},
{"Type", table_column_class_no_class},
{"Percent into proc", table_column_class_no_class},
{"Time into proc (Callseqs)", table_column_class_callseqs}],
Header = table_header(list.map(TextClassHdrGroupFunc, TextsClasses)),
Table = table(table_class_box_if_pref, 5, yes(Header), VarUseRows),
Title = "Dump of var use info",
Display = display(yes(Title), [AverageCostItem, display_table(Table)]).
:- pred format_var_uses(list(var_use_and_name)::in, int::in,
list(table_row)::out) is det.
format_var_uses([], _, []).
format_var_uses([VarUse | VarUses], RowNum, [Row | Rows]) :-
HeaderCell = table_cell(td_s(format("Argument: %i", [i(RowNum)]))),
VarUse = var_use_and_name(Name,
var_use_info(CostUntilUse, ProcCost, UseType)),
NameCell = table_cell(td_s(Name)),
(
UseType = var_use_production,
TypeText = "production"
;
UseType = var_use_consumption,
TypeText = "consumption"
;
UseType = var_use_other,
TypeText = "other/unknown"
),
TypeCell = table_cell(td_s(TypeText)),
PercentCell = table_cell(td_p(percent(CostUntilUse / ProcCost))),
TimeCell = table_cell(td_f(CostUntilUse)),
Row = table_row([HeaderCell, NameCell, TypeCell, PercentCell, TimeCell]),
format_var_uses(VarUses, RowNum + 1, Rows).
%---------------------------------------------------------------------------%
%
% Common column header strings.
%
:- func percent_label = table_data.
percent_label = td_s("%").
:- func override_order_criteria_header_data(cmd, preferences, order_criteria,
string) = table_data.
override_order_criteria_header_data(Cmd, Prefs0, Criteria, Label)
= TableData :-
Criteria0 = Prefs0 ^ pref_criteria,
Prefs = Prefs0 ^ pref_criteria := Criteria,
( if Criteria = Criteria0 then
% Should we display a simple string to indicate that this link
% leads back to the same page, or would that be confusing?
Link = deep_link(Cmd, yes(Prefs), attr_str([], Label),
link_class_link),
TableData = td_l(Link)
else
Link = deep_link(Cmd, yes(Prefs), attr_str([], Label),
link_class_link),
TableData = td_l(Link)
).
:- func dummy_order_criteria_header_data(preferences, order_criteria, string)
= table_data.
dummy_order_criteria_header_data(_, _, Label) = td_s(Label).
%---------------------------------------------------------------------------%
%
% The predicates in this section build
%
% (a) the headers of table columns that describe each aspect of performance,
% and
% (b) the entries in those columns.
%
% Each pair of predicates should follow the exact same logic when selecting
% what columns to display, and in what order.
%
:- type total_columns_meaning
---> total_columns_meaningful
; total_columns_not_meaningful.
% Convert the performance information in a row data into the cells
% of a table row according to the preferences.
%
:- pred perf_table_row(total_columns_meaning::in, fields::in,
perf_row_data(Subject)::in, list(table_cell)::out) is det.
perf_table_row(TotalsMeaningful, Fields, RowData, PerfCells) :-
perf_table_row_ports(Fields, RowData, PortCells),
perf_table_row_time(TotalsMeaningful, Fields, RowData, TimeCells),
perf_table_row_callseqs(TotalsMeaningful, Fields, RowData, CallSeqsCells),
perf_table_row_allocs(TotalsMeaningful, Fields, RowData, AllocCells),
perf_table_row_memory(TotalsMeaningful, Fields, RowData, MemoryCells),
PerfCells =
PortCells ++ TimeCells ++ CallSeqsCells ++
AllocCells ++ MemoryCells.
% Build the performance group of table headers
% according to the preferences.
%
:- pred perf_table_header(total_columns_meaning::in, preferences::in,
(func(preferences, order_criteria, string) = table_data)::in,
list(table_header_group)::out) is det.
perf_table_header(TotalsMeaningful, Prefs, MakeHeaderData, HeaderGroups) :-
perf_table_header_ports(Prefs, MakeHeaderData, PortHeaderGroups),
perf_table_header_time(TotalsMeaningful, Prefs, MakeHeaderData,
TimeHeaderGroups),
perf_table_header_callseqs(TotalsMeaningful, Prefs, MakeHeaderData,
CallSeqsHeaderGroups),
perf_table_header_allocs(TotalsMeaningful, Prefs, MakeHeaderData,
AllocHeaderGroups),
perf_table_header_memory(TotalsMeaningful, Prefs, MakeHeaderData,
MemoryHeaderGroups),
HeaderGroups =
PortHeaderGroups ++ TimeHeaderGroups ++ CallSeqsHeaderGroups ++
AllocHeaderGroups ++ MemoryHeaderGroups.
% Convert the port information in a row data into the cells
% of a table row according to the preferences.
%
:- pred perf_table_row_ports(fields::in, perf_row_data(Subject)::in,
list(table_cell)::out) is det.
perf_table_row_ports(Fields, RowData, PortCells) :-
PortFields = Fields ^ port_fields,
(
PortFields = no_port,
PortCells = []
;
PortFields = port,
Calls = RowData ^ perf_row_calls,
Exits = RowData ^ perf_row_exits,
Fails = RowData ^ perf_row_fails,
Redos = RowData ^ perf_row_redos,
Excps = RowData ^ perf_row_excps,
CallsCell = table_cell(td_i(Calls)),
ExitsCell = table_cell(td_i(Exits)),
FailsCell = table_cell(td_i(Fails)),
RedosCell = table_cell(td_i(Redos)),
ExcpsCell = table_cell(td_i(Excps)),
PortCells = [CallsCell, ExitsCell, FailsCell, RedosCell, ExcpsCell]
).
% Build the ports group of table headers according to the preferences.
%
:- pred perf_table_header_ports(preferences::in,
(func(preferences, order_criteria, string) = table_data)::in,
list(table_header_group)::out) is det.
perf_table_header_ports(Prefs, MakeHeaderData, HeaderGroups) :-
Fields = Prefs ^ pref_fields,
PortFields = Fields ^ port_fields,
(
PortFields = no_port,
HeaderGroups = []
;
PortFields = port,
CallsCrit = by_cost(cost_calls, self, overall),
RedosCrit = by_cost(cost_redos, self, overall),
CallsData = MakeHeaderData(Prefs, CallsCrit, "Calls"),
ExitsData = td_s("Exits"),
FailsData = td_s("Fails"),
RedosData = MakeHeaderData(Prefs, RedosCrit, "Redos"),
ExcpsData = td_s("Excps"),
SubHeaders = [CallsData, ExitsData, FailsData, RedosData, ExcpsData],
Title = "Port counts",
HeaderGroup = make_multi_table_header_group(Title, SubHeaders,
table_column_class_port_counts, column_colour_if_pref),
HeaderGroups = [HeaderGroup]
).
% Convert the time information in a row data into the cells
% of a table row according to the preferences.
%
:- pred perf_table_row_time(total_columns_meaning::in, fields::in,
perf_row_data(Subject)::in, list(table_cell)::out) is det.
perf_table_row_time(TotalsMeaningful, Fields, RowData, TimeCells) :-
TimeFields = Fields ^ time_fields,
(
TimeFields = no_time,
TimeCells = []
;
( TimeFields = ticks
; TimeFields = time
; TimeFields = ticks_and_time
; TimeFields = time_and_percall
; TimeFields = ticks_and_time_and_percall
),
Self = RowData ^ perf_row_self,
SelfTicks = Self ^ perf_row_ticks,
SelfTime = Self ^ perf_row_time,
SelfTimePercent = Self ^ perf_row_time_percent,
SelfTimePerCall = Self ^ perf_row_time_percall,
SelfTicksCell = table_cell(td_i(SelfTicks)),
SelfTimeCell = table_cell(td_t(SelfTime)),
SelfTimePercentCell = table_cell(td_p(SelfTimePercent)),
SelfTimePerCallCell = table_cell(td_t(SelfTimePerCall)),
(
TotalsMeaningful = total_columns_not_meaningful,
(
TimeFields = ticks,
TimeCells =
[SelfTicksCell, SelfTimePercentCell]
;
TimeFields = time,
TimeCells =
[SelfTimeCell, SelfTimePercentCell]
;
TimeFields = ticks_and_time,
TotalsMeaningful = total_columns_not_meaningful,
TimeCells =
[SelfTicksCell, SelfTimeCell, SelfTimePercentCell]
;
TimeFields = time_and_percall,
TimeCells =
[SelfTimeCell, SelfTimePercentCell, SelfTimePerCallCell]
;
TimeFields = ticks_and_time_and_percall,
TimeCells =
[SelfTicksCell, SelfTimeCell,
SelfTimePercentCell, SelfTimePerCallCell]
)
;
TotalsMeaningful = total_columns_meaningful,
MaybeTotal = RowData ^ perf_row_maybe_total,
(
MaybeTotal = yes(Total)
;
MaybeTotal = no,
unexpected($pred, "no total")
),
TotalTicks = Total ^ perf_row_ticks,
TotalTime = Total ^ perf_row_time,
TotalTimePercent = Total ^ perf_row_time_percent,
TotalTimePerCall = Total ^ perf_row_time_percall,
TotalTicksCell = table_cell(td_i(TotalTicks)),
TotalTimeCell = table_cell(td_t(TotalTime)),
TotalTimePercentCell = table_cell(td_p(TotalTimePercent)),
TotalTimePerCallCell = table_cell(td_t(TotalTimePerCall)),
(
TimeFields = ticks,
TimeCells =
[SelfTicksCell, SelfTimePercentCell,
TotalTicksCell, TotalTimePercentCell]
;
TimeFields = time,
TotalsMeaningful = total_columns_meaningful,
TimeCells =
[SelfTimeCell, SelfTimePercentCell,
TotalTimeCell, TotalTimePercentCell]
;
TimeFields = ticks_and_time,
TimeCells =
[SelfTicksCell, SelfTimeCell, SelfTimePercentCell,
TotalTicksCell, TotalTimeCell, TotalTimePercentCell]
;
TimeFields = time_and_percall,
TimeCells =
[SelfTimeCell, SelfTimePercentCell, SelfTimePerCallCell,
TotalTimeCell, TotalTimePercentCell, TotalTimePerCallCell]
;
TimeFields = ticks_and_time_and_percall,
TimeCells =
[SelfTicksCell, SelfTimeCell,
SelfTimePercentCell, SelfTimePerCallCell,
TotalTicksCell, TotalTimeCell,
TotalTimePercentCell, TotalTimePerCallCell]
)
)
).
% Build the time group of table headers according to the preferences.
%
:- pred perf_table_header_time(total_columns_meaning::in, preferences::in,
(func(preferences, order_criteria, string) = table_data)::in,
list(table_header_group)::out) is det.
perf_table_header_time(TotalsMeaningful, Prefs, MakeHeaderData,
HeaderGroups) :-
Fields = Prefs ^ pref_fields,
TimeFields = Fields ^ time_fields,
(
TimeFields = no_time,
HeaderGroups = []
;
( TimeFields = ticks
; TimeFields = time
; TimeFields = ticks_and_time
; TimeFields = time_and_percall
; TimeFields = ticks_and_time_and_percall
),
% Ordering by clock ticks is equivalent to ordering by time.
SelfTicksCrit = by_cost(cost_time, self, overall),
SelfTimeCrit = by_cost(cost_time, self, overall),
SelfTimePerCallCrit = by_cost(cost_time, self, per_call),
TotalTicksCrit = by_cost(cost_time, self_and_desc, overall),
TotalTimeCrit = by_cost(cost_time, self_and_desc, overall),
TotalTimePerCallCrit = by_cost(cost_time, self_and_desc, per_call),
SelfTicksData = MakeHeaderData(Prefs, SelfTicksCrit,
"Self"),
SelfTimeData = MakeHeaderData(Prefs, SelfTimeCrit,
"Time"),
SelfTimePercentData = percent_label,
SelfTimePerCallData = MakeHeaderData(Prefs, SelfTimePerCallCrit,
"/call"),
TotalTicksData = MakeHeaderData(Prefs,
TotalTicksCrit, "Total"),
TotalTimeData = MakeHeaderData(Prefs, TotalTimeCrit,
"Time"),
TotalTimePercentData = percent_label,
TotalTimePerCallData = MakeHeaderData(Prefs, TotalTimePerCallCrit,
"/call"),
(
TotalsMeaningful = total_columns_not_meaningful,
(
TimeFields = ticks,
Title = "Clock ticks",
SubHeaders =
[SelfTicksData, SelfTimePercentData]
;
TimeFields = time,
Title = "Time",
SubHeaders =
[SelfTimeData, SelfTimePercentData]
;
TimeFields = ticks_and_time,
Title = "Clock ticks and times",
SubHeaders =
[SelfTicksData, SelfTimeData, SelfTimePercentData]
;
TimeFields = time_and_percall,
Title = "Time",
SubHeaders =
[SelfTimeData, SelfTimePercentData, SelfTimePerCallData]
;
TimeFields = ticks_and_time_and_percall,
Title = "Clock ticks and times",
SubHeaders =
[SelfTicksData, SelfTimeData,
SelfTimePercentData, SelfTimePerCallData]
)
;
TotalsMeaningful = total_columns_meaningful,
(
TimeFields = ticks,
Title = "Clock ticks",
SubHeaders =
[SelfTicksData, SelfTimePercentData,
TotalTicksData, TotalTimePercentData]
;
TimeFields = time,
Title = "Time",
SubHeaders =
[SelfTimeData, SelfTimePercentData,
TotalTimeData, TotalTimePercentData]
;
TimeFields = ticks_and_time,
Title = "Clock ticks and times",
SubHeaders =
[SelfTicksData, SelfTimeData, SelfTimePercentData,
TotalTicksData, TotalTimeData, TotalTimePercentData]
;
TimeFields = time_and_percall,
Title = "Time",
SubHeaders =
[SelfTimeData, SelfTimePercentData, SelfTimePerCallData,
TotalTimeData, TotalTimePercentData, TotalTimePerCallData]
;
TimeFields = ticks_and_time_and_percall,
Title = "Clock ticks and times",
SubHeaders =
[SelfTicksData, SelfTimeData,
SelfTimePercentData, SelfTimePerCallData,
TotalTicksData, TotalTimeData,
TotalTimePercentData, TotalTimePerCallData]
)
),
HeaderGroup = make_multi_table_header_group(Title, SubHeaders,
table_column_class_ticks_and_times, column_colour_if_pref),
HeaderGroups = [HeaderGroup]
).
% Convert the callseqs information in a row data into the cells
% of a table row according to the preferences.
%
:- pred perf_table_row_callseqs(total_columns_meaning::in, fields::in,
perf_row_data(Subject)::in, list(table_cell)::out) is det.
perf_table_row_callseqs(TotalsMeaningful, Fields, RowData, CallSeqsCells) :-
CallSeqsFields = Fields ^ callseqs_fields,
(
CallSeqsFields = no_callseqs,
CallSeqsCells = []
;
( CallSeqsFields = callseqs
; CallSeqsFields = callseqs_and_percall
),
Self = RowData ^ perf_row_self,
SelfCallSeqs = Self ^ perf_row_callseqs,
SelfCallSeqsPercent = Self ^ perf_row_callseqs_percent,
SelfCallSeqsPerCall = Self ^ perf_row_callseqs_percall,
SelfCallSeqsCell = table_cell(td_i(SelfCallSeqs)),
SelfCallSeqsPercentCell = table_cell(td_p(SelfCallSeqsPercent)),
SelfCallSeqsPerCallCell = table_cell(td_f(SelfCallSeqsPerCall)),
(
TotalsMeaningful = total_columns_not_meaningful,
(
CallSeqsFields = callseqs,
CallSeqsCells =
[SelfCallSeqsCell, SelfCallSeqsPercentCell]
;
CallSeqsFields = callseqs_and_percall,
CallSeqsCells =
[SelfCallSeqsCell, SelfCallSeqsPercentCell,
SelfCallSeqsPerCallCell]
)
;
TotalsMeaningful = total_columns_meaningful,
MaybeTotal = RowData ^ perf_row_maybe_total,
(
MaybeTotal = yes(Total)
;
MaybeTotal = no,
unexpected($pred, "no total")
),
TotalCallSeqs = Total ^ perf_row_callseqs,
TotalCallSeqsPercent = Total ^ perf_row_callseqs_percent,
TotalCallSeqsPerCall = Total ^ perf_row_callseqs_percall,
TotalCallSeqsCell = table_cell(td_i(TotalCallSeqs)),
TotalCallSeqsPercentCell = table_cell(td_p(TotalCallSeqsPercent)),
TotalCallSeqsPerCallCell = table_cell(td_f(TotalCallSeqsPerCall)),
(
CallSeqsFields = callseqs,
CallSeqsCells =
[SelfCallSeqsCell, SelfCallSeqsPercentCell,
TotalCallSeqsCell, TotalCallSeqsPercentCell]
;
CallSeqsFields = callseqs_and_percall,
CallSeqsCells =
[SelfCallSeqsCell, SelfCallSeqsPercentCell,
SelfCallSeqsPerCallCell,
TotalCallSeqsCell, TotalCallSeqsPercentCell,
TotalCallSeqsPerCallCell]
)
)
).
% Build the callseqs group of table headers according to the preferences.
%
:- pred perf_table_header_callseqs(total_columns_meaning::in, preferences::in,
(func(preferences, order_criteria, string) = table_data)::in,
list(table_header_group)::out) is det.
perf_table_header_callseqs(TotalsMeaningful, Prefs, MakeHeaderData,
HeaderGroups) :-
Fields = Prefs ^ pref_fields,
CallSeqsFields = Fields ^ callseqs_fields,
(
CallSeqsFields = no_callseqs,
HeaderGroups = []
;
( CallSeqsFields = callseqs
; CallSeqsFields = callseqs_and_percall
),
SelfCrit = by_cost(cost_callseqs, self, overall),
SelfPerCallCrit = by_cost(cost_callseqs, self, per_call),
TotalCrit = by_cost(cost_callseqs, self_and_desc, overall),
TotalPerCallCrit = by_cost(cost_callseqs, self_and_desc, per_call),
SelfData = MakeHeaderData(Prefs, SelfCrit, "Self"),
SelfPercentData = percent_label,
SelfPerCallData = MakeHeaderData(Prefs, SelfPerCallCrit, "/call"),
TotalData = MakeHeaderData(Prefs, TotalCrit, "Total"),
TotalPercentData = percent_label,
TotalPerCallData = MakeHeaderData(Prefs, TotalPerCallCrit, "/call"),
(
TotalsMeaningful = total_columns_not_meaningful,
(
CallSeqsFields = callseqs,
SubHeaders =
[SelfData, SelfPercentData]
;
CallSeqsFields = callseqs_and_percall,
SubHeaders =
[SelfData, SelfPercentData, SelfPerCallData]
)
;
TotalsMeaningful = total_columns_meaningful,
(
CallSeqsFields = callseqs,
SubHeaders =
[SelfData, SelfPercentData,
TotalData, TotalPercentData]
;
CallSeqsFields = callseqs_and_percall,
SubHeaders =
[SelfData, SelfPercentData, SelfPerCallData,
TotalData, TotalPercentData, TotalPerCallData]
)
),
Title = "Call sequence numbers",
HeaderGroup = make_multi_table_header_group(Title, SubHeaders,
table_column_class_callseqs, column_colour_if_pref),
HeaderGroups = [HeaderGroup]
).
% Convert the allocation information in a row data into the cells
% of a table row according to the preferences.
%
:- pred perf_table_row_allocs(total_columns_meaning::in, fields::in,
perf_row_data(Subject)::in, list(table_cell)::out) is det.
perf_table_row_allocs(TotalsMeaningful, Fields, RowData, AllocCells) :-
AllocFields = Fields ^ alloc_fields,
(
AllocFields = no_alloc,
AllocCells = []
;
( AllocFields = alloc
; AllocFields = alloc_and_percall
),
Self = RowData ^ perf_row_self,
SelfAllocs = Self ^ perf_row_allocs,
SelfAllocsPercent = Self ^ perf_row_allocs_percent,
SelfAllocsPerCall = Self ^ perf_row_allocs_percall,
SelfAllocsCell = table_cell(td_i(SelfAllocs)),
SelfAllocsPercentCell = table_cell(td_p(SelfAllocsPercent)),
SelfAllocsPerCallCell = table_cell(td_f(SelfAllocsPerCall)),
(
TotalsMeaningful = total_columns_not_meaningful,
(
AllocFields = alloc,
AllocCells =
[SelfAllocsCell, SelfAllocsPercentCell]
;
AllocFields = alloc_and_percall,
AllocCells =
[SelfAllocsCell, SelfAllocsPercentCell,
SelfAllocsPerCallCell]
)
;
TotalsMeaningful = total_columns_meaningful,
MaybeTotal = RowData ^ perf_row_maybe_total,
(
MaybeTotal = yes(Total)
;
MaybeTotal = no,
unexpected($pred, "no total")
),
TotalAllocs = Total ^ perf_row_allocs,
TotalAllocsPercent = Total ^ perf_row_allocs_percent,
TotalAllocsPerCall = Total ^ perf_row_allocs_percall,
TotalAllocsCell = table_cell(td_i(TotalAllocs)),
TotalAllocsPercentCell = table_cell(td_p(TotalAllocsPercent)),
TotalAllocsPerCallCell = table_cell(td_f(TotalAllocsPerCall)),
(
AllocFields = alloc,
AllocCells =
[SelfAllocsCell, SelfAllocsPercentCell,
TotalAllocsCell, TotalAllocsPercentCell]
;
AllocFields = alloc_and_percall,
AllocCells =
[SelfAllocsCell, SelfAllocsPercentCell,
SelfAllocsPerCallCell,
TotalAllocsCell, TotalAllocsPercentCell,
TotalAllocsPerCallCell]
)
)
).
% Build the allocs group of table headers according to the preferences.
%
:- pred perf_table_header_allocs(total_columns_meaning::in, preferences::in,
(func(preferences, order_criteria, string) = table_data)::in,
list(table_header_group)::out) is det.
perf_table_header_allocs(TotalsMeaningful, Prefs, MakeHeaderData,
HeaderGroups) :-
Fields = Prefs ^ pref_fields,
AllocFields = Fields ^ alloc_fields,
(
AllocFields = no_alloc,
HeaderGroups = []
;
( AllocFields = alloc
; AllocFields = alloc_and_percall
),
SelfCrit = by_cost(cost_allocs, self, overall),
SelfPerCallCrit = by_cost(cost_allocs, self, per_call),
TotalCrit = by_cost(cost_allocs, self_and_desc, overall),
TotalPerCallCrit = by_cost(cost_allocs, self_and_desc, per_call),
SelfData = MakeHeaderData(Prefs, SelfCrit, "Self"),
SelfPercentData = percent_label,
SelfPerCallData = MakeHeaderData(Prefs, SelfPerCallCrit, "/call"),
TotalData = MakeHeaderData(Prefs, TotalCrit, "Total"),
TotalPercentData = percent_label,
TotalPerCallData = MakeHeaderData(Prefs, TotalPerCallCrit, "/call"),
(
AllocFields = alloc,
(
TotalsMeaningful = total_columns_not_meaningful,
SubHeaders =
[SelfData, SelfPercentData]
;
TotalsMeaningful = total_columns_meaningful,
SubHeaders =
[SelfData, SelfPercentData,
TotalData, TotalPercentData]
)
;
AllocFields = alloc_and_percall,
(
TotalsMeaningful = total_columns_not_meaningful,
SubHeaders =
[SelfData, SelfPercentData, SelfPerCallData]
;
TotalsMeaningful = total_columns_meaningful,
SubHeaders =
[SelfData, SelfPercentData, SelfPerCallData,
TotalData, TotalPercentData, TotalPerCallData]
)
),
Title = "Memory allocations",
HeaderGroup = make_multi_table_header_group(Title, SubHeaders,
table_column_class_allocations, column_colour_if_pref),
HeaderGroups = [HeaderGroup]
).
% Convert the memory information in a row data into the cells
% of a table row according to the preferences.
%
:- pred perf_table_row_memory(total_columns_meaning::in, fields::in,
perf_row_data(Subject)::in, list(table_cell)::out) is det.
perf_table_row_memory(TotalsMeaningful, Fields, RowData, MemoryCells) :-
MemoryFields = Fields ^ memory_fields,
(
MemoryFields = no_memory,
MemoryCells = []
;
( MemoryFields = memory(Units)
; MemoryFields = memory_and_percall(Units)
),
Self = RowData ^ perf_row_self,
SelfMem = Self ^ perf_row_mem,
SelfMemPerCall = Self ^ perf_row_mem_percall,
SelfMemPercent = Self ^ perf_row_mem_percent,
SelfMemCell = table_cell(td_m(SelfMem, Units, ndp_0)),
SelfMemPerCallCell = table_cell(td_m(SelfMemPerCall, Units, ndp_2)),
SelfMemPercentCell = table_cell(td_p(SelfMemPercent)),
(
TotalsMeaningful = total_columns_not_meaningful,
(
MemoryFields = memory(_),
MemoryCells =
[SelfMemCell, SelfMemPercentCell]
;
MemoryFields = memory_and_percall(_),
MemoryCells =
[SelfMemCell, SelfMemPercentCell, SelfMemPerCallCell]
)
;
TotalsMeaningful = total_columns_meaningful,
MaybeTotal = RowData ^ perf_row_maybe_total,
(
MaybeTotal = yes(Total)
;
MaybeTotal = no,
unexpected($pred, "no total")
),
TotalMem = Total ^ perf_row_mem,
TotalMemPerCall = Total ^ perf_row_mem_percall,
TotalMemPercent = Total ^ perf_row_mem_percent,
TotalMemCell = table_cell(td_m(TotalMem, Units, ndp_0)),
TotalMemPerCallCell = table_cell(td_m(TotalMemPerCall, Units,
ndp_2)),
TotalMemPercentCell = table_cell(td_p(TotalMemPercent)),
(
MemoryFields = memory(_),
MemoryCells =
[SelfMemCell, SelfMemPercentCell,
TotalMemCell, TotalMemPercentCell]
;
MemoryFields = memory_and_percall(_),
MemoryCells =
[SelfMemCell, SelfMemPercentCell, SelfMemPerCallCell,
TotalMemCell, TotalMemPercentCell, TotalMemPerCallCell]
)
)
).
% Build the memory group of table headers according to the preferences.
%
:- pred perf_table_header_memory(total_columns_meaning::in, preferences::in,
(func(preferences, order_criteria, string) = table_data)::in,
list(table_header_group)::out) is det.
perf_table_header_memory(TotalsMeaningful, Prefs, MakeHeaderData,
HeaderGroups) :-
Fields = Prefs ^ pref_fields,
MemoryFields = Fields ^ memory_fields,
(
MemoryFields = no_memory,
HeaderGroups = []
;
( MemoryFields = memory(_)
; MemoryFields = memory_and_percall(_)
),
SelfCrit = by_cost(cost_words, self, overall),
SelfPerCallCrit = by_cost(cost_words, self, per_call),
TotalCrit = by_cost(cost_words, self_and_desc, overall),
TotalPerCallCrit = by_cost(cost_words, self_and_desc, per_call),
SelfData = MakeHeaderData(Prefs, SelfCrit, "Self"),
SelfPercentData = percent_label,
SelfPerCallData = MakeHeaderData(Prefs, SelfPerCallCrit, "/call"),
TotalData = MakeHeaderData(Prefs, TotalCrit, "Total"),
TotalPercentData = percent_label,
TotalPerCallData = MakeHeaderData(Prefs, TotalPerCallCrit, "/call"),
(
TotalsMeaningful = total_columns_not_meaningful,
(
MemoryFields = memory(Units),
SubHeaders =
[SelfData, SelfPercentData]
;
MemoryFields = memory_and_percall(Units),
SubHeaders =
[SelfData, SelfPercentData, SelfPerCallData]
)
;
TotalsMeaningful = total_columns_meaningful,
(
MemoryFields = memory(Units),
SubHeaders =
[SelfData, SelfPercentData,
TotalData, TotalPercentData]
;
MemoryFields = memory_and_percall(Units),
SubHeaders =
[SelfData, SelfPercentData, SelfPerCallData,
TotalData, TotalPercentData, TotalPerCallData]
)
),
(
Units = units_words,
Title = "Memory words"
;
Units = units_bytes,
Title = "Memory bytes"
),
HeaderGroup = make_multi_table_header_group(Title, SubHeaders,
table_column_class_memory, column_colour_if_pref),
HeaderGroups = [HeaderGroup]
).
%---------------------------------------------------------------------------%
% Describes whether a table should be ranked or not. This means that
% each item has an ordinal number associated with it in an initial column
% labeled "Rank".
%
:- type ranked
---> ranked
; non_ranked.
% Build a header for a table of procedures.
%
:- pred maybe_ranked_proc_table_header(preferences::in, ranked::in,
(func(preferences, order_criteria, string) = table_data)::in,
int::out, table_header::out) is det.
maybe_ranked_proc_table_header(Prefs, Ranked, MakeHeaderData, NumColumns,
Header) :-
(
Ranked = non_ranked,
RankedHeaderGroups = []
;
Ranked = ranked,
RankedHeaderGroup =
make_single_table_header_group(td_s("Rank"),
table_column_class_ordinal_rank, column_do_not_colour),
RankedHeaderGroups = [RankedHeaderGroup]
),
ProcHeaderGroup =
make_single_table_header_group(td_s("Procedure"),
table_column_class_proc, column_do_not_colour),
ProcHeaderGroups = [ProcHeaderGroup],
perf_table_header(total_columns_meaningful, Prefs, MakeHeaderData,
PerfHeaderGroups),
AllHeaderGroups =
RankedHeaderGroups ++ ProcHeaderGroups ++ PerfHeaderGroups,
header_groups_to_header(AllHeaderGroups, NumColumns, Header).
% Convert row data of procedures from the deep profiler into a table row
% according to the preferences.
%
:- pred maybe_ranked_subject_perf_table_row(preferences::in, ranked::in,
total_columns_meaning::in, (func(preferences, Subject) = table_cell)::in,
perf_row_data(Subject)::in, table_row::out, int::in, int::out) is det.
maybe_ranked_subject_perf_table_row(Prefs, Ranked, TotalsMeaningful,
SubjectCellFunc, RowData, Row, Rank, Rank + 1) :-
(
Ranked = non_ranked,
RankCells = []
;
Ranked = ranked,
RankCells = [table_cell(td_i(Rank))]
),
% The name of the procedure,
SubjectCells = [SubjectCellFunc(Prefs, RowData ^ perf_row_subject)],
Fields = Prefs ^ pref_fields,
perf_table_row(TotalsMeaningful, Fields, RowData, PerfCells),
Cells = RankCells ++ SubjectCells ++ PerfCells,
Row = table_row(Cells).
%---------------------------------------------------------------------------%
%
% Some utility procedures.
%
:- func call_site_desc_source_cell(call_site_desc) = table_cell.
call_site_desc_source_cell(CallSiteDesc) = Cell :-
FileName = CallSiteDesc ^ csdesc_file_name,
LineNumber = CallSiteDesc ^ csdesc_line_number,
Context = string.format("%s:%d", [s(FileName), i(LineNumber)]),
Cell = table_cell(td_s(Context)).
:- func call_site_desc_clique_proc_cell(maybe(string), module_qual,
preferences, ancestor_desc) = table_cell.
call_site_desc_clique_proc_cell(MaybeCurModuleName, ModuleQual,
Prefs, AncestorDesc) = Cell :-
AncestorDesc = ancestor_desc(CallerCliquePtr, _CalleeCliquePtr,
_CalleeProcDesc, CallSiteDesc),
ContainingProcName =
call_site_desc_get_caller_refined_id(MaybeCurModuleName, ModuleQual,
CallSiteDesc),
Cmd = deep_cmd_clique(CallerCliquePtr),
Link = deep_link(Cmd, yes(Prefs), attr_str([], ContainingProcName),
link_class_link),
Cell = table_cell(td_l(Link)).
%---------------------------------------------------------------------------%
%
% The basic predicates for creating the controls at the bottoms of pages.
%
:- pred make_top_procs_cmd_items(preferences::in, bool::in, display_limit::in,
cost_kind::in, include_descendants::in, measurement_scope::in,
maybe(string)::in,
assoc_list(string,
(pred(display_limit, cost_kind, include_descendants,
measurement_scope, cmd)))::
in(list_skel(pair(ground, (pred(in, in, in, in, out) is det)))),
display_item::out) is det.
make_top_procs_cmd_items(Prefs, SetPrefs, DisplayLimit, CostKind, InclDesc,
Scope, MaybeLabel, LabelsCmdMakers, Item) :-
list.map(
make_top_procs_cmd_item(Prefs, SetPrefs, DisplayLimit, CostKind,
InclDesc, Scope),
LabelsCmdMakers, ControlItemLists),
list.condense(ControlItemLists, ControlItems),
Item = display_list(list_class_horizontal, MaybeLabel, ControlItems).
:- pred make_top_procs_cmd_item(preferences::in, bool::in, display_limit::in,
cost_kind::in, include_descendants::in, measurement_scope::in,
pair(string,
(pred(display_limit, cost_kind, include_descendants, measurement_scope,
cmd)))::
in(pair(ground, (pred(in, in, in, in, out) is det))),
list(display_item)::out) is det.
make_top_procs_cmd_item(Prefs0, SetPrefs, DisplayLimit, CostKind, InclDesc,
Scope, Label - CmdMaker, Items) :-
Cmd0 = deep_cmd_top_procs(DisplayLimit, CostKind, InclDesc, Scope),
CmdMaker(DisplayLimit, CostKind, InclDesc, Scope, Cmd),
( if Cmd = Cmd0 then
Items = []
% We could use this code instead.
% CurLabel = Label ++ " (current setting)",
% PseudoLink = pseudo_link(CurLabel, link_class_control),
% Item = display_pseudo_link(PseudoLink),
% Items = [Item]
else
(
SetPrefs = no,
Prefs = Prefs0
;
SetPrefs = yes,
% By default, sort the top procedures by the same criteria
% by which they were selected. Users can override this choice
% later if they wish.
( if
Cmd = deep_cmd_top_procs(_, CmdCostKind, CmdInclDesc,
CmdScope)
then
Prefs = Prefs0 ^ pref_criteria :=
by_cost(CmdCostKind, CmdInclDesc, CmdScope)
else
Prefs = Prefs0
)
),
Link = deep_link(Cmd, yes(Prefs), attr_str([], Label),
link_class_control),
Item = display_link(Link),
Items = [Item]
).
:- pred make_first_proc_callers_cmds_item(preferences::in, cmd::in,
proc_static_ptr::in, caller_groups::in, int::in, contour_exclusion::in,
maybe(string)::in,
assoc_list(string,
(pred(proc_static_ptr, caller_groups, int, contour_exclusion, cmd)))::
in(list_skel(pair(ground, (pred(in, in, in, in, out) is det)))),
display_item::out) is det.
make_first_proc_callers_cmds_item(Prefs, Cmd, PSPtr, CallerGroups, BunchSize,
ContourExcl, MaybeLabel, LabelsCmdMakers, Item) :-
list.map(
make_first_proc_callers_cmd_item(Prefs, Cmd, PSPtr, CallerGroups,
BunchSize, ContourExcl),
LabelsCmdMakers, ControlItemLists),
list.condense(ControlItemLists, ControlItems),
Item = display_list(list_class_horizontal, MaybeLabel, ControlItems).
:- pred make_first_proc_callers_cmd_item(preferences::in, cmd::in,
proc_static_ptr::in, caller_groups::in, int::in, contour_exclusion::in,
pair(string,
(pred(proc_static_ptr, caller_groups, int, contour_exclusion, cmd)))::
in(pair(ground, (pred(in, in, in, in, out) is det))),
list(display_item)::out) is det.
make_first_proc_callers_cmd_item(Prefs, Cmd0, PSPtr, CallerGroups, BunchSize,
ContourExcl, Label - CmdMaker, Items) :-
% When we just to a different kind of grouping or toggle contour exclusion,
% we always go to the first bunch of the resulting rows.
CmdMaker(PSPtr, CallerGroups, BunchSize, ContourExcl, Cmd),
( if Cmd = Cmd0 then
Items = []
% We could use this code instead.
% CurLabel = Label ++ " (current setting)",
% PseudoLink = pseudo_link(CurLabel, link_class_control),
% Item = display_pseudo_link(PseudoLink),
% Items = [Item]
else
Link = deep_link(Cmd, yes(Prefs), attr_str([], Label),
link_class_control),
Item = display_link(Link),
Items = [Item]
).
:- pred make_prefs_controls_item(preferences::in, cmd::in, maybe(string)::in,
assoc_list(string, (pred(preferences, preferences)))::
in(list_skel(pair(ground, (pred(in, out) is det)))),
display_item::out) is det.
make_prefs_controls_item(Prefs0, Cmd, MaybeLabel, LabelsPrefMakers, Item) :-
list.map(make_prefs_control_item(Prefs0, Cmd), LabelsPrefMakers,
ControlItemLists),
list.condense(ControlItemLists, ControlItems),
Item = display_list(list_class_horizontal, MaybeLabel, ControlItems).
:- pred make_prefs_control_item(preferences::in, cmd::in,
pair(string, (pred(preferences, preferences)))::
in(pair(ground, (pred(in, out) is det))),
list(display_item)::out) is det.
make_prefs_control_item(Prefs0, Cmd, Label - PrefMaker, Items) :-
PrefMaker(Prefs0, Prefs),
( if Prefs = Prefs0 then
Items = []
% We could use this code instead.
% CurLabel = Label ++ " (current setting)",
% PseudoLink = pseudo_link(CurLabel, link_class_control),
% Item = display_pseudo_link(PseudoLink),
% Items = [Item]
else
Link = deep_link(Cmd, yes(Prefs), attr_str([], Label),
link_class_control),
Item = display_link(Link),
Items = [Item]
).
%---------------------------------------------------------------------------%
%
% Control how the top_procs command selects what is "top".
%
:- func top_procs_controls(preferences, display_limit, cost_kind,
include_descendants, measurement_scope) = display_item.
top_procs_controls(Prefs, DisplayLimit, CostKind, InclDesc, Scope) =
ControlsItem :-
make_top_procs_cmd_items(Prefs, no, DisplayLimit, CostKind, InclDesc,
Scope, no, top_procs_limit_toggles, LimitControls),
make_top_procs_cmd_items(Prefs, yes, DisplayLimit, CostKind, InclDesc,
Scope, no, top_procs_sort_toggles, SortControls),
make_top_procs_cmd_items(Prefs, no, DisplayLimit, CostKind, InclDesc,
Scope, no, top_procs_incl_desc_toggles, InclDescControls),
make_top_procs_cmd_items(Prefs, no, DisplayLimit, CostKind, InclDesc,
Scope, no, top_procs_scope_toggles, ScopeControls),
ControlsItem = display_list(list_class_vertical_no_bullets,
yes("Toggle sorting criteria:"),
[LimitControls, SortControls, InclDescControls, ScopeControls]).
:- func top_procs_limit_toggles =
(assoc_list(string,
(pred(display_limit, cost_kind, include_descendants, measurement_scope,
cmd)))::
out(list_skel(pair(ground, (pred(in, in, in, in, out) is det)))))
is det.
top_procs_limit_toggles = [
"Top 10" -
set_top_procs_display_limit(rank_range(1, 10)),
"Top 100" -
set_top_procs_display_limit(rank_range(1, 100)),
"Top 1000" -
set_top_procs_display_limit(rank_range(1, 1000)),
"Top 0.01%" -
set_top_procs_display_limit(threshold_percent(0.01)),
"Top 0.1%" -
set_top_procs_display_limit(threshold_percent(0.1)),
"Top 1%" -
set_top_procs_display_limit(threshold_percent(1.0))
].
:- func top_procs_sort_toggles =
(assoc_list(string,
(pred(display_limit, cost_kind, include_descendants, measurement_scope,
cmd)))::
out(list_skel(pair(ground, (pred(in, in, in, in, out) is det)))))
is det.
top_procs_sort_toggles = [
"Sort by calls" -
set_top_procs_sort_criteria(cost_calls),
"Sort by redos" -
set_top_procs_sort_criteria(cost_redos),
"Sort by time" -
set_top_procs_sort_criteria(cost_time),
"Sort by call sequence numbers" -
set_top_procs_sort_criteria(cost_callseqs),
"Sort by allocs" -
set_top_procs_sort_criteria(cost_allocs),
"Sort by words" -
set_top_procs_sort_criteria(cost_words)
].
:- func top_procs_incl_desc_toggles =
(assoc_list(string,
(pred(display_limit, cost_kind, include_descendants, measurement_scope,
cmd)))::
out(list_skel(pair(ground, (pred(in, in, in, in, out) is det)))))
is det.
top_procs_incl_desc_toggles = [
"Exclude descendants" -
set_top_procs_incl_desc(self),
"Include descendants" -
set_top_procs_incl_desc(self_and_desc)
].
:- func top_procs_scope_toggles =
(assoc_list(string,
(pred(display_limit, cost_kind, include_descendants, measurement_scope,
cmd)))::
out(list_skel(pair(ground, (pred(in, in, in, in, out) is det)))))
is det.
top_procs_scope_toggles = [
"Overall" -
set_top_procs_scope(overall),
"Per call" -
set_top_procs_scope(per_call)
].
:- pred set_top_procs_display_limit(display_limit::in, display_limit::in,
cost_kind::in, include_descendants::in, measurement_scope::in, cmd::out)
is det.
set_top_procs_display_limit(DisplayLimit, _DisplayLimit, CostKind, InclDesc,
Scope, Cmd) :-
Cmd = deep_cmd_top_procs(DisplayLimit, CostKind, InclDesc, Scope).
:- pred set_top_procs_sort_criteria(cost_kind::in, display_limit::in,
cost_kind::in, include_descendants::in, measurement_scope::in, cmd::out)
is det.
set_top_procs_sort_criteria(CostKind, DisplayLimit, _CostKind, InclDesc, Scope,
Cmd) :-
Cmd = deep_cmd_top_procs(DisplayLimit, CostKind, InclDesc, Scope).
:- pred set_top_procs_incl_desc(include_descendants::in, display_limit::in,
cost_kind::in, include_descendants::in, measurement_scope::in, cmd::out)
is det.
set_top_procs_incl_desc(InclDesc, DisplayLimit, CostKind, _InclDesc, Scope,
Cmd) :-
Cmd = deep_cmd_top_procs(DisplayLimit, CostKind, InclDesc, Scope).
:- pred set_top_procs_scope(measurement_scope::in, display_limit::in,
cost_kind::in, include_descendants::in, measurement_scope::in, cmd::out)
is det.
set_top_procs_scope(Scope, DisplayLimit, CostKind, InclDesc, _Scope, Cmd) :-
Cmd = deep_cmd_top_procs(DisplayLimit, CostKind, InclDesc, Scope).
%---------------------------------------------------------------------------%
%
% Control how the proc command displays the procedure's callers.
%
:- func proc_callers_group_controls(deep, preferences, cmd, proc_static_ptr,
caller_groups, int, contour_exclusion) = display_item.
proc_callers_group_controls(Deep, Prefs, Cmd, PSPtr, CallerGroups,
BunchSize, ContourExcl) = ControlsItem :-
make_first_proc_callers_cmds_item(Prefs, Cmd, PSPtr, CallerGroups,
BunchSize, ContourExcl, no, proc_callers_group_toggles, GroupControls),
ExcludeFile = Deep ^ exclude_contour_file,
( if Cmd = deep_cmd_proc_callers(_, _, _, _, _) then
ExcludeFile = exclude_file(ExcludeFileName, ExcludeContents),
(
ExcludeContents = no_exclude_file,
List = [GroupControls]
;
ExcludeContents = unreadable_exclude_file(ErrorMsg),
ContourExclMsg = "Cannot apply contour exclusion:\n"
++ ExcludeFileName ++ ": " ++ ErrorMsg,
ContourExclItem = display_text(ContourExclMsg),
List = [GroupControls, ContourExclItem]
;
ExcludeContents = readable_exclude_file(_, _),
make_first_proc_callers_cmds_item(Prefs, Cmd, PSPtr, CallerGroups,
BunchSize, ContourExcl, no, proc_callers_contour_excl_toggles,
ContourExclControls),
List = [GroupControls, ContourExclControls]
)
else
List = [GroupControls]
),
ControlsItem = display_list(list_class_vertical_no_bullets,
yes("The procedure's callers:"), List).
:- func proc_callers_group_toggles =
(assoc_list(string,
(pred(proc_static_ptr, caller_groups, int, contour_exclusion, cmd)))::
out(list_skel(pair(ground, (pred(in, in, in, in, out) is det)))))
is det.
proc_callers_group_toggles = [
"Group by call site" -
set_proc_callers_caller_groups(group_by_call_site),
"Group by procedure" -
set_proc_callers_caller_groups(group_by_proc),
"Group by module" -
set_proc_callers_caller_groups(group_by_module),
"Group by clique" -
set_proc_callers_caller_groups(group_by_clique)
].
:- func proc_callers_contour_excl_toggles =
(assoc_list(string,
(pred(proc_static_ptr, caller_groups, int, contour_exclusion, cmd)))::
out(list_skel(pair(ground, (pred(in, in, in, in, out) is det)))))
is det.
proc_callers_contour_excl_toggles = [
"Apply contour exclusion" -
set_proc_callers_contour_excl(apply_contour_exclusion),
"Do not apply contour exclusion" -
set_proc_callers_contour_excl(do_not_apply_contour_exclusion)
].
:- pred set_proc_callers_caller_groups(caller_groups::in, proc_static_ptr::in,
caller_groups::in, int::in, contour_exclusion::in, cmd::out) is det.
set_proc_callers_caller_groups(CallerGroups, PSPtr, _CallerGroups, BunchSize,
ContourExcl, Cmd) :-
Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, 1, BunchSize,
ContourExcl).
:- pred set_proc_callers_contour_excl(contour_exclusion::in,
proc_static_ptr::in, caller_groups::in, int::in, contour_exclusion::in,
cmd::out) is det.
set_proc_callers_contour_excl(ContourExcl, PSPtr, CallerGroups, BunchSize,
_ContourExcl, Cmd) :-
Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, 1, BunchSize,
ContourExcl).
%---------------------------------------------------------------------------%
%
% Control how the rows in procedure displays are sorted.
%
:- func sort_controls(preferences, cmd) = display_item.
sort_controls(Prefs, Cmd) = ControlsItem :-
make_prefs_controls_item(Prefs, Cmd, no,
sort_main_toggles, SortMainControls),
make_prefs_controls_item(Prefs, Cmd, no,
sort_time_toggles, SortTimeControls),
make_prefs_controls_item(Prefs, Cmd, no,
sort_callseqs_toggles, SortCallSeqsControls),
make_prefs_controls_item(Prefs, Cmd, no,
sort_allocs_toggles, SortAllocsControls),
make_prefs_controls_item(Prefs, Cmd, no,
sort_memory_toggles, SortMemoryControls),
ControlsItem = display_list(list_class_vertical_no_bullets,
yes("Toggle sort options:"),
[SortMainControls, SortTimeControls, SortCallSeqsControls,
SortAllocsControls, SortMemoryControls]).
:- func sort_main_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
sort_main_toggles = [
"Sort by context" -
set_sort_criteria(by_context),
"Sort by name" -
set_sort_criteria(by_name),
% For cost_calls and cost_redos, the second and third fields do not matter.
"Sort by calls" -
set_sort_criteria(by_cost(cost_calls, self, overall)),
"Sort by redos" -
set_sort_criteria(by_cost(cost_redos, self, overall))
].
:- func sort_time_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
sort_time_toggles = [
"Sort by self time" -
set_sort_criteria(by_cost(cost_time, self, overall)),
"Sort by total time" -
set_sort_criteria(by_cost(cost_time, self_and_desc, overall)),
"Sort by self time per call" -
set_sort_criteria(by_cost(cost_time, self, per_call)),
"Sort by total time per call" -
set_sort_criteria(by_cost(cost_time, self_and_desc, per_call))
].
:- func sort_callseqs_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
sort_callseqs_toggles = [
"Sort by self callseqs" -
set_sort_criteria(by_cost(cost_callseqs, self, overall)),
"Sort by total callseqs" -
set_sort_criteria(by_cost(cost_callseqs, self_and_desc, overall)),
"Sort by self callseqs per call" -
set_sort_criteria(by_cost(cost_callseqs, self, per_call)),
"Sort by total callseqs per call" -
set_sort_criteria(by_cost(cost_callseqs, self_and_desc, per_call))
].
:- func sort_allocs_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
sort_allocs_toggles = [
"Sort by self allocs" -
set_sort_criteria(by_cost(cost_allocs, self, overall)),
"Sort by total allocs" -
set_sort_criteria(by_cost(cost_allocs, self_and_desc, overall)),
"Sort by self allocs per call" -
set_sort_criteria(by_cost(cost_allocs, self, per_call)),
"Sort by total allocs per call" -
set_sort_criteria(by_cost(cost_allocs, self_and_desc, per_call))
].
:- func sort_memory_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
sort_memory_toggles = [
"Sort by self memory" -
set_sort_criteria(by_cost(cost_words, self, overall)),
"Sort by total memory" -
set_sort_criteria(by_cost(cost_words, self_and_desc, overall)),
"Sort by self memory per call" -
set_sort_criteria(by_cost(cost_words, self, per_call)),
"Sort by total memory per call" -
set_sort_criteria(by_cost(cost_words, self_and_desc, per_call))
].
:- pred set_sort_criteria(order_criteria::in,
preferences::in, preferences::out) is det.
set_sort_criteria(SortCriteria, !Prefs) :-
!Prefs ^ pref_criteria := SortCriteria.
%---------------------------------------------------------------------------%
%
% Control whether we summarize the info from callees at multi call sites.
%
:- func summarize_controls(preferences, cmd) = display_item.
summarize_controls(Prefs, Cmd) = ControlsItem :-
make_prefs_controls_item(Prefs, Cmd, no,
summarize_toggles, SummarizeControls),
ControlsItem = display_list(list_class_vertical_no_bullets,
yes("Toggle summarize options:"), [SummarizeControls]).
:- func summarize_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
summarize_toggles = [
"Do not summarize higher order calls" -
set_summarize(do_not_summarize_ho_call_sites),
"Summarize higher order calls" -
set_summarize(summarize_ho_call_sites)
].
:- pred set_summarize(summarize_ho_call_sites::in,
preferences::in, preferences::out) is det.
set_summarize(Summarize, !Prefs) :-
!Prefs ^ pref_summarize := Summarize.
%---------------------------------------------------------------------------%
%
% Control whether we display call sites that do not get any calls.
%
:- func module_qual_controls(preferences, cmd) = display_item.
module_qual_controls(Prefs, Cmd) = ControlsItem :-
make_prefs_controls_item(Prefs, Cmd, no,
module_qual_toggles, ShowNoCallSitesControls),
ControlsItem = display_list(list_class_vertical_no_bullets,
yes("Toggle show no calls site options:"), [ShowNoCallSitesControls]).
:- func module_qual_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
module_qual_toggles = [
"Never module qualify predicate and function names" -
set_module_qual(module_qual_never),
"Module qualify predicate and function names only when needed" -
set_module_qual(module_qual_when_diff),
"Always module qualify predicate and function names" -
set_module_qual(module_qual_always)
].
:- pred set_module_qual(module_qual::in,
preferences::in, preferences::out) is det.
set_module_qual(ModuleQual, !Prefs) :-
!Prefs ^ pref_module_qual := ModuleQual.
%---------------------------------------------------------------------------%
%
% Control how pages are displayed.
%
:- func format_controls(preferences, cmd) = display_item.
format_controls(Prefs, Cmd) = ControlsItem :-
make_prefs_controls_item(Prefs, Cmd, no,
format_toggles, FormatControls),
ControlsItem = display_list(list_class_vertical_no_bullets,
yes("Toggle format options:"), [FormatControls]).
:- func format_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
format_toggles = [
"Fade column groups" -
set_colour_column_groups(do_not_colour_column_groups),
"Colour column groups" -
set_colour_column_groups(colour_column_groups),
"Unbox" -
set_box_tables(do_not_box_tables),
"Box" -
set_box_tables(box_tables)
].
:- pred set_colour_column_groups(colour_column_groups::in,
preferences::in, preferences::out) is det.
set_colour_column_groups(Colour, !Prefs) :-
!Prefs ^ pref_colour := Colour.
:- pred set_box_tables(box_tables::in,
preferences::in, preferences::out) is det.
set_box_tables(Box, !Prefs) :-
!Prefs ^ pref_box := Box.
%---------------------------------------------------------------------------%
%
% Controls for related procedure and clique reports.
%
:- func proc_reports_controls(preferences, proc_static_ptr, cmd)
= display_item.
proc_reports_controls(Prefs, Proc, NotCmd) = ControlsItem :-
Makers = [
control_maker(deep_cmd_proc(Proc),
"Procedure", no),
% XXX: Should procrep_coverage be a developer-only option?
control_maker(deep_cmd_static_procrep_coverage(Proc),
"Coverage annotated procedure representation", no),
control_maker(deep_cmd_dump_proc_static(Proc),
"Unprocessed proc static data", yes)
],
make_controls(Prefs, NotCmd, Makers, ProcReportControls),
ControlsItem = display_list(list_class_vertical_no_bullets,
yes("Related procedure reports:"), ProcReportControls).
:- func clique_reports_controls(preferences, clique_ptr, cmd) = display_item.
clique_reports_controls(Prefs, CliquePtr, NotCmd) = ControlsItem :-
Makers = [
control_maker(deep_cmd_clique(CliquePtr),
"Clique", no),
control_maker(deep_cmd_clique_recursive_costs(CliquePtr),
"Clique's recursion information", yes),
control_maker(deep_cmd_dump_clique(CliquePtr),
"Unprocessed clique data", yes)
],
make_controls(Prefs, NotCmd, Makers, CliqueReportControls),
ControlsItem = display_list(list_class_vertical_no_bullets,
yes("Related clique reports:"), CliqueReportControls).
:- type control_maker
---> control_maker(cmd, string, bool).
:- pred make_controls(preferences::in, cmd::in, list(control_maker)::in,
list(display_item)::out) is det.
make_controls(_Prefs, _NotCmd, [], []).
make_controls(Prefs, NotCmd, [Maker | Makers], Items) :-
make_controls(Prefs, NotCmd, Makers, TailItems),
Maker = control_maker(Cmd, Label, Developer),
( if Cmd = NotCmd then
Items = TailItems
else
make_control(yes(Prefs), Cmd, Label, Developer, Control),
Items = [Control | TailItems]
).
%---------------------------------------------------------------------------%
%
% Control whether we display inactive modules.
%
:- func inactive_module_controls(preferences, cmd) = display_item.
inactive_module_controls(Prefs, Cmd) = ControlsItem :-
make_prefs_controls_item(Prefs, Cmd, no,
inactive_module_toggles, InactiveModuleControls),
Controls = [InactiveModuleControls],
ControlsItem = display_list(list_class_vertical_no_bullets,
yes("Toggle display of inactive modules:"), Controls).
:- func inactive_module_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
inactive_module_toggles = [
"Do not display inactive modules" -
set_inactive_modules(inactive_hide),
"Display inactive modules" -
set_inactive_modules(inactive_show)
].
:- pred set_inactive_modules(inactive_status::in,
preferences::in, preferences::out) is det.
set_inactive_modules(Status, !Prefs) :-
!Prefs ^ pref_inactive ^ inactive_modules := Status.
%---------------------------------------------------------------------------%
%
% Control whether we display inactive procedures.
%
:- func inactive_proc_controls(preferences, cmd) = display_item.
inactive_proc_controls(Prefs, Cmd) = ControlsItem :-
make_prefs_controls_item(Prefs, Cmd, no,
inactive_proc_toggles, InactiveModuleControls),
Controls = [InactiveModuleControls],
ControlsItem = display_list(list_class_vertical_no_bullets,
yes("Toggle display of inactive procedures:"), Controls).
:- func inactive_proc_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
inactive_proc_toggles = [
"Do not display inactive procedures" -
set_inactive_procs(inactive_hide),
"Display inactive procedures" -
set_inactive_procs(inactive_show)
].
:- pred set_inactive_procs(inactive_status::in,
preferences::in, preferences::out) is det.
set_inactive_procs(Status, !Prefs) :-
!Prefs ^ pref_inactive ^ inactive_procs := Status.
%---------------------------------------------------------------------------%
%
% Control whether we display inactive call sites.
%
:- func inactive_call_site_controls(preferences, cmd) = display_item.
inactive_call_site_controls(Prefs, Cmd) = ControlsItem :-
make_prefs_controls_item(Prefs, Cmd, no,
inactive_call_site_toggles, InactiveModuleControls),
Controls = [InactiveModuleControls],
ControlsItem = display_list(list_class_vertical_no_bullets,
yes("Toggle display of inactive call_sites:"), Controls).
:- func inactive_call_site_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
inactive_call_site_toggles = [
"Do not display inactive call sites" -
set_inactive_call_sites(inactive_hide),
"Display inactive call sites" -
set_inactive_call_sites(inactive_show)
].
:- pred set_inactive_call_sites(inactive_status::in,
preferences::in, preferences::out) is det.
set_inactive_call_sites(Status, !Prefs) :-
!Prefs ^ pref_inactive ^ inactive_call_sites := Status.
%---------------------------------------------------------------------------%
%
% Control how many ancestors we display.
%
:- func ancestor_controls(preferences, cmd) = display_item.
ancestor_controls(Prefs, Cmd) = ControlsItem :-
MaybeAncestorLimit = Prefs ^ pref_anc,
(
MaybeAncestorLimit = no,
make_prefs_controls_item(Prefs, Cmd, no,
ancestor_toggles_no, AncestorControls)
;
MaybeAncestorLimit = yes(AncestorLimit),
make_prefs_controls_item(Prefs, Cmd, no,
ancestor_toggles_yes(AncestorLimit), AncestorControls)
),
Controls = [AncestorControls],
ControlsItem = display_list(list_class_vertical_no_bullets,
yes("Toggle display of ancestors:"), Controls).
:- func ancestor_toggles_no =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
ancestor_toggles_no = [
"One ancestor" -
set_ancestor_limit(yes(1)),
"Two ancestors" -
set_ancestor_limit(yes(2)),
"Three ancestors" -
set_ancestor_limit(yes(3)),
"Five ancestors" -
set_ancestor_limit(yes(5)),
"Ten ancestors" -
set_ancestor_limit(yes(10))
].
:- func ancestor_toggles_yes(int::in) =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
ancestor_toggles_yes(CurrentLimit) = [
"Halve ancestors" -
set_ancestor_limit(yes(CurrentLimit // 2)),
"Remove an ancestor" -
set_ancestor_limit(yes(CurrentLimit - 1)),
"Add an ancestor" -
set_ancestor_limit(yes(CurrentLimit + 1)),
"Double ancestors" -
set_ancestor_limit(yes(CurrentLimit * 2)),
"Unlimited ancestors" -
set_ancestor_limit(no)
].
:- pred set_ancestor_limit(maybe(int)::in,
preferences::in, preferences::out) is det.
set_ancestor_limit(MaybeAncestorLimit, !Prefs) :-
!Prefs ^ pref_anc := MaybeAncestorLimit.
%---------------------------------------------------------------------------%
%
% Control the set of displayed fields.
%
:- func field_controls(preferences, cmd) = display_item.
field_controls(Prefs, Cmd) = ControlsItem :-
make_prefs_controls_item(Prefs, Cmd, no,
port_field_toggles, PortControls),
make_prefs_controls_item(Prefs, Cmd, no,
time_field_toggles, TimeControls),
make_prefs_controls_item(Prefs, Cmd, no,
callseqs_field_toggles, CallSeqsControls),
make_prefs_controls_item(Prefs, Cmd, no,
alloc_field_toggles, AllocControls),
make_prefs_controls_item(Prefs, Cmd, no,
memory_field_toggles, MemoryControls),
Controls = [PortControls, TimeControls, CallSeqsControls,
AllocControls, MemoryControls],
ControlsItem = display_list(list_class_vertical_no_bullets,
yes("Toggle displayed fields:"), Controls).
:- func port_field_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
port_field_toggles = [
"No port counts" -
set_port_fields(no_port),
"Port counts" -
set_port_fields(port)
].
:- func time_field_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
time_field_toggles = [
"No time info" -
set_time_fields(no_time),
"Ticks" -
set_time_fields(ticks),
"Times" -
set_time_fields(time),
"Ticks and times" -
set_time_fields(ticks_and_time),
"Times and per-call-times" -
set_time_fields(time_and_percall),
"Ticks and times and per-call times" -
set_time_fields(ticks_and_time_and_percall)
].
:- func callseqs_field_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
callseqs_field_toggles = [
"No call sequence numbers" -
set_callseqs_fields(no_callseqs),
"Call sequence numbers" -
set_callseqs_fields(callseqs),
"Call sequence numbers including per-call" -
set_callseqs_fields(callseqs_and_percall)
].
:- func alloc_field_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
alloc_field_toggles = [
"No allocation info" -
set_alloc_fields(no_alloc),
"Allocations" -
set_alloc_fields(alloc),
"Allocations and per-call allocations" -
set_alloc_fields(alloc_and_percall)
].
:- func memory_field_toggles =
(assoc_list(string, (pred(preferences, preferences)))::
out(list_skel(pair(ground, (pred(in, out) is det))))) is det.
memory_field_toggles = [
"No memory info" -
set_memory_fields(no_memory),
"Words" -
set_memory_fields(memory(units_words)),
"Bytes" -
set_memory_fields(memory(units_bytes)),
"Words and per-call words" -
set_memory_fields(memory_and_percall(units_words)),
"Bytes and per-call bytes" -
set_memory_fields(memory_and_percall(units_bytes))
].
:- pred set_port_fields(port_fields::in,
preferences::in, preferences::out) is det.
set_port_fields(PortFields, !Prefs) :-
Fields0 = !.Prefs ^ pref_fields,
Fields = Fields0 ^ port_fields := PortFields,
!Prefs ^ pref_fields := Fields.
:- pred set_time_fields(time_fields::in,
preferences::in, preferences::out) is det.
set_time_fields(TimeFields, !Prefs) :-
Fields0 = !.Prefs ^ pref_fields,
Fields = Fields0 ^ time_fields := TimeFields,
!Prefs ^ pref_fields := Fields.
:- pred set_callseqs_fields(callseqs_fields::in,
preferences::in, preferences::out) is det.
set_callseqs_fields(CallSeqsFields, !Prefs) :-
Fields0 = !.Prefs ^ pref_fields,
Fields = Fields0 ^ callseqs_fields := CallSeqsFields,
!Prefs ^ pref_fields := Fields.
:- pred set_alloc_fields(alloc_fields::in,
preferences::in, preferences::out) is det.
set_alloc_fields(AllocFields, !Prefs) :-
Fields0 = !.Prefs ^ pref_fields,
Fields = Fields0 ^ alloc_fields := AllocFields,
!Prefs ^ pref_fields := Fields.
:- pred set_memory_fields(memory_fields::in,
preferences::in, preferences::out) is det.
set_memory_fields(MemoryFields, !Prefs) :-
Fields0 = !.Prefs ^ pref_fields,
Fields = Fields0 ^ memory_fields := MemoryFields,
!Prefs ^ pref_fields := Fields.
%---------------------------------------------------------------------------%
%
% Links to the basic commands.
%
% Give the common list of commands seen at the bottom of all deep-profiler
% displayed reports.
%
:- func cmds_menu_restart_quit(maybe(preferences)) = display_item.
cmds_menu_restart_quit(MaybePrefs) = ControlsItem :-
Menu = display_link(deep_link(deep_cmd_menu, MaybePrefs,
attr_str([], "Menu"), link_class_control)),
Restart = display_link(deep_link(deep_cmd_restart, MaybePrefs,
attr_str([], "Restart"), link_class_control)),
Quit = display_link(deep_link(deep_cmd_quit, MaybePrefs,
attr_str([], "Quit"), link_class_control)),
List = [Menu, Restart, Quit],
ControlsItem = display_list(list_class_horizontal_except_title,
yes("General commands:"), List).
:- func general_options_controls(cmd, preferences) = display_item.
general_options_controls(Cmd, Prefs) = ControlsItem :-
DeveloperMode = Prefs ^ pref_developer_mode,
(
DeveloperMode = developer_options_visible,
DeveloperText = "Disable developer options",
DeveloperPrefs =
Prefs ^ pref_developer_mode := developer_options_invisible
;
DeveloperMode = developer_options_invisible,
DeveloperText = "Enable developer options",
DeveloperPrefs =
Prefs ^ pref_developer_mode := developer_options_visible
),
DeveloperControl = display_link(deep_link(Cmd, yes(DeveloperPrefs),
attr_str([], DeveloperText), link_class_control)),
List = [DeveloperControl],
ControlsItem = display_list(list_class_horizontal_except_title,
yes("General options:"), List).
%---------------------------------------------------------------------------%
%
% Convert procedure, call site and clique descriptions into table cells.
%
:- func module_active_to_module_name_cell(preferences, module_active) =
table_cell.
module_active_to_module_name_cell(Prefs, ModuleActive) = Cell :-
ModuleName = ModuleActive ^ ma_module_name,
Cmd = deep_cmd_module(ModuleName),
Link = deep_link(Cmd, yes(Prefs), attr_str([], ModuleName),
link_class_link),
Cell = table_cell(td_l(Link)).
:- func proc_active_to_proc_name_cell(maybe(string), module_qual,
preferences, proc_active) = table_cell.
proc_active_to_proc_name_cell(MaybeCurModuleName, ModuleQual,
Prefs, ProcActive) =
proc_desc_to_proc_name_cell(MaybeCurModuleName, ModuleQual,
Prefs, ProcActive ^ pa_proc_desc).
:- func proc_desc_to_source_cell(proc_desc) = table_cell.
proc_desc_to_source_cell(ProcDesc) = Cell :-
FileName = ProcDesc ^ pdesc_file_name,
LineNumber = ProcDesc ^ pdesc_line_number,
Source = string.format("%s:%d", [s(FileName), i(LineNumber)]),
Cell = table_cell(td_s(Source)).
:- func proc_desc_to_proc_name_cell(maybe(string), module_qual,
preferences, proc_desc) = table_cell.
proc_desc_to_proc_name_cell(MaybeCurModuleName, ModuleQual, Prefs, ProcDesc)
= Cell :-
PSPtr = ProcDesc ^ pdesc_ps_ptr,
RefinedName = proc_desc_get_refined_id(MaybeCurModuleName, ModuleQual,
ProcDesc),
Cmd = deep_cmd_proc(PSPtr),
Link = deep_link(Cmd, yes(Prefs), attr_str([], RefinedName),
link_class_link),
Cell = table_cell(td_l(Link)).
:- func proc_desc_to_proc_name_cell_span(maybe(string), module_qual,
preferences, list(str_attr), proc_desc, int) = table_cell.
proc_desc_to_proc_name_cell_span(MaybeCurModuleName, ModuleQual,
Prefs, Attrs, ProcDesc, Span) = Cell :-
PSPtr = ProcDesc ^ pdesc_ps_ptr,
RefinedName = proc_desc_get_refined_id(MaybeCurModuleName, ModuleQual,
ProcDesc),
Cmd = deep_cmd_proc(PSPtr),
Link = deep_link(Cmd, yes(Prefs), attr_str(Attrs, RefinedName),
link_class_link),
Cell = table_multi_cell(td_l(Link), Span).
:- func proc_desc_to_prefix_proc_name_cell(maybe(string), module_qual,
preferences, list(str_attr), proc_desc, string) = table_cell.
proc_desc_to_prefix_proc_name_cell(MaybeCurModuleName, ModuleQual,
Prefs, Attrs, ProcDesc, Prefix) = Cell :-
PSPtr = ProcDesc ^ pdesc_ps_ptr,
RefinedName = proc_desc_get_refined_id(MaybeCurModuleName, ModuleQual,
ProcDesc),
Cmd = deep_cmd_proc(PSPtr),
Link = deep_link(Cmd, yes(Prefs), attr_str(Attrs, Prefix ++ RefinedName),
link_class_link),
Cell = table_cell(td_l(Link)).
:- func call_site_desc_to_name_path_slot_cell(maybe(string), module_qual,
preferences, call_site_desc) = table_cell.
call_site_desc_to_name_path_slot_cell(MaybeCurModuleName, ModuleQual,
Prefs, CallSiteDesc) = Cell :-
CallSiteDesc = call_site_desc(CSSPtr, _ContainerPSPtr,
_FileName, _LineNumber, _ModuleName,
_UnQualRefinedName, _QualRefinedName,
SlotNumber, RevGoalPath, _MaybeCallee),
RefinedName = call_site_desc_get_caller_refined_id(MaybeCurModuleName,
ModuleQual, CallSiteDesc),
GoalPathStr = rev_goal_path_to_string(RevGoalPath),
string.format("%s @ %s #%d",
[s(RefinedName), s(GoalPathStr), i(SlotNumber)], Name),
Cmd = deep_cmd_dump_call_site_static(CSSPtr),
Link = deep_link(Cmd, yes(Prefs), attr_str([], Name), link_class_link),
Cell = table_cell(td_l(Link)).
:- func call_site_desc_to_source_cell(call_site_desc) = table_cell.
call_site_desc_to_source_cell(CallSiteDesc) = Cell :-
FileName = CallSiteDesc ^ csdesc_file_name,
LineNumber = CallSiteDesc ^ csdesc_line_number,
Source = string.format("%s:%d", [s(FileName), i(LineNumber)]),
Cell = table_cell(td_s(Source)).
:- func call_site_desc_to_caller_proc_name_cell(maybe(string), module_qual,
preferences, call_site_desc) = table_cell.
call_site_desc_to_caller_proc_name_cell(MaybeCurModuleName, ModuleQual,
Prefs, CallSiteDesc) = Cell :-
PSPtr = CallSiteDesc ^ csdesc_container,
CallerRefinedName =
call_site_desc_get_caller_refined_id(MaybeCurModuleName, ModuleQual,
CallSiteDesc),
Cmd = deep_cmd_proc(PSPtr),
Link = deep_link(Cmd, yes(Prefs), attr_str([], CallerRefinedName),
link_class_link),
Cell = table_cell(td_l(Link)).
:- func clique_desc_to_non_self_link_proc_name_cell(maybe(string), module_qual,
preferences, clique_desc, clique_ptr) = table_cell.
clique_desc_to_non_self_link_proc_name_cell(MaybeCurModuleName, ModuleQual,
Prefs, CliqueDesc, SelfCliquePtr) = Cell :-
CliqueDesc = clique_desc(CliquePtr, EntryProcDesc, _OtherProcDescs),
EntryProcName = proc_desc_get_refined_id(MaybeCurModuleName, ModuleQual,
EntryProcDesc),
( if CliquePtr = SelfCliquePtr then
Cell = table_cell(td_s(EntryProcName))
else
Cmd = deep_cmd_clique(CliquePtr),
Link = deep_link(Cmd, yes(Prefs), attr_str([], EntryProcName),
link_class_link),
Cell = table_cell(td_l(Link))
).
%---------------------------------------------------------------------------%
%
% Utility predicates.
%
% Make a table row with two columns: a label and a value.
%
:- func make_labelled_table_row(pair(string, table_data)) = table_row.
make_labelled_table_row(Label - Value) =
table_row([table_cell(td_s(Label)), table_cell(Value)]).
:- type link_base
---> link_base(cmd, maybe(preferences), string).
% Make a link from a command, preferences and a label.
%
:- pred make_link(link_base::in, display_item::out) is det.
make_link(link_base(Cmd, MaybePrefs, Label), Item) :-
Item = display_link(deep_link(Cmd, MaybePrefs, attr_str([], Label),
link_class_link)).
% Make a control from a command and label and optional preferences
% structure.
%
:- pred make_control(maybe(preferences)::in, cmd::in, string::in, bool::in,
display_item::out) is det.
make_control(MaybePrefs, Cmd, Label, Developer, Item) :-
Item0 = display_link(deep_link(Cmd, MaybePrefs, attr_str([], Label),
link_class_control)),
(
Developer = yes,
Item = display_developer(Item0)
;
Developer = no,
Item = Item0
).
%---------------------------------------------------------------------------%
%
% Sort procedures in a clique by the preferred criteria of performance.
%
:- pred sort_clique_procs_by_preferences(maybe(string)::in, module_qual::in,
preferences::in,
list(clique_proc_report)::in, list(clique_proc_report)::out) is det.
sort_clique_procs_by_preferences(MaybeCurModuleName, ModuleQual, Prefs,
!CliqueProcs) :-
OrderCriteria = Prefs ^ pref_criteria,
(
OrderCriteria = by_context,
list.sort(compare_clique_procs_by_context, !CliqueProcs)
;
OrderCriteria = by_name,
list.sort(
compare_clique_procs_by_name(MaybeCurModuleName, ModuleQual),
!CliqueProcs)
;
OrderCriteria = by_cost(CostKind, InclDesc, Scope),
list.sort(compare_clique_procs_by_cost(CostKind, InclDesc, Scope),
!CliqueProcs),
% We want the most expensive procedures to appear first.
list.reverse(!CliqueProcs)
).
:- pred compare_clique_procs_by_context(
clique_proc_report::in, clique_proc_report::in, comparison_result::out)
is det.
compare_clique_procs_by_context(CliqueProcReportA, CliqueProcReportB,
Result) :-
CliqueProcDescA = CliqueProcReportA ^ cpr_proc_summary ^ perf_row_subject,
CliqueProcDescB = CliqueProcReportB ^ cpr_proc_summary ^ perf_row_subject,
compare_proc_descs_by_context(CliqueProcDescA, CliqueProcDescB, Result).
:- pred compare_clique_procs_by_name(maybe(string)::in, module_qual::in,
clique_proc_report::in, clique_proc_report::in, comparison_result::out)
is det.
compare_clique_procs_by_name(MaybeCurModuleName, ModuleQual,
CliqueProcReportA, CliqueProcReportB, Result) :-
CliqueProcDescA = CliqueProcReportA ^ cpr_proc_summary ^ perf_row_subject,
CliqueProcDescB = CliqueProcReportB ^ cpr_proc_summary ^ perf_row_subject,
compare_proc_descs_by_name(MaybeCurModuleName, ModuleQual,
CliqueProcDescA, CliqueProcDescB, Result).
:- pred compare_clique_procs_by_cost(
cost_kind::in, include_descendants::in, measurement_scope::in,
clique_proc_report::in, clique_proc_report::in, comparison_result::out)
is det.
compare_clique_procs_by_cost(CostKind, InclDesc, Scope,
CliqueProcReportA, CliqueProcReportB, Result) :-
ProcRowDataA = CliqueProcReportA ^ cpr_proc_summary,
ProcRowDataB = CliqueProcReportB ^ cpr_proc_summary,
compare_perf_row_datas_by_cost(CostKind, InclDesc, Scope,
ProcRowDataA, ProcRowDataB, Result).
%---------------------------------------------------------------------------%
%
% Sort proc_dynamics in a clique by the preferred criteria of performance.
%
:- pred sort_clique_proc_dynamics_by_preferences(preferences::in,
list(clique_proc_dynamic_report)::in,
list(clique_proc_dynamic_report)::out) is det.
sort_clique_proc_dynamics_by_preferences(Prefs, !CliqueProcDynamics) :-
OrderCriteria = Prefs ^ pref_criteria,
(
( OrderCriteria = by_context
; OrderCriteria = by_name
),
% All the proc_dynamics we want to sort have the same name and context,
% so it does not make sense to sort on these criteria. Instead, we sort
% on the default performance criteria.
CostKind = default_cost_kind,
InclDesc = default_incl_desc,
Scope = default_scope
;
OrderCriteria = by_cost(CostKind, InclDesc, Scope)
),
list.sort(compare_clique_proc_dynamics_by_cost(CostKind, InclDesc, Scope),
!CliqueProcDynamics),
% We want the most expensive procedures to appear first.
list.reverse(!CliqueProcDynamics).
:- pred compare_clique_proc_dynamics_by_cost(
cost_kind::in, include_descendants::in, measurement_scope::in,
clique_proc_dynamic_report::in, clique_proc_dynamic_report::in,
comparison_result::out) is det.
compare_clique_proc_dynamics_by_cost(CostKind, InclDesc, Scope,
CliqueProcDynamicReportA, CliqueProcDynamicReportB, Result) :-
ProcDynamicRowDataA = CliqueProcDynamicReportA ^ cpdr_proc_summary,
ProcDynamicRowDataB = CliqueProcDynamicReportB ^ cpdr_proc_summary,
compare_perf_row_datas_by_cost(CostKind, InclDesc, Scope,
ProcDynamicRowDataA, ProcDynamicRowDataB, Result).
%---------------------------------------------------------------------------%
%
% Sort clique_call_site_reports by the preferred criteria of performance.
%
:- pred sort_clique_call_site_reports_by_preferences(maybe(string)::in,
module_qual::in, preferences::in,
list(clique_call_site_report)::in, list(clique_call_site_report)::out)
is det.
sort_clique_call_site_reports_by_preferences(MaybeCurModuleName, ModuleQual,
Prefs, !CallSiteReports) :-
OrderCriteria = Prefs ^ pref_criteria,
(
OrderCriteria = by_context,
list.sort(compare_clique_call_site_reports_by_context,
!CallSiteReports)
;
OrderCriteria = by_name,
list.sort(
compare_clique_call_site_reports_by_name(MaybeCurModuleName,
ModuleQual),
!CallSiteReports)
;
OrderCriteria = by_cost(CostKind, InclDesc, Scope),
list.sort(compare_clique_call_site_reports_by_cost(CostKind,
InclDesc, Scope), !CallSiteReports),
% We want the most expensive call sites to appear first.
list.reverse(!CallSiteReports)
).
:- pred compare_clique_call_site_reports_by_context(
clique_call_site_report::in, clique_call_site_report::in,
comparison_result::out) is det.
compare_clique_call_site_reports_by_context(CallSiteReportA,
CallSiteReportB, Result) :-
CallSiteDescA =
CallSiteReportA ^ ccsr_call_site_summary ^ perf_row_subject,
CallSiteDescB =
CallSiteReportB ^ ccsr_call_site_summary ^ perf_row_subject,
compare_call_site_descs_by_context(CallSiteDescA, CallSiteDescB, Result).
:- pred compare_clique_call_site_reports_by_name(maybe(string)::in,
module_qual::in, clique_call_site_report::in, clique_call_site_report::in,
comparison_result::out) is det.
compare_clique_call_site_reports_by_name(MaybeCurModuleName, ModuleQual,
CallSiteReportA, CallSiteReportB, Result) :-
CallSiteDescA =
CallSiteReportA ^ ccsr_call_site_summary ^ perf_row_subject,
CallSiteDescB =
CallSiteReportB ^ ccsr_call_site_summary ^ perf_row_subject,
compare_call_site_descs_by_name(MaybeCurModuleName, ModuleQual,
CallSiteDescA, CallSiteDescB, Result).
:- pred compare_clique_call_site_reports_by_cost(
cost_kind::in, include_descendants::in, measurement_scope::in,
clique_call_site_report::in, clique_call_site_report::in,
comparison_result::out) is det.
compare_clique_call_site_reports_by_cost(CostKind, InclDesc, Scope,
CliqueCallSiteReportA, CliqueCallSiteReportB, Result) :-
PerfA = CliqueCallSiteReportA ^ ccsr_call_site_summary,
PerfB = CliqueCallSiteReportB ^ ccsr_call_site_summary,
compare_perf_row_datas_by_cost(CostKind, InclDesc, Scope, PerfA, PerfB,
Result).
%---------------------------------------------------------------------------%
%
% Sort call_site_perfs by the preferred criteria of performance.
%
:- pred sort_call_sites_by_preferences(maybe(string)::in, module_qual::in,
preferences::in,
list(call_site_perf)::in, list(call_site_perf)::out) is det.
sort_call_sites_by_preferences(MaybeCurModuleName, ModuleQual, Prefs,
!CallSitePerfs) :-
OrderCriteria = Prefs ^ pref_criteria,
(
OrderCriteria = by_context,
list.sort(compare_call_site_perfs_by_context, !CallSitePerfs)
;
OrderCriteria = by_name,
list.sort(
compare_call_site_perfs_by_callee_name(MaybeCurModuleName,
ModuleQual),
!CallSitePerfs)
;
OrderCriteria = by_cost(CostKind, InclDesc, Scope),
list.sort(compare_call_site_perfs_by_cost(CostKind, InclDesc, Scope),
!CallSitePerfs),
% We want the most expensive call sites to appear first.
list.reverse(!CallSitePerfs)
).
:- pred compare_call_site_perfs_by_context(
call_site_perf::in, call_site_perf::in, comparison_result::out) is det.
compare_call_site_perfs_by_context(CallSitePerfA, CallSitePerfB, Result) :-
CallSiteDescA = CallSitePerfA ^ csf_summary_perf ^ perf_row_subject,
CallSiteDescB = CallSitePerfB ^ csf_summary_perf ^ perf_row_subject,
compare_call_site_descs_by_context(CallSiteDescA, CallSiteDescB, Result).
:- pred compare_call_site_perfs_by_callee_name(maybe(string)::in,
module_qual::in,
call_site_perf::in, call_site_perf::in, comparison_result::out) is det.
compare_call_site_perfs_by_callee_name(MaybeCurModuleName, ModuleQual,
CallSitePerfA, CallSitePerfB, Result) :-
CallSiteDescA = CallSitePerfA ^ csf_summary_perf ^ perf_row_subject,
CallSiteDescB = CallSitePerfB ^ csf_summary_perf ^ perf_row_subject,
compare_call_site_descs_by_callee_name(MaybeCurModuleName, ModuleQual,
CallSiteDescA, CallSiteDescB, Result).
:- pred compare_call_site_perfs_by_cost(
cost_kind::in, include_descendants::in, measurement_scope::in,
call_site_perf::in, call_site_perf::in, comparison_result::out) is det.
compare_call_site_perfs_by_cost(CostKind, InclDesc, Scope,
CallSitePerfA, CallSitePerfB, Result) :-
PerfA = CallSitePerfA ^ csf_summary_perf,
PerfB = CallSitePerfB ^ csf_summary_perf,
compare_perf_row_datas_by_cost(CostKind, InclDesc, Scope, PerfA, PerfB,
Result).
%---------------------------------------------------------------------------%
%
% Sort perf_data_rows of call_site_descs by the preferred criteria.
%
:- pred sort_call_site_desc_rows_by_preferences(maybe(string)::in,
module_qual::in, preferences::in,
list(perf_row_data(call_site_desc))::in,
list(perf_row_data(call_site_desc))::out) is det.
sort_call_site_desc_rows_by_preferences(MaybeCurModuleName, ModuleQual, Prefs,
!CallSiteRowDatas) :-
OrderCriteria = Prefs ^ pref_criteria,
(
OrderCriteria = by_context,
list.sort(compare_call_site_desc_rows_by_context, !CallSiteRowDatas)
;
OrderCriteria = by_name,
list.sort(
compare_call_site_desc_rows_by_name(MaybeCurModuleName,
ModuleQual),
!CallSiteRowDatas)
;
OrderCriteria = by_cost(CostKind, InclDesc, Scope),
list.sort(compare_perf_row_datas_by_cost(CostKind, InclDesc, Scope),
!CallSiteRowDatas),
% We want the most expensive rows to appear first.
list.reverse(!CallSiteRowDatas)
).
:- pred compare_call_site_desc_rows_by_context(
perf_row_data(call_site_desc)::in, perf_row_data(call_site_desc)::in,
comparison_result::out) is det.
compare_call_site_desc_rows_by_context(
CallSiteDescRowDataA, CallSiteDescRowDataB, Result) :-
CallSiteDescA = CallSiteDescRowDataA ^ perf_row_subject,
CallSiteDescB = CallSiteDescRowDataB ^ perf_row_subject,
compare_call_site_descs_by_context(CallSiteDescA, CallSiteDescB, Result).
:- pred compare_call_site_desc_rows_by_name(maybe(string)::in, module_qual::in,
perf_row_data(call_site_desc)::in, perf_row_data(call_site_desc)::in,
comparison_result::out) is det.
compare_call_site_desc_rows_by_name(MaybeCurModuleName, ModuleQual,
CallSiteDescRowDataA, CallSiteDescRowDataB, Result) :-
CallSiteDescA = CallSiteDescRowDataA ^ perf_row_subject,
CallSiteDescB = CallSiteDescRowDataB ^ perf_row_subject,
compare_call_site_descs_by_name(MaybeCurModuleName, ModuleQual,
CallSiteDescA, CallSiteDescB, Result).
%---------------------------------------------------------------------------%
%
% Sort perf_data_rows of proc_descs by the preferred criteria.
%
:- pred sort_proc_desc_rows_by_preferences(maybe(string)::in, module_qual::in,
preferences::in,
list(perf_row_data(proc_desc))::in, list(perf_row_data(proc_desc))::out)
is det.
sort_proc_desc_rows_by_preferences(MaybeCurModuleName, ModuleQual, Prefs,
!ProcDescRowDatas) :-
OrderCriteria = Prefs ^ pref_criteria,
(
OrderCriteria = by_context,
list.sort(compare_proc_desc_rows_by_context, !ProcDescRowDatas)
;
OrderCriteria = by_name,
list.sort(
compare_proc_desc_rows_by_name(MaybeCurModuleName, ModuleQual),
!ProcDescRowDatas)
;
OrderCriteria = by_cost(CostKind, InclDesc, Scope),
list.sort(compare_perf_row_datas_by_cost(CostKind, InclDesc, Scope),
!ProcDescRowDatas),
% We want the most expensive rows to appear first.
list.reverse(!ProcDescRowDatas)
).
:- pred compare_proc_desc_rows_by_context(
perf_row_data(proc_desc)::in, perf_row_data(proc_desc)::in,
comparison_result::out) is det.
compare_proc_desc_rows_by_context(ProcDescRowDataA, ProcDescRowDataB,
Result) :-
ProcDescA = ProcDescRowDataA ^ perf_row_subject,
ProcDescB = ProcDescRowDataB ^ perf_row_subject,
compare_proc_descs_by_context(ProcDescA, ProcDescB, Result).
:- pred compare_proc_desc_rows_by_name(maybe(string)::in, module_qual::in,
perf_row_data(proc_desc)::in, perf_row_data(proc_desc)::in,
comparison_result::out) is det.
compare_proc_desc_rows_by_name(MaybeCurModuleName, ModuleQual,
ProcDescRowDataA, ProcDescRowDataB, Result) :-
ProcDescA = ProcDescRowDataA ^ perf_row_subject,
ProcDescB = ProcDescRowDataB ^ perf_row_subject,
compare_proc_descs_by_name(MaybeCurModuleName, ModuleQual,
ProcDescA, ProcDescB, Result).
%---------------------------------------------------------------------------%
%
% Sort perf_data_rows of proc_actives by the preferred criteria.
%
:- pred sort_proc_active_rows_by_preferences(maybe(string)::in,
module_qual::in, preferences::in,
list(perf_row_data(proc_active))::in,
list(perf_row_data(proc_active))::out) is det.
sort_proc_active_rows_by_preferences(MaybeCurModuleName, ModuleQual, Prefs,
!ProcRowDatas) :-
OrderCriteria = Prefs ^ pref_criteria,
(
OrderCriteria = by_context,
list.sort(compare_proc_active_rows_by_context, !ProcRowDatas)
;
OrderCriteria = by_name,
list.sort(
compare_proc_active_rows_by_name(MaybeCurModuleName, ModuleQual),
!ProcRowDatas)
;
OrderCriteria = by_cost(CostKind, InclDesc, Scope),
list.sort(compare_perf_row_datas_by_cost(CostKind, InclDesc, Scope),
!ProcRowDatas),
% We want the most expensive rows to appear first.
list.reverse(!ProcRowDatas)
).
:- pred compare_proc_active_rows_by_context(
perf_row_data(proc_active)::in, perf_row_data(proc_active)::in,
comparison_result::out) is det.
compare_proc_active_rows_by_context(ProcRowDataA, ProcRowDataB, Result) :-
ProcDescA = ProcRowDataA ^ perf_row_subject ^ pa_proc_desc,
ProcDescB = ProcRowDataB ^ perf_row_subject ^ pa_proc_desc,
compare_proc_descs_by_context(ProcDescA, ProcDescB, Result).
:- pred compare_proc_active_rows_by_name(maybe(string)::in, module_qual::in,
perf_row_data(proc_active)::in, perf_row_data(proc_active)::in,
comparison_result::out) is det.
compare_proc_active_rows_by_name(MaybeCurModuleName, ModuleQual,
ModuleRowDataA, ModuleRowDataB, Result) :-
ProcDescA = ModuleRowDataA ^ perf_row_subject ^ pa_proc_desc,
ProcDescB = ModuleRowDataB ^ perf_row_subject ^ pa_proc_desc,
compare_proc_descs_by_name(MaybeCurModuleName, ModuleQual,
ProcDescA, ProcDescB, Result).
%---------------------------------------------------------------------------%
%
% Sort perf_data_rows of module_getter_setters by the preferred criteria.
%
:- pred sort_getter_setter_fields(preferences::in,
assoc_list(field_name, gs_field_info)::in,
assoc_list(field_name, gs_field_info)::out) is det.
sort_getter_setter_fields(Prefs, !FieldPairs) :-
OrderCriteria = Prefs ^ pref_criteria,
(
% In the common case, each FieldPair has two contexts, one each from
% the getter and the setter. Neither context is all that useful to sort
% by, so we sort by the field name instead.
( OrderCriteria = by_context
; OrderCriteria = by_name
),
list.sort(compare_getter_setters_by_name, !FieldPairs)
;
OrderCriteria = by_cost(CostKind, InclDesc, Scope),
list.sort(compare_getter_setters_by_cost(CostKind, InclDesc, Scope),
!FieldPairs),
% We want the most expensive fields to appear first.
list.reverse(!FieldPairs)
).
:- pred compare_getter_setters_by_name(
pair(field_name, gs_field_info)::in, pair(field_name, gs_field_info)::in,
comparison_result::out) is det.
compare_getter_setters_by_name(PairA, PairB, Result) :-
PairA = FieldNameA - _,
PairB = FieldNameB - _,
compare(Result, FieldNameA, FieldNameB).
:- pred compare_getter_setters_by_cost(
cost_kind::in, include_descendants::in, measurement_scope::in,
pair(field_name, gs_field_info)::in, pair(field_name, gs_field_info)::in,
comparison_result::out) is det.
compare_getter_setters_by_cost(CostKind, InclDesc, Scope, PairA, PairB,
Result) :-
PairA = FieldNameA - FieldInfoA,
PairB = FieldNameB - FieldInfoB,
PerfA = representative_field_perf_row(FieldInfoA),
PerfB = representative_field_perf_row(FieldInfoB),
compare_perf_row_datas_by_cost(CostKind, InclDesc, Scope, PerfA, PerfB,
PerfResult),
(
( PerfResult = (<)
; PerfResult = (>)
),
Result = PerfResult
;
PerfResult = (=),
% We switch the field names to rank in reverse alphabetical order.
% We do this because our caller will reverse the final sorted list,
% and *this* reversal is needed to undo the effects of *that* reversal.
compare(Result, FieldNameB, FieldNameA)
).
:- func representative_field_perf_row(gs_field_info) = perf_row_data(unit).
representative_field_perf_row(FieldInfo) = Perf :-
(
FieldInfo = gs_field_getter(Perf0),
Perf = Perf0 ^ perf_row_subject := unit
;
FieldInfo = gs_field_setter(Perf0),
Perf = Perf0 ^ perf_row_subject := unit
;
FieldInfo = gs_field_both(_, _, Perf)
).
%---------------------------------------------------------------------------%
%
% Sort perf_data_rows of module_actives by the preferred criteria.
%
:- pred sort_module_active_rows_by_preferences(preferences::in,
list(perf_row_data(module_active))::in,
list(perf_row_data(module_active))::out) is det.
sort_module_active_rows_by_preferences(Prefs, !ModuleRowDatas) :-
OrderCriteria = Prefs ^ pref_criteria,
(
% A context is a filename and line number. The filename is derived
% from the module name, and for modules, the line number part of the
% context isn't relevant. Therefore sorting by context is equivalent to
% sorting by name.
( OrderCriteria = by_context
; OrderCriteria = by_name
),
list.sort(compare_module_active_rows_by_name, !ModuleRowDatas)
;
OrderCriteria = by_cost(CostKind, InclDesc, Scope),
list.sort(compare_perf_row_datas_by_cost(CostKind, InclDesc, Scope),
!ModuleRowDatas),
% We want the most expensive rows to appear first.
list.reverse(!ModuleRowDatas)
).
:- pred compare_module_active_rows_by_name(
perf_row_data(module_active)::in, perf_row_data(module_active)::in,
comparison_result::out) is det.
compare_module_active_rows_by_name(ModuleRowDataA, ModuleRowDataB, Result) :-
ModuleDescA = ModuleRowDataA ^ perf_row_subject,
ModuleDescB = ModuleRowDataB ^ perf_row_subject,
ModuleNameA = ModuleDescA ^ ma_module_name,
ModuleNameB = ModuleDescB ^ ma_module_name,
compare(Result, ModuleNameA, ModuleNameB).
%---------------------------------------------------------------------------%
%
% Sort perf_data_rows of module names by the preferred criteria.
%
:- pred sort_module_name_rows_by_preferences(preferences::in,
list(perf_row_data(string))::in, list(perf_row_data(string))::out) is det.
sort_module_name_rows_by_preferences(Prefs, !ModuleRowDatas) :-
OrderCriteria = Prefs ^ pref_criteria,
(
% A context is a filename and line number. The filename is derived
% from the module name, and for modules, the line number part of the
% context isn't relevant. Therefore sorting by context is equivalent to
% sorting by name.
( OrderCriteria = by_context
; OrderCriteria = by_name
),
list.sort(compare_module_name_rows_by_name, !ModuleRowDatas)
;
OrderCriteria = by_cost(CostKind, InclDesc, Scope),
list.sort(compare_perf_row_datas_by_cost(CostKind, InclDesc, Scope),
!ModuleRowDatas),
% We want the most expensive rows to appear first.
list.reverse(!ModuleRowDatas)
).
:- pred compare_module_name_rows_by_name(
perf_row_data(string)::in, perf_row_data(string)::in,
comparison_result::out) is det.
compare_module_name_rows_by_name(ModuleRowDataA, ModuleRowDataB, Result) :-
ModuleNameA = ModuleRowDataA ^ perf_row_subject,
ModuleNameB = ModuleRowDataB ^ perf_row_subject,
compare(Result, ModuleNameA, ModuleNameB).
%---------------------------------------------------------------------------%
%
% Sort perf_data_rows of cliques by the preferred criteria.
%
:- pred sort_clique_rows_by_preferences(preferences::in,
list(perf_row_data(clique_desc))::in,
list(perf_row_data(clique_desc))::out) is det.
sort_clique_rows_by_preferences(Prefs, !CliqueRowDatas) :-
OrderCriteria = Prefs ^ pref_criteria,
(
OrderCriteria = by_context,
% For cliques, we do not have a single context. We could use the
% contexts of the procedures in the clique, but using the clique number
% seems more useful.
list.sort(compare_clique_rows_by_number, !CliqueRowDatas)
;
OrderCriteria = by_name,
list.sort(compare_clique_rows_by_first_proc_name, !CliqueRowDatas)
;
OrderCriteria = by_cost(CostKind, InclDesc, Scope),
list.sort(compare_perf_row_datas_by_cost(CostKind, InclDesc, Scope),
!CliqueRowDatas),
% We want the most expensive rows to appear first.
list.reverse(!CliqueRowDatas)
).
:- pred compare_clique_rows_by_number(
perf_row_data(clique_desc)::in, perf_row_data(clique_desc)::in,
comparison_result::out) is det.
compare_clique_rows_by_number(CliqueRowDataA, CliqueRowDataB, Result) :-
CliqueDescA = CliqueRowDataA ^ perf_row_subject,
CliqueDescB = CliqueRowDataB ^ perf_row_subject,
CliqueDescA ^ cdesc_clique_ptr = clique_ptr(CliqueNumA),
CliqueDescB ^ cdesc_clique_ptr = clique_ptr(CliqueNumB),
compare(Result, CliqueNumA, CliqueNumB).
:- pred compare_clique_rows_by_first_proc_name(
perf_row_data(clique_desc)::in, perf_row_data(clique_desc)::in,
comparison_result::out) is det.
compare_clique_rows_by_first_proc_name(CliqueRowDataA, CliqueRowDataB,
Result) :-
CliqueDescA = CliqueRowDataA ^ perf_row_subject,
CliqueDescB = CliqueRowDataB ^ perf_row_subject,
EntryProcDescA = CliqueDescA ^ cdesc_entry_member,
EntryProcDescB = CliqueDescB ^ cdesc_entry_member,
compare_proc_descs_by_context(EntryProcDescA, EntryProcDescB, Result).
%---------------------------------------------------------------------------%
%
% Sort call_site_descs and proc_descs by context and by name.
%
:- pred compare_call_site_descs_by_context(
call_site_desc::in, call_site_desc::in, comparison_result::out) is det.
compare_call_site_descs_by_context(CallSiteDescA, CallSiteDescB, Result) :-
FileNameA = CallSiteDescA ^ csdesc_file_name,
FileNameB = CallSiteDescB ^ csdesc_file_name,
compare(FileNameResult, FileNameA, FileNameB),
(
( FileNameResult = (<)
; FileNameResult = (>)
),
Result = FileNameResult
;
FileNameResult = (=),
LineNumberA = CallSiteDescA ^ csdesc_line_number,
LineNumberB = CallSiteDescB ^ csdesc_line_number,
compare(Result, LineNumberA, LineNumberB)
).
:- pred compare_proc_descs_by_context(proc_desc::in, proc_desc::in,
comparison_result::out) is det.
compare_proc_descs_by_context(ProcDescA, ProcDescB, Result) :-
FileNameA = ProcDescA ^ pdesc_file_name,
FileNameB = ProcDescB ^ pdesc_file_name,
compare(FileNameResult, FileNameA, FileNameB),
(
( FileNameResult = (<)
; FileNameResult = (>)
),
Result = FileNameResult
;
FileNameResult = (=),
LineNumberA = ProcDescA ^ pdesc_line_number,
LineNumberB = ProcDescB ^ pdesc_line_number,
compare(Result, LineNumberA, LineNumberB)
).
:- pred compare_call_site_descs_by_name(maybe(string)::in, module_qual::in,
call_site_desc::in, call_site_desc::in, comparison_result::out) is det.
compare_call_site_descs_by_name(MaybeCurModuleName, ModuleQual,
CallSiteDescA, CallSiteDescB, Result) :-
NameA = call_site_desc_get_caller_refined_id(MaybeCurModuleName,
ModuleQual, CallSiteDescA),
NameB = call_site_desc_get_caller_refined_id(MaybeCurModuleName,
ModuleQual, CallSiteDescB),
compare(Result, NameA, NameB).
:- pred compare_call_site_descs_by_callee_name(maybe(string)::in,
module_qual::in, call_site_desc::in, call_site_desc::in,
comparison_result::out) is det.
compare_call_site_descs_by_callee_name(MaybeCurModuleName, ModuleQual,
CallSiteDescA, CallSiteDescB, Result) :-
MaybeCalleeA = CallSiteDescA ^ csdesc_maybe_callee,
MaybeCalleeB = CallSiteDescB ^ csdesc_maybe_callee,
(
MaybeCalleeA = no,
MaybeCalleeB = no,
Result = (=)
;
MaybeCalleeA = no,
MaybeCalleeB = yes(_),
Result = (<)
;
MaybeCalleeA = yes(_),
MaybeCalleeB = no,
Result = (>)
;
MaybeCalleeA = yes(CalleeNameA),
MaybeCalleeB = yes(CalleeNameB),
compare_proc_descs_by_name(MaybeCurModuleName, ModuleQual,
CalleeNameA, CalleeNameB, Result)
).
:- pred compare_proc_descs_by_name(maybe(string)::in, module_qual::in,
proc_desc::in, proc_desc::in, comparison_result::out) is det.
compare_proc_descs_by_name(MaybeCurModuleName, ModuleQual,
ProcDescA, ProcDescB, Result) :-
NameA =
proc_desc_get_refined_id(MaybeCurModuleName, ModuleQual, ProcDescA),
NameB =
proc_desc_get_refined_id(MaybeCurModuleName, ModuleQual, ProcDescB),
compare(Result, NameA, NameB).
%---------------------------------------------------------------------------%
%
% Sort perf_row_datas by a criterion of performance.
%
:- pred compare_perf_row_datas_by_cost(
cost_kind::in, include_descendants::in, measurement_scope::in,
perf_row_data(T)::in, perf_row_data(T)::in, comparison_result::out) is det.
compare_perf_row_datas_by_cost(CostKind, InclDesc, Scope, PerfA, PerfB,
Result) :-
(
CostKind = cost_calls,
CallsA = PerfA ^ perf_row_calls,
CallsB = PerfB ^ perf_row_calls,
compare(Result, CallsA, CallsB)
;
CostKind = cost_redos,
RedosA = PerfA ^ perf_row_redos,
RedosB = PerfB ^ perf_row_redos,
compare(Result, RedosA, RedosB)
;
CostKind = cost_time,
compare_perf_row_datas_by_time(InclDesc, Scope, PerfA, PerfB,
TimeResult),
(
TimeResult = (=),
compare_perf_row_datas_by_callseqs(InclDesc, Scope, PerfA, PerfB,
Result)
;
( TimeResult = (<)
; TimeResult = (>)
),
Result = TimeResult
)
;
CostKind = cost_callseqs,
compare_perf_row_datas_by_callseqs(InclDesc, Scope, PerfA, PerfB,
Result)
;
CostKind = cost_allocs,
compare_perf_row_datas_by_allocs(InclDesc, Scope, PerfA, PerfB, Result)
;
CostKind = cost_words,
compare_perf_row_datas_by_words(InclDesc, Scope, PerfA, PerfB, Result)
).
:- pred compare_perf_row_datas_by_time(
include_descendants::in, measurement_scope::in,
perf_row_data(T)::in, perf_row_data(T)::in, comparison_result::out) is det.
compare_perf_row_datas_by_time(InclDesc, Scope, PerfA, PerfB, Result) :-
(
InclDesc = self,
SelfA = PerfA ^ perf_row_self,
SelfB = PerfB ^ perf_row_self,
(
Scope = overall,
TimeA = SelfA ^ perf_row_time,
TimeB = SelfB ^ perf_row_time,
compare(Result, TimeA, TimeB)
;
Scope = per_call,
TimeA = SelfA ^ perf_row_time_percall,
TimeB = SelfB ^ perf_row_time_percall,
compare(Result, TimeA, TimeB)
)
;
InclDesc = self_and_desc,
MaybeTotalA = PerfA ^ perf_row_maybe_total,
MaybeTotalB = PerfB ^ perf_row_maybe_total,
( if
MaybeTotalA = yes(TotalA),
MaybeTotalB = yes(TotalB)
then
(
Scope = overall,
TimeA = TotalA ^ perf_row_time,
TimeB = TotalB ^ perf_row_time,
compare(Result, TimeA, TimeB)
;
Scope = per_call,
TimeA = TotalA ^ perf_row_time_percall,
TimeB = TotalB ^ perf_row_time_percall,
compare(Result, TimeA, TimeB)
)
else
unexpected($pred, "self_and_desc")
)
).
:- pred compare_perf_row_datas_by_callseqs(
include_descendants::in, measurement_scope::in,
perf_row_data(T)::in, perf_row_data(T)::in, comparison_result::out) is det.
compare_perf_row_datas_by_callseqs(InclDesc, Scope, PerfA, PerfB, Result) :-
(
InclDesc = self,
SelfA = PerfA ^ perf_row_self,
SelfB = PerfB ^ perf_row_self,
(
Scope = overall,
CallSeqsA = SelfA ^ perf_row_callseqs,
CallSeqsB = SelfB ^ perf_row_callseqs,
compare(Result, CallSeqsA, CallSeqsB)
;
Scope = per_call,
CallSeqsA = SelfA ^ perf_row_callseqs_percall,
CallSeqsB = SelfB ^ perf_row_callseqs_percall,
compare(Result, CallSeqsA, CallSeqsB)
)
;
InclDesc = self_and_desc,
MaybeTotalA = PerfA ^ perf_row_maybe_total,
MaybeTotalB = PerfB ^ perf_row_maybe_total,
( if
MaybeTotalA = yes(TotalA),
MaybeTotalB = yes(TotalB)
then
(
Scope = overall,
CallSeqsA = TotalA ^ perf_row_callseqs,
CallSeqsB = TotalB ^ perf_row_callseqs,
compare(Result, CallSeqsA, CallSeqsB)
;
Scope = per_call,
CallSeqsA = TotalA ^ perf_row_callseqs_percall,
CallSeqsB = TotalB ^ perf_row_callseqs_percall,
compare(Result, CallSeqsA, CallSeqsB)
)
else
unexpected($pred, "self_and_desc")
)
).
:- pred compare_perf_row_datas_by_allocs(
include_descendants::in, measurement_scope::in,
perf_row_data(T)::in, perf_row_data(T)::in, comparison_result::out) is det.
compare_perf_row_datas_by_allocs(InclDesc, Scope, PerfA, PerfB, Result) :-
(
InclDesc = self,
SelfA = PerfA ^ perf_row_self,
SelfB = PerfB ^ perf_row_self,
(
Scope = overall,
AllocsA = SelfA ^ perf_row_allocs,
AllocsB = SelfB ^ perf_row_allocs,
compare(Result, AllocsA, AllocsB)
;
Scope = per_call,
AllocsA = SelfA ^ perf_row_allocs_percall,
AllocsB = SelfB ^ perf_row_allocs_percall,
compare(Result, AllocsA, AllocsB)
)
;
InclDesc = self_and_desc,
MaybeTotalA = PerfA ^ perf_row_maybe_total,
MaybeTotalB = PerfB ^ perf_row_maybe_total,
( if
MaybeTotalA = yes(TotalA),
MaybeTotalB = yes(TotalB)
then
(
Scope = overall,
AllocsA = TotalA ^ perf_row_allocs,
AllocsB = TotalB ^ perf_row_allocs,
compare(Result, AllocsA, AllocsB)
;
Scope = per_call,
AllocsA = TotalA ^ perf_row_allocs_percall,
AllocsB = TotalB ^ perf_row_allocs_percall,
compare(Result, AllocsA, AllocsB)
)
else
unexpected($pred, "missing total")
)
).
:- pred compare_perf_row_datas_by_words(
include_descendants::in, measurement_scope::in,
perf_row_data(T)::in, perf_row_data(T)::in, comparison_result::out) is det.
compare_perf_row_datas_by_words(InclDesc, Scope, PerfA, PerfB, Result) :-
(
InclDesc = self,
SelfA = PerfA ^ perf_row_self,
SelfB = PerfB ^ perf_row_self,
(
Scope = overall,
MemoryA = SelfA ^ perf_row_mem,
MemoryB = SelfB ^ perf_row_mem,
compare_memory(MemoryA, MemoryB, Result)
;
Scope = per_call,
MemoryA = SelfA ^ perf_row_mem_percall,
MemoryB = SelfB ^ perf_row_mem_percall,
compare_memory(MemoryA, MemoryB, Result)
)
;
InclDesc = self_and_desc,
MaybeTotalA = PerfA ^ perf_row_maybe_total,
MaybeTotalB = PerfB ^ perf_row_maybe_total,
( if
MaybeTotalA = yes(TotalA),
MaybeTotalB = yes(TotalB)
then
(
Scope = overall,
MemoryA = TotalA ^ perf_row_mem,
MemoryB = TotalB ^ perf_row_mem,
compare_memory(MemoryA, MemoryB, Result)
;
Scope = per_call,
MemoryA = TotalA ^ perf_row_mem_percall,
MemoryB = TotalB ^ perf_row_mem_percall,
compare_memory(MemoryA, MemoryB, Result)
)
else
unexpected($pred, "missing total")
)
).
%---------------------------------------------------------------------------%
:- func proc_desc_get_refined_id(maybe(string), module_qual, proc_desc)
= string.
proc_desc_get_refined_id(MaybeCurModuleName, ModuleQual, ProcDesc) = Name :-
(
ModuleQual = module_qual_never,
Name = ProcDesc ^ pdesc_uq_refined_name
;
ModuleQual = module_qual_when_diff,
(
MaybeCurModuleName = no,
Name = ProcDesc ^ pdesc_q_refined_name
;
MaybeCurModuleName = yes(CurModuleName),
ModuleName = ProcDesc ^ pdesc_module_name,
( if ModuleName = CurModuleName then
Name = ProcDesc ^ pdesc_uq_refined_name
else
Name = ProcDesc ^ pdesc_q_refined_name
)
)
;
ModuleQual = module_qual_always,
Name = ProcDesc ^ pdesc_q_refined_name
).
:- func call_site_desc_get_caller_refined_id(maybe(string), module_qual,
call_site_desc) = string.
call_site_desc_get_caller_refined_id(MaybeCurModuleName, ModuleQual,
CallSiteDesc) = Name :-
(
ModuleQual = module_qual_never,
Name = CallSiteDesc ^ csdesc_caller_uq_refined_name
;
ModuleQual = module_qual_when_diff,
(
MaybeCurModuleName = no,
Name = CallSiteDesc ^ csdesc_caller_q_refined_name
;
MaybeCurModuleName = yes(CurModuleName),
ModuleName = CallSiteDesc ^ csdesc_caller_module_name,
( if ModuleName = CurModuleName then
Name = CallSiteDesc ^ csdesc_caller_uq_refined_name
else
Name = CallSiteDesc ^ csdesc_caller_q_refined_name
)
)
;
ModuleQual = module_qual_always,
Name = CallSiteDesc ^ csdesc_caller_q_refined_name
).
%---------------------------------------------------------------------------%
:- end_module display_report.
%---------------------------------------------------------------------------%