Files
mercury/deep_profiler/query.m
Zoltan Somogyi 672f77c4ec Add a new compiler option. --inform-ite-instead-of-switch.
Estimated hours taken: 20
Branches: main

Add a new compiler option. --inform-ite-instead-of-switch. If this is enabled,
the compiler will generate informational messages about if-then-elses that
it thinks should be converted to switches for the sake of program reliability.

Act on the output generated by this option.

compiler/simplify.m:
	Implement the new option.

	Fix an old bug that could cause us to generate warnings about code
	that was OK in one duplicated copy but not in another (where a switch
	arm's code is duplicated due to the case being selected for more than
	one cons_id).

compiler/options.m:
	Add the new option.

	Add a way to test for the bug fix in simplify.

doc/user_guide.texi:
	Document the new option.

NEWS:
	Mention the new option.

library/*.m:
mdbcomp/*.m:
browser/*.m:
compiler/*.m:
deep_profiler/*.m:
	Convert if-then-elses to switches at most of the sites suggested by the
	new option. At the remaining sites, switching to switches would have
	nontrivial downsides. This typically happens with the switched-on type
	has many functors, and we treat one or two specially (e.g. cons/2 in
	the cons_id type).

	Perform misc cleanups in the vicinity of the if-then-else to switch
	conversions.

	In a few cases, improve the error messages generated.

compiler/accumulator.m:
compiler/hlds_goal.m:
	(Rename and) move insts for particular kinds of goal from
	accumulator.m to hlds_goal.m, to allow them to be used in other
	modules. Using these insts allowed us to eliminate some if-then-elses
	entirely.

compiler/exprn_aux.m:
	Instead of fixing some if-then-elses, delete the predicates containing
	them, since they aren't used, and (as pointed out by the new option)
	would need considerable other fixing if they were ever needed again.

compiler/lp_rational.m:
	Add prefixes to the names of the function symbols on some types,
	since without those prefixes, it was hard to figure out what type
	the switch corresponding to an old if-then-else was switching on.

tests/invalid/reserve_tag.err_exp:
	Expect a new, improved error message.
2007-11-23 07:36:01 +00:00

2008 lines
77 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2001-2003, 2005-2007 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: query.m.
% Authors: conway, zs.
%
% This module contains the top level predicates for servicing individual
% queries.
%-----------------------------------------------------------------------------%
:- module query.
:- interface.
:- import_module profile.
:- import_module interface.
:- import_module io.
%-----------------------------------------------------------------------------%
:- pred try_exec(cmd::in, preferences::in, deep::in, string::out,
io::di, io::uo) is cc_multi.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module exclude.
:- import_module html_format.
:- import_module measurements.
:- import_module top_procs.
:- import_module array.
:- import_module assoc_list.
:- import_module bool.
:- import_module exception.
:- import_module float.
:- import_module int.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module pair.
:- import_module require.
:- import_module string.
:- import_module univ.
:- import_module unit.
%-----------------------------------------------------------------------------%
try_exec(Cmd, Pref, Deep, HTML, !IO) :-
try_io(exec(Cmd, Pref, Deep), Result, !IO),
(
Result = succeeded(HTML)
;
Result = exception(Exception),
( univ_to_type(Exception, MsgPrime) ->
Msg = MsgPrime
; univ_to_type(Exception, software_error(MsgPrime)) ->
Msg = MsgPrime
;
Msg = "unknown exception"
),
HTML = string.format("<H3>AN EXCEPTION HAS OCCURRED: %s</H3>\n",
[s(Msg)])
).
:- pred exec(cmd::in, preferences::in, deep::in, string::out,
io::di, io::uo) is det.
exec(deep_cmd_restart, _Pref, _Deep, _HTML, !IO) :-
% Our caller is supposed to filter out restart commands.
error("exec: found restart command").
exec(deep_cmd_quit, _Pref, Deep, HTML, !IO) :-
HTML = string.format(
"<H3>Shutting down deep profile server for %s.</H3>\n",
[s(Deep ^ data_file_name)]).
exec(deep_cmd_timeout(TimeOut), _Pref, _Deep, HTML, !IO) :-
HTML = string.format("<H3>Timeout set to %d minutes</H3>\n", [i(TimeOut)]).
exec(Cmd, Pref, Deep, HTML, !IO) :-
Cmd = deep_cmd_menu,
HTML = generate_menu_page(Cmd, Pref, Deep).
exec(Cmd, Pref, Deep, HTML, !IO) :-
Cmd = deep_cmd_root(MaybePercent),
deep_lookup_clique_index(Deep, Deep ^ root, RootCliquePtr),
RootCliquePtr = clique_ptr(RootCliqueNum),
(
MaybePercent = yes(Percent),
HTML = chase_the_action(Cmd, RootCliqueNum, Pref, Deep, Percent)
;
MaybePercent = no,
generate_clique_page(Cmd, RootCliqueNum, Pref, Deep, HTML, 100, _)
).
exec(Cmd, Pref, Deep, HTML, !IO) :-
Cmd = deep_cmd_clique(CliqueNum),
CliquePtr = clique_ptr(CliqueNum),
( valid_clique_ptr(Deep, CliquePtr) ->
generate_clique_page(Cmd, CliqueNum, Pref, Deep, HTML, 100, _)
;
HTML =
page_banner(Cmd, Pref) ++
"There is no clique with that number.\n" ++
page_footer(Cmd, Pref, Deep)
).
exec(Cmd, Pref, Deep, HTML, !IO) :-
Cmd = deep_cmd_proc(PSI),
PSPtr = proc_static_ptr(PSI),
( valid_proc_static_ptr(Deep, PSPtr) ->
HTML = generate_proc_page(Cmd, PSPtr, Pref, Deep)
;
HTML =
page_banner(Cmd, Pref) ++
"There is no procedure with that number.\n" ++
page_footer(Cmd, Pref, Deep)
).
exec(Cmd, Pref, Deep, HTML, !IO) :-
Cmd = deep_cmd_proc_callers(PSI, CallerGroups, BunchNum),
PSPtr = proc_static_ptr(PSI),
( valid_proc_static_ptr(Deep, PSPtr) ->
generate_proc_callers_page(Cmd, PSPtr, CallerGroups, BunchNum,
Pref, Deep, HTML, !IO)
;
HTML =
page_banner(Cmd, Pref) ++
"There is no procedure with that number.\n" ++
page_footer(Cmd, Pref, Deep)
).
exec(Cmd, Pref, Deep, HTML, !IO) :-
Cmd = deep_cmd_modules,
HTML = generate_modules_page(Cmd, Pref, Deep).
exec(Cmd, Pref, Deep, HTML, !IO) :-
Cmd = deep_cmd_module(ModuleName),
( map.search(Deep ^ module_data, ModuleName, ModuleData) ->
HTML = generate_module_page(Cmd, ModuleName, ModuleData, Pref, Deep)
;
HTML =
page_banner(Cmd, Pref) ++
"There is no procedure with that number.\n" ++
page_footer(Cmd, Pref, Deep)
).
exec(Cmd, Pref, Deep, HTML, !IO) :-
Cmd = deep_cmd_top_procs(Limit, CostKind, InclDesc, Scope),
HTML = generate_top_procs_page(Cmd, Limit, CostKind, InclDesc, Scope,
Pref, Deep).
exec(deep_cmd_proc_static(PSI), _Pref, Deep, HTML, !IO) :-
HTML = generate_proc_static_debug_page(PSI, Deep).
exec(deep_cmd_proc_dynamic(PDI), _Pref, Deep, HTML, !IO) :-
HTML = generate_proc_dynamic_debug_page(PDI, Deep).
exec(deep_cmd_call_site_static(CSSI), _Pref, Deep, HTML, !IO) :-
HTML = generate_call_site_static_debug_page(CSSI, Deep).
exec(deep_cmd_call_site_dynamic(CSDI), _Pref, Deep, HTML, !IO) :-
HTML = generate_call_site_dynamic_debug_page(CSDI, Deep).
exec(deep_cmd_raw_clique(CI), _Pref, Deep, HTML, !IO) :-
HTML = generate_clique_debug_page(CI, Deep).
%-----------------------------------------------------------------------------%
:- func generate_proc_static_debug_page(int, deep) = string.
generate_proc_static_debug_page(PSI, Deep) = HTML :-
PSPtr = proc_static_ptr(PSI),
( valid_proc_static_ptr(Deep, PSPtr) ->
deep_lookup_proc_statics(Deep, PSPtr, PS),
Refined = PS ^ ps_refined_id,
Raw = PS ^ ps_raw_id,
FileName = PS ^ ps_file_name,
HTML =
"<HTML>\n" ++
Refined ++ " " ++ Raw ++ " " ++ FileName ++ " " ++
string.int_to_string(array.max(PS ^ ps_sites)) ++
"</HTML>\n"
;
HTML =
"<HTML>\n" ++
"Invalid proc_static_ptr" ++
"</HTML>\n"
).
:- func generate_proc_dynamic_debug_page(int, deep) = string.
generate_proc_dynamic_debug_page(PDI, Deep) = HTML :-
PDPtr = proc_dynamic_ptr(PDI),
( valid_proc_dynamic_ptr(Deep, PDPtr) ->
deep_lookup_proc_dynamics(Deep, PDPtr, PD),
PSPtr = PD ^ pd_proc_static,
PSPtr = proc_static_ptr(PSI),
HTML =
"<HTML>\n" ++
string.format("proc_static %d, ", [i(PSI)]) ++
array_slots_to_html(PD ^ pd_sites) ++
"</HTML>\n"
;
HTML =
"<HTML>\n" ++
"Invalid proc_dynamic_ptr" ++
"</HTML>\n"
).
:- func generate_call_site_static_debug_page(int, deep) = string.
generate_call_site_static_debug_page(CSSI, Deep) = HTML :-
CSSPtr = call_site_static_ptr(CSSI),
( valid_call_site_static_ptr(Deep, CSSPtr) ->
deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
ContainerPtr = CSS ^ css_container,
ContainerPtr = proc_static_ptr(Container),
HTML =
"<HTML>\n" ++
string.int_to_string(Container) ++ " " ++
string.int_to_string(CSS ^ css_slot_num) ++ " " ++
string.int_to_string(CSS ^ css_line_num) ++ " " ++
kind_and_callee_to_string(CSS ^ css_kind) ++ " " ++
CSS ^ css_goal_path ++
"</HTML>\n"
;
HTML =
"<HTML>\n" ++
"Invalid call_site_static_ptr" ++
"</HTML>\n"
).
:- func generate_call_site_dynamic_debug_page(int, deep) = string.
generate_call_site_dynamic_debug_page(CSDI, Deep) = HTML :-
CSDPtr = call_site_dynamic_ptr(CSDI),
( valid_call_site_dynamic_ptr(Deep, CSDPtr) ->
deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
CSD ^ csd_caller = proc_dynamic_ptr(CallerPDI),
CSD ^ csd_callee = proc_dynamic_ptr(CalleePDI),
HTML =
"<HTML>\n" ++
string.int_to_string(CallerPDI) ++ " -> " ++
string.int_to_string(CalleePDI) ++ ": " ++
own_to_string(CSD ^ csd_own_prof) ++
"</HTML>\n"
;
HTML =
"<HTML>\n" ++
"Invalid call_site_dynamic_ptr" ++
"</HTML>\n"
).
:- func generate_clique_debug_page(int, deep) = string.
generate_clique_debug_page(CI, Deep) = HTML :-
CliquePtr = clique_ptr(CI),
( valid_clique_ptr(Deep, CliquePtr) ->
deep_lookup_clique_parents(Deep, CliquePtr, Parent),
Parent = call_site_dynamic_ptr(ParentPDI),
ParentStr = string.format("%d ->", [i(ParentPDI)]),
deep_lookup_clique_members(Deep, CliquePtr, Members),
HTML =
"<HTML>\n" ++
ParentStr ++
list.foldl(append_pdi_to_string, Members, "") ++
"</HTML>\n"
;
HTML =
"<HTML>\n" ++
"Invalid call_site_dynamic_ptr" ++
"</HTML>\n"
).
%-----------------------------------------------------------------------------%
:- func array_slots_to_html(array(call_site_array_slot)) = string.
array_slots_to_html(SlotArray) = HTML :-
array.to_list(SlotArray, SlotList),
list.foldl(append_slot_to_string, SlotList, "multi", HTML).
:- pred append_slot_to_string(call_site_array_slot::in,
string::in, string::out) is det.
append_slot_to_string(Slot, Str0, Str) :-
Str = Str0 ++ " " ++ array_slot_to_html(Slot).
:- func array_slot_to_html(call_site_array_slot) = string.
array_slot_to_html(slot_normal(CSDPtr)) = HTML :-
CSDPtr = call_site_dynamic_ptr(CSDI),
HTML = "normal " ++ string.int_to_string(CSDI).
array_slot_to_html(slot_multi(_, CSDPtrArray)) = HTML :-
array.to_list(CSDPtrArray, CSDPtrs),
list.foldl(append_csdi_to_string, CSDPtrs, "", CSDI_HTML),
list.length(CSDPtrs, CSDPtrCount),
HTML = string.format("multi(%d): [", [i(CSDPtrCount)]) ++ CSDI_HTML ++ "]".
:- pred append_csdi_to_string(call_site_dynamic_ptr::in,
string::in, string::out) is det.
append_csdi_to_string(call_site_dynamic_ptr(CSDI), Str0, Str) :-
Str = Str0 ++ " " ++ string.int_to_string(CSDI).
:- func append_pdi_to_string(proc_dynamic_ptr, string) = string.
append_pdi_to_string(proc_dynamic_ptr(PDI), Str0) =
Str0 ++ " " ++ string.int_to_string(PDI).
:- func kind_and_callee_to_string(call_site_kind_and_callee) = string.
kind_and_callee_to_string(normal_call_and_callee(proc_static_ptr(PSI),
TypeSpec)) =
"normal " ++ string.int_to_string(PSI) ++ " " ++ TypeSpec.
kind_and_callee_to_string(special_call_and_no_callee) = "special_call".
kind_and_callee_to_string(higher_order_call_and_no_callee) =
"higher_order_call".
kind_and_callee_to_string(method_call_and_no_callee) = "method_call".
kind_and_callee_to_string(callback_and_no_callee) = "callback".
%-----------------------------------------------------------------------------%
:- func call_site_kind_and_callee_to_html(call_site_kind_and_callee) = string.
call_site_kind_and_callee_to_html(normal_call_and_callee(_, _)) =
"normal_call".
call_site_kind_and_callee_to_html(special_call_and_no_callee) =
"special_call".
call_site_kind_and_callee_to_html(higher_order_call_and_no_callee) =
"higher_order_call".
call_site_kind_and_callee_to_html(method_call_and_no_callee) =
"method_call".
call_site_kind_and_callee_to_html(callback_and_no_callee) =
"callback".
%-----------------------------------------------------------------------------%
:- func generate_menu_page(cmd, preferences, deep) = string.
generate_menu_page(Cmd, Pref, Deep) = HTML :-
ShouldDisplayTimes = should_display_times(Deep),
HTML =
page_banner(Cmd, Pref) ++
"<p>\n" ++
menu_text ++
"<ul>\n" ++
"<li>\n" ++
menu_item(Deep, Pref, deep_cmd_root(no),
"Exploring the call graph, starting at the root.") ++
"<li>\n" ++
menu_item(Deep, Pref, deep_cmd_root(yes(90)),
"Exploring the call graph, starting at the action.") ++
"<li>\n" ++
menu_item(Deep, Pref, deep_cmd_modules,
"Exploring the program module by module.") ++
( ShouldDisplayTimes = yes ->
"<li>\n" ++
menu_item(Deep, Pref,
deep_cmd_top_procs(rank_range(1, 100), cost_time,
self, overall),
"Top 100 most expensive procedures: time, self.") ++
"<li>\n" ++
menu_item(Deep, Pref,
deep_cmd_top_procs(rank_range(1, 100), cost_time,
self_and_desc, overall),
"Top 100 most expensive procedures: time, self+descendants.")
;
""
) ++
"<li>\n" ++
menu_item(Deep, Pref,
deep_cmd_top_procs(rank_range(1, 100), cost_callseqs,
self, overall),
"Top 100 most expensive procedures: callseqs, self.") ++
"<li>\n" ++
menu_item(Deep, Pref,
deep_cmd_top_procs(rank_range(1, 100), cost_callseqs,
self_and_desc, overall),
"Top 100 most expensive procedures: callseqs, self+descendants.") ++
"<li>\n" ++
menu_item(Deep, Pref,
deep_cmd_top_procs(rank_range(1, 100), cost_words, self, overall),
"Top 100 most expensive procedures: words, self.") ++
"<li>\n" ++
menu_item(Deep, Pref,
deep_cmd_top_procs(rank_range(1, 100), cost_words, self_and_desc,
overall),
"Top 100 most expensive procedures: words, self+descendants.")
++
( ShouldDisplayTimes = yes ->
"<li>\n" ++
menu_item(Deep, Pref,
deep_cmd_top_procs(threshold_percent(0.1), cost_time, self,
overall),
"Procedures above 0.1% threshold: time, self.") ++
"<li>\n" ++
menu_item(Deep, Pref,
deep_cmd_top_procs(threshold_percent(1.0), cost_time,
self_and_desc, overall),
"Procedures above 1% threshold: time, self+descendants.") ++
"<li>\n" ++
menu_item(Deep, Pref,
deep_cmd_top_procs(threshold_value(100.0), cost_time,
self_and_desc, overall),
"Procedures above 1 second threshold: " ++
"time, self+descendants.")
;
""
) ++
"<li>\n" ++
menu_item(Deep, Pref,
deep_cmd_top_procs(threshold_percent(0.1), cost_callseqs, self,
overall),
"Procedures above 0.1% threshold: callseqs, self.") ++
"<li>\n" ++
menu_item(Deep, Pref,
deep_cmd_top_procs(threshold_percent(1.0), cost_callseqs,
self_and_desc, overall),
"Procedures above 1% threshold: callseqs, self+descendants.")
++
"<li>\n" ++
menu_item(Deep, Pref,
deep_cmd_top_procs(threshold_value(1000000.0), cost_callseqs,
self_and_desc, overall),
"Procedures above 1,000,000 callseqs threshold: callseqs, " ++
"self+descendants.")
++
"<li>\n" ++
menu_item(Deep, Pref,
deep_cmd_top_procs(threshold_percent(0.1), cost_words, self,
overall),
"Procedures above 0.1% threshold: words, self.") ++
"<li>\n" ++
menu_item(Deep, Pref,
deep_cmd_top_procs(threshold_percent(1.0), cost_words,
self_and_desc, overall),
"Procedures above 1% threshold: words, self+descendants.")
++
"<li>\n" ++
% 2M words is chosen arbitrary because it is 8MB on ia32
menu_item(Deep, Pref,
deep_cmd_top_procs(threshold_value(float(1024 * 1024 * 2)),
cost_words, self_and_desc, overall),
"Procedures above 2M words threshold: words, self+descendants.")
++
"</ul>\n" ++
"<p>\n" ++
present_stats(Deep) ++
page_footer(Cmd, Pref, Deep).
:- func menu_text = string.
menu_text =
"You can start exploring the deep profile at the following points.\n".
:- func menu_item(deep, preferences, cmd, string) = string.
menu_item(Deep, Pref, Cmd, Text) =
string.format("<A HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, Cmd)), s(Text)]).
:- func present_stats(deep) = string.
present_stats(Deep) = HTML :-
Stats = Deep ^ profile_stats,
lookup_ticks_per_sec(Stats, TicksPerSec, Assumed),
(
Assumed = yes,
AssumedStr = " (assumed)"
;
Assumed = no,
AssumedStr = ""
),
HTML =
"<TABLE>\n" ++
"<TR><TD ALIGN=left>Quanta per second:</TD>\n" ++
string.format("<TD ALIGN=right>%d%s</TD></TR>\n",
[i(TicksPerSec), s(AssumedStr)]) ++
"<TR><TD ALIGN=left>Quanta in user code:</TD>\n" ++
string.format("<TD ALIGN=right>%d</TD></TR>\n",
[i(Stats ^ user_quanta)]) ++
"<TR><TD ALIGN=left>Quanta in instrumentation:</TD>\n" ++
string.format("<TD ALIGN=right>%d</TD></TR>\n",
[i(Stats ^ instrument_quanta)]) ++
"<TR><TD ALIGN=left>Call sequence numbers:</TD>\n" ++
string.format("<TD ALIGN=right>%d</TD></TR>\n",
[i(Stats ^ num_callseqs)]) ++
"<TR><TD ALIGN=left>CallSiteDynamic structures:</TD>\n" ++
string.format("<TD ALIGN=right>%d</TD></TR>\n",
[i(Stats ^ max_csd)]) ++
"<TR><TD ALIGN=left>ProcDynamic structures:</TD>\n" ++
string.format("<TD ALIGN=right>%d</TD></TR>\n",
[i(Stats ^ max_pd)]) ++
"<TR><TD ALIGN=left>CallSiteStatic structures:</TD>\n" ++
string.format("<TD ALIGN=right>%d</TD></TR>\n",
[i(Stats ^ max_css)]) ++
"<TR><TD ALIGN=left>ProcStatic structures:</TD>\n" ++
string.format("<TD ALIGN=right>%d</TD></TR>\n",
[i(Stats ^ max_ps)]) ++
"<TR><TD ALIGN=left>Cliques:</TD>\n" ++
string.format("<TD ALIGN=right>%d</TD></TR>\n",
[i(array.max(Deep ^ clique_members))]) ++
"</TABLE>\n".
%-----------------------------------------------------------------------------%
:- func chase_the_action(cmd, int, preferences, deep, int) = string.
chase_the_action(Cmd, CliqueNum, Pref, Deep, Percent) = HTML :-
generate_clique_page(Cmd, CliqueNum, Pref, Deep, HTML0,
Percent, ActionPtrs),
( ActionPtrs = [clique_ptr(ActionCliqueNum)] ->
HTML = chase_the_action(Cmd, ActionCliqueNum, Pref, Deep, Percent)
;
HTML = HTML0
).
%-----------------------------------------------------------------------------%
:- pred generate_clique_page(cmd::in, int::in, preferences::in, deep::in,
string::out, int::in, list(clique_ptr)::out) is det.
generate_clique_page(Cmd, CliqueNum, Pref, Deep, HTML, Percent, ActionPtrs) :-
clique_to_html(Pref, Deep, clique_ptr(CliqueNum), CliqueHTML, Percent,
ActionPtrs),
HTML =
page_banner(Cmd, Pref) ++
string.format("<H3>Clique %d:</H3>\n", [i(CliqueNum)]) ++
table_start(Pref) ++
fields_header(Pref, source_proc, totals_meaningful,
wrap_clique_links(clique_ptr(CliqueNum), Pref, Deep)) ++
CliqueHTML ++
table_end(Pref) ++
page_footer(Cmd, Pref, Deep).
:- func generate_proc_page(cmd, proc_static_ptr, preferences, deep)
= string.
generate_proc_page(Cmd, PSPtr, Pref, Deep) =
page_banner(Cmd, Pref) ++
string.format("<H3>Summary of procedure %s:</H3>\n",
[s(escape_html_string(proc_static_name(Deep, PSPtr)))]) ++
table_start(Pref) ++
fields_header(Pref, source_proc, totals_meaningful,
wrap_proc_links(PSPtr, Pref, Deep)) ++
proc_summary_to_html(Pref, Deep, PSPtr) ++
table_end(Pref) ++
"<p>\n" ++
proc_summary_toggles_to_html(Pref, Deep, PSPtr) ++
page_footer(Cmd, Pref, Deep).
:- pred generate_proc_callers_page(cmd::in, proc_static_ptr::in,
caller_groups::in, int::in, preferences::in, deep::in, string::out,
io::di, io::uo) is det.
generate_proc_callers_page(Cmd, PSPtr, CallerGroups, BunchNum, Pref, Deep,
HTML, IO0, IO) :-
proc_callers_to_html(Pref, Deep, PSPtr, CallerGroups, BunchNum,
MaybePage, IO0, IO),
(
MaybePage = ok({IdFields, Heading, CallersHTML, Toggles}),
HTML =
page_banner(Cmd, Pref) ++
Heading ++
( CallersHTML = "" ->
""
;
table_start(Pref) ++
fields_header(Pref, IdFields, totals_meaningful,
wrap_proc_callers_links(PSPtr, CallerGroups, 1,
Pref, Deep)) ++
CallersHTML ++
table_end(Pref) ++
"<p>\n"
) ++
Toggles ++
page_footer(Cmd, Pref, Deep)
;
MaybePage = error(Msg),
HTML =
string.format("<H3>%s</H3>\n", [s(Msg)])
).
:- func generate_modules_page(cmd, preferences, deep) = string.
generate_modules_page(Cmd, Pref, Deep) =
page_banner(Cmd, Pref) ++
"<H3>The modules of the program:</H3>\n" ++
table_start(Pref) ++
fields_header(Pref, rank_module, totals_not_meaningful,
wrap_modules_links(Pref, Deep)) ++
modules_to_html(Pref, Deep) ++
table_end(Pref) ++
page_footer(Cmd, Pref, Deep).
:- func generate_module_page(cmd, string, module_data, preferences, deep)
= string.
generate_module_page(Cmd, ModuleName, ModuleData, Pref, Deep) = HTML :-
module_to_html(Pref, Deep, ModuleName, ModuleData,
IdFields, ModulesHTML),
HTML =
page_banner(Cmd, Pref) ++
string.format("<H3>The procedures of module %s:</H3>\n",
[s(ModuleName)]) ++
table_start(Pref) ++
fields_header(Pref, IdFields, totals_meaningful,
wrap_module_links(ModuleName, Pref, Deep)) ++
ModulesHTML ++
table_end(Pref) ++
page_footer(Cmd, Pref, Deep).
:- func generate_top_procs_page(cmd, display_limit,
cost_kind, include_descendants, measurement_scope,
preferences, deep) = string.
generate_top_procs_page(Cmd, Limit, CostKind, InclDesc0, Scope0, Pref, Deep)
= HTML :-
(
CostKind = cost_calls,
% Counting calls is incompatible both with self_and_desc
% and per_call.
InclDesc = self,
Scope = overall
;
( CostKind = cost_redos
; CostKind = cost_time
; CostKind = cost_callseqs
; CostKind = cost_allocs
; CostKind = cost_words
),
InclDesc = InclDesc0,
Scope = Scope0
),
MaybeTopPSIs = find_top_procs(CostKind, InclDesc, Scope, Limit, Deep),
(
MaybeTopPSIs = error(ErrorMessage),
HTML =
page_banner(Cmd, Pref) ++
ErrorMessage ++ "\n" ++
page_footer(Cmd, Pref, Deep)
;
MaybeTopPSIs = ok(TopPSIs),
ToggleLimitHTML = "",
ToggleCostHTML = toggle_cost_criteria_in_top_procs_cmd(
Pref, Deep, Limit, CostKind, InclDesc, Scope),
Desc = cost_criteria_to_description(CostKind, InclDesc, Scope),
Heading = string.format("<H3>Top procedures %s</H3>\n",
[s(Desc)]),
(
TopPSIs = [],
HTML =
page_banner(Cmd, Pref) ++
Heading ++ "<p>\n" ++
"No procedures match the specification.\n" ++
"<p>\n" ++
ToggleLimitHTML ++
ToggleCostHTML ++
page_footer(Cmd, Pref, Deep)
;
TopPSIs = [_ | _],
TopProcs = list.filter_map(
lookup_proc_total_to_html(Pref, Deep, no, ""),
list.map(wrap_proc_static_ptr, TopPSIs)),
RankedTopProcs = add_ranks(TopProcs),
SummaryHTMLs = list.map(
two_id_line_to_html(Pref, Deep, totals_meaningful),
RankedTopProcs),
HTML =
page_banner(Cmd, Pref) ++
Heading ++ "<p>\n" ++
table_start(Pref) ++
fields_header(Pref, rank_proc, totals_meaningful,
wrap_top_procs_links(Limit, Pref, Deep)) ++
string.append_list(SummaryHTMLs) ++
table_end(Pref) ++
"<p>\n" ++
ToggleLimitHTML ++
ToggleCostHTML ++
page_footer(Cmd, Pref, Deep)
)
).
%-----------------------------------------------------------------------------%
:- func modules_to_html(preferences, deep) = string.
modules_to_html(Pref, Deep) = HTML :-
map.to_assoc_list(Deep ^ module_data, ModulePairs0),
list.filter(not_mercury_runtime, ModulePairs0, ModulePairs),
ModuleLines = list.filter_map(module_summary_to_html(Pref, Deep),
ModulePairs),
SortedModuleLines = sort_line_groups(Pref ^ pref_criteria,
ModuleLines),
RankedModuleLines = add_ranks(SortedModuleLines),
ModuleHTMLs = list.map(
two_id_line_to_html(Pref, Deep, totals_not_meaningful),
RankedModuleLines),
HTML =
separator_row(Pref, rank_module, totals_not_meaningful) ++
string.append_list(ModuleHTMLs).
:- pred not_mercury_runtime(pair(string, module_data)::in) is semidet.
not_mercury_runtime(ModuleName - _) :-
ModuleName \= "Mercury runtime".
:- func module_summary_to_html(preferences, deep, pair(string, module_data))
= one_id_line is semidet.
module_summary_to_html(Pref, Deep, ModuleName - ModuleData) = LineGroup :-
ModuleData = module_data(Own, Desc, _),
not (
Pref ^ pref_inactive ^ inactive_modules = inactive_hide,
is_inactive(Own)
),
HTML = string.format("<TD><A HREF=""%s"">%s</A></TD>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, deep_cmd_module(ModuleName))),
s(ModuleName)]),
LineGroup = line_group(ModuleName, 0, ModuleName, Own, Desc, HTML, unit).
%-----------------------------------------------------------------------------%
:- pred module_to_html(preferences::in, deep::in, string::in, module_data::in,
id_fields::out, string::out) is det.
module_to_html(Pref, Deep, _ModuleName, ModuleData, IdHeaders, HTML) :-
ModuleData = module_data(_Own, _Desc, PSPtrs),
ProcLines = list.filter_map(lookup_proc_total_to_html(Pref, Deep, yes, ""),
PSPtrs),
Criteria = Pref ^ pref_criteria,
SortedProcLines = sort_line_groups(Criteria, ProcLines),
(
Criteria = by_cost(_, _, _),
IdHeaders = rank_proc,
RankedProcLines = add_ranks(SortedProcLines)
;
( Criteria = by_name
; Criteria = by_context
),
IdHeaders = source_proc,
RankedProcLines = list.map(add_self_context, SortedProcLines)
),
ProcHTMLs = list.map(two_id_line_to_html(Pref, Deep, totals_meaningful),
RankedProcLines),
HTML =
separator_row(Pref, IdHeaders, totals_meaningful) ++
string.append_list(ProcHTMLs).
%-----------------------------------------------------------------------------%
:- pred clique_to_html(preferences::in, deep::in, clique_ptr::in,
string::out, int::in, list(clique_ptr)::out) is det.
clique_to_html(Pref, Deep, CliquePtr, HTML, PerCent, ActionPtrs) :-
(
Pref ^ pref_anc = yes(AncestorLimit),
RespectLimit = yes
;
Pref ^ pref_anc = no,
AncestorLimit = 0, % the value doesn't matter
RespectLimit = no
),
clique_ancestors_to_html(Pref, Deep,
AncestorLimit, RespectLimit, CliquePtr, Ancestors, Cutoff),
deep_lookup_clique_members(Deep, CliquePtr, PDPtrs),
list.foldl(group_proc_dynamics_by_proc_static(Deep), PDPtrs,
map.init, PStoPDsMap),
map.to_assoc_list(PStoPDsMap, PStoPDsList0),
deep_lookup_clique_parents(Deep, CliquePtr, EntryCSDPtr),
( valid_call_site_dynamic_ptr(Deep, EntryCSDPtr) ->
deep_lookup_call_site_dynamics(Deep, EntryCSDPtr, EntryCSD),
EntryPDPtr = EntryCSD ^ csd_callee,
list.filter(proc_group_contains(EntryPDPtr), PStoPDsList0,
EntryGroup, RestGroup),
list.append(EntryGroup, RestGroup, PStoPDsList)
;
PStoPDsList = PStoPDsList0
),
list.map2(procs_in_clique_to_html(Pref, Deep, CliquePtr, PerCent),
PStoPDsList, PDsStrs, ActionPtrLists),
list.condense(ActionPtrLists, ActionPtrs),
string.append_list(PDsStrs, ProcGroups),
(
Cutoff = yes,
Heading = string.format("The %d closest ancestors:",
[i(AncestorLimit)])
;
Cutoff = no,
Heading = "Ancestors:"
),
HTML =
header_row(Heading, Pref, source_proc, totals_meaningful) ++
separator_row(Pref, source_proc, totals_meaningful) ++
Ancestors ++
separator_row(Pref, source_proc, totals_meaningful) ++
header_row("Procedures of the clique:", Pref, source_proc,
totals_meaningful) ++
separator_row(Pref, source_proc, totals_meaningful) ++
ProcGroups.
:- pred proc_group_contains(proc_dynamic_ptr::in,
pair(proc_static_ptr, list(proc_dynamic_ptr))::in) is semidet.
proc_group_contains(EntryPDPtr, _ - PDPtrs) :-
list.member(EntryPDPtr, PDPtrs).
:- pred clique_ancestors_to_html(preferences::in, deep::in, int::in, bool::in,
clique_ptr::in, string::out, bool::out) is det.
clique_ancestors_to_html(Pref, Deep, AncestorLimit, RespectLimit, CliquePtr,
HTML, Cutoff) :-
deep_lookup_clique_parents(Deep, CliquePtr, EntryCSDPtr),
( valid_call_site_dynamic_ptr(Deep, EntryCSDPtr) ->
deep_lookup_call_site_dynamics(Deep, EntryCSDPtr, EntryCSD),
EntryPDPtr = EntryCSD ^ csd_caller,
( EntryPDPtr = Deep ^ root ->
% We have reached the root.
HTML = "",
Cutoff = no
; RespectLimit = yes, AncestorLimit =< 0 ->
HTML = "",
Cutoff = yes
;
deep_lookup_clique_index(Deep, EntryPDPtr, EntryCliquePtr),
ThisLine = call_site_dynamic_to_html(Pref, Deep,
ancestor_display, yes(EntryCliquePtr), EntryCSDPtr),
ThisHTML = two_id_line_to_html(Pref, Deep, totals_meaningful,
ThisLine),
clique_ancestors_to_html(Pref, Deep, AncestorLimit - 1,
RespectLimit, EntryCliquePtr, AncestorHTML, Cutoff),
HTML = AncestorHTML ++ ThisHTML
)
;
% We have reached the parent of root.
HTML = "",
Cutoff = no
).
:- pred group_proc_dynamics_by_proc_static(deep::in, proc_dynamic_ptr::in,
map(proc_static_ptr, list(proc_dynamic_ptr))::in,
map(proc_static_ptr, list(proc_dynamic_ptr))::out) is det.
group_proc_dynamics_by_proc_static(Deep, PDPtr, PStoPDsMap0, PStoPDsMap) :-
require(valid_proc_dynamic_ptr(Deep, PDPtr),
"group_proc_dynamics_by_proc_static: invalid PDPtr"),
deep_lookup_proc_dynamics(Deep, PDPtr, PD),
PSPtr = PD ^ pd_proc_static,
( map.search(PStoPDsMap0, PSPtr, PSPDs0) ->
PSPDs = [PDPtr | PSPDs0],
map.det_update(PStoPDsMap0, PSPtr, PSPDs, PStoPDsMap)
;
map.det_insert(PStoPDsMap0, PSPtr, [PDPtr], PStoPDsMap)
).
:- pred procs_in_clique_to_html(preferences::in, deep::in, clique_ptr::in,
int::in, pair(proc_static_ptr, list(proc_dynamic_ptr))::in,
string::out, list(clique_ptr)::out) is det.
procs_in_clique_to_html(Pref, Deep, CliquePtr, Percent, PSPtr - PDPtrs,
HTML, ActionPtrs) :-
(
PDPtrs = [],
HTML = "",
ActionPtrs = []
;
PDPtrs = [PDPtr],
proc_in_clique_to_html(Pref, Deep, CliquePtr, Percent, PDPtr,
HTML, ActionPtrs)
;
PDPtrs = [_, _ | _],
list.map(deep_lookup_pd_own(Deep), PDPtrs, ProcOwns),
list.map(deep_lookup_pd_desc(Deep), PDPtrs, ProcDescs),
ProcOwn = sum_own_infos(ProcOwns),
ProcDesc = sum_inherit_infos(ProcDescs),
ProcTotal = proc_total_to_two_id_line(Pref, Deep, yes, "summary ",
PSPtr, ProcOwn, ProcDesc),
list.map2(proc_in_clique_to_html(Pref, Deep, CliquePtr, Percent),
PDPtrs, ComponentHTMLs, ActionPtrLists),
list.condense(ActionPtrLists, ActionPtrs),
string.append_list(ComponentHTMLs, ComponentHTML),
HTML =
separator_row(Pref, source_proc, totals_meaningful) ++
two_id_line_to_html(Pref, Deep, totals_meaningful, ProcTotal) ++
separator_row(Pref, source_proc, totals_meaningful) ++
ComponentHTML
).
:- pred proc_in_clique_to_html(preferences::in, deep::in, clique_ptr::in,
int::in, proc_dynamic_ptr::in, string::out, list(clique_ptr)::out) is det.
proc_in_clique_to_html(Pref, Deep, CliquePtr, Percent, PDPtr,
HTML, ActionPtrs) :-
( valid_proc_dynamic_ptr(Deep, PDPtr) ->
deep_lookup_pd_own(Deep, PDPtr, ProcOwn),
deep_lookup_pd_desc(Deep, PDPtr, ProcDesc),
deep_lookup_proc_dynamics(Deep, PDPtr, PD),
PSPtr = PD ^ pd_proc_static,
ProcTotal = proc_total_to_two_id_line(Pref, Deep, yes, "",
PSPtr, ProcOwn, ProcDesc),
child_call_sites(Deep ^ proc_dynamics, Deep ^ proc_statics,
PDPtr, GroupPairs),
ProcHTML =
separator_row(Pref, source_proc, totals_meaningful) ++
two_id_line_to_html(Pref, Deep, totals_meaningful, ProcTotal),
(
GroupPairs = [],
HTML = ProcHTML,
ActionPtrs = []
;
GroupPairs = [_ | _],
list.map2(call_site_clique_to_html(Pref, Deep, CliquePtr, Percent),
GroupPairs, CallSiteLists, ActionPtrLists),
list.condense(CallSiteLists, CallSites),
list.condense(ActionPtrLists, ActionPtrs),
SortedCallSites = sort_line_groups(Pref ^ pref_criteria,
CallSites),
BodyHTMLs = list.map(
two_id_line_group_to_html(Pref, Deep, totals_meaningful),
SortedCallSites),
HTML = ProcHTML ++
separator_row(Pref, source_proc, totals_meaningful) ++
string.append_list(BodyHTMLs)
)
;
HTML = "",
ActionPtrs = []
).
:- pred child_call_sites(proc_dynamics::in, proc_statics::in,
proc_dynamic_ptr::in,
assoc_list(call_site_static_ptr, call_site_array_slot)::out) is det.
child_call_sites(ProcDynamics, ProcStatics, PDPtr, PairedSlots) :-
lookup_proc_dynamics(ProcDynamics, PDPtr, PD),
PSPtr = PD ^ pd_proc_static,
CSDArray = PD ^ pd_sites,
lookup_proc_statics(ProcStatics, PSPtr, PS),
CSSArray = PS ^ ps_sites,
array.to_list(CSDArray, CSDSlots),
array.to_list(CSSArray, CSSSlots),
assoc_list.from_corresponding_lists(CSSSlots, CSDSlots, PairedSlots).
%-----------------------------------------------------------------------------%
% Fails if the procedure is inactive and the preferences say to
% hide inactive procedures.
%
:- func lookup_proc_total_to_html(preferences, deep, bool, string,
proc_static_ptr) = one_id_line is semidet.
lookup_proc_total_to_html(Pref, Deep, Bold, Prefix, PSPtr) = LineGroup :-
deep_lookup_ps_own(Deep, PSPtr, Own),
not (
Pref ^ pref_inactive ^ inactive_procs = inactive_hide,
is_inactive(Own)
),
deep_lookup_ps_desc(Deep, PSPtr, Desc),
LineGroup = proc_total_to_html(Pref, Deep, Bold, Prefix, PSPtr, Own, Desc).
:- func lookup_proc_total_to_two_id_line(preferences, deep, bool, string,
proc_static_ptr) = two_id_line.
lookup_proc_total_to_two_id_line(Pref, Deep, Bold, Prefix, PSPtr)
= LineGroup :-
deep_lookup_ps_own(Deep, PSPtr, Own),
deep_lookup_ps_desc(Deep, PSPtr, Desc),
LineGroup = proc_total_to_two_id_line(Pref, Deep, Bold, Prefix,
PSPtr, Own, Desc).
:- func proc_total_to_html(preferences, deep, bool, string,
proc_static_ptr, own_prof_info, inherit_prof_info) = one_id_line.
proc_total_to_html(Pref, Deep, Bold, Prefix, PSPtr, Own, Desc)
= LineGroup :-
proc_total_to_html_base(Pref, Deep, 1, Bold, Prefix, PSPtr,
FileName, LineNumber, ProcName, HTML),
LineGroup = line_group(FileName, LineNumber, ProcName, Own, Desc, HTML,
unit).
:- func proc_total_to_two_id_line(preferences, deep, bool, string,
proc_static_ptr, own_prof_info, inherit_prof_info) = two_id_line.
proc_total_to_two_id_line(Pref, Deep, Bold, Prefix, PSPtr, Own, Desc)
= LineGroup :-
proc_total_to_html_base(Pref, Deep, 2, Bold, Prefix, PSPtr,
FileName, LineNumber, ProcName, HTML),
LineGroup = line_group(FileName, LineNumber, ProcName, Own, Desc, HTML,
unit).
:- pred proc_total_to_html_base(preferences::in, deep::in,
int::in, bool::in, string::in, proc_static_ptr::in,
string::out, int::out, string::out, string::out) is det.
proc_total_to_html_base(Pref, Deep, Span, Bold, Prefix, PSPtr,
FileName, LineNumber, ProcName, HTML) :-
proc_static_to_line_group_info(Pref, Deep, PSPtr, FileName, LineNumber,
ProcName, WrappedProcName),
(
Bold = no,
BoldStart = "",
BoldEnd = ""
;
Bold = yes,
BoldStart = "<B>",
BoldEnd = "</B>"
),
HTML = string.format("<TD CLASS=id COLSPAN=%d>%s%s%s%s</TD>\n",
[i(Span), s(BoldStart), s(Prefix), s(WrappedProcName), s(BoldEnd)]).
%-----------------------------------------------------------------------------%
:- pred call_site_clique_to_html(preferences::in, deep::in,
clique_ptr::in, int::in,
pair(call_site_static_ptr, call_site_array_slot)::in,
list(two_id_line_group)::out, list(clique_ptr)::out) is det.
call_site_clique_to_html(Pref, Deep, CallerCliquePtr, Percent, Pair,
LineGroups, ActionPtrs) :-
Pair = CSSPtr - CallSiteArraySlot,
deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
Kind = CSS ^ css_kind,
( Kind = normal_call_and_callee(_CalleePSPtr, _) ->
(
CallSiteArraySlot = slot_normal(CSDPtr0),
CSDPtr = CSDPtr0
;
CallSiteArraySlot = slot_multi(_, _),
error("call_site_clique_to_html: normal_call error")
),
normal_call_site_clique_to_html(Pref, Deep, CallerCliquePtr,
CSDPtr, LineGroups, Percent, ActionPtrs)
;
(
CallSiteArraySlot = slot_multi(_, CSDPtrs0),
array.to_list(CSDPtrs0, CSDPtrs)
;
CallSiteArraySlot = slot_normal(_),
error("call_site_clique_to_html: non-normal_call error")
),
call_site_context(Deep, CSSPtr, FileName, LineNumber),
multi_call_site_clique_to_html(Pref, Deep, FileName, LineNumber,
Kind, CallerCliquePtr, CSDPtrs, LineGroups, Percent, ActionPtrs)
).
:- func maybe_extract_action_clique(deep, clique_ptr, int,
call_site_dynamic_ptr) = list(clique_ptr).
maybe_extract_action_clique(Deep, CallerCliquePtr, Percent, CSDPtr)
= ActionPtrs :-
( Percent > 100 ->
ActionPtrs = []
;
deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
deep_lookup_clique_index(Deep, CSD ^ csd_callee, CalleeCliquePtr),
( CalleeCliquePtr = CallerCliquePtr ->
ActionPtrs = []
;
deep_lookup_csd_desc(Deep, CSDPtr, CSDDesc),
CSDOwn = CSD ^ csd_own_prof,
CSDTotal = add_own_to_inherit(CSDOwn, CSDDesc),
RootTotal = root_total_info(Deep),
CSDCallSeqs = inherit_callseqs(CSDTotal),
RootCallSeqs = inherit_callseqs(RootTotal),
( CSDCallSeqs * 100 > RootCallSeqs * Percent ->
ActionPtrs = [CalleeCliquePtr]
;
ActionPtrs = []
)
)
).
:- pred normal_call_site_clique_to_html(preferences::in, deep::in,
clique_ptr::in, call_site_dynamic_ptr::in,
list(two_id_line_group)::out, int::in, list(clique_ptr)::out) is det.
normal_call_site_clique_to_html(Pref, Deep, CallerCliquePtr, CSDPtr,
LineGroups, Percent, ActionPtrs) :-
( valid_call_site_dynamic_ptr(Deep, CSDPtr) ->
LineGroup = call_site_dynamic_to_html(Pref, Deep,
downward_display, yes(CallerCliquePtr), CSDPtr),
LineGroups = [line_to_two_id_subline_group(LineGroup)],
ActionPtrs = maybe_extract_action_clique(Deep, CallerCliquePtr,
Percent, CSDPtr)
;
LineGroups = [],
ActionPtrs = []
).
:- pred multi_call_site_clique_to_html(preferences::in, deep::in,
string::in, int::in, call_site_kind_and_callee::in, clique_ptr::in,
list(call_site_dynamic_ptr)::in, list(two_id_line_group)::out,
int::in, list(clique_ptr)::out) is det.
multi_call_site_clique_to_html(Pref, Deep, FileName, LineNumber, Kind,
CallerCliquePtr, CSDPtrs, LineGroups, Percent, ActionPtrs) :-
ValidCSDPtrs = CSDPtrs,
RawCallSiteName = call_site_kind_and_callee_to_html(Kind),
CallSiteName = multi_call_site_add_suffix(Pref, RawCallSiteName,
ValidCSDPtrs),
SubLines = list.map(call_site_dynamic_to_html(Pref, Deep,
downward_summary_display, yes(CallerCliquePtr)),
ValidCSDPtrs),
ActionPtrLists = list.map(
maybe_extract_action_clique(Deep, CallerCliquePtr, Percent),
ValidCSDPtrs),
list.condense(ActionPtrLists, ActionPtrs),
sum_line_group_measurements(SubLines, Own, Desc),
SummaryHTML =
string.format("<TD CLASS=id>%s:%d</TD>\n",
[s(escape_break_html_string(FileName)), i(LineNumber)]) ++
% NOTE: we don't escape HTML special characters for
% 'CallSiteName' because it has already been done.
string.format("<TD CLASS=id>%s</TD>\n", [s(CallSiteName)]),
(
Pref ^ pref_summarize = summarize,
LineGroup = line_group(FileName, LineNumber, RawCallSiteName,
Own, Desc, SummaryHTML, sub_lines(two_id, []))
;
Pref ^ pref_summarize = dont_summarize,
LineGroup = line_group(FileName, LineNumber, RawCallSiteName,
Own, Desc, SummaryHTML, sub_lines(two_id, SubLines))
),
LineGroups = [LineGroup].
%-----------------------------------------------------------------------------%
:- func call_site_summary_to_html(preferences, deep,
call_site_static_ptr) = two_id_line_group.
call_site_summary_to_html(Pref, Deep, CSSPtr) = LineGroup :-
deep_lookup_call_site_calls(Deep, CSSPtr, CallSiteCallMap),
map.to_assoc_list(CallSiteCallMap, CallSiteCallList),
deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
Kind = CSS ^ css_kind,
CallerPSPtr = CSS ^ css_container,
call_site_context(Deep, CSSPtr, FileName, LineNumber),
( Kind = normal_call_and_callee(CalleePSPtr, _) ->
LineGroup0 = normal_call_site_summary_to_html(Pref, Deep,
FileName, LineNumber, CallerPSPtr, CalleePSPtr, CallSiteCallList)
;
LineGroup0 = multi_call_site_summary_to_html(Pref, Deep,
FileName, LineNumber, Kind, CallerPSPtr, CallSiteCallList)
),
CSSContext = string.format("%s:%d",
[s(escape_break_html_string(FileName)), i(LineNumber)]),
LineGroup = add_context(CSSContext, LineGroup0).
:- func normal_call_site_summary_to_html(preferences, deep, string, int,
proc_static_ptr, proc_static_ptr,
assoc_list(proc_static_ptr, list(call_site_dynamic_ptr))) =
one_two_id_line_group.
normal_call_site_summary_to_html(Pref, Deep, FileName, LineNumber,
CallerPSPtr, CalleePSPtr, CallSiteCallList) = LineGroup :-
deep_lookup_proc_statics(Deep, CalleePSPtr, CalleePS),
ProcName = CalleePS ^ ps_refined_id,
(
CallSiteCallList = [],
Own = zero_own_prof_info,
Desc = zero_inherit_prof_info,
SummaryHTML =
string.format("<TD CLASS=id>%s</TD>\n",
[s(proc_static_to_html_ref(Pref, Deep, CalleePSPtr))]),
LineGroup = line_group(FileName, LineNumber,
ProcName, Own, Desc, SummaryHTML,
sub_lines(two_id, []))
;
CallSiteCallList = [CallSiteCall],
CallSiteCall = CalleePSPtrFromCall - _,
require(unify(CalleePSPtr, CalleePSPtrFromCall),
"call_site_summary_to_html: callee mismatch"),
LineGroup0 = call_site_summary_group_to_html(Pref, Deep,
FileName, LineNumber, ProcName, CallerPSPtr, CallSiteCall),
LineGroup = line_to_two_id_subline_group(LineGroup0)
;
CallSiteCallList = [_, _ | _],
error("normal_call_site_summary_to_html: too many procedures")
).
:- func multi_call_site_summary_to_html(preferences, deep, string, int,
call_site_kind_and_callee, proc_static_ptr,
assoc_list(proc_static_ptr, list(call_site_dynamic_ptr))) =
one_two_id_line_group.
multi_call_site_summary_to_html(Pref, Deep, FileName, LineNumber, Kind,
CallerPSPtr, CallSiteCallList) = LineGroup :-
RawCallSiteName = call_site_kind_and_callee_to_html(Kind),
CallSiteName = multi_call_site_add_suffix(Pref, RawCallSiteName,
CallSiteCallList),
SubLines = list.map(call_site_summary_group_to_html(Pref, Deep,
FileName, LineNumber, RawCallSiteName, CallerPSPtr),
CallSiteCallList),
sum_line_group_measurements(SubLines, Own, Desc),
% NOTE: we don't escape HTML special characters for
% 'CallSiteName' because it has already been done.
SummaryHTML =
string.format("<TD CLASS=id>%s</TD>\n", [s(CallSiteName)]),
(
Pref ^ pref_summarize = summarize,
LineGroup = line_group(FileName, LineNumber, RawCallSiteName,
Own, Desc, SummaryHTML, sub_lines(two_id, []))
;
Pref ^ pref_summarize = dont_summarize,
ContextSubLines = list.map(add_context(""), SubLines),
LineGroup = line_group(FileName, LineNumber, RawCallSiteName,
Own, Desc, SummaryHTML, sub_lines(two_id, ContextSubLines))
).
:- func call_site_summary_group_to_html(preferences, deep,
string, int, string, proc_static_ptr,
pair(proc_static_ptr, list(call_site_dynamic_ptr))) = one_id_line.
call_site_summary_group_to_html(Pref, Deep, FileName, LineNumber, ProcName,
CallerPSPtr, PSPtr - CSDPtrs) = LineGroup :-
list.foldl2(accumulate_csd_prof_info(Deep, CallerPSPtr), CSDPtrs,
zero_own_prof_info, Own, zero_inherit_prof_info, Desc),
HTML =
string.format("<TD CLASS=id>%s</TD>\n",
[s(proc_static_to_html_ref(Pref, Deep, PSPtr))]),
LineGroup = line_group(FileName, LineNumber, ProcName,
Own, Desc, HTML, unit).
%-----------------------------------------------------------------------------%
:- func multi_call_site_add_suffix(preferences, string, list(T)) = string.
multi_call_site_add_suffix(Pref, RawCallSiteName, CallList) = CallSiteName :-
(
CallList = [],
CallSiteName = RawCallSiteName ++ " (no&nbsp;calls&nbsp;made)"
;
CallList = [_ | _],
Summarize = Pref ^ pref_summarize,
(
Summarize = summarize,
CallSiteName = RawCallSiteName ++ " (summary)"
;
Summarize = dont_summarize,
CallSiteName = RawCallSiteName
)
).
%-----------------------------------------------------------------------------%
:- pred process_call_site_dynamics_group(list(call_site_dynamic_ptr)::in,
deep::in, proc_static_ptr::in,
maybe(clique_ptr)::in, maybe(clique_ptr)::out,
own_prof_info::in, own_prof_info::out,
inherit_prof_info::in, inherit_prof_info::out) is det.
process_call_site_dynamics_group([], _, _,
MaybeCalleeCliquePtr, MaybeCalleeCliquePtr, Own, Own, Desc, Desc).
process_call_site_dynamics_group([CSDPtr | CSDPtrs], Deep, CalleePSPtr,
MaybeCalleeCliquePtr0, MaybeCalleeCliquePtr, Own0, Own, Desc0, Desc) :-
deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
PDPtr = CSD ^ csd_callee,
deep_lookup_proc_dynamics(Deep, PDPtr, PD),
PSPtr = PD ^ pd_proc_static,
require(unify(CalleePSPtr, PSPtr),
"process_call_site_dynamics_group: callee mismatch"),
deep_lookup_clique_index(Deep, PDPtr, CalleeCliquePtr),
(
MaybeCalleeCliquePtr0 = no,
MaybeCalleeCliquePtr1 = yes(CalleeCliquePtr)
;
MaybeCalleeCliquePtr0 = yes(PrevCalleeCliquePtr),
MaybeCalleeCliquePtr1 = MaybeCalleeCliquePtr0,
require(unify(PrevCalleeCliquePtr, CalleeCliquePtr),
"process_call_site_dynamics_group: clique mismatch")
),
deep_lookup_csd_own(Deep, CSDPtr, CSDOwn),
deep_lookup_csd_desc(Deep, CSDPtr, CSDDesc),
Own1 = add_own_to_own(Own0, CSDOwn),
Desc1 = add_inherit_to_inherit(Desc0, CSDDesc),
process_call_site_dynamics_group(CSDPtrs, Deep, CalleePSPtr,
MaybeCalleeCliquePtr1, MaybeCalleeCliquePtr, Own1, Own, Desc1, Desc).
:- pred accumulate_csd_prof_info(deep::in, proc_static_ptr::in,
call_site_dynamic_ptr::in,
own_prof_info::in, own_prof_info::out,
inherit_prof_info::in, inherit_prof_info::out) is det.
accumulate_csd_prof_info(Deep, CallerPSPtr, CSDPtr, Own0, Own, Desc0, Desc) :-
deep_lookup_csd_own(Deep, CSDPtr, CSDOwn),
deep_lookup_csd_desc(Deep, CSDPtr, CSDDesc),
add_own_to_own(Own0, CSDOwn) = Own,
add_inherit_to_inherit(Desc0, CSDDesc) = Desc1,
deep_lookup_csd_comp_table(Deep, CSDPtr, CompTableArray),
( map.search(CompTableArray, CallerPSPtr, InnerTotal) ->
Desc = subtract_inherit_from_inherit(InnerTotal, Desc1)
;
Desc = Desc1
).
:- func call_site_dynamic_to_html_with_caller(preferences, deep,
call_site_display, call_site_dynamic_ptr) = two_id_line.
call_site_dynamic_to_html_with_caller(Pref, Deep, Display, CSDPtr)
= LineGroup :-
deep_extract_csdptr_caller(Deep, CSDPtr, CallerPDPtr),
deep_lookup_clique_index(Deep, CallerPDPtr, CallerClique),
LineGroup = call_site_dynamic_to_html(Pref, Deep, Display,
yes(CallerClique), CSDPtr).
:- func call_site_dynamic_to_html(preferences, deep, call_site_display,
maybe(clique_ptr), call_site_dynamic_ptr) = two_id_line.
call_site_dynamic_to_html(Pref, Deep, CallSiteDisplay, MaybeCallerCliquePtr,
CSDPtr) = LineGroup :-
require(valid_call_site_dynamic_ptr(Deep, CSDPtr),
"call_site_dynamic_to_html: invalid call_site_dynamic_ptr"),
deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
CallerPDPtr = CSD ^ csd_caller,
CalleePDPtr = CSD ^ csd_callee,
CallSiteOwn = CSD ^ csd_own_prof,
deep_lookup_csd_desc(Deep, CSDPtr, CallSiteDesc),
deep_lookup_clique_index(Deep, CalleePDPtr, CalleeCliquePtr),
call_site_dynamic_context(Deep, CSDPtr, FileName, LineNumber),
Context = string.format("%s:%d", [s(escape_break_html_string(FileName)), i(LineNumber)]),
HTML = call_to_html(Pref, Deep, CallSiteDisplay, Context,
CallerPDPtr, CalleePDPtr, MaybeCallerCliquePtr, CalleeCliquePtr),
ProcName = escape_break_html_string(proc_dynamic_name(Deep, CalleePDPtr)),
LineGroup = line_group(FileName, LineNumber, ProcName,
CallSiteOwn, CallSiteDesc, HTML, unit).
%-----------------------------------------------------------------------------%
:- type call_site_display
---> call_site_display(
display_context :: call_site_context,
display_proc_name :: call_site_proc_name,
display_url :: url_with_proc_name,
display_wrap :: wrap_with_url
).
:- type call_site_context
---> call_context
; empty_context.
:- type call_site_proc_name
---> caller_proc_name
; callee_proc_name.
:- type url_with_proc_name
---> caller_clique
; callee_clique.
:- type wrap_with_url
---> wrap_url_always
; wrap_url_if_cross_clique(assume_cross_clique)
; wrap_url_never.
:- type assume_cross_clique
---> assume_cross_clique
; assume_within_clique.
:- func ancestor_display = call_site_display.
:- func upward_display = call_site_display.
:- func downward_display = call_site_display.
:- func downward_summary_display = call_site_display.
ancestor_display =
call_site_display(call_context, caller_proc_name, caller_clique,
wrap_url_always).
upward_display =
call_site_display(call_context, caller_proc_name, callee_clique,
wrap_url_always).
downward_display =
call_site_display(call_context, callee_proc_name, callee_clique,
wrap_url_if_cross_clique(assume_within_clique)).
downward_summary_display =
call_site_display(empty_context, callee_proc_name, callee_clique,
wrap_url_if_cross_clique(assume_within_clique)).
%-----------------------------------------------------------------------------%
:- func call_to_html(preferences, deep, call_site_display, string,
proc_dynamic_ptr, proc_dynamic_ptr, maybe(clique_ptr), clique_ptr)
= string.
call_to_html(Pref, Deep, CallSiteDisplay, CallContext,
CallerPDPtr, CalleePDPtr,
MaybeCallerCliquePtr, CalleeCliquePtr) = HTML :-
(
MaybeCallerCliquePtr = yes(CallerCliquePtr0),
CallerCliquePtr = CallerCliquePtr0
;
MaybeCallerCliquePtr = no,
CallerCliquePtr = dummy_clique_ptr
),
(
CallSiteDisplay ^ display_context = call_context,
Context = CallContext
;
CallSiteDisplay ^ display_context = empty_context,
Context = ""
),
(
CallSiteDisplay ^ display_proc_name = caller_proc_name,
ProcName = proc_dynamic_name(Deep, CallerPDPtr)
;
CallSiteDisplay ^ display_proc_name = callee_proc_name,
ProcName = proc_dynamic_name(Deep, CalleePDPtr)
),
(
CallSiteDisplay ^ display_url = caller_clique,
ChosenCliquePtr = CallerCliquePtr
;
CallSiteDisplay ^ display_url = callee_clique,
ChosenCliquePtr = CalleeCliquePtr
),
ChosenCliquePtr = clique_ptr(ChosenCliqueNum),
WrappedProcName = string.format("<A HREF=""%s"">%s</A>",
[s(deep_cmd_pref_to_url(Pref, Deep, deep_cmd_clique(ChosenCliqueNum))),
s(escape_break_html_string(ProcName))]),
(
CallSiteDisplay ^ display_wrap = wrap_url_always,
UsedProcName0 = WrappedProcName
;
CallSiteDisplay ^ display_wrap = wrap_url_if_cross_clique(Assume),
(
MaybeCallerCliquePtr = yes(_),
( CallerCliquePtr \= CalleeCliquePtr ->
UsedProcName0 = WrappedProcName
;
UsedProcName0 = escape_break_html_string(ProcName)
)
;
MaybeCallerCliquePtr = no,
(
Assume = assume_cross_clique,
UsedProcName0 = WrappedProcName
;
Assume = assume_within_clique,
UsedProcName0 = escape_break_html_string(ProcName)
)
)
;
CallSiteDisplay ^ display_wrap = wrap_url_never,
UsedProcName0 = escape_break_html_string(ProcName)
),
(
UsedProcName0 = WrappedProcName,
valid_clique_ptr(Deep, ChosenCliquePtr)
->
UsedProcName = UsedProcName0
;
UsedProcName = escape_break_html_string(ProcName)
),
HTML =
string.format("<TD CLASS=id>%s</TD>\n", [s(Context)]) ++
string.format("<TD CLASS=id>%s</TD>\n", [s(UsedProcName)]).
%-----------------------------------------------------------------------------%
:- pred call_site_dynamic_context(deep::in, call_site_dynamic_ptr::in,
string::out, int::out) is det.
call_site_dynamic_context(Deep, CSDPtr, FileName, LineNumber) :-
deep_lookup_call_site_static_map(Deep, CSDPtr, CSSPtr),
deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
PSPtr = CSS ^ css_container,
LineNumber = CSS ^ css_line_num,
deep_lookup_proc_statics(Deep, PSPtr, PS),
FileName = PS ^ ps_file_name.
%-----------------------------------------------------------------------------%
:- pred proc_callers_to_html(preferences::in, deep::in, proc_static_ptr::in,
caller_groups::in, int::in,
maybe_error({id_fields, string, string, string})::out,
io::di, io::uo) is det.
proc_callers_to_html(Pref, Deep, PSPtr, CallerGroups, BunchNum0, MaybePage,
!IO) :-
deep_lookup_proc_callers(Deep, PSPtr, CallerCSDPtrs),
(
Pref ^ pref_contour = no_contour,
CallerCSDPtrPairs = list.map(pair_self, CallerCSDPtrs),
MaybeErrorMsg = no
;
Pref ^ pref_contour = apply_contour,
read_exclude_file(contour_file_name(Deep ^ data_file_name),
Deep, Result, !IO),
(
Result = ok(ExcludeSpec),
CallerCSDPtrPairs = list.map(pair_contour(Deep, ExcludeSpec),
CallerCSDPtrs),
MaybeErrorMsg = no
;
Result = error(ErrorMsg),
MaybeErrorMsg = yes(ErrorMsg ++ "\n<br>"),
CallerCSDPtrPairs = list.map(pair_self, CallerCSDPtrs)
)
),
ProcName = proc_static_name(Deep, PSPtr),
PSPtr = proc_static_ptr(PSI),
CmdSite = deep_cmd_proc_callers(PSI, group_by_call_site, 1),
CmdProc = deep_cmd_proc_callers(PSI, group_by_proc, 1),
CmdModule = deep_cmd_proc_callers(PSI, group_by_module, 1),
CmdClique = deep_cmd_proc_callers(PSI, group_by_clique, 1),
LinkSite = "[Group callers by call site]",
LinkProc = "[Group callers by procedure]",
LinkModule = "[Group callers by module]",
LinkClique = "[Group callers by clique]",
% Don't display more lines than BunchSize, to avoid quadratic behaviour
% in Netscape.
BunchSize = 100,
(
CallerGroups = group_by_call_site,
GroupMap = list.foldl(accumulate_csds_by_call_site(Deep),
CallerCSDPtrPairs, map.init),
map.to_assoc_list(GroupMap, GroupList),
Lines = list.map(proc_callers_call_site_to_html(Pref, Deep, PSPtr),
GroupList),
SortedLines = sort_line_groups(Pref ^ pref_criteria, Lines),
IdFields = source_proc,
Entity = "call site",
GroupToggles =
string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, CmdProc)),
s(LinkProc)]) ++
string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, CmdModule)),
s(LinkModule)]) ++
string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, CmdClique)),
s(LinkClique)])
;
CallerGroups = group_by_proc,
GroupMap = list.foldl(accumulate_csds_by_procedure(Deep),
CallerCSDPtrPairs, map.init),
map.to_assoc_list(GroupMap, GroupList),
Lines = list.map(proc_callers_proc_to_html(Pref, Deep, PSPtr),
GroupList),
SortedLines = sort_line_groups(Pref ^ pref_criteria, Lines),
IdFields = source_proc,
Entity = "procedure",
GroupToggles =
string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, CmdSite)),
s(LinkSite)]) ++
string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, CmdModule)),
s(LinkModule)]) ++
string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, CmdClique)),
s(LinkClique)])
;
CallerGroups = group_by_module,
GroupMap = list.foldl(accumulate_csds_by_module(Deep),
CallerCSDPtrPairs, map.init),
map.to_assoc_list(GroupMap, GroupList),
RawLines = list.map(proc_callers_module_to_html(Pref, Deep, PSPtr),
GroupList),
SortedRawLines = sort_line_groups(Pref ^ pref_criteria, RawLines),
SortedLines = add_ranks(SortedRawLines),
IdFields = rank_module,
Entity = "module",
GroupToggles =
string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, CmdSite)),
s(LinkSite)]) ++
string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, CmdProc)),
s(LinkProc)]) ++
string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, CmdClique)),
s(LinkClique)])
;
CallerGroups = group_by_clique,
GroupMap = list.foldl(accumulate_csds_by_clique(Deep),
CallerCSDPtrPairs, map.init),
map.to_assoc_list(GroupMap, GroupList),
RawLines = list.map(proc_callers_clique_to_html(Pref, Deep, PSPtr),
GroupList),
SortedRawLines = sort_line_groups(Pref ^ pref_criteria, RawLines),
SortedLines = add_ranks(SortedRawLines),
IdFields = source_proc,
Entity = "clique",
GroupToggles =
string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, CmdSite)),
s(LinkSite)]) ++
string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, CmdProc)),
s(LinkProc)]) ++
string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, CmdModule)),
s(LinkModule)])
),
% SortedLines may contain many thousand elements, and Netscape
% chokes on the output unless we filter them or break them into chunks.
% This simple limit device is temporary until we decide what filtering
% and/or chunking mechanism we want to use.
list.length(SortedLines, NumLines),
select_line_bunch(NumLines, BunchNum0, BunchNum, BunchSize,
SortedLines, DisplayedLines),
Banner = proc_callers_banner(PSI, ProcName, Pref, Deep,
NumLines, BunchSize, BunchNum, Entity),
DisplayedHTMLs = list.map(
two_id_line_to_html(Pref, Deep, totals_meaningful),
DisplayedLines),
HTML = string.append_list(DisplayedHTMLs),
( BunchNum > 1 ->
FirstCmd = deep_cmd_proc_callers(PSI, CallerGroups, 1),
FirstLink = "First group",
FirstToggle =
string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, FirstCmd)), s(FirstLink)])
;
FirstToggle = ""
),
( BunchNum > 2 ->
PrevCmd = deep_cmd_proc_callers(PSI, CallerGroups, BunchNum - 1),
PrevLink = "Previous group",
PrevToggle =
string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, PrevCmd)), s(PrevLink)])
;
PrevToggle = ""
),
( NumLines > BunchNum * BunchSize ->
NextCmd = deep_cmd_proc_callers(PSI, CallerGroups, BunchNum + 1),
NextLink = "Next group",
NextToggle =
string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, NextCmd)), s(NextLink)])
;
NextToggle = ""
),
Toggles = GroupToggles ++ FirstToggle ++ PrevToggle ++ NextToggle,
(
MaybeErrorMsg = no,
MaybePage = ok({IdFields, Banner, HTML, Toggles})
;
MaybeErrorMsg = yes(Msg),
MaybePage = error(Msg)
).
:- pred select_line_bunch(int::in, int::in, int::out, int::in, list(T)::in,
list(T)::out) is det.
select_line_bunch(NumLines, BunchNum0, BunchNum, BunchSize,
Lines, DisplayedLines) :-
ToDelete = (BunchNum0 - 1) * BunchSize,
(
list.drop(ToDelete, Lines, RemainingLines0),
RemainingLines0 = [_ | _]
->
BunchNum = BunchNum0,
RemainingLines = RemainingLines0,
RemainingNumLines = NumLines - ToDelete
;
BunchNum = 1,
RemainingLines = Lines,
RemainingNumLines = NumLines
),
( RemainingNumLines > BunchSize ->
list.take_upto(BunchSize, RemainingLines, DisplayedLines)
;
DisplayedLines = RemainingLines
).
:- func proc_callers_banner(int, string, preferences, deep, int, int, int,
string) = string.
proc_callers_banner(PSI, ProcName, Pref, Deep, NumLines, BunchSize, BunchNum,
Parent) = HTML :-
Cmd = deep_cmd_proc(PSI),
WrappedProcName = string.format("<A HREF=""%s"">%s</A>",
[s(deep_cmd_pref_to_url(Pref, Deep, Cmd)),
s(escape_break_html_string(ProcName))]),
( NumLines = 0 ->
HTML = string.format("<H3>There are no %ss calling %s</H3>",
[s(Parent), s(WrappedProcName)])
; NumLines = 1 ->
HTML = string.format("<H3>There is one %s calling %s:</H3>\n",
[s(Parent), s(WrappedProcName)])
; NumLines =< BunchSize ->
HTML = string.format("<H3>The %d %ss calling %s:</H3>",
[i(NumLines), s(Parent), s(WrappedProcName)])
; BunchNum = 1 ->
HTML = string.format(
"<H3>There are %d %ss calling %s, showing first %d:</H3>",
[i(NumLines), s(Parent), s(WrappedProcName), i(BunchSize)])
;
First = (BunchNum - 1) * BunchSize + 1,
Last0 = (BunchNum) * BunchSize,
( Last0 > NumLines ->
Last = NumLines
;
Last = Last0
),
HTML = string.format(
"<H3>There are %d %ss calling %s, showing %d to %d:</H3>",
[i(NumLines), s(Parent), s(WrappedProcName), i(First), i(Last)])
).
:- func proc_callers_call_site_to_html(preferences, deep, proc_static_ptr,
pair(call_site_static_ptr, list(call_site_dynamic_ptr))) = two_id_line.
proc_callers_call_site_to_html(Pref, Deep, CalleePSPtr, CSSPtr - CSDPtrs)
= LineGroup :-
call_site_context(Deep, CSSPtr, FileName, LineNumber),
deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
CallerPSPtr = CSS ^ css_container,
deep_lookup_proc_statics(Deep, CallerPSPtr, CallerPS),
CallerProcName = CallerPS ^ ps_refined_id,
list.foldl2(accumulate_parent_csd_prof_info(Deep, CalleePSPtr), CSDPtrs,
zero_own_prof_info, Own, zero_inherit_prof_info, Desc),
HTML =
string.format("<TD CLASS=id>%s:%d</TD>\n",
[s(escape_break_html_string(FileName)), i(LineNumber)]) ++
string.format("<TD CLASS=id>%s</TD>\n",
[s(proc_static_to_html_ref(Pref, Deep, CallerPSPtr))]),
LineGroup = line_group(FileName, LineNumber, CallerProcName,
Own, Desc, HTML, unit).
:- func proc_callers_proc_to_html(preferences, deep, proc_static_ptr,
pair(proc_static_ptr, list(call_site_dynamic_ptr))) = two_id_line.
proc_callers_proc_to_html(Pref, Deep, CalleePSPtr, CallerPSPtr - CSDPtrs)
= LineGroup :-
proc_static_context(Deep, CallerPSPtr, FileName, LineNumber),
deep_lookup_proc_statics(Deep, CallerPSPtr, CallerPS),
CallerProcName = CallerPS ^ ps_refined_id,
list.foldl2(accumulate_parent_csd_prof_info(Deep, CalleePSPtr), CSDPtrs,
zero_own_prof_info, Own, zero_inherit_prof_info, Desc),
HTML =
string.format("<TD CLASS=id>%s:%d</TD>\n",
[s(escape_break_html_string(FileName)), i(LineNumber)]) ++
string.format("<TD CLASS=id>%s</TD>\n",
[s(proc_static_to_html_ref(Pref, Deep, CallerPSPtr))]),
LineGroup = line_group(FileName, LineNumber, CallerProcName,
Own, Desc, HTML, unit).
:- func proc_callers_module_to_html(preferences, deep, proc_static_ptr,
pair(string, list(call_site_dynamic_ptr))) = one_id_line.
proc_callers_module_to_html(Pref, Deep, CalleePSPtr, ModuleName - CSDPtrs)
= LineGroup :-
list.foldl2(accumulate_parent_csd_prof_info(Deep, CalleePSPtr), CSDPtrs,
zero_own_prof_info, Own, zero_inherit_prof_info, Desc),
HTML = string.format("<TD CLASS=id>%s</TD>\n",
[s(module_name_to_html_ref(Pref, Deep, ModuleName))]),
% We don't have filename information for modules, and line numbers
% are not meaningful for modules.
LineGroup = line_group(ModuleName, 0, ModuleName,
Own, Desc, HTML, unit).
:- func proc_callers_clique_to_html(preferences, deep, proc_static_ptr,
pair(clique_ptr, list(call_site_dynamic_ptr))) = one_id_line.
proc_callers_clique_to_html(Pref, Deep, CalleePSPtr, CliquePtr - CSDPtrs)
= LineGroup :-
list.foldl2(accumulate_parent_csd_prof_info(Deep, CalleePSPtr), CSDPtrs,
zero_own_prof_info, Own, zero_inherit_prof_info, Desc),
deep_lookup_clique_parents(Deep, CliquePtr, EntryCSDPtr),
deep_lookup_call_site_dynamics(Deep, EntryCSDPtr, EntryCSD),
EntryPDPtr = EntryCSD ^ csd_callee,
proc_dynamic_context(Deep, EntryPDPtr, FileName, LineNumber),
ProcName = proc_dynamic_name(Deep, EntryPDPtr),
HTML = string.format("<TD CLASS=id>%s</TD>\n",
[s(clique_ptr_to_html_ref(Pref, Deep, ProcName, CliquePtr))]),
LineGroup = line_group(FileName, LineNumber, ProcName,
Own, Desc, HTML, unit).
:- func accumulate_csds_by_call_site(deep, pair(call_site_dynamic_ptr),
map(call_site_static_ptr, list(call_site_dynamic_ptr))) =
map(call_site_static_ptr, list(call_site_dynamic_ptr)).
accumulate_csds_by_call_site(Deep, GroupCSDPtr - CostCSDPtr, Map0) = Map :-
deep_lookup_call_site_static_map(Deep, GroupCSDPtr, GroupCSSPtr),
( map.search(Map0, GroupCSSPtr, CostCSDPtrs0) ->
map.det_update(Map0, GroupCSSPtr, [CostCSDPtr | CostCSDPtrs0], Map)
;
map.det_insert(Map0, GroupCSSPtr, [CostCSDPtr], Map)
).
:- func accumulate_csds_by_procedure(deep, pair(call_site_dynamic_ptr),
map(proc_static_ptr, list(call_site_dynamic_ptr))) =
map(proc_static_ptr, list(call_site_dynamic_ptr)).
accumulate_csds_by_procedure(Deep, GroupCSDPtr - CostCSDPtr, Map0) = Map :-
deep_lookup_call_site_static_map(Deep, GroupCSDPtr, GroupCSSPtr),
deep_lookup_call_site_statics(Deep, GroupCSSPtr, GroupCSS),
GroupPSPtr = GroupCSS ^ css_container,
( map.search(Map0, GroupPSPtr, CostCSDPtrs0) ->
map.det_update(Map0, GroupPSPtr, [CostCSDPtr | CostCSDPtrs0], Map)
;
map.det_insert(Map0, GroupPSPtr, [CostCSDPtr], Map)
).
:- func accumulate_csds_by_module(deep, pair(call_site_dynamic_ptr),
map(string, list(call_site_dynamic_ptr))) =
map(string, list(call_site_dynamic_ptr)).
accumulate_csds_by_module(Deep, GroupCSDPtr - CostCSDPtr, Map0) = Map :-
deep_lookup_call_site_static_map(Deep, GroupCSDPtr, GroupCSSPtr),
deep_lookup_call_site_statics(Deep, GroupCSSPtr, GroupCSS),
GroupPSPtr = GroupCSS ^ css_container,
deep_lookup_proc_statics(Deep, GroupPSPtr, GroupPS),
GroupModuleName = GroupPS ^ ps_decl_module,
( map.search(Map0, GroupModuleName, CostCSDPtrs0) ->
map.det_update(Map0, GroupModuleName, [CostCSDPtr | CostCSDPtrs0], Map)
;
map.det_insert(Map0, GroupModuleName, [CostCSDPtr], Map)
).
:- func accumulate_csds_by_clique(deep, pair(call_site_dynamic_ptr),
map(clique_ptr, list(call_site_dynamic_ptr))) =
map(clique_ptr, list(call_site_dynamic_ptr)).
accumulate_csds_by_clique(Deep, GroupCSDPtr - CostCSDPtr, Map0) = Map :-
deep_lookup_call_site_dynamics(Deep, GroupCSDPtr, GroupCSD),
CallerPDPtr = GroupCSD ^ csd_caller,
deep_lookup_clique_index(Deep, CallerPDPtr, CliquePtr),
( map.search(Map0, CliquePtr, CostCSDPtrs0) ->
map.det_update(Map0, CliquePtr, [CostCSDPtr | CostCSDPtrs0], Map)
;
map.det_insert(Map0, CliquePtr, [CostCSDPtr], Map)
).
:- pred accumulate_parent_csd_prof_info(deep::in, proc_static_ptr::in,
call_site_dynamic_ptr::in,
own_prof_info::in, own_prof_info::out,
inherit_prof_info::in, inherit_prof_info::out) is det.
accumulate_parent_csd_prof_info(Deep, CallerPSPtr, CSDPtr,
Own0, Own, Desc0, Desc) :-
deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
( CSD ^ csd_callee = CSD ^ csd_caller ->
% We want to sum only cross-clique callers.
Own = Own0,
Desc = Desc0
;
deep_lookup_csd_own(Deep, CSDPtr, CSDOwn),
deep_lookup_csd_desc(Deep, CSDPtr, CSDDesc),
add_own_to_own(Own0, CSDOwn) = Own,
add_inherit_to_inherit(Desc0, CSDDesc) = Desc1,
deep_lookup_clique_index(Deep, CSD ^ csd_callee, CalleeCliquePtr),
deep_lookup_clique_members(Deep, CalleeCliquePtr, CalleeCliquePDPtrs),
list.foldl(compensate_using_comp_table(Deep, CallerPSPtr),
CalleeCliquePDPtrs, Desc1, Desc)
).
:- pred compensate_using_comp_table(deep::in, proc_static_ptr::in,
proc_dynamic_ptr::in, inherit_prof_info::in, inherit_prof_info::out)
is det.
compensate_using_comp_table(Deep, CallerPSPtr, PDPtr, Desc0, Desc) :-
deep_lookup_pd_comp_table(Deep, PDPtr, CompTableArray),
( map.search(CompTableArray, CallerPSPtr, InnerTotal) ->
Desc = subtract_inherit_from_inherit(InnerTotal, Desc0)
;
Desc = Desc0
).
:- func pair_self(call_site_dynamic_ptr) = pair(call_site_dynamic_ptr).
pair_self(CSDPtr) = CSDPtr - CSDPtr.
:- func pair_contour(deep, exclude_file, call_site_dynamic_ptr)
= pair(call_site_dynamic_ptr).
pair_contour(Deep, ExcludeSpec, CSDPtr) =
apply_contour_exclusion(Deep, ExcludeSpec, CSDPtr) - CSDPtr.
%-----------------------------------------------------------------------------%
:- func proc_summary_to_html(preferences, deep, proc_static_ptr) = string.
proc_summary_to_html(Pref, Deep, PSPtr) = HTML :-
SumHTML = two_id_line_to_html(Pref, Deep, totals_meaningful,
lookup_proc_total_to_two_id_line(Pref, Deep, yes, "", PSPtr)),
deep_lookup_proc_statics(Deep, PSPtr, PS),
CSSPtrsArray = PS ^ ps_sites,
array.to_list(CSSPtrsArray, CSSPtrs),
CallSiteGroups = list.map(call_site_summary_to_html(Pref, Deep), CSSPtrs),
SortedCallSiteGroups = sort_line_groups(Pref ^ pref_criteria,
CallSiteGroups),
BodyHTMLs = list.map(
two_id_line_group_to_html(Pref, Deep, totals_meaningful),
SortedCallSiteGroups),
string.append_list(BodyHTMLs, BodyHTML0),
(
SortedCallSiteGroups = [],
BodyHTML = BodyHTML0
;
SortedCallSiteGroups = [_ | _],
BodyHTML =
BodyHTML0 ++
separator_row(Pref, source_proc, totals_meaningful)
),
HTML =
SumHTML ++
separator_row(Pref, source_proc, totals_meaningful) ++
BodyHTML.
:- func proc_summary_toggles_to_html(preferences, deep, proc_static_ptr)
= string.
proc_summary_toggles_to_html(Pref, Deep, PSPtr) = HTML :-
PSPtr = proc_static_ptr(PSI),
Msg1 = "[Parent call sites]",
Cmd1 = deep_cmd_proc_callers(PSI, group_by_call_site, 1),
Msg2 = "[Parent procedures]",
Cmd2 = deep_cmd_proc_callers(PSI, group_by_proc, 1),
Msg3 = "[Parent modules]",
Cmd3 = deep_cmd_proc_callers(PSI, group_by_module, 1),
Msg4 = "[Parent cliques]",
Cmd4 = deep_cmd_proc_callers(PSI, group_by_clique, 1),
Link1 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, Cmd1)), s(Msg1)]),
Link2 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, Cmd2)), s(Msg2)]),
Link3 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, Cmd3)), s(Msg3)]),
Link4 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
[s(deep_cmd_pref_to_url(Pref, Deep, Cmd4)), s(Msg4)]),
HTML =
Link1 ++
Link2 ++
Link3 ++
Link4.
%-----------------------------------------------------------------------------%
:- func wrap_clique_links(clique_ptr, preferences, deep, string,
order_criteria) = string.
wrap_clique_links(CliquePtr, Pref0, Deep, Str0, Criteria) = Str :-
CliquePtr = clique_ptr(CI),
Cmd = deep_cmd_clique(CI),
Pref = Pref0 ^ pref_criteria := Criteria,
URL = deep_cmd_pref_to_url(Pref, Deep, Cmd),
Str = string.format("<A HREF=%s>%s</A>",
[s(URL), s(escape_break_html_string(Str0))]).
:- func wrap_proc_links(proc_static_ptr, preferences, deep, string,
order_criteria) = string.
wrap_proc_links(PSPtr, Pref0, Deep, Str0, Criteria) = Str :-
PSPtr = proc_static_ptr(PSI),
Cmd = deep_cmd_proc(PSI),
Pref = Pref0 ^ pref_criteria := Criteria,
URL = deep_cmd_pref_to_url(Pref, Deep, Cmd),
Str = string.format("<A HREF=%s>%s</A>",
[s(URL), s(escape_break_html_string(Str0))]).
:- func wrap_proc_callers_links(proc_static_ptr, caller_groups, int,
preferences, deep, string, order_criteria) = string.
wrap_proc_callers_links(PSPtr, CallerGroups, BunchNum, Pref0, Deep,
Str0, Criteria) = Str :-
PSPtr = proc_static_ptr(PSI),
Cmd = deep_cmd_proc_callers(PSI, CallerGroups, BunchNum),
Pref = Pref0 ^ pref_criteria := Criteria,
URL = deep_cmd_pref_to_url(Pref, Deep, Cmd),
Str = string.format("<A HREF=%s>%s</A>",
[s(URL), s(escape_break_html_string(Str0))]).
:- func wrap_module_links(string, preferences, deep, string,
order_criteria) = string.
wrap_module_links(ModuleName, Pref0, Deep, Str0, Criteria) = Str :-
Cmd = deep_cmd_module(ModuleName),
Pref = Pref0 ^ pref_criteria := Criteria,
URL = deep_cmd_pref_to_url(Pref, Deep, Cmd),
Str = string.format("<A HREF=%s>%s</A>",
[s(URL), s(escape_break_html_string(Str0))]).
:- func wrap_modules_links(preferences, deep, string, order_criteria) = string.
wrap_modules_links(Pref0, Deep, Str0, Criteria) = Str :-
Cmd = deep_cmd_modules,
Pref = Pref0 ^ pref_criteria := Criteria,
URL = deep_cmd_pref_to_url(Pref, Deep, Cmd),
Str = string.format("<A HREF=%s>%s</A>",
[s(URL), s(escape_break_html_string(Str0))]).
:- func wrap_top_procs_links(display_limit, preferences, deep, string,
order_criteria) = string.
wrap_top_procs_links(Limit, Pref, Deep, Str0, Criteria) = Str :-
(
Criteria = by_context,
Str = Str0
;
Criteria = by_name,
Str = Str0
;
Criteria = by_cost(CostKind, InclDesc, Scope),
Cmd = deep_cmd_top_procs(Limit, CostKind, InclDesc, Scope),
URL = deep_cmd_pref_to_url(Pref, Deep, Cmd),
Str = string.format("<A HREF=%s>%s</A>",
[s(URL), s(escape_break_html_string(Str0))])
).
%----------------------------------------------------------------------------%
:- end_module query.
%----------------------------------------------------------------------------%