mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
Estimated hours taken: 3 Branches: main Use reports to handle the call_site_static and call_site_dynamic queries. deep_profiler/report.m: Add the two new report types. Give a better name to a type. deep_profiler/create_report.m: Add the code for the two new report types. Make the predicate for creating a perf_row_data more general, and avoid defining the perf_row_data field-by-field. deep_profiler/display.m: Rename a table_class that is now used not just by the top_procs command. Represent columns as cords, to avoid the need for keeping track of whether the list of columns is reversed. deep_profiler/display_report.m: Add the code to handle the two new report types. Refactor some existing code to allow it to called by the new code. Conform to the other changes in report.m. deep_profiler/html_format.m: Add newlines to the output, in order to make it more readable and to make using diff between two versions of the output feasible. Print the HTML title as a heading. Add a XXX about a disrespected control. deep_profiler/query.m: Use the new method to handle the two query types.
2863 lines
104 KiB
Mathematica
2863 lines
104 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2001-2003, 2005-2008 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.
|
|
%
|
|
% This module defines the types of commands and preferences. It provides
|
|
% mechanisms for converting queries to strings and strings to queries, but
|
|
% it does not expose the encoding. The encoding is compositional; each
|
|
% component of the query (say x) has a x_to_string function to convert it to
|
|
% a string and a string_to_x predicate to try to convert a string fragment
|
|
% back to it. The function/predicate pairs are adjacent to make it easy to
|
|
% update both at the same time. This is essential, because we have no other
|
|
% mechanism to ensure that the URLs embeded in the HTML pages generated by
|
|
% the mdprof_cgi tool will be recognized and correctly parsed by mdprof_cgi.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module query.
|
|
:- interface.
|
|
|
|
:- import_module measurement_units.
|
|
:- import_module profile.
|
|
|
|
:- import_module bool.
|
|
:- import_module char.
|
|
:- import_module io.
|
|
:- import_module maybe.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred try_exec(cmd::in, preferences::in, deep::in, string::out,
|
|
io::di, io::uo) is cc_multi.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type resp
|
|
---> html(string).
|
|
|
|
:- type cmd_pref
|
|
---> cmd_pref(cmd, preferences_indication).
|
|
|
|
:- type cmd
|
|
---> deep_cmd_quit
|
|
; deep_cmd_restart
|
|
; deep_cmd_timeout(int)
|
|
; deep_cmd_menu
|
|
; deep_cmd_root(maybe(int))
|
|
; deep_cmd_clique(int)
|
|
; deep_cmd_proc(int)
|
|
; deep_cmd_proc_callers(int, caller_groups, int)
|
|
; deep_cmd_modules
|
|
; deep_cmd_module(string)
|
|
; deep_cmd_top_procs(display_limit, cost_kind, include_descendants,
|
|
measurement_scope)
|
|
|
|
% The following commands are for debugging.
|
|
|
|
; deep_cmd_proc_static(int)
|
|
; deep_cmd_proc_dynamic(int)
|
|
; deep_cmd_call_site_static(int)
|
|
; deep_cmd_call_site_dynamic(int)
|
|
; deep_cmd_raw_clique(int).
|
|
|
|
:- type caller_groups
|
|
---> group_by_call_site
|
|
; group_by_proc
|
|
; group_by_module
|
|
; group_by_clique.
|
|
|
|
:- type cost_kind
|
|
---> cost_calls
|
|
; cost_redos
|
|
; cost_time
|
|
; cost_callseqs
|
|
; cost_allocs
|
|
; cost_words.
|
|
|
|
:- type include_descendants
|
|
---> self
|
|
; self_and_desc.
|
|
|
|
:- type display_limit
|
|
---> rank_range(int, int)
|
|
% rank_range(M, N): display procedures with rank M to N,
|
|
% both inclusive.
|
|
|
|
; threshold_percent(float)
|
|
% threshold(Percent): display procedures whose cost is at least
|
|
% Percent% of the whole program's cost.
|
|
|
|
; threshold_value(float).
|
|
% threshold_value(Value): display procedures whose cost is at least
|
|
% this value.
|
|
|
|
:- type preferences_indication
|
|
---> given_pref(preferences)
|
|
; default_pref
|
|
; all_pref.
|
|
|
|
:- type preferences
|
|
---> preferences(
|
|
% The set of fields to display.
|
|
pref_fields :: fields,
|
|
|
|
% Whether displays should be boxed.
|
|
pref_box :: box,
|
|
|
|
% What principle governs colours.
|
|
pref_colour :: colour_scheme,
|
|
|
|
% The max number of ancestors to display.
|
|
pref_anc :: maybe(int),
|
|
|
|
% Whether pages should summarize at higher order call sites.
|
|
pref_summarize :: summarize,
|
|
|
|
% The criteria for ordering lines in pages, if the command
|
|
% doesn't specify otherwise.
|
|
pref_criteria :: order_criteria,
|
|
|
|
% Whether contour exclusion should be applied.
|
|
pref_contour :: contour,
|
|
|
|
pref_time :: time_format,
|
|
|
|
% Whether we should show modules/procs that haven't been
|
|
% called.
|
|
pref_inactive :: inactive_items
|
|
).
|
|
|
|
:- type port_fields
|
|
---> no_port
|
|
; port.
|
|
|
|
:- type time_fields
|
|
---> no_time
|
|
; ticks
|
|
; time
|
|
; ticks_and_time
|
|
; time_and_percall
|
|
; ticks_and_time_and_percall.
|
|
|
|
:- type callseqs_fields
|
|
---> no_callseqs
|
|
; callseqs
|
|
; callseqs_and_percall.
|
|
|
|
:- type alloc_fields
|
|
---> no_alloc
|
|
; alloc
|
|
; alloc_and_percall.
|
|
|
|
:- type memory_fields
|
|
---> no_memory
|
|
; memory(memory_units)
|
|
; memory_and_percall(memory_units).
|
|
|
|
:- type fields
|
|
---> fields(
|
|
port_fields :: port_fields,
|
|
time_fields :: time_fields,
|
|
callseqs_fields :: callseqs_fields,
|
|
alloc_fields :: alloc_fields,
|
|
memory_fields :: memory_fields
|
|
).
|
|
|
|
:- type box
|
|
---> box
|
|
; nobox.
|
|
|
|
:- type colour_scheme
|
|
---> colour_column_groups
|
|
; colour_none.
|
|
|
|
:- type summarize
|
|
---> summarize
|
|
; dont_summarize.
|
|
|
|
:- type order_criteria
|
|
---> by_context
|
|
; by_name
|
|
; by_cost(
|
|
cost_kind,
|
|
include_descendants,
|
|
measurement_scope
|
|
).
|
|
|
|
:- type measurement_scope
|
|
---> per_call
|
|
; overall.
|
|
|
|
:- type contour
|
|
---> apply_contour
|
|
; no_contour.
|
|
|
|
:- type time_format
|
|
---> no_scale
|
|
; scale_by_millions
|
|
; scale_by_thousands.
|
|
|
|
:- type inactive_status
|
|
---> inactive_hide
|
|
; inactive_show.
|
|
|
|
:- type inactive_items
|
|
---> inactive_items(
|
|
inactive_procs :: inactive_status,
|
|
inactive_modules :: inactive_status
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Return "yes" if it is worth displaying times for this profile.
|
|
%
|
|
:- func should_display_times(deep) = bool.
|
|
|
|
:- func solidify_preference(deep, preferences_indication) = preferences.
|
|
|
|
:- func default_preferences(deep) = preferences.
|
|
|
|
:- func default_fields(deep) = fields.
|
|
:- func all_fields = fields.
|
|
:- func default_box = box.
|
|
:- func default_colour_scheme = colour_scheme.
|
|
:- func default_ancestor_limit = maybe(int).
|
|
:- func default_summarize = summarize.
|
|
:- func default_order_criteria = order_criteria.
|
|
:- func default_cost_kind = cost_kind.
|
|
:- func default_incl_desc = include_descendants.
|
|
:- func default_scope = measurement_scope.
|
|
:- func default_contour = contour.
|
|
:- func default_time_format = time_format.
|
|
:- func default_inactive_items = inactive_items.
|
|
|
|
:- func query_separator_char = char.
|
|
|
|
:- func preferences_to_string(preferences) = string.
|
|
:- func cmd_to_string(cmd) = string.
|
|
:- func string_to_cmd(string, cmd) = cmd.
|
|
:- func string_to_maybe_cmd(string) = maybe(cmd).
|
|
:- func string_to_maybe_pref(string) = maybe(preferences).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module create_report.
|
|
:- import_module display_report.
|
|
:- import_module exclude.
|
|
:- import_module html_format.
|
|
:- import_module measurements.
|
|
:- import_module report.
|
|
:- import_module top_procs.
|
|
:- import_module util.
|
|
|
|
:- 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(Cmd, Prefs, Deep, HTMLStr, !IO) :-
|
|
( Cmd = deep_cmd_quit
|
|
; Cmd = deep_cmd_timeout(_)
|
|
; Cmd = deep_cmd_restart
|
|
; Cmd = deep_cmd_menu
|
|
; Cmd = deep_cmd_top_procs(_, _, _, _)
|
|
; Cmd = deep_cmd_proc_static(_)
|
|
; Cmd = deep_cmd_proc_dynamic(_)
|
|
; Cmd = deep_cmd_call_site_static(_)
|
|
; Cmd = deep_cmd_call_site_dynamic(_)
|
|
),
|
|
create_report(Cmd, Deep, Report),
|
|
Display = report_to_display(Deep, Prefs, Report),
|
|
HTML = htmlize_display(Deep, Display),
|
|
HTMLStr = html_to_string(HTML).
|
|
|
|
% Old deep profiler cgi code. This should remain supported until all the deep
|
|
% profiler reports have been updated to use the data structures in report.m.
|
|
|
|
%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_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(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(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 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 ^ num_csd)]) ++
|
|
"<TR><TD ALIGN=left>ProcDynamic structures:</TD>\n" ++
|
|
string.format("<TD ALIGN=right>%d</TD></TR>\n",
|
|
[i(Stats ^ num_pd)]) ++
|
|
"<TR><TD ALIGN=left>CallSiteStatic structures:</TD>\n" ++
|
|
string.format("<TD ALIGN=right>%d</TD></TR>\n",
|
|
[i(Stats ^ num_css)]) ++
|
|
"<TR><TD ALIGN=left>ProcStatic structures:</TD>\n" ++
|
|
string.format("<TD ALIGN=right>%d</TD></TR>\n",
|
|
[i(Stats ^ num_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 calls 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 contour_file_name(string) = string.
|
|
|
|
contour_file_name(DataFileName) =
|
|
DataFileName ++ ".contour".
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- 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))])
|
|
).
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Display times only if the profile was derived from a run that ran for
|
|
% at least this many quanta.
|
|
%
|
|
:- func minimum_meaningful_quanta = int.
|
|
|
|
minimum_meaningful_quanta = 10.
|
|
|
|
should_display_times(Deep) =
|
|
( Deep ^ profile_stats ^ user_quanta > minimum_meaningful_quanta ->
|
|
yes
|
|
;
|
|
no
|
|
).
|
|
|
|
solidify_preference(Deep, PrefInd) = Pref :-
|
|
(
|
|
PrefInd = given_pref(Pref)
|
|
;
|
|
PrefInd = default_pref,
|
|
Pref = default_preferences(Deep)
|
|
;
|
|
PrefInd = all_pref,
|
|
Pref = default_preferences(Deep) ^ pref_fields := all_fields
|
|
).
|
|
|
|
default_preferences(Deep) =
|
|
preferences(
|
|
default_fields(Deep),
|
|
default_box,
|
|
default_colour_scheme,
|
|
default_ancestor_limit,
|
|
default_summarize,
|
|
default_order_criteria,
|
|
default_contour,
|
|
default_time_format,
|
|
default_inactive_items
|
|
).
|
|
|
|
default_fields(Deep) = Fields :-
|
|
ShouldDisplayTimes = should_display_times(Deep),
|
|
(
|
|
ShouldDisplayTimes = yes,
|
|
Time = ticks
|
|
;
|
|
ShouldDisplayTimes = no,
|
|
Time = no_time
|
|
),
|
|
Fields = fields(port, Time, callseqs, no_alloc, memory(units_words)).
|
|
|
|
all_fields = fields(port, ticks_and_time_and_percall, callseqs_and_percall,
|
|
alloc, memory(units_words)).
|
|
|
|
default_box = box.
|
|
default_colour_scheme = colour_column_groups.
|
|
default_ancestor_limit = yes(5).
|
|
default_summarize = dont_summarize.
|
|
default_order_criteria = by_context.
|
|
default_cost_kind = cost_callseqs.
|
|
default_incl_desc = self_and_desc.
|
|
default_scope = overall.
|
|
default_contour = no_contour.
|
|
default_time_format = scale_by_thousands.
|
|
default_inactive_items = inactive_items(inactive_hide, inactive_hide).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- func cmd_separator_char = char.
|
|
:- func pref_separator_char = char.
|
|
:- func criteria_separator_char = char.
|
|
:- func field_separator_char = char.
|
|
:- func limit_separator_char = char.
|
|
|
|
query_separator_char = ('&').
|
|
cmd_separator_char = ('/').
|
|
pref_separator_char = ('/').
|
|
criteria_separator_char = ('-').
|
|
field_separator_char = ('-').
|
|
limit_separator_char = ('-').
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
cmd_to_string(Cmd) = CmdStr :-
|
|
(
|
|
Cmd = deep_cmd_quit,
|
|
CmdStr = "quit"
|
|
;
|
|
Cmd = deep_cmd_restart,
|
|
CmdStr = "restart"
|
|
;
|
|
Cmd = deep_cmd_timeout(Minutes),
|
|
CmdStr = string.format("timeout%c%d",
|
|
[c(cmd_separator_char), i(Minutes)])
|
|
;
|
|
Cmd = deep_cmd_menu,
|
|
CmdStr = "menu"
|
|
;
|
|
Cmd = deep_cmd_root(MaybePercent),
|
|
(
|
|
MaybePercent = yes(Percent),
|
|
CmdStr = string.format("root%c%d",
|
|
[c(cmd_separator_char), i(Percent)])
|
|
;
|
|
MaybePercent = no,
|
|
CmdStr = string.format("root%c%s",
|
|
[c(cmd_separator_char), s("no")])
|
|
)
|
|
;
|
|
Cmd = deep_cmd_clique(CliqueNum),
|
|
CmdStr = string.format("clique%c%d",
|
|
[c(cmd_separator_char), i(CliqueNum)])
|
|
;
|
|
Cmd = deep_cmd_proc(ProcNum),
|
|
CmdStr = string.format("proc%c%d",
|
|
[c(cmd_separator_char), i(ProcNum)])
|
|
;
|
|
Cmd = deep_cmd_proc_callers(ProcNum, GroupCallers, BunchNum),
|
|
GroupCallersStr = caller_groups_to_string(GroupCallers),
|
|
CmdStr = string.format("proc_callers%c%d%c%s%c%d",
|
|
[c(cmd_separator_char), i(ProcNum),
|
|
c(cmd_separator_char), s(GroupCallersStr),
|
|
c(cmd_separator_char), i(BunchNum)])
|
|
;
|
|
Cmd = deep_cmd_modules,
|
|
CmdStr = "modules"
|
|
;
|
|
Cmd = deep_cmd_module(ModuleName),
|
|
CmdStr = string.format("module%c%s",
|
|
[c(cmd_separator_char), s(ModuleName)])
|
|
;
|
|
Cmd = deep_cmd_top_procs(Limit, CostKind, InclDesc, Scope),
|
|
LimitStr = limit_to_string(Limit),
|
|
CostKindStr = cost_kind_to_string(CostKind),
|
|
InclDescStr = incl_desc_to_string(InclDesc),
|
|
ScopeStr = scope_to_string(Scope),
|
|
CmdStr = string.format("top_procs%c%s%c%s%c%s%c%s",
|
|
[c(cmd_separator_char), s(LimitStr),
|
|
c(cmd_separator_char), s(CostKindStr),
|
|
c(cmd_separator_char), s(InclDescStr),
|
|
c(cmd_separator_char), s(ScopeStr)])
|
|
;
|
|
Cmd = deep_cmd_proc_static(PSI),
|
|
CmdStr = string.format("proc_static%c%d",
|
|
[c(cmd_separator_char), i(PSI)])
|
|
;
|
|
Cmd = deep_cmd_proc_dynamic(PDI),
|
|
CmdStr = string.format("proc_dynamic%c%d",
|
|
[c(cmd_separator_char), i(PDI)])
|
|
;
|
|
Cmd = deep_cmd_call_site_static(CSSI),
|
|
CmdStr = string.format("call_site_static%c%d",
|
|
[c(cmd_separator_char), i(CSSI)])
|
|
;
|
|
Cmd = deep_cmd_call_site_dynamic(CSDI),
|
|
CmdStr = string.format("call_site_dynamic%c%d",
|
|
[c(cmd_separator_char), i(CSDI)])
|
|
;
|
|
Cmd = deep_cmd_raw_clique(CI),
|
|
CmdStr = string.format("raw_clique%c%d",
|
|
[c(cmd_separator_char), i(CI)])
|
|
).
|
|
|
|
preferences_to_string(Pref) = PrefStr :-
|
|
Pref = preferences(Fields, Box, Colour, MaybeAncestorLimit,
|
|
Summarize, Order, Contour, Time, InactiveItems),
|
|
(
|
|
MaybeAncestorLimit = yes(AncestorLimit),
|
|
MaybeAncestorLimitStr =
|
|
string.format("%d", [i(AncestorLimit)])
|
|
;
|
|
MaybeAncestorLimit = no,
|
|
MaybeAncestorLimitStr = "no"
|
|
),
|
|
PrefStr = string.format("%s%c%s%c%s%c%s%c%s%c%s%c%s%c%s%c%s",
|
|
[s(fields_to_string(Fields)),
|
|
c(pref_separator_char), s(box_to_string(Box)),
|
|
c(pref_separator_char), s(colour_scheme_to_string(Colour)),
|
|
c(pref_separator_char), s(MaybeAncestorLimitStr),
|
|
c(pref_separator_char), s(summarize_to_string(Summarize)),
|
|
c(pref_separator_char), s(order_criteria_to_string(Order)),
|
|
c(pref_separator_char), s(contour_to_string(Contour)),
|
|
c(pref_separator_char), s(time_format_to_string(Time)),
|
|
c(pref_separator_char), s(inactive_items_to_string(InactiveItems))
|
|
]).
|
|
|
|
string_to_cmd(QueryString, DefaultCmd) = Cmd :-
|
|
MaybeCmd = string_to_maybe_cmd(QueryString),
|
|
(
|
|
MaybeCmd = yes(Cmd)
|
|
;
|
|
MaybeCmd = no,
|
|
Cmd = DefaultCmd
|
|
).
|
|
|
|
string_to_maybe_cmd(QueryString) = MaybeCmd :-
|
|
split(QueryString, pref_separator_char, Pieces),
|
|
(
|
|
Pieces = ["root", MaybePercentStr],
|
|
( MaybePercentStr = "no" ->
|
|
MaybePercent = no
|
|
; string.to_int(MaybePercentStr, Percent) ->
|
|
MaybePercent = yes(Percent)
|
|
;
|
|
fail
|
|
)
|
|
->
|
|
MaybeCmd = yes(deep_cmd_root(MaybePercent))
|
|
;
|
|
Pieces = ["clique", CliqueNumStr],
|
|
string.to_int(CliqueNumStr, CliqueNum)
|
|
->
|
|
MaybeCmd = yes(deep_cmd_clique(CliqueNum))
|
|
;
|
|
Pieces = ["proc", PSIStr],
|
|
string.to_int(PSIStr, PSI)
|
|
->
|
|
MaybeCmd = yes(deep_cmd_proc(PSI))
|
|
;
|
|
Pieces = ["proc_callers", PSIStr, GroupCallersStr, BunchNumStr],
|
|
string.to_int(PSIStr, PSI),
|
|
string.to_int(BunchNumStr, BunchNum),
|
|
string_to_caller_groups(GroupCallersStr, GroupCallers)
|
|
->
|
|
MaybeCmd = yes(deep_cmd_proc_callers(PSI, GroupCallers, BunchNum))
|
|
;
|
|
Pieces = ["modules"]
|
|
->
|
|
MaybeCmd = yes(deep_cmd_modules)
|
|
;
|
|
Pieces = ["module", ModuleName]
|
|
->
|
|
MaybeCmd = yes(deep_cmd_module(ModuleName))
|
|
;
|
|
Pieces = ["top_procs", LimitStr, CostKindStr, InclDescStr, ScopeStr],
|
|
string_to_limit(LimitStr, Limit),
|
|
string_to_cost_kind(CostKindStr, CostKind),
|
|
string_to_incl_desc(InclDescStr, InclDesc),
|
|
string_to_scope(ScopeStr, Scope)
|
|
->
|
|
MaybeCmd = yes(deep_cmd_top_procs(Limit, CostKind, InclDesc, Scope))
|
|
;
|
|
Pieces = ["menu"]
|
|
->
|
|
MaybeCmd = yes(deep_cmd_menu)
|
|
;
|
|
Pieces = ["proc_static", PSIStr],
|
|
string.to_int(PSIStr, PSI)
|
|
->
|
|
MaybeCmd = yes(deep_cmd_proc_static(PSI))
|
|
;
|
|
Pieces = ["proc_dynamic", PDIStr],
|
|
string.to_int(PDIStr, PDI)
|
|
->
|
|
MaybeCmd = yes(deep_cmd_proc_dynamic(PDI))
|
|
;
|
|
Pieces = ["call_site_static", CSSIStr],
|
|
string.to_int(CSSIStr, CSSI)
|
|
->
|
|
MaybeCmd = yes(deep_cmd_call_site_static(CSSI))
|
|
;
|
|
Pieces = ["call_site_dynamic", CSDIStr],
|
|
string.to_int(CSDIStr, CSDI)
|
|
->
|
|
MaybeCmd = yes(deep_cmd_call_site_dynamic(CSDI))
|
|
;
|
|
Pieces = ["raw_clique", CliqueNumStr],
|
|
string.to_int(CliqueNumStr, CliqueNum)
|
|
->
|
|
MaybeCmd = yes(deep_cmd_raw_clique(CliqueNum))
|
|
;
|
|
Pieces = ["timeout", TimeOutStr],
|
|
string.to_int(TimeOutStr, TimeOut)
|
|
->
|
|
MaybeCmd = yes(deep_cmd_timeout(TimeOut))
|
|
;
|
|
Pieces = ["restart"]
|
|
->
|
|
MaybeCmd = yes(deep_cmd_restart)
|
|
;
|
|
Pieces = ["quit"]
|
|
->
|
|
MaybeCmd = yes(deep_cmd_quit)
|
|
;
|
|
MaybeCmd = no
|
|
).
|
|
|
|
string_to_maybe_pref(QueryString) = MaybePreferences :-
|
|
split(QueryString, pref_separator_char, Pieces),
|
|
(
|
|
Pieces = [FieldsStr, BoxStr, ColourStr, MaybeAncestorLimitStr,
|
|
SummarizeStr, OrderStr, ContourStr, TimeStr, InactiveItemsStr],
|
|
string_to_fields(FieldsStr, Fields),
|
|
string_to_box(BoxStr, Box),
|
|
string_to_colour_scheme(ColourStr, Colour),
|
|
( string.to_int(MaybeAncestorLimitStr, AncestorLimit) ->
|
|
MaybeAncestorLimit = yes(AncestorLimit)
|
|
; MaybeAncestorLimitStr = "no" ->
|
|
MaybeAncestorLimit = no
|
|
;
|
|
fail
|
|
),
|
|
string_to_summarize(SummarizeStr, Summarize),
|
|
string_to_order_criteria(OrderStr, Order),
|
|
string_to_contour(ContourStr, Contour),
|
|
string_to_time_format(TimeStr, Time),
|
|
string_to_inactive_items(InactiveItemsStr, InactiveItems)
|
|
->
|
|
Preferences = preferences(Fields, Box, Colour, MaybeAncestorLimit,
|
|
Summarize, Order, Contour, Time, InactiveItems),
|
|
MaybePreferences = yes(Preferences)
|
|
;
|
|
MaybePreferences = no
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- func port_fields_to_string(port_fields) = string.
|
|
|
|
port_fields_to_string(PortFields) = String :-
|
|
string_to_port_fields(String, PortFields).
|
|
|
|
:- pred string_to_port_fields(string, port_fields).
|
|
:- mode string_to_port_fields(in, out) is semidet.
|
|
:- mode string_to_port_fields(out, in) is det.
|
|
|
|
string_to_port_fields("_", no_port).
|
|
string_to_port_fields("p", port).
|
|
|
|
:- func time_fields_to_string(time_fields) = string.
|
|
|
|
time_fields_to_string(TimeFields) = String :-
|
|
string_to_time_fields(String, TimeFields).
|
|
|
|
:- pred string_to_time_fields(string, time_fields).
|
|
:- mode string_to_time_fields(in, out) is semidet.
|
|
:- mode string_to_time_fields(out, in) is det.
|
|
|
|
string_to_time_fields("_", no_time).
|
|
string_to_time_fields("q", ticks).
|
|
string_to_time_fields("t", time).
|
|
string_to_time_fields("qt", ticks_and_time).
|
|
string_to_time_fields("tp", time_and_percall).
|
|
string_to_time_fields("qtp", ticks_and_time_and_percall).
|
|
|
|
:- func callseqs_fields_to_string(callseqs_fields) = string.
|
|
|
|
callseqs_fields_to_string(AllocFields) = String :-
|
|
string_to_callseqs_fields(String, AllocFields).
|
|
|
|
:- pred string_to_callseqs_fields(string, callseqs_fields).
|
|
:- mode string_to_callseqs_fields(in, out) is semidet.
|
|
:- mode string_to_callseqs_fields(out, in) is det.
|
|
|
|
string_to_callseqs_fields("_", no_callseqs).
|
|
string_to_callseqs_fields("s", callseqs).
|
|
string_to_callseqs_fields("S", callseqs_and_percall).
|
|
|
|
:- func alloc_fields_to_string(alloc_fields) = string.
|
|
|
|
alloc_fields_to_string(AllocFields) = String :-
|
|
string_to_alloc_fields(String, AllocFields).
|
|
|
|
:- pred string_to_alloc_fields(string, alloc_fields).
|
|
:- mode string_to_alloc_fields(in, out) is semidet.
|
|
:- mode string_to_alloc_fields(out, in) is det.
|
|
|
|
string_to_alloc_fields("_", no_alloc).
|
|
string_to_alloc_fields("a", alloc).
|
|
string_to_alloc_fields("A", alloc_and_percall).
|
|
|
|
:- func memory_fields_to_string(memory_fields) = string.
|
|
|
|
memory_fields_to_string(MemoryFields) = String :-
|
|
string_to_memory_fields(String, MemoryFields).
|
|
|
|
:- pred string_to_memory_fields(string, memory_fields).
|
|
:- mode string_to_memory_fields(in, out) is semidet.
|
|
:- mode string_to_memory_fields(out, in) is det.
|
|
|
|
string_to_memory_fields("_", no_memory).
|
|
string_to_memory_fields("b", memory(units_bytes)).
|
|
string_to_memory_fields("w", memory(units_words)).
|
|
string_to_memory_fields("B", memory_and_percall(units_bytes)).
|
|
string_to_memory_fields("W", memory_and_percall(units_words)).
|
|
|
|
:- func fields_to_string(fields) = string.
|
|
|
|
fields_to_string(fields(Port, Time, CallSeqs, Allocs, Memory)) =
|
|
port_fields_to_string(Port) ++
|
|
string.char_to_string(field_separator_char) ++
|
|
time_fields_to_string(Time) ++
|
|
string.char_to_string(field_separator_char) ++
|
|
callseqs_fields_to_string(CallSeqs) ++
|
|
string.char_to_string(field_separator_char) ++
|
|
alloc_fields_to_string(Allocs) ++
|
|
string.char_to_string(field_separator_char) ++
|
|
memory_fields_to_string(Memory).
|
|
|
|
:- pred string_to_fields(string::in, fields::out) is semidet.
|
|
|
|
string_to_fields(FieldsStr, Fields) :-
|
|
(
|
|
split(FieldsStr, field_separator_char, Pieces),
|
|
Pieces = [PortStr, TimeStr, CallSeqsStr, AllocStr, MemoryStr],
|
|
string_to_port_fields(PortStr, Port),
|
|
string_to_time_fields(TimeStr, Time),
|
|
string_to_callseqs_fields(CallSeqsStr, CallSeqs),
|
|
string_to_alloc_fields(AllocStr, Alloc),
|
|
string_to_memory_fields(MemoryStr, Memory)
|
|
->
|
|
Fields = fields(Port, Time, CallSeqs, Alloc, Memory)
|
|
;
|
|
fail
|
|
).
|
|
|
|
:- func caller_groups_to_string(caller_groups) = string.
|
|
|
|
caller_groups_to_string(CallerGroups) = String :-
|
|
string_to_caller_groups(String, CallerGroups).
|
|
|
|
:- pred string_to_caller_groups(string, caller_groups).
|
|
:- mode string_to_caller_groups(in, out) is semidet.
|
|
:- mode string_to_caller_groups(out, in) is det.
|
|
|
|
string_to_caller_groups("cs", group_by_call_site).
|
|
string_to_caller_groups("pr", group_by_proc).
|
|
string_to_caller_groups("mo", group_by_module).
|
|
string_to_caller_groups("cl", group_by_clique).
|
|
|
|
:- func cost_kind_to_string(cost_kind) = string.
|
|
|
|
cost_kind_to_string(CostKind) = String :-
|
|
string_to_cost_kind(String, CostKind).
|
|
|
|
:- pred string_to_cost_kind(string, cost_kind).
|
|
:- mode string_to_cost_kind(in, out) is semidet.
|
|
:- mode string_to_cost_kind(out, in) is det.
|
|
|
|
string_to_cost_kind("calls", cost_calls).
|
|
string_to_cost_kind("redos", cost_redos).
|
|
string_to_cost_kind("time", cost_time).
|
|
string_to_cost_kind("callseqs", cost_callseqs).
|
|
string_to_cost_kind("allocs", cost_allocs).
|
|
string_to_cost_kind("words", cost_words).
|
|
|
|
:- func incl_desc_to_string(include_descendants) = string.
|
|
|
|
incl_desc_to_string(InclDesc) = String :-
|
|
string_to_incl_desc(String, InclDesc).
|
|
|
|
:- pred string_to_incl_desc(string, include_descendants).
|
|
:- mode string_to_incl_desc(in, out) is semidet.
|
|
:- mode string_to_incl_desc(out, in) is det.
|
|
|
|
string_to_incl_desc("self", self).
|
|
string_to_incl_desc("both", self_and_desc).
|
|
|
|
:- func limit_to_string(display_limit) = string.
|
|
|
|
limit_to_string(rank_range(Lo, Hi)) =
|
|
string.format("%d%c%d", [i(Lo), c(limit_separator_char), i(Hi)]).
|
|
limit_to_string(threshold_percent(Threshold)) =
|
|
string.format("p%g", [f(Threshold)]).
|
|
limit_to_string(threshold_value(Value)) =
|
|
string.format("v%g", [f(Value)]).
|
|
|
|
:- pred string_to_limit(string::in, display_limit::out) is semidet.
|
|
|
|
string_to_limit(LimitStr, Limit) :-
|
|
(
|
|
split(LimitStr, limit_separator_char, Pieces),
|
|
Pieces = [FirstStr, LastStr],
|
|
string.to_int(FirstStr, First),
|
|
string.to_int(LastStr, Last)
|
|
->
|
|
Limit = rank_range(First, Last)
|
|
;
|
|
string.append("p", PercentStr, LimitStr),
|
|
string.to_float(PercentStr, Threshold)
|
|
->
|
|
Limit = threshold_percent(Threshold)
|
|
;
|
|
string.append("v", ValueStr, LimitStr),
|
|
string.to_float(ValueStr, Value)
|
|
->
|
|
Limit = threshold_value(Value)
|
|
;
|
|
fail
|
|
).
|
|
|
|
:- func summarize_to_string(summarize) = string.
|
|
|
|
summarize_to_string(summarize) = "sum".
|
|
summarize_to_string(dont_summarize) = "nosum".
|
|
|
|
:- pred string_to_summarize(string::in, summarize::out) is semidet.
|
|
|
|
string_to_summarize("sum", summarize).
|
|
string_to_summarize("nosum", dont_summarize).
|
|
|
|
:- func order_criteria_to_string(order_criteria) = string.
|
|
|
|
order_criteria_to_string(by_context) = "context".
|
|
order_criteria_to_string(by_name) = "name".
|
|
order_criteria_to_string(by_cost(CostKind, InclDesc, Scope)) =
|
|
"cost" ++
|
|
string.char_to_string(criteria_separator_char) ++
|
|
cost_kind_to_string(CostKind) ++
|
|
string.char_to_string(criteria_separator_char) ++
|
|
incl_desc_to_string(InclDesc) ++
|
|
string.char_to_string(criteria_separator_char) ++
|
|
scope_to_string(Scope).
|
|
|
|
:- pred string_to_order_criteria(string::in, order_criteria::out) is semidet.
|
|
|
|
string_to_order_criteria(CriteriaStr, Criteria) :-
|
|
(
|
|
CriteriaStr = "context"
|
|
->
|
|
Criteria = by_context
|
|
;
|
|
CriteriaStr = "name"
|
|
->
|
|
Criteria = by_name
|
|
;
|
|
split(CriteriaStr, criteria_separator_char, Pieces),
|
|
Pieces = ["cost", CostKindStr, InclDescStr, ScopeStr],
|
|
string_to_cost_kind(CostKindStr, CostKind),
|
|
string_to_incl_desc(InclDescStr, InclDesc),
|
|
string_to_scope(ScopeStr, Scope)
|
|
->
|
|
Criteria = by_cost(CostKind, InclDesc, Scope)
|
|
;
|
|
fail
|
|
).
|
|
|
|
:- func scope_to_string(measurement_scope) = string.
|
|
|
|
scope_to_string(Scope) = String :-
|
|
string_to_scope(String, Scope).
|
|
|
|
:- pred string_to_scope(string, measurement_scope).
|
|
:- mode string_to_scope(in, out) is semidet.
|
|
:- mode string_to_scope(out, in) is det.
|
|
|
|
string_to_scope("pc", per_call).
|
|
string_to_scope("oa", overall).
|
|
|
|
:- func contour_to_string(contour) = string.
|
|
|
|
contour_to_string(Contour) = String :-
|
|
string_to_contour(String, Contour).
|
|
|
|
:- pred string_to_contour(string, contour).
|
|
:- mode string_to_contour(in, out) is semidet.
|
|
:- mode string_to_contour(out, in) is det.
|
|
|
|
string_to_contour("ac", apply_contour).
|
|
string_to_contour("nc", no_contour).
|
|
|
|
:- func time_format_to_string(time_format) = string.
|
|
|
|
time_format_to_string(no_scale) = "no".
|
|
time_format_to_string(scale_by_millions) = "mi".
|
|
time_format_to_string(scale_by_thousands) = "th".
|
|
|
|
:- pred string_to_time_format(string, time_format).
|
|
:- mode string_to_time_format(in, out) is semidet.
|
|
:- mode string_to_time_format(out, in) is det.
|
|
|
|
string_to_time_format("no", no_scale).
|
|
string_to_time_format("mi", scale_by_millions).
|
|
string_to_time_format("th", scale_by_thousands).
|
|
|
|
:- func inactive_items_to_string(inactive_items) = string.
|
|
|
|
inactive_items_to_string(Items) = String :-
|
|
string_to_inactive_items(String, Items).
|
|
|
|
:- pred string_to_inactive_items(string, inactive_items).
|
|
:- mode string_to_inactive_items(in, out) is semidet.
|
|
:- mode string_to_inactive_items(out, in) is det.
|
|
|
|
string_to_inactive_items("hh", inactive_items(inactive_hide, inactive_hide)).
|
|
string_to_inactive_items("sh", inactive_items(inactive_show, inactive_hide)).
|
|
string_to_inactive_items("hs", inactive_items(inactive_hide, inactive_show)).
|
|
string_to_inactive_items("ss", inactive_items(inactive_show, inactive_show)).
|
|
|
|
:- func colour_scheme_to_string(colour_scheme) = string.
|
|
|
|
colour_scheme_to_string(Scheme) = String :-
|
|
string_to_colour_scheme(String, Scheme).
|
|
|
|
:- pred string_to_colour_scheme(string, colour_scheme).
|
|
:- mode string_to_colour_scheme(in, out) is semidet.
|
|
:- mode string_to_colour_scheme(out, in) is det.
|
|
|
|
string_to_colour_scheme("cols", colour_column_groups).
|
|
string_to_colour_scheme("none", colour_none).
|
|
|
|
:- func box_to_string(box) = string.
|
|
|
|
box_to_string(Box) = String :-
|
|
string_to_box(String, Box).
|
|
|
|
:- pred string_to_box(string, box).
|
|
:- mode string_to_box(in, out) is semidet.
|
|
:- mode string_to_box(out, in) is det.
|
|
|
|
string_to_box("box", box).
|
|
string_to_box("nobox", nobox).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
:- end_module query.
|
|
%----------------------------------------------------------------------------%
|