Files
mercury/deep_profiler/query.m
Julien Fischer cfbbf931e8 Update more copyright notices in deep_profiler.
deep_profiler/*.m:
    As above.
2024-12-29 16:53:18 +11:00

1389 lines
46 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2001-2003, 2005-2012 The University of Melbourne.
% Copyright (C) 2014-2015, 2017, 2020 The Mercury team.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% File: 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 embedded 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 maybe.
%---------------------------------------------------------------------------%
%
% Reading and writing queries.
%
% A deep profiler query.
%
:- type deep_query
---> deep_query(
maybe_cmd :: maybe(cmd),
deep_file_name :: string,
maybe_prefs :: maybe(preferences)
).
% A subtype of the above, with a mandatory command field.
%
:- inst deep_query_with_cmd for deep_query/0
---> deep_query(bound(yes(ground)), ground, ground).
:- type cmd
---> deep_cmd_quit
; deep_cmd_restart
; deep_cmd_timeout(
cmd_timeout_minutes :: int
)
; deep_cmd_menu
; deep_cmd_root(
% If set to yes(Action), chase the dominant call sites
% until we get to a clique that is responsible for less than
% or equal to Action percent of the program's total callseqs.
cmd_root_maybe_action :: maybe(int)
)
; deep_cmd_clique(
cmd_clique_clique_id :: clique_ptr
)
; deep_cmd_clique_recursive_costs(
% Construct formulas for calculating the costs of the
% recursive calls within this clique.
cmd_crc_clique_id :: clique_ptr
)
; deep_cmd_proc(
cmd_proc_proc_id :: proc_static_ptr
)
; deep_cmd_proc_callers(
cmd_pc_proc_id :: proc_static_ptr,
cmd_pc_called_groups :: caller_groups,
cmd_pc_bunch_number :: int,
cmd_pc_callers_per_bunch :: int,
cmd_pc_contour_exclusion :: contour_exclusion
)
; deep_cmd_static_procrep_coverage(
cmd_static_coverage_ps :: proc_static_ptr
)
; deep_cmd_dynamic_procrep_coverage(
cmd_dynamic_coverage_pd :: proc_dynamic_ptr
)
; deep_cmd_call_site_dynamic_var_use(
cmd_csdvu_id :: call_site_dynamic_ptr
)
; deep_cmd_recursion_types_frequency
% Generate a frequency table about how often each recursion
% type occurs in the program.
; deep_cmd_program_modules
; deep_cmd_module(
cmd_module_module_name :: string
)
; deep_cmd_module_getter_setters(
cmd_mgs_module_name :: string
)
; deep_cmd_module_rep(
cmd_mr_module_name :: string
)
; deep_cmd_top_procs(
cmd_tp_display_limit :: display_limit,
cmd_tp_sort_cost_kind :: cost_kind,
cmd_tp_incl_desc :: include_descendants,
cmd_tp_scope :: measurement_scope
)
% The following commands are for debugging.
; deep_cmd_dump_clique(
cmd_dcl_id :: clique_ptr
)
; deep_cmd_dump_proc_static(
cmd_dps_id :: proc_static_ptr
)
; deep_cmd_dump_proc_dynamic(
cmd_dpd_id :: proc_dynamic_ptr
)
; deep_cmd_dump_call_site_static(
cmd_dcss_id :: call_site_static_ptr
)
; deep_cmd_dump_call_site_dynamic(
cmd_dcsd_id :: call_site_dynamic_ptr
).
:- 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 descendants_meaningful
---> descendants_meaningful
; descendants_not_meaningful.
:- 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
---> preferences(
% The set of fields to display.
pref_fields :: fields,
% Whether displays should be boxed.
pref_box :: box_tables,
% What principle governs colours.
pref_colour :: colour_column_groups,
% The max number of ancestors to display.
pref_anc :: maybe(int),
% The max number of proc statics to display for each recursion
% type group in the recursion type frequency report.
pref_proc_statics_per_rec_type
:: int,
% Whether pages should summarize at higher order call sites.
pref_summarize :: summarize_ho_call_sites,
% The criteria for ordering lines in pages, if the command
% doesn't specify otherwise.
pref_criteria :: order_criteria,
% Whether contour exclusion should be applied. The commands
% that depend on this setting take a contour value as an
% argument that will override this setting. However, we do not
% want to require users to restate their preferences about
% contour exclusion over and over again, so we store their
% preference here. A link from a page for which contour
% exclusion is irrelevant to a page for which it is relevant
% can pick up the preferred value of this parameter from here.
pref_contour :: contour_exclusion,
pref_time :: time_format,
pref_module_qual :: module_qual,
% Whether we should show modules/procs that haven't been
% called.
pref_inactive :: inactive_items,
% Whether to show developer-only options.
pref_developer_mode :: developer_mode
).
:- type preferences_indication
---> given_pref(preferences)
; default_pref
; all_pref.
:- 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_tables
---> box_tables
; do_not_box_tables.
:- type colour_column_groups
---> colour_column_groups
; do_not_colour_column_groups.
:- type summarize_ho_call_sites
---> summarize_ho_call_sites
; do_not_summarize_ho_call_sites.
:- type order_criteria
---> by_context
; by_name
; by_cost(
cost_kind,
include_descendants,
measurement_scope
).
:- type measurement_scope
---> per_call
; overall.
:- type contour_exclusion
---> apply_contour_exclusion
; do_not_apply_contour_exclusion.
:- type time_format
---> no_scale
; scale_by_millions
; scale_by_thousands.
:- type module_qual
---> module_qual_always
; module_qual_when_diff
; module_qual_never.
:- type inactive_status
---> inactive_hide
; inactive_show.
:- type inactive_items
---> inactive_items(
inactive_call_sites :: inactive_status,
inactive_procs :: inactive_status,
inactive_modules :: inactive_status
).
:- type developer_mode
---> developer_options_visible
; developer_options_invisible.
%---------------------------------------------------------------------------%
:- func query_to_string(deep_query) = string.
:- mode query_to_string(in(deep_query_with_cmd)) = out is det.
:- func string_to_maybe_query(string) = maybe(deep_query).
:- func string_to_maybe_cmd(string) = maybe(cmd).
:- func string_to_maybe_pref(string) = maybe(preferences).
%---------------------------------------------------------------------------%
:- pred try_exec(cmd::in, preferences::in, deep::in, string::out) is cc_multi.
%---------------------------------------------------------------------------%
% Return "yes" if it is worth displaying times for this profile.
%
:- func should_display_times(deep) = bool.
:- func default_command = cmd.
:- func solidify_preference(deep, preferences_indication) = preferences.
:- func default_preferences(deep) = preferences.
:- func default_fields(deep) = fields.
:- func all_fields = fields.
:- func default_box_tables = box_tables.
:- func default_colour_column_groups = colour_column_groups.
:- func default_ancestor_limit = maybe(int).
:- func default_proc_statics_per_rec_type_limit = int.
:- func default_summarize_ho_call_sites = summarize_ho_call_sites.
:- 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_exclusion = contour_exclusion.
:- func default_time_format = time_format.
:- func default_module_qual = module_qual.
:- func default_inactive_items = inactive_items.
:- func default_developer_mode = developer_mode.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module create_report.
:- import_module display_report.
:- import_module html_format.
:- import_module report.
:- import_module util.
:- import_module char.
:- import_module exception.
:- import_module int.
:- import_module io.
:- import_module list.
:- import_module string.
:- import_module univ.
%---------------------------------------------------------------------------%
%
% Converting whole queries to and from strings.
%
query_to_string(DeepQuery) = String :-
DeepQuery = deep_query(yes(Cmd), DeepFileName, MaybePreferences),
(
MaybePreferences = yes(Preferences),
PreferencesString = preferences_to_string(Preferences)
;
MaybePreferences = no,
PreferencesString = ""
),
String = cmd_to_string(Cmd) ++
string.char_to_string(query_separator_char) ++
PreferencesString ++
string.char_to_string(query_separator_char) ++
DeepFileName.
string_to_maybe_query(String) = MaybeDeepQuery :-
( if
split_query_string(String, MaybeCmdStr, MaybePrefStr, DeepFileName)
then
(
MaybeCmdStr = no,
MaybeCmd = no
;
MaybeCmdStr = yes(CmdStr),
MaybeCmd = yes(string_to_cmd(CmdStr, deep_cmd_menu))
),
(
MaybePrefStr = yes(PrefStr),
MaybePreferences = string_to_maybe_pref(PrefStr)
;
MaybePrefStr = no,
MaybePreferences = no
),
MaybeDeepQuery =
yes(deep_query(MaybeCmd, DeepFileName, MaybePreferences))
else
MaybeDeepQuery = no
).
% Break up the string into separate pieces.
% There may be one, two or three pieces.
%
:- pred split_query_string(string::in, maybe(string)::out, maybe(string)::out,
string::out) is semidet.
split_query_string(QueryString, MaybeCmdStr, MaybePrefStr, DeepFileName) :-
split(QueryString, query_separator_char, Pieces),
( if Pieces = [DeepFileName0] then
MaybeCmdStr = no,
MaybePrefStr = no,
DeepFileName = DeepFileName0
else if Pieces = [CmdStr, DeepFileName0] then
MaybeCmdStr = yes(CmdStr),
MaybePrefStr = no,
DeepFileName = DeepFileName0
else if Pieces = [CmdStr, PrefsStr, DeepFileName0] then
MaybeCmdStr = yes(CmdStr),
MaybePrefStr = yes(PrefsStr),
DeepFileName = DeepFileName0
else
fail
).
%---------------------------------------------------------------------------%
%
% Converting commands to and from strings.
%
:- func cmd_to_string(cmd) = string.
cmd_to_string(Cmd) = CmdStr :-
(
Cmd = deep_cmd_quit,
CmdStr = cmd_str_quit
;
Cmd = deep_cmd_restart,
CmdStr = cmd_str_restart
;
Cmd = deep_cmd_timeout(Minutes),
CmdStr = string.format("%s%c%d",
[s(cmd_str_timeout), c(cmd_separator_char), i(Minutes)])
;
Cmd = deep_cmd_menu,
CmdStr = cmd_str_menu
;
Cmd = deep_cmd_root(MaybePercent),
(
MaybePercent = yes(Percent),
CmdStr = string.format("%s%c%d",
[s(cmd_str_root), c(cmd_separator_char), i(Percent)])
;
MaybePercent = no,
CmdStr = string.format("%s%c%s",
[s(cmd_str_root), c(cmd_separator_char), s("no")])
)
;
(
Cmd = deep_cmd_clique(CliquePtr),
Name = cmd_str_clique
;
Cmd = deep_cmd_clique_recursive_costs(CliquePtr),
Name = cmd_str_clique_recursive_costs
),
CliquePtr = clique_ptr(CliqueNum),
CmdStr = string.format("%s%c%d",
[s(Name), c(cmd_separator_char), i(CliqueNum)])
;
Cmd = deep_cmd_recursion_types_frequency,
CmdStr = cmd_str_recursion_types_frequency
;
Cmd = deep_cmd_proc(PSPtr),
PSPtr = proc_static_ptr(PSI),
CmdStr = string.format("%s%c%d",
[s(cmd_str_proc), c(cmd_separator_char), i(PSI)])
;
Cmd = deep_cmd_proc_callers(PSPtr, GroupCallers, BunchNum,
CallersPerBunch, Contour),
PSPtr = proc_static_ptr(PSI),
GroupCallersStr = caller_groups_to_string(GroupCallers),
ContourStr = contour_exclusion_to_string(Contour),
CmdStr = string.format("%s%c%d%c%s%c%d%c%d%c%s",
[s(cmd_str_proc_callers),
c(cmd_separator_char), i(PSI),
c(cmd_separator_char), s(GroupCallersStr),
c(cmd_separator_char), i(BunchNum),
c(cmd_separator_char), i(CallersPerBunch),
c(cmd_separator_char), s(ContourStr)])
;
Cmd = deep_cmd_program_modules,
CmdStr = cmd_str_program_modules
;
Cmd = deep_cmd_module(ModuleName),
CmdStr = string.format("%s%c%s",
[s(cmd_str_module), c(cmd_separator_char), s(ModuleName)])
;
Cmd = deep_cmd_module_getter_setters(ModuleName),
CmdStr = string.format("%s%c%s",
[s(cmd_str_module_getter_setters), c(cmd_separator_char),
s(ModuleName)])
;
Cmd = deep_cmd_module_rep(ModuleName),
CmdStr = string.format("%s%c%s",
[s(cmd_str_module_rep), 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("%s%c%s%c%s%c%s%c%s",
[s(cmd_str_top_procs),
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_static_procrep_coverage(PSPtr),
PSPtr = proc_static_ptr(PSI),
CmdStr = string.format("%s%c%d",
[s(cmd_str_static_coverage), c(cmd_separator_char), i(PSI)])
;
Cmd = deep_cmd_dynamic_procrep_coverage(PDPtr),
PDPtr = proc_dynamic_ptr(PDI),
CmdStr = string.format("%s%c%d",
[s(cmd_str_dynamic_coverage), c(cmd_separator_char), i(PDI)])
;
Cmd = deep_cmd_dump_proc_static(PSPtr),
PSPtr = proc_static_ptr(PSI),
CmdStr = string.format("%s%c%d",
[s(cmd_str_dump_proc_static), c(cmd_separator_char), i(PSI)])
;
Cmd = deep_cmd_dump_proc_dynamic(PDPtr),
PDPtr = proc_dynamic_ptr(PDI),
CmdStr = string.format("%s%c%d",
[s(cmd_str_dump_proc_dynamic), c(cmd_separator_char), i(PDI)])
;
Cmd = deep_cmd_dump_call_site_static(CSSPtr),
CSSPtr = call_site_static_ptr(CSSI),
CmdStr = string.format("%s%c%d",
[s(cmd_str_dump_call_site_static), c(cmd_separator_char), i(CSSI)])
;
Cmd = deep_cmd_dump_call_site_dynamic(CSDPtr),
CSDPtr = call_site_dynamic_ptr(CSDI),
CmdStr = string.format("%s%c%d",
[s(cmd_str_dump_call_site_dynamic), c(cmd_separator_char),
i(CSDI)])
;
Cmd = deep_cmd_dump_clique(CliquePtr),
CliquePtr = clique_ptr(CliqueNum),
CmdStr = string.format("%s%c%d",
[s(cmd_str_dump_raw_clique), c(cmd_separator_char), i(CliqueNum)])
;
Cmd = deep_cmd_call_site_dynamic_var_use(CSDPtr),
CSDPtr = call_site_dynamic_ptr(CSDI),
CmdStr = string.format("%s%c%d",
[s(cmd_str_call_site_dynamic_var_use), c(cmd_separator_char),
i(CSDI)])
).
:- func string_to_cmd(string, cmd) = cmd.
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),
( if
Pieces = [cmd_str_root, MaybePercentStr],
( if MaybePercentStr = "no" then
MaybePercent = no
else if string.to_int(MaybePercentStr, Percent) then
MaybePercent = yes(Percent)
else
fail
)
then
Cmd = deep_cmd_root(MaybePercent),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_clique, CliqueNumStr],
string.to_int(CliqueNumStr, CliqueNum)
then
CliquePtr = clique_ptr(CliqueNum),
Cmd = deep_cmd_clique(CliquePtr),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_clique_recursive_costs, CliqueNumStr],
string.to_int(CliqueNumStr, CliqueNum)
then
CliquePtr = clique_ptr(CliqueNum),
Cmd = deep_cmd_clique_recursive_costs(CliquePtr),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_recursion_types_frequency]
then
Cmd = deep_cmd_recursion_types_frequency,
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_proc, PSIStr],
string.to_int(PSIStr, PSI)
then
PSPtr = proc_static_ptr(PSI),
Cmd = deep_cmd_proc(PSPtr),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_proc_callers, PSIStr, GroupCallersStr, BunchNumStr,
CallersPerBunchStr, ContourStr],
string.to_int(PSIStr, PSI),
string_to_caller_groups(GroupCallersStr, GroupCallers),
string.to_int(BunchNumStr, BunchNum),
string.to_int(CallersPerBunchStr, CallersPerBunch),
string_to_contour_exclusion(ContourStr, Contour)
then
PSPtr = proc_static_ptr(PSI),
Cmd = deep_cmd_proc_callers(PSPtr, GroupCallers, BunchNum,
CallersPerBunch, Contour),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_program_modules]
then
Cmd = deep_cmd_program_modules,
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_module, ModuleName]
then
Cmd = deep_cmd_module(ModuleName),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_module_getter_setters, ModuleName]
then
Cmd = deep_cmd_module_getter_setters(ModuleName),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_module_rep, ModuleName]
then
Cmd = deep_cmd_module_rep(ModuleName),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_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)
then
Cmd = deep_cmd_top_procs(Limit, CostKind, InclDesc, Scope),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_static_coverage, PSIStr],
string.to_int(PSIStr, PSI)
then
PSPtr = proc_static_ptr(PSI),
MaybeCmd = yes(deep_cmd_static_procrep_coverage(PSPtr))
else if
Pieces = [cmd_str_dynamic_coverage, PDIStr],
string.to_int(PDIStr, PDI)
then
PDPtr = proc_dynamic_ptr(PDI),
MaybeCmd = yes(deep_cmd_dynamic_procrep_coverage(PDPtr))
else if
Pieces = [cmd_str_menu]
then
Cmd = deep_cmd_menu,
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_dump_proc_static, PSIStr],
string.to_int(PSIStr, PSI)
then
PSPtr = proc_static_ptr(PSI),
Cmd = deep_cmd_dump_proc_static(PSPtr),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_dump_proc_dynamic, PDIStr],
string.to_int(PDIStr, PDI)
then
PDPtr = proc_dynamic_ptr(PDI),
Cmd = deep_cmd_dump_proc_dynamic(PDPtr),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_dump_call_site_static, CSSIStr],
string.to_int(CSSIStr, CSSI)
then
CSSPtr = call_site_static_ptr(CSSI),
Cmd = deep_cmd_dump_call_site_static(CSSPtr),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_dump_call_site_dynamic, CSDIStr],
string.to_int(CSDIStr, CSDI)
then
CSDPtr = call_site_dynamic_ptr(CSDI),
Cmd = deep_cmd_dump_call_site_dynamic(CSDPtr),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_dump_raw_clique, CliqueNumStr],
string.to_int(CliqueNumStr, CliqueNum)
then
CliquePtr = clique_ptr(CliqueNum),
Cmd = deep_cmd_dump_clique(CliquePtr),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_call_site_dynamic_var_use, CSDIStr],
string.to_int(CSDIStr, CSDI)
then
CSDPtr = call_site_dynamic_ptr(CSDI),
Cmd = deep_cmd_call_site_dynamic_var_use(CSDPtr),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_timeout, TimeOutStr],
string.to_int(TimeOutStr, TimeOut)
then
Cmd = deep_cmd_timeout(TimeOut),
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_restart]
then
Cmd = deep_cmd_restart,
MaybeCmd = yes(Cmd)
else if
Pieces = [cmd_str_quit]
then
Cmd = deep_cmd_quit,
MaybeCmd = yes(Cmd)
else
MaybeCmd = no
).
%---------------------%
% Constant strings used in command links and parsing.
%
:- func cmd_str_quit = string.
cmd_str_quit = "quit".
:- func cmd_str_restart = string.
cmd_str_restart = "restart".
:- func cmd_str_timeout = string.
cmd_str_timeout = "timeout".
:- func cmd_str_menu = string.
cmd_str_menu = "menu".
:- func cmd_str_root = string.
cmd_str_root = "root".
:- func cmd_str_clique = string.
cmd_str_clique = "clique".
:- func cmd_str_clique_recursive_costs = string.
cmd_str_clique_recursive_costs = "clique_rc".
:- func cmd_str_recursion_types_frequency = string.
cmd_str_recursion_types_frequency = "recursion_type_freq".
:- func cmd_str_proc = string.
cmd_str_proc = "proc".
:- func cmd_str_proc_callers = string.
cmd_str_proc_callers = "proc_callers".
:- func cmd_str_program_modules = string.
cmd_str_program_modules = "program_modules".
:- func cmd_str_module = string.
cmd_str_module = "module".
:- func cmd_str_module_getter_setters = string.
cmd_str_module_getter_setters = "module_getter_setters".
:- func cmd_str_module_rep = string.
cmd_str_module_rep = "module_rep".
:- func cmd_str_top_procs = string.
cmd_str_top_procs = "top_procs".
:- func cmd_str_static_coverage = string.
cmd_str_static_coverage = "proc_static_coverage".
:- func cmd_str_dynamic_coverage = string.
cmd_str_dynamic_coverage = "proc_dynamic_coverage".
:- func cmd_str_dump_proc_static = string.
cmd_str_dump_proc_static = "dump_proc_static".
:- func cmd_str_dump_proc_dynamic = string.
cmd_str_dump_proc_dynamic = "dump_proc_dynamic".
:- func cmd_str_dump_call_site_static = string.
cmd_str_dump_call_site_static = "dump_call_site_static".
:- func cmd_str_dump_call_site_dynamic = string.
cmd_str_dump_call_site_dynamic = "dump_call_site_dynamic".
:- func cmd_str_dump_raw_clique = string.
cmd_str_dump_raw_clique = "dump_raw_clique".
:- func cmd_str_call_site_dynamic_var_use = string.
cmd_str_call_site_dynamic_var_use = "call_site_dynamic_var_use".
%---------------------%
:- 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 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) :-
( if
split(LimitStr, limit_separator_char, Pieces),
Pieces = [FirstStr, LastStr],
string.to_int(FirstStr, First),
string.to_int(LastStr, Last)
then
Limit = rank_range(First, Last)
else if
string.append("p", PercentStr, LimitStr),
string.to_float(PercentStr, Threshold)
then
Limit = threshold_percent(Threshold)
else if
string.append("v", ValueStr, LimitStr),
string.to_float(ValueStr, Value)
then
Limit = threshold_value(Value)
else
fail
).
:- 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 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).
%---------------------------------------------------------------------------%
%
% Converting preferences to and from strings.
%
:- func preferences_to_string(preferences) = string.
preferences_to_string(Pref) = PrefStr :-
Pref = preferences(Fields, Box, Colour, MaybeAncestorLimit,
ProcStaticsPerRecTypeLimit, SummarizeHoCallSites, Order, Contour,
Time, ModuleQual, InactiveItems, DeveloperMode),
(
MaybeAncestorLimit = yes(AncestorLimit),
MaybeAncestorLimitStr =
string.format("%d", [i(AncestorLimit)])
;
MaybeAncestorLimit = no,
MaybeAncestorLimitStr = "no"
),
PrefStr = string.format("%s%c%s%c%s%c%s%c%d%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), i(ProcStaticsPerRecTypeLimit),
c(pref_separator_char), s(summarize_to_string(SummarizeHoCallSites)),
c(pref_separator_char), s(order_criteria_to_string(Order)),
c(pref_separator_char), s(contour_exclusion_to_string(Contour)),
c(pref_separator_char), s(time_format_to_string(Time)),
c(pref_separator_char), s(module_qual_to_string(ModuleQual)),
c(pref_separator_char), s(inactive_items_to_string(InactiveItems)),
c(pref_separator_char), s(developer_mode_to_string(DeveloperMode))
]).
string_to_maybe_pref(QueryString) = MaybePreferences :-
split(QueryString, pref_separator_char, Pieces),
( if
Pieces = [FieldsStr, BoxStr, ColourStr,
MaybeAncestorLimitStr, ProcStaticsPerRecTypeLimitStr,
SummarizeHoCallSitesStr, OrderStr, ContourStr, TimeStr,
ModuleQualStr, InactiveItemsStr, DeveloperModeStr],
string_to_fields(FieldsStr, Fields),
string_to_box(BoxStr, Box),
string_to_colour_scheme(ColourStr, Colour),
( if string.to_int(MaybeAncestorLimitStr, AncestorLimit) then
MaybeAncestorLimit = yes(AncestorLimit)
else if MaybeAncestorLimitStr = "no" then
MaybeAncestorLimit = no
else
fail
),
string.to_int(ProcStaticsPerRecTypeLimitStr,
ProcStaticsPerRecTypeLimit),
string_to_summarize(SummarizeHoCallSitesStr, SummarizeHoCallSites),
string_to_order_criteria(OrderStr, Order),
string_to_contour_exclusion(ContourStr, Contour),
string_to_time_format(TimeStr, Time),
string_to_module_qual(ModuleQualStr, ModuleQual),
string_to_inactive_items(InactiveItemsStr, InactiveItems),
string_to_developer_mode(DeveloperModeStr, DeveloperMode)
then
Preferences = preferences(Fields, Box, Colour, MaybeAncestorLimit,
ProcStaticsPerRecTypeLimit, SummarizeHoCallSites, Order, Contour,
Time, ModuleQual, InactiveItems, DeveloperMode),
MaybePreferences = yes(Preferences)
else
MaybePreferences = no
).
%---------------------------------------------------------------------------%
:- 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) :-
( if
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)
then
Fields = fields(Port, Time, CallSeqs, Alloc, Memory)
else
fail
).
%---------------------%
:- 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 box_to_string(box_tables) = string.
box_to_string(Box) = String :-
string_to_box(String, Box).
:- pred string_to_box(string, box_tables).
:- mode string_to_box(in, out) is semidet.
:- mode string_to_box(out, in) is det.
string_to_box("box", box_tables).
string_to_box("nobox", do_not_box_tables).
:- func colour_scheme_to_string(colour_column_groups) = string.
colour_scheme_to_string(Scheme) = String :-
string_to_colour_scheme(String, Scheme).
:- pred string_to_colour_scheme(string, colour_column_groups).
:- 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", do_not_colour_column_groups).
:- func summarize_to_string(summarize_ho_call_sites) = string.
summarize_to_string(summarize_ho_call_sites) = "sum".
summarize_to_string(do_not_summarize_ho_call_sites) = "nosum".
:- pred string_to_summarize(string::in, summarize_ho_call_sites::out)
is semidet.
string_to_summarize("sum", summarize_ho_call_sites).
string_to_summarize("nosum", do_not_summarize_ho_call_sites).
:- 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) :-
( if
CriteriaStr = "context"
then
Criteria = by_context
else if
CriteriaStr = "name"
then
Criteria = by_name
else if
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)
then
Criteria = by_cost(CostKind, InclDesc, Scope)
else
fail
).
:- func contour_exclusion_to_string(contour_exclusion) = string.
contour_exclusion_to_string(Contour) = String :-
string_to_contour_exclusion(String, Contour).
:- pred string_to_contour_exclusion(string, contour_exclusion).
:- mode string_to_contour_exclusion(in, out) is semidet.
:- mode string_to_contour_exclusion(out, in) is det.
string_to_contour_exclusion("ac", apply_contour_exclusion).
string_to_contour_exclusion("nc", do_not_apply_contour_exclusion).
:- 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 module_qual_to_string(module_qual) = string.
module_qual_to_string(module_qual_always) = "mqa".
module_qual_to_string(module_qual_when_diff) = "mqwd".
module_qual_to_string(module_qual_never) = "mqn".
:- pred string_to_module_qual(string::in, module_qual::out) is semidet.
string_to_module_qual("mqa", module_qual_always).
string_to_module_qual("mqwd", module_qual_when_diff).
string_to_module_qual("mqn", module_qual_never).
:- 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("hhh",
inactive_items(inactive_hide, inactive_hide, inactive_hide)).
string_to_inactive_items("hhs",
inactive_items(inactive_hide, inactive_hide, inactive_show)).
string_to_inactive_items("hsh",
inactive_items(inactive_hide, inactive_show, inactive_hide)).
string_to_inactive_items("hss",
inactive_items(inactive_hide, inactive_show, inactive_show)).
string_to_inactive_items("shh",
inactive_items(inactive_show, inactive_hide, inactive_hide)).
string_to_inactive_items("shs",
inactive_items(inactive_show, inactive_hide, inactive_show)).
string_to_inactive_items("ssh",
inactive_items(inactive_show, inactive_show, inactive_hide)).
string_to_inactive_items("sss",
inactive_items(inactive_show, inactive_show, inactive_show)).
:- func developer_mode_to_string(developer_mode) = string.
developer_mode_to_string(DevMode) = String :-
string_to_developer_mode(String, DevMode).
:- pred string_to_developer_mode(string, developer_mode).
:- mode string_to_developer_mode(in, out) is semidet.
:- mode string_to_developer_mode(out, in) is det.
string_to_developer_mode("dev", developer_options_visible).
string_to_developer_mode("nodev", developer_options_invisible).
%---------------------------------------------------------------------------%
:- func query_separator_char = char.
:- 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 = ('-').
%---------------------------------------------------------------------------%
try_exec(Cmd, Pref, Deep, HTML) :-
try(exec(Cmd, Pref, Deep), Result),
(
Result = succeeded(HTML)
;
Result = exception(Exception),
( if univ_to_type(Exception, MsgPrime) then
Msg = MsgPrime
else if univ_to_type(Exception, software_error(ErrorMsg)) then
Msg = "internal software error: " ++ ErrorMsg
else if univ_to_type(Exception, domain_error(DomainMsg)) then
Msg = "domain error: " ++ DomainMsg
else
Msg = "unknown exception",
trace [compile_time(flag("query_exception")), io(!DebugIO)] (
io.open_output("/tmp/deep_profiler_exception_debug",
DebugResult, !DebugIO),
(
DebugResult = ok(DebugStream),
io.write(DebugStream, Exception, !DebugIO),
io.close_output(DebugStream, !DebugIO)
;
DebugResult = error(_)
)
)
),
HTML = string.format("<H3>AN EXCEPTION HAS OCCURRED: %s</H3>\n",
[s(Msg)])
).
:- pred exec(cmd::in, preferences::in, deep::in, string::out) is det.
exec(Cmd, Prefs, Deep, HTMLStr) :-
( if slow_cmd(Cmd) then
create_and_memoize_report(Cmd, Deep, Report)
else
create_report(Cmd, Deep, Report)
),
Display = report_to_display(Deep, Prefs, Report),
HTMLStr = htmlize_display(Deep, Prefs, Display).
% slow_cmd(Cmd) is slow for any command that is slow and is probably going
% to be executed more than once.
%
:- pred slow_cmd(cmd::in) is semidet.
slow_cmd(deep_cmd_recursion_types_frequency).
:- pred create_and_memoize_report(cmd::in, deep::in, deep_report::out) is det.
:- pragma memo(create_and_memoize_report(in, in, out),
[disable_warning_if_ignored, specified([value, addr, output])]).
create_and_memoize_report(Cmd, Deep, Report) :-
create_report(Cmd, Deep, Report).
%---------------------------------------------------------------------------%
should_display_times(Deep) = ShouldDisplayTimes :-
UserQuanta = Deep ^ profile_stats ^ prs_user_quanta,
( if UserQuanta > minimum_meaningful_quanta then
ShouldDisplayTimes = yes
else
ShouldDisplayTimes = no
).
% 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.
default_command = deep_cmd_menu.
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_tables,
default_colour_column_groups,
default_ancestor_limit,
default_proc_statics_per_rec_type_limit,
default_summarize_ho_call_sites,
default_order_criteria,
default_contour_exclusion,
default_time_format,
default_module_qual,
default_inactive_items,
default_developer_mode
).
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_tables = box_tables.
default_colour_column_groups = colour_column_groups.
default_ancestor_limit = yes(5).
default_proc_statics_per_rec_type_limit = 20.
default_summarize_ho_call_sites = do_not_summarize_ho_call_sites.
default_order_criteria = by_context.
default_cost_kind = cost_callseqs.
default_incl_desc = self_and_desc.
default_scope = overall.
default_contour_exclusion = do_not_apply_contour_exclusion.
default_time_format = scale_by_thousands.
default_module_qual = module_qual_when_diff.
default_inactive_items =
inactive_items(inactive_hide, inactive_hide, inactive_hide).
default_developer_mode = developer_options_invisible.
%---------------------------------------------------------------------------%
:- end_module query.
%---------------------------------------------------------------------------%