Files
mercury/deep_profiler/display_report.m
Zoltan Somogyi 48473e23b8 On the module summary page, display the link to the module representation page
Estimated hours taken: 0.3
Branches: main

On the module summary page, display the link to the module representation page
only if we actually have access to the module representation.

deep_profiler/report.m:
	Add a field giving this information to the data structure representing
	the module summary page.

deep_profiler/create_report.m:
	Fill in this field when generating module summary information.

deep_profiler/display_report.m:
	Act on this field when displaying module summary pages.
2012-10-24 05:33:07 +00:00

5385 lines
207 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2008-2011 The University of Melbourne.
% 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 solutions.
:- 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,
(
MaybeAncestorLimit = yes(AncestorLimit),
NumAncestors > AncestorLimit
->
AncestorTitle = string.format("The %d closest ancestor call sites:",
[i(AncestorLimit)]),
list.take_upto(AncestorLimit,
InnerToOuterAncestorCallSites0, InnerToOuterAncestorCallSites)
;
AncestorTitle = "Ancestor call sites:",
InnerToOuterAncestorCallSites = InnerToOuterAncestorCallSites0
),
list.reverse(InnerToOuterAncestorCallSites, AncestorCallSites),
ModuleQual = Prefs ^ pref_module_qual,
CliqueModuleNames = list.map(clique_proc_report_module_name, CliqueProcs0),
(
CliqueModuleNames = [FirstModuleName | _],
list.all_same(CliqueModuleNames)
->
MaybeCurModuleName = yes(FirstModuleName)
;
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),
( NumCliqueprocs > 1 ->
ProcsTitle = "Procedures of the clique:"
;
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($module, $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 = format("Mutual recursion between %d procedures",
[i(NumProcs)])
),
Items = [display_text(Text)]
;
RecursionType = rt_errors(Errors),
ErrorItems = 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 = round_to_int(AvgDepth),
ExtraTableRows0 =
[{"Average recursion depth:", AvgDepth},
{"Average recursive call cost (excluding the call itself):",
AvgRecCost}] ++
map(
(func(Level) =
{string.format("Cost at depth %d:", [i(Level)]),
AnyRecCost(Level)}),
[0, 1, 2, round_to_int(AvgDepth / 2.0),
MaxDepthI - 2, MaxDepthI - 1, MaxDepthI]),
ExtraTableRows = 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 = map((func(Level) = {Label, Level} :-
Label = string.format("Case for %d recursive calls",
[i(Level ^ rlr_level)])
), Levels),
Text = "Unknown recursion type:",
ExtraTableRows = []
),
Rows = map(make_recursion_table_row, RowData),
ExtraTable = display_table(table(table_class_do_not_box, 2, no,
ExtraTableRows)),
Header = table_header(map(
(func({Name, Class}) =
table_header_group(table_header_group_single(td_s(Name)),
Class, column_do_not_colour)
),
[{"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}])),
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),
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(", ", 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 :-
( Prefs ^ pref_criteria = by_cost(CostKind, self_and_desc, Scope) ->
SortPrefs = Prefs ^ pref_criteria := by_cost(CostKind, self, Scope)
;
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 = 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),
( TypeSubstStr = "" ->
CallSiteStr = CalleeRefinedName
;
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"),
( FirstBunchNum = LastBunchNum ->
Message = string.format("There are %d:",
[i(TotalNumRows)]),
BunchControls = []
;
Message = string.format("There are %d, showing %d to %d:",
[i(TotalNumRows), i(FirstRowNum), i(LastRowNum)]),
( BunchNum > FirstBunchNum ->
BunchControlsFirst = [make_proc_callers_link(Prefs,
"First group", PSPtr, CallerGroups, FirstBunchNum,
BunchSize, ContourExcl)]
;
BunchControlsFirst = []
),
( BunchNum - 1 > FirstBunchNum ->
BunchControlsPrev = [make_proc_callers_link(Prefs,
"Previous group", PSPtr, CallerGroups, BunchNum - 1,
BunchSize, ContourExcl)]
;
BunchControlsPrev = []
),
( BunchNum + 1 < LastBunchNum ->
BunchControlsNext = [make_proc_callers_link(Prefs,
"Next group", PSPtr, CallerGroups, BunchNum + 1,
BunchSize, ContourExcl)]
;
BunchControlsNext = []
),
( BunchNum < LastBunchNum ->
BunchControlsLast = [make_proc_callers_link(Prefs,
"Last group", PSPtr, CallerGroups, LastBunchNum,
BunchSize, ContourExcl)]
;
BunchControlsLast = []
),
BunchControlList = BunchControlsFirst ++ BunchControlsPrev ++
BunchControlsNext ++ BunchControlsLast,
BunchControls = [display_paragraph_break,
display_list(list_class_horizontal_except_title,
yes("Show other groups:"), BunchControlList)]
),
(
BunchSize > 5,
HalfBunchSize = BunchSize / 2,
HalfBunchSize \= 10,
HalfBunchSize \= 20,
HalfBunchSize \= 50,
HalfBunchSize \= 100
->
BunchSizeControlPairsHalf = [(BunchSize / 2) -
make_proc_callers_link(Prefs,
"Halve group size", PSPtr, CallerGroups, BunchNum * 2,
BunchSize / 2, ContourExcl)]
;
BunchSizeControlPairsHalf = []
),
(
DoubleBunchSize = BunchSize * 2,
DoubleBunchSize \= 10,
DoubleBunchSize \= 20,
DoubleBunchSize \= 50,
DoubleBunchSize \= 100
->
BunchSizeControlPairsDouble = [(BunchSize * 2) -
make_proc_callers_link(Prefs,
"Double group size", PSPtr, CallerGroups, BunchNum / 2,
BunchSize * 2, ContourExcl)]
;
BunchSizeControlPairsDouble = []
),
(
TotalNumRows > 10,
BunchSize \= 10
->
BunchSizeControlPairs10 = [10 -
make_proc_callers_link(Prefs,
"Set group size to 10", PSPtr, CallerGroups,
(BunchNum * BunchSize) / 10, 10, ContourExcl)]
;
BunchSizeControlPairs10 = []
),
(
TotalNumRows > 20,
BunchSize \= 20
->
BunchSizeControlPairs20 = [20 -
make_proc_callers_link(Prefs,
"Set group size to 20", PSPtr, CallerGroups,
(BunchNum * BunchSize) / 20, 20, ContourExcl)]
;
BunchSizeControlPairs20 = []
),
(
TotalNumRows > 50,
BunchSize \= 50
->
BunchSizeControlPairs50 = [50 -
make_proc_callers_link(Prefs,
"Set group size to 50", PSPtr, CallerGroups,
(BunchNum * BunchSize) / 50, 50, ContourExcl)]
;
BunchSizeControlPairs50 = []
),
(
TotalNumRows > 100,
BunchSize \= 100
->
BunchSizeControlPairs100 = [100 -
make_proc_callers_link(Prefs,
"Set group size to 100", PSPtr, CallerGroups,
(BunchNum * BunchSize) / 100, 100, ContourExcl)]
;
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,
(
list.drop(NumRowsToDelete, RowDatas, RemainingRowDatasPrime),
RemainingRowDatasPrime = [_ | _]
->
DisplayedBunchNum = BunchNum,
% We start counting rows at 1, not 0.
FirstRowNum = NumRowsToDelete + 1,
RemainingRowDatas = RemainingRowDatasPrime,
NumRemainingRows = TotalNumRows - NumRowsToDelete
;
DisplayedBunchNum = 1,
FirstRowNum = 1,
RemainingRowDatas = RowDatas,
NumRemainingRows = TotalNumRows
),
( NumRemainingRows > BunchSize ->
list.take_upto(BunchSize, RemainingRowDatas, DisplayRowDatas)
;
DisplayRowDatas = RemainingRowDatas
),
LastRowNum = FirstRowNum - 1 + list.length(DisplayRowDatas),
( TotalNumRows > 0 ->
FirstBunchNum = 1,
LastBunchNum = (TotalNumRows + BunchSize - 1) / BunchSize,
MaybeFirstAndLastBunchNum = yes({FirstBunchNum, LastBunchNum})
;
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, 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),
( TypeSpecDesc = "" ->
CalleeDesc = CalleeDesc0
;
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)),
("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),
Header = table_header(
map((func({Text, Class}) = table_header_group(
table_header_group_single(td_s(Text)),
Class, column_do_not_colour)
), [{"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}])),
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,
( Criteria = Criteria0 ->
% 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)
;
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($module, $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($module, $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($module, $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, 0)),
SelfMemPerCallCell = table_cell(td_m(SelfMemPerCall, Units, 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($module, $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, 0)),
TotalMemPerCallCell = table_cell(td_m(TotalMemPerCall, Units, 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),
( Cmd = Cmd0 ->
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]
;
(
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.
(
Cmd = deep_cmd_top_procs(_, CmdCostKind, CmdInclDesc,
CmdScope)
->
Prefs = Prefs0 ^ pref_criteria :=
by_cost(CmdCostKind, CmdInclDesc, CmdScope)
;
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),
( Cmd = Cmd0 ->
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]
;
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),
( Prefs = Prefs0 ->
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]
;
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,
( Cmd = deep_cmd_proc_callers(_, _, _, _, _) ->
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]
)
;
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 :-
solutions((pred(Control::out) is nondet :-
(
Cmd = deep_cmd_proc(Proc),
Label = "Procedure",
Developer = no
;
Cmd = deep_cmd_static_procrep_coverage(Proc),
Label = "Coverage annotated procedure representation",
% XXX: Should this option be considered a developer-only
% option?
Developer = no
;
Cmd = deep_cmd_dump_proc_static(Proc),
Label = "Unprocessed proc static data",
Developer = yes
),
Cmd \= NotCmd,
make_control(yes(Prefs), Cmd, Label, Developer, Control)
), 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(Perfs, CliquePtr, NotCmd) = ControlsItem :-
MakeControls =
( pred(Control::out) is nondet :-
(
Cmd = deep_cmd_clique(CliquePtr),
Label = "Clique",
Developer = no
;
Cmd = deep_cmd_clique_recursive_costs(CliquePtr),
Label = "Clique's recursion information",
Developer = yes
;
Cmd = deep_cmd_dump_clique(CliquePtr),
Label = "Unprocessed clique data",
Developer = yes
),
Cmd \= NotCmd,
make_control(yes(Perfs), Cmd, Label, Developer, Control)
),
solutions(MakeControls, CliqueReportControls),
ControlsItem = display_list(list_class_vertical_no_bullets,
yes("Related clique reports:"), CliqueReportControls).
%-----------------------------------------------------------------------------%
%
% 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),
( CliquePtr = SelfCliquePtr ->
Cell = table_cell(td_s(EntryProcName))
;
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 don't have 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,
(
MaybeTotalA = yes(TotalA),
MaybeTotalB = yes(TotalB)
->
(
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)
)
;
unexpected($module, $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,
(
MaybeTotalA = yes(TotalA),
MaybeTotalB = yes(TotalB)
->
(
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)
)
;
unexpected($module, $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,
(
MaybeTotalA = yes(TotalA),
MaybeTotalB = yes(TotalB)
->
(
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)
)
;
unexpected($module, $pred, "self_and_desc")
)
).
:- 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,
(
MaybeTotalA = yes(TotalA),
MaybeTotalB = yes(TotalB)
->
(
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)
)
;
unexpected($module, $pred, "self_and_desc")
)
).
%-----------------------------------------------------------------------------%
:- 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,
( ModuleName = CurModuleName ->
Name = ProcDesc ^ pdesc_uq_refined_name
;
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,
( ModuleName = CurModuleName ->
Name = CallSiteDesc ^ csdesc_caller_uq_refined_name
;
Name = CallSiteDesc ^ csdesc_caller_q_refined_name
)
)
;
ModuleQual = module_qual_always,
Name = CallSiteDesc ^ csdesc_caller_q_refined_name
).
%-----------------------------------------------------------------------------%
:- end_module display_report.
%-----------------------------------------------------------------------------%