[m-rev.] diff: move deprecated deep profiler code to separate modules

Zoltan Somogyi zs at csse.unimelb.edu.au
Thu Sep 25 17:32:51 AEST 2008


This diff has no algorithmic changes.

Zoltan.

deep_profiler/html_format.m:
deep_profiler/old_html_format.m:
deep_profiler/query.m:
deep_profiler/old_query.m:
	Move old, now deprecated code from query.m and html_format.m to two new
	modules: old_query.m and old_html_format.m. In each case, the leftover
	module, whose cord works via reports, is significantly smaller.

deep_profiler/measurement_units.m:
	Move some functions here from html_format.m, since they are needed
	with both the old and the new approaches, and the functions belong
	here.


cvs diff: Diffing .
Index: html_format.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/html_format.m,v
retrieving revision 1.32
diff -u -b -r1.32 html_format.m
--- html_format.m	25 Sep 2008 03:47:03 -0000	1.32
+++ html_format.m	25 Sep 2008 07:12:51 -0000
@@ -23,16 +23,11 @@
 :- module html_format.
 :- interface.
 
-:- import_module measurements.
 :- import_module profile.
 :- import_module query.
-:- import_module top_procs.
 :- import_module display.
 
-:- import_module bool.
 :- import_module cord.
-:- import_module list.
-:- import_module unit.
 
 %-----------------------------------------------------------------------------%
 
@@ -52,110 +47,6 @@
 
 %-----------------------------------------------------------------------------%
 
-:- func table_start(preferences) = string.
-:- func table_end(preferences) = string.
-
-:- func page_banner(cmd, preferences) = string.
-:- func page_footer(cmd, preferences, deep) = string.
-
-:- func toggle_cost_criteria_in_top_procs_cmd(preferences, deep, display_limit,
-    cost_kind, include_descendants, measurement_scope) = string.
-
-:- func criteria_to_description(order_criteria) = string.
-:- func cost_criteria_to_description(cost_kind, include_descendants,
-    measurement_scope) = string.
-
-:- type id_fields
-    --->    source_proc
-    ;       rank_module
-    ;       rank_proc
-    ;       proc.
-
-:- type totals_disposition
-    --->    totals_meaningful
-    ;       totals_not_meaningful.
-
-:- type header_wrap_func == (func(string, order_criteria) = string).
-
-:- func fields_header(preferences, id_fields, totals_disposition,
-    header_wrap_func) = string.
-
-:- func header_row(string, preferences, id_fields, totals_disposition)
-    = string.
-:- func separator_row(preferences, id_fields, totals_disposition)
-    = string.
-
-:- type sub_lines(T)
-    --->    sub_lines(
-                sub_line_type   :: T,
-                sub_line_list   :: list(line_group(T, unit))
-             ).
-
-:- type one_id ---> one_id.
-:- type two_id ---> two_id.
-
-:- type one_id_sub_lines == sub_lines(one_id).
-:- type two_id_sub_lines == sub_lines(two_id).
-
-:- type one_id_line == line_group(one_id, unit).
-:- type two_id_line == line_group(two_id, unit).
-:- type one_id_line_group == line_group(one_id, one_id_sub_lines).
-:- type two_id_line_group == line_group(two_id, two_id_sub_lines).
-:- type one_two_id_line_group == line_group(one_id, two_id_sub_lines).
-
-    % This function takes a context description (which may be empty) and a
-    % HTML string describing all fields in a row but the first, and returns
-    % the HTML for the full row.
-    %
-:- func add_context(string, line_group(one_id, LL)) = line_group(two_id, LL).
-
-    % This adds the context from the line group to the HTML as the first field
-    % of the resulting line group.
-    %
-:- func add_self_context(line_group(one_id, LL)) = line_group(two_id, LL).
-
-:- func add_ranks(list(line_group(one_id, LL))) = list(line_group(two_id, LL)).
-
-:- func line_to_one_id_subline_group(line_group(FL, unit))
-    = line_group(FL, one_id_sub_lines).
-
-:- func line_to_two_id_subline_group(line_group(FL, unit))
-    = line_group(FL, two_id_sub_lines).
-
-:- func one_id_line_to_html(preferences, deep, totals_disposition,
-    one_id_line) = string.
-:- func one_id_line_group_to_html(preferences, deep, totals_disposition,
-    one_id_line_group) = string.
-:- func two_id_line_to_html(preferences, deep, totals_disposition,
-    two_id_line) = string.
-:- func two_id_line_group_to_html(preferences, deep, totals_disposition,
-    two_id_line_group) = string.
-
-:- func own_and_desc_to_html(own_prof_info, inherit_prof_info,
-    preferences, deep, totals_disposition) = string.
-
-:- pred lookup_ticks_per_sec(profile_stats::in, int::out, bool::out) is det.
-
-:- func proc_dynamic_name(deep, proc_dynamic_ptr) = string.
-:- func proc_static_name(deep, proc_static_ptr) = string.
-
-:- pred proc_dynamic_context(deep::in, proc_dynamic_ptr::in,
-    string::out, int::out) is det.
-:- pred proc_static_context(deep::in, proc_static_ptr::in,
-    string::out, int::out) is det.
-:- pred call_site_context(deep::in, call_site_static_ptr::in,
-    string::out, int::out) is det.
-
-:- pred proc_static_to_line_group_info(preferences::in, deep::in,
-    proc_static_ptr::in, string::out, int::out, string::out, string::out)
-    is det.
-:- func proc_static_to_html_ref(preferences, deep, proc_static_ptr) = string.
-:- func module_name_to_html_ref(preferences, deep, string) = string.
-:- func clique_ptr_to_html_ref(preferences, deep, string, clique_ptr) = string.
-:- func deep_cmd_pref_to_url(preferences, deep, cmd) = string.
-
-:- func plural(int) = string.
-
     % Convert any special characters in a string into appropriate HTML
     % escapes.
     %
@@ -179,6 +70,7 @@
 :- import_module char.
 :- import_module exception.
 :- import_module float.
+:- import_module list.
 :- import_module int.
 :- import_module map.
 :- import_module maybe.
@@ -1036,2094 +928,6 @@
         !.ColumnNum, Tail, !HTML).
 
 %-----------------------------------------------------------------------------%
-%
-% Deprecated html_format code.
-%
-
-page_banner(_Cmd, Pref) =
-    "<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.01//EN""\n" ++
-    """http://www.w3.org/TR/html4/strict.dtd"">\n" ++
-    "<HTML>\n" ++
-    "<HEAD>\n" ++
-    "<TITLE>Page created by the Mercury Deep Profiler.</TITLE>\n" ++
-    banner_style(Pref) ++
-    "</HEAD>\n" ++
-    "<BODY>\n".
-
-:- func banner_style(preferences) = string.
-
-banner_style(Pref) = HTML :-
-    Fields = Pref ^ pref_fields,
-
-    some [!GroupNum] (
-        !:GroupNum = 0,
-        IdStyle = string.format("  TD.id       { %s }\n",
-            [s(select_colgroup_background(Pref, !.GroupNum))]),
-        !:GroupNum = !.GroupNum + 1,
-        Fields = fields(PortFields, TimeFields, CallSeqsFields,
-            AllocFields, MemoryFields),
-        (
-            PortFields = no_port,
-            PortStyle = ""
-        ;
-            PortFields = port,
-            PortStyle = string.format("  TD.port     { %s }\n",
-                [s(select_colgroup_background(Pref, !.GroupNum))]),
-            !:GroupNum = !.GroupNum + 1
-        ),
-        (
-            TimeFields = no_time,
-            TimeStyle = ""
-        ;
-            ( TimeFields = ticks
-            ; TimeFields = time
-            ; TimeFields = ticks_and_time
-            ; TimeFields = time_and_percall
-            ; TimeFields = ticks_and_time_and_percall
-            ),
-            TimeStyle = string.format("  TD.time     { %s }\n",
-                [s(select_colgroup_background(Pref, !.GroupNum))]),
-            !:GroupNum = !.GroupNum + 1
-        ),
-        (
-            CallSeqsFields = no_callseqs,
-            CallSeqsStyle = ""
-        ;
-            ( CallSeqsFields = callseqs
-            ; CallSeqsFields = callseqs_and_percall
-            ),
-            CallSeqsStyle = string.format("  TD.callseqs { %s }\n",
-                [s(select_colgroup_background(Pref, !.GroupNum))]),
-            !:GroupNum = !.GroupNum + 1
-        ),
-        (
-            AllocFields = no_alloc,
-            AllocStyle = ""
-        ;
-            ( AllocFields = alloc
-            ; AllocFields = alloc_and_percall
-            ),
-            AllocStyle = string.format("  TD.alloc    { %s }\n",
-                [s(select_colgroup_background(Pref, !.GroupNum))]),
-            !:GroupNum = !.GroupNum + 1
-        ),
-        (
-            MemoryFields = no_memory,
-            MemoryStyle = ""
-        ;
-            ( MemoryFields = memory(_)
-            ; MemoryFields = memory_and_percall(_)
-            ),
-            MemoryStyle = string.format("  TD.memory   { %s }\n",
-                [s(select_colgroup_background(Pref, !.GroupNum))])
-        )
-    ),
-    ButtonStyle = "  A.button { margin: 5px; text-decoration: none; }\n",
-    HTML =
-        "<STYLE TYPE=""text/css"">\n" ++
-        IdStyle ++
-        PortStyle ++
-        TimeStyle ++
-        CallSeqsStyle ++
-        AllocStyle ++
-        MemoryStyle ++
-        ButtonStyle ++
-        "</STYLE>\n".
-
-:- func select_colgroup_background(preferences, int) = string.
-
-select_colgroup_background(Pref, N) = HTML :-
-    (
-        Pref ^ pref_colour = colour_column_groups,
-        ( N /\ 1 = 0 ->
-            Background = even_background
-        ;
-            Background = odd_background
-        ),
-        string.format("background: %s", [s(Background)], HTML)
-    ;
-        Pref ^ pref_colour = do_not_colour_column_groups,
-        HTML = ""
-    ).
-
-:- func even_background = string.
-
-even_background = "rgb(255, 255, 240)".
-
-:- func odd_background = string.
-
-odd_background =  "rgb(240, 240, 255)".
-
-%-----------------------------------------------------------------------------%
-
-table_start(Pref) = HTML :-
-    (
-        Pref ^ pref_box = do_not_box_tables,
-        HTML = "\n<TABLE width=100%>\n"
-    ;
-        Pref ^ pref_box = box_tables,
-        HTML = "\n<TABLE width=100% border=1>\n"
-    ).
-
-table_end(_) = "</TABLE>\n".
-
-%-----------------------------------------------------------------------------%
-
-page_footer(Cmd, Pref, Deep) =
-    "<p>\n" ++
-    footer_pref_toggles(Cmd, Pref, Deep) ++
-    "<br>\n" ++
-    string.format("<A CLASS=""button"" HREF=""%s"">[Menu]</A>\n",
-        [s(deep_cmd_pref_to_url(Pref, Deep, deep_cmd_menu))]) ++
-    string.format("<A CLASS=""button"" HREF=""%s"">[Restart]</A>\n",
-        [s(deep_cmd_pref_to_url(Pref, Deep, deep_cmd_restart))]) ++
-    string.format("<A CLASS=""button"" HREF=""%s"">[Quit]</A>\n",
-        [s(deep_cmd_pref_to_url(Pref, Deep, deep_cmd_quit))]) ++
-    "</BODY>\n" ++
-    "</HTML>\n".
-
-:- func footer_pref_toggles(cmd, preferences, deep) = string.
-
-footer_pref_toggles(Cmd, Pref, Deep) = AllToggles :-
-    RelevantToggles = command_relevant_toggles(Cmd),
-    ( list.member(toggle_fields, RelevantToggles) ->
-        FieldToggle = footer_field_toggle(Cmd, Pref, Deep)
-    ;
-        FieldToggle = ""
-    ),
-    ( list.member(toggle_ancestor_limit, RelevantToggles) ->
-        AncestorToggle =
-            footer_ancestor_toggle(Cmd, Pref, Deep) ++
-            "<br>\n"
-    ;
-        AncestorToggle = ""
-    ),
-    ( list.member(toggle_order_criteria, RelevantToggles) ->
-        OrderToggle =
-            footer_order_criteria_toggle(Cmd, Pref, Deep) ++
-            "<br>\n"
-    ;
-        OrderToggle = ""
-    ),
-    ( list.member(toggle_summarize, RelevantToggles) ->
-        SummarizeToggle = footer_summarize_toggle(Cmd, Pref, Deep)
-    ;
-        SummarizeToggle = ""
-    ),
-    ( list.member(toggle_contour, RelevantToggles) ->
-        ContourToggle = footer_contour_toggle(Cmd, Pref, Deep)
-    ;
-        ContourToggle = ""
-    ),
-    ( list.member(toggle_time_format, RelevantToggles) ->
-        TimeFormatToggle = footer_time_format_toggle(Cmd, Pref, Deep)
-    ;
-        TimeFormatToggle = ""
-    ),
-    ( list.member(toggle_colour, RelevantToggles) ->
-        ColourToggle = footer_colour_toggle(Cmd, Pref, Deep)
-    ;
-        ColourToggle = ""
-    ),
-    ( list.member(toggle_box, RelevantToggles) ->
-        BoxToggle = footer_box_toggle(Cmd, Pref, Deep)
-    ;
-        BoxToggle = ""
-    ),
-    ( list.member(toggle_inactive_modules, RelevantToggles) ->
-        InactiveModuleToggle = footer_inactive_modules_toggle(Cmd, Pref, Deep)
-    ;
-        InactiveModuleToggle = ""
-    ),
-    ( list.member(toggle_inactive_procs, RelevantToggles) ->
-        InactiveProcsToggle = footer_inactive_procs_toggle(Cmd, Pref, Deep)
-    ;
-        InactiveProcsToggle = ""
-    ),
-    AllToggles =
-        FieldToggle ++
-        AncestorToggle ++
-        OrderToggle ++
-        SummarizeToggle ++
-        ContourToggle ++
-        TimeFormatToggle ++
-        ColourToggle ++
-        BoxToggle ++
-        InactiveModuleToggle ++
-        InactiveProcsToggle.
-
-%-----------------------------------------------------------------------------%
-
-:- type toggle_kind
-    --->    toggle_fields
-    ;       toggle_box
-    ;       toggle_colour
-    ;       toggle_ancestor_limit
-    ;       toggle_summarize
-    ;       toggle_order_criteria
-    ;       toggle_contour
-    ;       toggle_time_format
-    ;       toggle_inactive_modules
-    ;       toggle_inactive_procs.
-
-:- func command_relevant_toggles(cmd) = list(toggle_kind).
-
-command_relevant_toggles(deep_cmd_quit) = [].
-command_relevant_toggles(deep_cmd_restart) = [].
-command_relevant_toggles(deep_cmd_timeout(_)) = [].
-command_relevant_toggles(deep_cmd_menu) = [].
-command_relevant_toggles(deep_cmd_root(_)) =
-    % The clique_ptr doesn't matter.
-    command_relevant_toggles(deep_cmd_clique(clique_ptr(1))).
-command_relevant_toggles(deep_cmd_clique(_)) =
-    [toggle_fields, toggle_box, toggle_colour, toggle_ancestor_limit,
-    toggle_summarize, toggle_order_criteria, toggle_time_format].
-command_relevant_toggles(deep_cmd_proc(_)) =
-    [toggle_fields, toggle_box, toggle_colour, toggle_summarize,
-    toggle_order_criteria, toggle_time_format].
-command_relevant_toggles(deep_cmd_proc_callers(_, _, _, _)) =
-    [toggle_fields, toggle_box, toggle_colour, toggle_order_criteria,
-    toggle_contour, toggle_time_format].
-command_relevant_toggles(deep_cmd_program_modules) =
-    [toggle_fields, toggle_box, toggle_colour, toggle_order_criteria,
-    toggle_time_format, toggle_inactive_modules].
-command_relevant_toggles(deep_cmd_module(_)) =
-    [toggle_fields, toggle_box, toggle_colour, toggle_order_criteria,
-    toggle_time_format, toggle_inactive_procs].
-command_relevant_toggles(deep_cmd_top_procs(_, _, _, _)) =
-    [toggle_fields, toggle_box, toggle_colour, toggle_time_format].
-command_relevant_toggles(deep_cmd_procrep_coverage(_)) = 
-    [].
-command_relevant_toggles(deep_cmd_dump_proc_static(_)) = [].
-command_relevant_toggles(deep_cmd_dump_proc_dynamic(_)) = [].
-command_relevant_toggles(deep_cmd_dump_call_site_static(_)) = [].
-command_relevant_toggles(deep_cmd_dump_call_site_dynamic(_)) = [].
-command_relevant_toggles(deep_cmd_dump_clique(_)) = [].
-
-:- func footer_field_toggle(cmd, preferences, deep) = string.
-
-footer_field_toggle(Cmd, Pref, Deep) = HTML :-
-    Fields = Pref ^ pref_fields,
-    ( Fields ^ port_fields = no_port ->
-        Port1Toggle = ""
-    ;
-        Port1Fields = Fields ^ port_fields := no_port,
-        Port1Pref = Pref ^ pref_fields := Port1Fields,
-        Port1Msg = "[No port counts]",
-        Port1Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(deep_cmd_pref_to_url(Port1Pref, Deep, Cmd)), s(Port1Msg)])
-    ),
-    ( Fields ^ port_fields = port ->
-        Port2Toggle = ""
-    ;
-        Port2Fields = Fields ^ port_fields := port,
-        Port2Pref = Pref ^ pref_fields := Port2Fields,
-        Port2Msg = "[Port counts]",
-        Port2Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(deep_cmd_pref_to_url(Port2Pref, Deep, Cmd)), s(Port2Msg)])
-    ),
-    ( Fields ^ time_fields = no_time ->
-        Time1Toggle = ""
-    ;
-        Time1Fields = Fields ^ time_fields := no_time,
-        Time1Pref = Pref ^ pref_fields := Time1Fields,
-        Time1Msg = "[No time info]",
-        Time1Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(deep_cmd_pref_to_url(Time1Pref, Deep, Cmd)), s(Time1Msg)])
-    ),
-    ( Fields ^ time_fields = ticks ->
-        Time2Toggle = ""
-    ;
-        Time2Fields = Fields ^ time_fields := ticks,
-        Time2Pref = Pref ^ pref_fields := Time2Fields,
-        Time2Msg = "[Ticks]",
-        Time2Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(deep_cmd_pref_to_url(Time2Pref, Deep, Cmd)), s(Time2Msg)])
-    ),
-    ( Fields ^ time_fields = time ->
-        Time3Toggle = ""
-    ;
-        Time3Fields = Fields ^ time_fields := time,
-        Time3Pref = Pref ^ pref_fields := Time3Fields,
-        Time3Msg = "[Times]",
-        Time3Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(deep_cmd_pref_to_url(Time3Pref, Deep, Cmd)), s(Time3Msg)])
-    ),
-    ( Fields ^ time_fields = ticks_and_time->
-        Time4Toggle = ""
-    ;
-        Time4Fields = Fields ^ time_fields := ticks_and_time,
-        Time4Pref = Pref ^ pref_fields := Time4Fields,
-        Time4Msg = "[Ticks and times]",
-        Time4Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(deep_cmd_pref_to_url(Time4Pref, Deep, Cmd)), s(Time4Msg)])
-    ),
-    ( Fields ^ time_fields = time_and_percall ->
-        Time5Toggle = ""
-    ;
-        Time5Fields = Fields ^ time_fields := time_and_percall,
-        Time5Pref = Pref ^ pref_fields := Time5Fields,
-        Time5Msg = "[Times and per-call times]",
-        Time5Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(deep_cmd_pref_to_url(Time5Pref, Deep, Cmd)), s(Time5Msg)])
-    ),
-    ( Fields ^ time_fields = ticks_and_time_and_percall ->
-        Time6Toggle = ""
-    ;
-        Time6Fields = Fields ^ time_fields := ticks_and_time_and_percall,
-        Time6Pref = Pref ^ pref_fields := Time6Fields,
-        Time6Msg = "[Ticks and times and per-call times]",
-        Time6Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(deep_cmd_pref_to_url(Time6Pref, Deep, Cmd)), s(Time6Msg)])
-    ),
-    ( Fields ^ callseqs_fields = no_callseqs ->
-        CallSeqs1Toggle = ""
-    ;
-        CallSeqs1Fields = Fields ^ callseqs_fields := no_callseqs,
-        CallSeqs1Pref = Pref ^ pref_fields := CallSeqs1Fields,
-        CallSeqs1Msg = "[No call sequence number info]",
-        CallSeqs1Toggle =
-            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-                [s(deep_cmd_pref_to_url(CallSeqs1Pref, Deep, Cmd)),
-                s(CallSeqs1Msg)])
-    ),
-    ( Fields ^ callseqs_fields = callseqs ->
-        CallSeqs2Toggle = ""
-    ;
-        CallSeqs2Fields = Fields ^ callseqs_fields := callseqs,
-        CallSeqs2Pref = Pref ^ pref_fields := CallSeqs2Fields,
-        CallSeqs2Msg = "[Call sequence numbers]",
-        CallSeqs2Toggle =
-            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-                [s(deep_cmd_pref_to_url(CallSeqs2Pref, Deep, Cmd)),
-                s(CallSeqs2Msg)])
-    ),
-    ( Fields ^ callseqs_fields = callseqs_and_percall ->
-        CallSeqs3Toggle = ""
-    ;
-        CallSeqs3Fields = Fields ^ callseqs_fields := callseqs_and_percall,
-        CallSeqs3Pref = Pref ^ pref_fields := CallSeqs3Fields,
-        CallSeqs3Msg = "[Call sequence numbers including per-call]",
-        CallSeqs3Toggle =
-            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-                [s(deep_cmd_pref_to_url(CallSeqs3Pref, Deep, Cmd)),
-                s(CallSeqs3Msg)])
-    ),
-    ( Fields ^ alloc_fields = no_alloc ->
-        Alloc1Toggle = ""
-    ;
-        Alloc1Fields = Fields ^ alloc_fields := no_alloc,
-        Alloc1Pref = Pref ^ pref_fields := Alloc1Fields,
-        Alloc1Msg = "[No allocations]",
-        Alloc1Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(deep_cmd_pref_to_url(Alloc1Pref, Deep, Cmd)), s(Alloc1Msg)])
-    ),
-    ( Fields ^ alloc_fields = alloc ->
-        Alloc2Toggle = ""
-    ;
-        Alloc2Fields = Fields ^ alloc_fields := alloc,
-        Alloc2Pref = Pref ^ pref_fields := Alloc2Fields,
-        Alloc2Msg = "[Allocations]",
-        Alloc2Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(deep_cmd_pref_to_url(Alloc2Pref, Deep, Cmd)), s(Alloc2Msg)])
-    ),
-    ( Fields ^ alloc_fields = alloc_and_percall ->
-        Alloc3Toggle = ""
-    ;
-        Alloc3Fields = Fields ^ alloc_fields := alloc_and_percall,
-        Alloc3Pref = Pref ^ pref_fields := Alloc3Fields,
-        Alloc3Msg = "[Allocations and per-call allocations]",
-        Alloc3Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(deep_cmd_pref_to_url(Alloc3Pref, Deep, Cmd)), s(Alloc3Msg)])
-    ),
-    ( Fields ^ memory_fields = no_memory ->
-        Memory1Toggle = ""
-    ;
-        Memory1Fields = Fields ^ memory_fields := no_memory,
-        Memory1Pref = Pref ^ pref_fields := Memory1Fields,
-        Memory1Msg = "[No memory info]",
-        Memory1Toggle =
-            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-                [s(deep_cmd_pref_to_url(Memory1Pref, Deep, Cmd)),
-                s(Memory1Msg)])
-    ),
-    ( Fields ^ memory_fields = memory(units_words) ->
-        Memory2Toggle = ""
-    ;
-        Memory2Fields = Fields ^ memory_fields := memory(units_words),
-        Memory2Pref = Pref ^ pref_fields := Memory2Fields,
-        Memory2Msg = "[Words]",
-        Memory2Toggle =
-            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-                [s(deep_cmd_pref_to_url(Memory2Pref, Deep, Cmd)),
-                s(Memory2Msg)])
-    ),
-    ( Fields ^ memory_fields = memory(units_bytes) ->
-        Memory3Toggle = ""
-    ;
-        Memory3Fields = Fields ^ memory_fields := memory(units_bytes),
-        Memory3Pref = Pref ^ pref_fields := Memory3Fields,
-        Memory3Msg = "[Bytes]",
-        Memory3Toggle =
-            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-                [s(deep_cmd_pref_to_url(Memory3Pref, Deep, Cmd)),
-                s(Memory3Msg)])
-    ),
-    ( Fields ^ memory_fields = memory_and_percall(units_words) ->
-        Memory4Toggle = ""
-    ;
-        Memory4Fields = Fields ^ memory_fields :=
-            memory_and_percall(units_words),
-        Memory4Pref = Pref ^ pref_fields := Memory4Fields,
-        Memory4Msg = "[Words and per-call words]",
-        Memory4Toggle =
-            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-                [s(deep_cmd_pref_to_url(Memory4Pref, Deep, Cmd)),
-                s(Memory4Msg)])
-    ),
-    ( Fields ^ memory_fields = memory_and_percall(units_bytes) ->
-        Memory5Toggle = ""
-    ;
-        Memory5Fields = Fields ^ memory_fields :=
-            memory_and_percall(units_bytes),
-        Memory5Pref = Pref ^ pref_fields := Memory5Fields,
-        Memory5Msg = "[Bytes and per-call bytes]",
-        Memory5Toggle =
-            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-                [s(deep_cmd_pref_to_url(Memory5Pref, Deep, Cmd)),
-                s(Memory5Msg)])
-    ),
-    ( Fields = default_fields(Deep) ->
-        DefaultToggle = ""
-    ;
-        DefaultMsg  = "[Restore defaults]",
-        DefaultPref = Pref ^ pref_fields := default_fields(Deep),
-        DefaultToggle =
-            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-                [s(deep_cmd_pref_to_url(DefaultPref, Deep, Cmd)),
-                s(DefaultMsg)])
-    ),
-    HTML =
-        "<strong>Toggle fields:</strong>\n" ++
-        DefaultToggle ++
-        "<br>\n" ++
-        Port1Toggle ++ Port2Toggle ++
-        "<br>\n" ++
-        Time1Toggle ++ Time2Toggle ++ Time3Toggle ++
-        Time4Toggle ++ Time5Toggle ++ Time6Toggle ++
-        "<br>\n" ++
-        CallSeqs1Toggle ++ CallSeqs2Toggle ++ CallSeqs3Toggle ++
-        "<br>\n" ++
-        Alloc1Toggle ++ Alloc2Toggle ++ Alloc3Toggle ++
-        "<br>\n" ++
-        Memory1Toggle ++ Memory2Toggle ++ Memory3Toggle ++
-        Memory4Toggle ++ Memory5Toggle ++
-        "<br>\n".
-
-:- func footer_ancestor_toggle(cmd, preferences, deep) = string.
-
-footer_ancestor_toggle(Cmd, Pref, Deep) = HTML :-
-    (
-        Pref ^ pref_anc = no,
-        Display1 = yes,
-        Display2 = yes,
-        Msg1 = "[One ancestor]",
-        Pref1 = Pref ^ pref_anc := yes(1),
-        Msg2 = "[Two ancestors]",
-        Pref2 = Pref ^ pref_anc := yes(2),
-        Msg3 = "[Three ancestors]",
-        Pref3 = Pref ^ pref_anc := yes(3),
-        Msg4 = "[Five ancestors]",
-        Pref4 = Pref ^ pref_anc := yes(5),
-        Msg5 = "[Ten ancestors]",
-        Pref5 = Pref ^ pref_anc := yes(10)
-    ;
-        Pref ^ pref_anc = yes(OldAncestorLimit),
-        ( OldAncestorLimit > 2 ->
-            Display1 = yes
-        ;
-            Display1 = no
-        ),
-        ( OldAncestorLimit > 1 ->
-            Display2 = yes
-        ;
-            Display2 = no
-        ),
-        Msg1 = "[Halve ancestors]",
-        Pref1 = Pref ^ pref_anc := yes(OldAncestorLimit // 2),
-        Msg2 = "[Remove an ancestor]",
-        Pref2 = Pref ^ pref_anc := yes(OldAncestorLimit - 1),
-        Msg3 = "[Add an ancestor]",
-        Pref3 = Pref ^ pref_anc := yes(OldAncestorLimit + 1),
-        Msg4 = "[Double ancestors]",
-        Pref4 = Pref ^ pref_anc := yes(OldAncestorLimit * 2),
-        Msg5 = "[Unlimited ancestors]",
-        Pref5 = Pref ^ pref_anc := no
-    ),
-    Toggle1 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-        [s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]),
-    Toggle2 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-        [s(deep_cmd_pref_to_url(Pref2, Deep, Cmd)), s(Msg2)]),
-    Toggle3 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-        [s(deep_cmd_pref_to_url(Pref3, Deep, Cmd)), s(Msg3)]),
-    Toggle4 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-        [s(deep_cmd_pref_to_url(Pref4, Deep, Cmd)), s(Msg4)]),
-    Toggle5 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-        [s(deep_cmd_pref_to_url(Pref5, Deep, Cmd)), s(Msg5)]),
-    (
-        Display1 = yes,
-        MaybeToggle1 = Toggle1
-    ;
-        Display1 = no,
-        MaybeToggle1 = ""
-    ),
-    (
-        Display2 = yes,
-        MaybeToggle2 = Toggle2
-    ;
-        Display2 = no,
-        MaybeToggle2 = ""
-    ),
-    HTML =
-        "<strong>Toggle ancestors:</strong><br />\n" ++
-        MaybeToggle1 ++ MaybeToggle2 ++ Toggle3 ++ Toggle4 ++ Toggle5.
-
-:- func footer_box_toggle(cmd, preferences, deep) = string.
-
-footer_box_toggle(Cmd, Pref, Deep) = HTML :-
-    (
-        Pref ^ pref_box = do_not_box_tables,
-        Pref1 = Pref ^ pref_box := box_tables,
-        Msg1 = "[Box]"
-    ;
-        Pref ^ pref_box = box_tables,
-        Pref1 = Pref ^ pref_box := do_not_box_tables,
-        Msg1 = "[Unbox]"
-    ),
-    HTML = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-        [s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]).
-
-:- func footer_colour_toggle(cmd, preferences, deep) = string.
-
-footer_colour_toggle(Cmd, Pref, Deep) = HTML :-
-    (
-        Pref ^ pref_colour = do_not_colour_column_groups,
-        Pref1 = Pref ^ pref_colour := colour_column_groups,
-        Msg1 = "[Colour column groups]"
-    ;
-        Pref ^ pref_colour = colour_column_groups,
-        Pref1 = Pref ^ pref_colour := do_not_colour_column_groups,
-        Msg1 = "[Fade column groups]"
-    ),
-    HTML = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-        [s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]).
-
-:- func footer_summarize_toggle(cmd, preferences, deep) = string.
-
-footer_summarize_toggle(Cmd, Pref, Deep) = HTML :-
-    (
-        Pref ^ pref_summarize = summarize,
-        Pref1 = Pref ^ pref_summarize := do_not_summarize,
-        Msg1 = "[Expand higher order calls]"
-    ;
-        Pref ^ pref_summarize = do_not_summarize,
-        Pref1 = Pref ^ pref_summarize := summarize,
-        Msg1 = "[Summarize higher order calls]"
-    ),
-    HTML = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-        [s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]).
-
-:- func footer_contour_toggle(cmd, preferences, deep) = string.
-
-footer_contour_toggle(Cmd, Pref, Deep) = HTML :-
-    (
-        Pref ^ pref_contour = do_not_apply_contour_exclusion,
-        Pref1 = Pref ^ pref_contour := apply_contour_exclusion,
-        Msg1 = "[Apply contour exclusion]"
-    ;
-        Pref ^ pref_contour = apply_contour_exclusion,
-        Pref1 = Pref ^ pref_contour := do_not_apply_contour_exclusion,
-        Msg1 = "[Don't apply contour exclusion]"
-    ),
-    HTML = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-        [s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]).
-
-:- func footer_time_format_toggle(cmd, preferences, deep) = string.
-
-footer_time_format_toggle(Cmd, Pref, Deep) = HTML :-
-    TimeFields = Pref ^ pref_fields ^ time_fields,
-    (
-        ( TimeFields = no_time
-        ; TimeFields = ticks
-        ),
-        ToggleTimeFormat = no
-    ;
-        ( TimeFields = time
-        ; TimeFields = ticks_and_time
-        ; TimeFields = time_and_percall
-        ; TimeFields = ticks_and_time_and_percall
-        ),
-        ToggleTimeFormat = yes
-    ),
-    (
-        ToggleTimeFormat = no,
-        HTML = ""
-    ;
-        ToggleTimeFormat = yes,
-        (
-            Pref ^ pref_time = no_scale,
-            Pref1 = Pref ^ pref_time := scale_by_millions,
-            Msg1  = "[Time in s, us]",
-            Pref2 = Pref ^ pref_time := scale_by_thousands,
-            Msg2  = "[Time in s, ms, us, ns]"
-        ;
-            Pref ^ pref_time = scale_by_millions,
-            Pref1 = Pref ^ pref_time := no_scale,
-            Msg1  = "[Time in s]",
-            Pref2 = Pref ^ pref_time := scale_by_thousands,
-            Msg2  = "[Time in s, ms, us, ns]"
-        ;
-            Pref ^ pref_time = scale_by_thousands,
-            Pref1 = Pref ^ pref_time := no_scale,
-            Msg1  = "[Time in s]",
-            Pref2 = Pref ^ pref_time := scale_by_millions,
-            Msg2  = "[Time in s, us]"
-        ),
-        HTML =
-            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-                [s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]) ++
-            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-                [s(deep_cmd_pref_to_url(Pref2, Deep, Cmd)), s(Msg2)])
-    ).
-
-%-----------------------------------------------------------------------------%
-
-:- func footer_order_criteria_toggle(cmd, preferences, deep) = string.
-
-footer_order_criteria_toggle(Cmd, Pref, Deep) =
-    toggle_criteria(Pref ^ pref_criteria,
-        update_criteria_in_prefs(Pref, Deep, Cmd),
-        update_cost_criteria_in_prefs(Pref, Deep, Cmd)).
-
-toggle_cost_criteria_in_top_procs_cmd(Pref, Deep, Limit,
-        CostKind, InclDesc, Scope) =
-    toggle_cost_criteria(CostKind, InclDesc, Scope,
-        update_cost_criteria_in_top_procs_cmd(Pref, Deep, Limit)).
-
-%-----------------------------------------------------------------------------%
-
-:- type update_criteria_func == (func(order_criteria) = string).
-
-:- type update_cost_criteria_func ==
-    (func(cost_kind, include_descendants, measurement_scope) = string).
-
-:- func toggle_criteria(order_criteria,
-    update_criteria_func, update_cost_criteria_func) = string.
-
-toggle_criteria(Criteria, UpdateCriteria, UpdateCostCriteria) = HTML :-
-    (
-        Criteria = by_context,
-        Criteria1 = by_name,
-        Msg1 = "[Sort by name]",
-        Criteria2 =
-            by_cost(default_cost_kind, default_incl_desc, default_scope),
-        Msg2 = "[Sort by cost]"
-    ;
-        Criteria = by_name,
-        Criteria1 = by_context,
-        Msg1 = "[Sort by context]",
-        Criteria2 =
-            by_cost(default_cost_kind, default_incl_desc, default_scope),
-        Msg2 = "[Sort by cost]"
-    ;
-        Criteria = by_cost(_, _, _),
-        Criteria1 = by_context,
-        Msg1 = "[Sort by context]",
-        Criteria2 = by_name,
-        Msg2 = "[Sort by name]"
-    ),
-    Toggle1 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-        [s(UpdateCriteria(Criteria1)), s(Msg1)]),
-    Toggle2 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-        [s(UpdateCriteria(Criteria2)), s(Msg2)]),
-    (
-        Criteria = by_cost(CostKind, InclDesc, Scope),
-        ToggleRest = toggle_cost_criteria(CostKind, InclDesc, Scope,
-            UpdateCostCriteria)
-    ;
-        ( Criteria = by_context
-        ; Criteria = by_name
-        ),
-        ToggleRest = ""
-    ),
-    HTML = "<strong>Toggle ordering criteria:</strong><br />\n" ++
-        Toggle1 ++ Toggle2 ++ ToggleRest.
-
-:- func toggle_cost_criteria(cost_kind, include_descendants, measurement_scope,
-    update_cost_criteria_func) = string.
-
-toggle_cost_criteria(CostKind, InclDesc, Scope, UpdateCriteria) = Toggles :-
-    (
-        ( CostKind = cost_redos
-        ; CostKind = cost_time
-        ; CostKind = cost_callseqs
-        ; CostKind = cost_allocs
-        ; CostKind = cost_words
-        ),
-        MsgCalls = "[Sort by calls]",
-        ToggleCalls = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(UpdateCriteria(cost_calls, InclDesc, Scope)), s(MsgCalls)])
-    ;
-        CostKind = cost_calls,
-        ToggleCalls = ""
-    ),
-    (
-        ( CostKind = cost_calls
-        ; CostKind = cost_time
-        ; CostKind = cost_callseqs
-        ; CostKind = cost_allocs
-        ; CostKind = cost_words
-        ),
-        MsgRedos = "[Sort by redos]",
-        ToggleRedos = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(UpdateCriteria(cost_redos, InclDesc, Scope)), s(MsgRedos)])
-    ;
-        CostKind = cost_redos,
-        ToggleRedos = ""
-    ),
-    (
-        ( CostKind = cost_calls
-        ; CostKind = cost_redos
-        ; CostKind = cost_callseqs
-        ; CostKind = cost_allocs
-        ; CostKind = cost_words
-        ),
-        MsgTime = "[Sort by time]",
-        ToggleTime = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(UpdateCriteria(cost_time, InclDesc, Scope)), s(MsgTime)])
-    ;
-        CostKind = cost_time,
-        ToggleTime = ""
-    ),
-    (
-        ( CostKind = cost_calls
-        ; CostKind = cost_redos
-        ; CostKind = cost_time
-        ; CostKind = cost_allocs
-        ; CostKind = cost_words
-        ),
-        MsgCallSeqs = "[Sort by call sequence numbers]",
-        ToggleCallSeqs =
-            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-                [s(UpdateCriteria(cost_callseqs, InclDesc, Scope)),
-                s(MsgCallSeqs)])
-    ;
-        CostKind = cost_callseqs,
-        ToggleCallSeqs = ""
-    ),
-    (
-        ( CostKind = cost_calls
-        ; CostKind = cost_redos
-        ; CostKind = cost_time
-        ; CostKind = cost_callseqs
-        ; CostKind = cost_words
-        ),
-        MsgAllocs = "[Sort by allocations]",
-        ToggleAllocs =
-            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-                [s(UpdateCriteria(cost_allocs, InclDesc, Scope)),
-                s(MsgAllocs)])
-    ;
-        CostKind = cost_allocs,
-        ToggleAllocs = ""
-    ),
-    (
-        ( CostKind = cost_calls
-        ; CostKind = cost_redos
-        ; CostKind = cost_time
-        ; CostKind = cost_callseqs
-        ; CostKind = cost_allocs
-        ),
-        MsgWords = "[Sort by words]",
-        ToggleWords = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(UpdateCriteria(cost_words, InclDesc, Scope)), s(MsgWords)])
-    ;
-        CostKind = cost_words,
-        ToggleWords = ""
-    ),
-    (
-        InclDesc = self,
-        MsgDesc = "[Include descendants]",
-        ToggleDesc = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(UpdateCriteria(CostKind, self_and_desc, Scope)), s(MsgDesc)])
-    ;
-        InclDesc = self_and_desc,
-        MsgDesc = "[Exclude descendants]",
-        ToggleDesc = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(UpdateCriteria(CostKind, self, Scope)), s(MsgDesc)])
-    ),
-    (
-        Scope = per_call,
-        MsgScope = "[Count overall cost]",
-        ToggleScope = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(UpdateCriteria(CostKind, InclDesc, overall)), s(MsgScope)])
-    ;
-        Scope = overall,
-        MsgScope = "[Count per-call cost]",
-        ToggleScope = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-            [s(UpdateCriteria(CostKind, InclDesc, per_call)), s(MsgScope)])
-    ),
-    Toggles = ToggleCalls ++ ToggleRedos ++ ToggleTime ++ ToggleCallSeqs ++
-        ToggleAllocs ++ ToggleWords ++
-        "\n<br>\n" ++ ToggleDesc ++ ToggleScope.
-
-%-----------------------------------------------------------------------------%
-%
-% Toggles to control showing/hiding inactive modules/procedures
-%
-
-:- func footer_inactive_modules_toggle(cmd, preferences, deep) = string.
-
-footer_inactive_modules_toggle(Cmd, Pref0, Deep) = HTML :-
-    Pref0 ^ pref_inactive = inactive_items(Procs, Modules),
-    (
-        Modules = inactive_show,
-        Msg  = "[Hide inactive modules]",
-        Pref = Pref0 ^ pref_inactive := inactive_items(Procs, inactive_hide)
-    ;
-        Modules = inactive_hide,
-        Msg  = "[Show inactive modules]",
-        Pref = Pref0 ^ pref_inactive := inactive_items(Procs, inactive_show)
-    ),
-    HTML = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-        [s(deep_cmd_pref_to_url(Pref, Deep, Cmd)), s(Msg)]).
-
-:- func footer_inactive_procs_toggle(cmd, preferences, deep) = string.
-
-footer_inactive_procs_toggle(Cmd, Pref0, Deep) = HTML :-
-    Pref0 ^ pref_inactive = inactive_items(Procs, Modules),
-    (
-        Procs = inactive_show,
-        Msg = "[Hide inactive procedures]",
-        Pref = Pref0 ^ pref_inactive := inactive_items(inactive_hide, Modules)
-    ;
-        Procs = inactive_hide,
-        Msg = "[Show inactive procedures]",
-        Pref = Pref0 ^ pref_inactive := inactive_items(inactive_show, Modules)
-    ),
-    HTML = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
-        [s(deep_cmd_pref_to_url(Pref, Deep, Cmd)), s(Msg)]).
-
-%-----------------------------------------------------------------------------%
-
-:- func update_criteria_in_prefs(preferences, deep, cmd, order_criteria)
-    = string.
-
-update_criteria_in_prefs(Pref0, Deep, Cmd, Criteria) = HTML :-
-    Pref = Pref0 ^ pref_criteria := Criteria,
-    HTML = deep_cmd_pref_to_url(Pref, Deep, Cmd).
-
-:- func update_cost_criteria_in_prefs(preferences, deep, cmd,
-    cost_kind, include_descendants, measurement_scope) = string.
-
-update_cost_criteria_in_prefs(Pref0, Deep, Cmd, CostKind, InclDesc, Scope)
-        = HTML :-
-    Pref = Pref0 ^ pref_criteria := by_cost(CostKind, InclDesc, Scope),
-    HTML = deep_cmd_pref_to_url(Pref, Deep, Cmd).
-
-:- func update_cost_criteria_in_top_procs_cmd(preferences, deep, display_limit,
-    cost_kind, include_descendants, measurement_scope) = string.
-
-update_cost_criteria_in_top_procs_cmd(Pref, Deep, Limit,
-        CostKind, InclDesc, Scope) = HTML :-
-    Cmd = deep_cmd_top_procs(Limit, CostKind, InclDesc, Scope),
-    HTML = deep_cmd_pref_to_url(Pref, Deep, Cmd).
-
-%-----------------------------------------------------------------------------%
-
-criteria_to_description(by_context) = "ordered by context".
-criteria_to_description(by_name) = "ordered by name".
-criteria_to_description(by_cost(CostKind, InclDesc, Scope)) =
-    cost_criteria_to_description(CostKind, InclDesc, Scope).
-
-cost_criteria_to_description(CostKind, InclDesc, Scope) = Desc :-
-    Desc =
-        "ordered by " ++
-        incl_desc_to_description(InclDesc) ++ " " ++
-        cost_kind_to_description(CostKind) ++ " " ++
-        scope_to_description(Scope).
-
-:- func cost_kind_to_description(cost_kind) = string.
-
-cost_kind_to_description(cost_calls)    = "number of calls".
-cost_kind_to_description(cost_redos)    = "number of redos".
-cost_kind_to_description(cost_time)     = "time".
-cost_kind_to_description(cost_callseqs) = "call sequence numbers".
-cost_kind_to_description(cost_allocs)   = "memory allocations".
-cost_kind_to_description(cost_words)    = "words allocated".
-
-:- func incl_desc_to_description(include_descendants) = string.
-
-incl_desc_to_description(self) = "self".
-incl_desc_to_description(self_and_desc) = "total".
-
-:- func scope_to_description(measurement_scope) = string.
-
-scope_to_description(per_call) = "per call".
-scope_to_description(overall) = "overall".
-
-%-----------------------------------------------------------------------------%
-
-% The predicates banner_style, fields_header, table_width and
-% own_and_desc_to_html all make decisions about what columns each row
-% in the table will have.  They therefore have similar control structures,
-% and a change in one may require changes in the others as well.
-
-fields_header(Pref, IdFields, TotalsDisp, WrapFunc) = HTML :-
-    Fields = Pref ^ pref_fields,
-    ProcName = WrapFunc("Procedure", by_name),
-    ModuleName = WrapFunc("Module", by_name),
-
-    some [!FirstRow, !SecondRow] (
-        (
-            IdFields = source_proc,
-            Source = WrapFunc("Source", by_context),
-            !:FirstRow =
-                "<TR>\n" ++
-                string.format("<TH ALIGN=LEFT ROWSPAN=2>%s\n", [s(Source)]) ++
-                string.format("<TH ALIGN=LEFT ROWSPAN=2>%s\n", [s(ProcName)])
-        ;
-            IdFields = rank_proc,
-            !:FirstRow =
-                "<TR>\n" ++
-                "<TH ALIGN=LEFT ROWSPAN=2>Rank\n" ++
-                string.format("<TH ALIGN=LEFT ROWSPAN=2>%s\n", [s(ProcName)])
-        ;
-            IdFields = rank_module,
-            !:FirstRow =
-                "<TR>\n" ++
-                "<TH ALIGN=LEFT ROWSPAN=2>Rank\n" ++
-                string.format("<TH ALIGN=LEFT ROWSPAN=2>%s\n", [s(ModuleName)])
-        ;
-            IdFields = proc,
-            !:FirstRow =
-                "<TR>\n" ++
-                string.format("<TH ALIGN=LEFT ROWSPAN=2>%s\n", [s(ProcName)])
-        ),
-        !:SecondRow = "<TR>\n",
-
-        ShowPortCounts = show_port_counts(Fields),
-        (
-            ShowPortCounts = yes,
-            Calls = WrapFunc("Calls", by_cost(cost_calls, self, overall)),
-            Redos = WrapFunc("Redos", by_cost(cost_redos, self, overall)),
-            !:FirstRow = !.FirstRow ++
-                "<TH COLSPAN=5>Port counts\n",
-            !:SecondRow = !.SecondRow ++
-                string.format("<TH ALIGN=RIGHT>%s\n", [s(Calls)]) ++
-                "<TH ALIGN=RIGHT>Exits\n" ++
-                "<TH ALIGN=RIGHT>Fails\n" ++
-                string.format("<TH ALIGN=RIGHT>%s\n", [s(Redos)]) ++
-                "<TH ALIGN=RIGHT>Excps\n"
-        ;
-            ShowPortCounts = no
-        ),
-
-        ShowQuanta = show_quanta(Fields),
-        (
-            ShowQuanta = yes,
-            TicksSelfOverall = WrapFunc("Self",
-                by_cost(cost_time, self, overall)),
-            TicksSelfHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n", [s(TicksSelfOverall)]),
-            TicksSelfFields = 1
-        ;
-            ShowQuanta = no,
-            TicksSelfHeading = "",
-            TicksSelfFields = 0
-        ),
-        ShowTimes = show_times(Fields),
-        (
-            ShowTimes = yes,
-            ( show_quanta(Fields) = yes ->
-                TimeSelfOverall = WrapFunc("Time",
-                    by_cost(cost_time, self, overall))
-            ;
-                TimeSelfOverall = WrapFunc("Self",
-                    by_cost(cost_time, self, overall))
-            ),
-            TimeSelfHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n", [s(TimeSelfOverall)]),
-            TimeSelfFields = 1
-        ;
-            ShowTimes = no,
-            TimeSelfHeading = "",
-            TimeSelfFields = 0
-        ),
-        ( ( ShowQuanta = yes ; ShowTimes = yes ) ->
-            TimeSelfPercentHeading = "<TH ALIGN=RIGHT>%\n",
-            TimeSelfPercentFields = 1
-        ;
-            TimeSelfPercentHeading = "",
-            TimeSelfPercentFields = 0
-        ),
-        ShowTimesPerCall = show_times_per_call(Fields),
-        (
-            ShowTimesPerCall = yes,
-            TimeSelfPerCall = WrapFunc("/call",
-                by_cost(cost_time, self, per_call)),
-            TimeSelfPerCallHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n", [s(TimeSelfPerCall)]),
-            TimeSelfPerCallFields = 1
-        ;
-            ShowTimesPerCall = no,
-            TimeSelfPerCallHeading = "",
-            TimeSelfPerCallFields = 0
-        ),
-        ( TotalsDisp = totals_meaningful, ShowQuanta = yes ->
-            TicksTotalOverall = WrapFunc("Total",
-                by_cost(cost_time, self_and_desc, overall)),
-            TicksTotalHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n", [s(TicksTotalOverall)]),
-            TicksTotalFields = 1
-        ;
-            TicksTotalHeading = "",
-            TicksTotalFields = 0
-        ),
-        ( TotalsDisp = totals_meaningful, ShowTimes = yes ->
-            ( show_quanta(Fields) = yes ->
-                TimeTotalOverall = WrapFunc("Time",
-                    by_cost(cost_time, self_and_desc, overall))
-            ;
-                TimeTotalOverall = WrapFunc("Total",
-                    by_cost(cost_time, self_and_desc, overall))
-            ),
-            TimeTotalHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n", [s(TimeTotalOverall)]),
-            TimeTotalFields = 1
-        ;
-            TimeTotalHeading = "",
-            TimeTotalFields = 0
-        ),
-        (
-            TotalsDisp = totals_meaningful,
-            ( ShowQuanta = yes ; ShowTimes = yes )
-        ->
-            TimeTotalPercentHeading = "<TH ALIGN=RIGHT>%\n",
-            TimeTotalPercentFields = 1
-        ;
-            TimeTotalPercentHeading = "",
-            TimeTotalPercentFields = 0
-        ),
-        ( TotalsDisp = totals_meaningful, ShowTimesPerCall = yes ->
-            TimeTotalPerCall = WrapFunc("/call",
-                by_cost(cost_time, self_and_desc, per_call)),
-            TimeTotalPerCallHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n", [s(TimeTotalPerCall)]),
-            TimeTotalPerCallFields = 1
-        ;
-            TimeTotalPerCallHeading = "",
-            TimeTotalPerCallFields = 0
-        ),
-        TimeFields =
-            TicksSelfFields + TimeSelfFields +
-            TimeSelfPercentFields + TimeSelfPerCallFields +
-            TicksTotalFields + TimeTotalFields +
-            TimeTotalPercentFields + TimeTotalPerCallFields,
-        !:SecondRow = !.SecondRow ++
-            TicksSelfHeading ++ TimeSelfHeading ++
-            TimeSelfPercentHeading ++ TimeSelfPerCallHeading ++
-            TicksTotalHeading ++ TimeTotalHeading ++
-            TimeTotalPercentHeading ++ TimeTotalPerCallHeading,
-        (
-            ShowQuanta = yes,
-            ShowTimes = yes,
-            !:FirstRow = !.FirstRow ++
-                string.format("<TH COLSPAN=%d>Clock ticks and times\n",
-                    [i(TimeFields)])
-        ;
-            ShowQuanta = yes,
-            ShowTimes = no,
-            !:FirstRow = !.FirstRow ++
-                string.format("<TH COLSPAN=%d>Clock ticks\n", [i(TimeFields)])
-        ;
-            ShowQuanta = no,
-            ShowTimes = yes,
-            !:FirstRow = !.FirstRow ++
-                string.format("<TH COLSPAN=%d>Time\n", [i(TimeFields)])
-        ;
-            ShowQuanta = no,
-            ShowTimes = no
-        ),
-
-        ShowCallSeqs = show_callseqs(Fields),
-        (
-            ShowCallSeqs = yes,
-            CallSeqsSelfOverall = WrapFunc("Self",
-                by_cost(cost_callseqs, self, overall)),
-            CallSeqsSelfHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n",
-                    [s(CallSeqsSelfOverall)]) ++
-                "<TH ALIGN=RIGHT>%\n",
-            CallSeqsSelfFields = 2
-        ;
-            ShowCallSeqs = no,
-            CallSeqsSelfHeading = "",
-            CallSeqsSelfFields = 0
-        ),
-        ShowCallSeqsPerCall = show_callseqs_per_call(Fields),
-        (
-            ShowCallSeqsPerCall = yes,
-            CallSeqsSelfPerCall = WrapFunc("/call",
-                by_cost(cost_callseqs, self, per_call)),
-            CallSeqsSelfPerCallHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n", [s(CallSeqsSelfPerCall)]),
-            CallSeqsSelfPerCallFields = 1
-        ;
-            ShowCallSeqsPerCall = no,
-            CallSeqsSelfPerCallHeading = "",
-            CallSeqsSelfPerCallFields = 0
-        ),
-        ( TotalsDisp = totals_meaningful, ShowCallSeqs = yes ->
-            CallSeqsTotalOverall = WrapFunc("Total",
-                by_cost(cost_callseqs, self_and_desc, overall)),
-            CallSeqsTotalHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n",
-                    [s(CallSeqsTotalOverall)]) ++
-                "<TH ALIGN=RIGHT>%\n",
-            CallSeqsTotalFields = 2
-        ;
-            CallSeqsTotalHeading = "",
-            CallSeqsTotalFields = 0
-        ),
-        ( TotalsDisp = totals_meaningful, ShowCallSeqsPerCall = yes ->
-            CallSeqsTotalPerCall = WrapFunc("/call",
-                by_cost(cost_callseqs, self_and_desc, per_call)),
-            CallSeqsTotalPerCallHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n",
-                    [s(CallSeqsTotalPerCall)]),
-            CallSeqsTotalPerCallFields = 1
-        ;
-            CallSeqsTotalPerCallHeading = "",
-            CallSeqsTotalPerCallFields = 0
-        ),
-        CallSeqsFields =
-            CallSeqsSelfFields + CallSeqsSelfPerCallFields +
-            CallSeqsTotalFields + CallSeqsTotalPerCallFields,
-        !:SecondRow = !.SecondRow ++
-            CallSeqsSelfHeading ++ CallSeqsSelfPerCallHeading ++
-            CallSeqsTotalHeading ++ CallSeqsTotalPerCallHeading,
-        (
-            ShowCallSeqs = yes,
-            !:FirstRow = !.FirstRow ++
-                string.format("<TH COLSPAN=%d>Call sequence numbers\n",
-                    [i(CallSeqsFields)])
-        ;
-            ShowCallSeqs = no
-        ),
-
-        ShowAlloc = show_alloc(Fields),
-        (
-            ShowAlloc = yes,
-            AllocsSelfOverall = WrapFunc("Self",
-                by_cost(cost_allocs, self, overall)),
-            AllocsSelfHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n",
-                    [s(AllocsSelfOverall)]) ++
-                "<TH ALIGN=RIGHT>%\n",
-            AllocsSelfFields = 2
-        ;
-            ShowAlloc = no,
-            AllocsSelfHeading = "",
-            AllocsSelfFields = 0
-        ),
-        ShowAllocPerCall = show_alloc_per_call(Fields),
-        (
-            ShowAllocPerCall = yes,
-            AllocsSelfPerCall = WrapFunc("/call",
-                by_cost(cost_allocs, self, per_call)),
-            AllocsSelfPerCallHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n", [s(AllocsSelfPerCall)]),
-            AllocsSelfPerCallFields = 1
-        ;
-            ShowAllocPerCall = no,
-            AllocsSelfPerCallHeading = "",
-            AllocsSelfPerCallFields = 0
-        ),
-        ( TotalsDisp = totals_meaningful, ShowAlloc = yes ->
-            AllocsTotalOverall = WrapFunc("Total",
-                by_cost(cost_allocs, self_and_desc, overall)),
-            AllocsTotalHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n",
-                    [s(AllocsTotalOverall)]) ++
-                "<TH ALIGN=RIGHT>%\n",
-            AllocsTotalFields = 2
-        ;
-            AllocsTotalHeading = "",
-            AllocsTotalFields = 0
-        ),
-        ( TotalsDisp = totals_meaningful, ShowAllocPerCall = yes ->
-            AllocsTotalPerCall = WrapFunc("/call",
-                by_cost(cost_allocs, self_and_desc, per_call)),
-            AllocsTotalPerCallHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n", [s(AllocsTotalPerCall)]),
-            AllocsTotalPerCallFields = 1
-        ;
-            AllocsTotalPerCallHeading = "",
-            AllocsTotalPerCallFields = 0
-        ),
-        AllocsFields =
-            AllocsSelfFields + AllocsSelfPerCallFields +
-            AllocsTotalFields + AllocsTotalPerCallFields,
-        !:SecondRow = !.SecondRow ++
-            AllocsSelfHeading ++ AllocsSelfPerCallHeading ++
-            AllocsTotalHeading ++ AllocsTotalPerCallHeading,
-        (
-            ShowAlloc = yes,
-            !:FirstRow = !.FirstRow ++
-                string.format("<TH COLSPAN=%d>Memory allocations\n",
-                    [i(AllocsFields)])
-        ;
-            ShowAlloc = no
-        ),
-
-        ShowMemory = show_memory(Fields),
-        (
-            ShowMemory = yes(_),
-            MemorySelfOverall = WrapFunc("Self",
-                by_cost(cost_words, self, overall)),
-            MemorySelfHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n",
-                    [s(MemorySelfOverall)]) ++
-                "<TH ALIGN=RIGHT>%\n",
-            MemorySelfFields = 2
-        ;
-            ShowMemory = no,
-            MemorySelfHeading = "",
-            MemorySelfFields = 0
-        ),
-        ShowMemoryPerCall = show_memory_per_call(Fields),
-        (
-            ShowMemoryPerCall = yes(_),
-            MemorySelfPerCall = WrapFunc("/call",
-                by_cost(cost_words, self, per_call)),
-            MemorySelfPerCallHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n", [s(MemorySelfPerCall)]),
-            MemorySelfPerCallFields = 1
-        ;
-            ShowMemoryPerCall = no,
-            MemorySelfPerCallHeading = "",
-            MemorySelfPerCallFields = 0
-        ),
-        ( TotalsDisp = totals_meaningful, ShowMemory = yes(_) ->
-            MemoryTotalOverall = WrapFunc("Total",
-                by_cost(cost_words, self_and_desc, overall)),
-            MemoryTotalHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n",
-                    [s(MemoryTotalOverall)]) ++
-                "<TH ALIGN=RIGHT>%\n",
-            MemoryTotalFields = 2
-        ;
-            MemoryTotalHeading = "",
-            MemoryTotalFields = 0
-        ),
-        ( TotalsDisp = totals_meaningful, ShowMemoryPerCall = yes(_) ->
-            MemoryTotalPerCall = WrapFunc("/call",
-                by_cost(cost_words, self_and_desc, per_call)),
-            MemoryTotalPerCallHeading =
-                string.format("<TH ALIGN=RIGHT>%s\n", [s(MemoryTotalPerCall)]),
-            MemoryTotalPerCallFields = 1
-        ;
-            MemoryTotalPerCallHeading = "",
-            MemoryTotalPerCallFields = 0
-        ),
-        MemoryFields =
-            MemorySelfFields + MemorySelfPerCallFields +
-            MemoryTotalFields + MemoryTotalPerCallFields,
-        !:SecondRow = !.SecondRow ++
-            MemorySelfHeading ++ MemorySelfPerCallHeading ++
-            MemoryTotalHeading ++ MemoryTotalPerCallHeading,
-        (
-            ShowMemory = yes(Units),
-            (
-                Units = units_words,
-                !:FirstRow = !.FirstRow ++
-                    string.format("<TH COLSPAN=%d>Memory words\n",
-                        [i(MemoryFields)])
-            ;
-                Units = units_bytes,
-                !:FirstRow = !.FirstRow ++
-                    string.format("<TH COLSPAN=%d>Memory bytes\n",
-                        [i(MemoryFields)])
-            )
-        ;
-            ShowMemory = no
-        ),
-        HTML =
-            "<THEAD>\n" ++
-            !.FirstRow ++
-            !.SecondRow ++
-            "<TBODY>\n" ++
-            separator_row(Pref, IdFields, TotalsDisp)
-    ).
-
-%-----------------------------------------------------------------------------%
-
-header_row(Heading, Pref, IdFields, TotalsDisp) = Separator :-
-    Separator = string.format("<TR><TD COLSPAN=%d>%s</TD></TR>\n",
-        [i(table_width(Pref, IdFields, TotalsDisp)), s(Heading)]).
-
-separator_row(Pref, IdFields, TotalsDisp) = Separator :-
-    Separator = string.format("<TR><TD COLSPAN=%d></TD></TR>\n",
-        [i(table_width(Pref, IdFields, TotalsDisp))]).
-
-:- func table_width(preferences, id_fields, totals_disposition) = int.
-
-table_width(Pref, IdFields, TotalsDisp) = Width :-
-    Fields = Pref ^ pref_fields,
-    (
-        IdFields = source_proc,
-        Id = 2
-    ;
-        IdFields = rank_module,
-        Id = 2
-    ;
-        IdFields = rank_proc,
-        Id = 2
-    ;
-        IdFields = proc,
-        Id = 1
-    ),
-    (
-        Fields ^ port_fields = no_port,
-        Port = 0
-    ;
-        Fields ^ port_fields = port,
-        Port = 5
-    ),
-    (
-        Fields ^ time_fields = no_time,
-        Time = 0
-    ;
-        Fields ^ time_fields = ticks,
-        Time = 2
-    ;
-        Fields ^ time_fields = time,
-        Time = 2
-    ;
-        Fields ^ time_fields = ticks_and_time,
-        Time = 3
-    ;
-        Fields ^ time_fields = time_and_percall,
-        Time = 3
-    ;
-        Fields ^ time_fields = ticks_and_time_and_percall,
-        Time = 4
-    ),
-    (
-        Fields ^ callseqs_fields = no_callseqs,
-        CallSeqs = 0
-    ;
-        Fields ^ callseqs_fields = callseqs,
-        CallSeqs = 2
-    ;
-        Fields ^ callseqs_fields = callseqs_and_percall,
-        CallSeqs = 3
-    ),
-    (
-        Fields ^ alloc_fields = no_alloc,
-        Alloc = 0
-    ;
-        Fields ^ alloc_fields = alloc,
-        Alloc = 2
-    ;
-        Fields ^ alloc_fields = alloc_and_percall,
-        Alloc = 3
-    ),
-    (
-        Fields ^ memory_fields = no_memory,
-        Memory = 0
-    ;
-        Fields ^ memory_fields = memory(_),
-        Memory = 2
-    ;
-        Fields ^ memory_fields = memory_and_percall(_),
-        Memory = 3
-    ),
-    (
-        TotalsDisp = totals_meaningful,
-        Width = Id + Port + Time * 2 + CallSeqs * 2 + Alloc * 2 + Memory * 2
-    ;
-        TotalsDisp = totals_not_meaningful,
-        Width = Id + Port + Time + CallSeqs + Alloc + Memory
-    ).
-
-%-----------------------------------------------------------------------------%
-
-add_context(Context, LineGroup0) = LineGroup :-
-    LineGroup0 =
-        line_group(FileName, LineNumber, Name, Own, Desc, HTML0, LaterLines),
-    HTML = string.format("<TD CLASS=id>%s</TD>%s",
-        [s(Context), s(HTML0)]),
-    LineGroup =
-        line_group(FileName, LineNumber, Name, Own, Desc, HTML, LaterLines).
-
-add_self_context(LineGroup0) = LineGroup :-
-    LineGroup0 =
-        line_group(FileName, LineNumber, Name, Own, Desc, HTML0, LaterLines),
-    HTML = string.format("<TD CLASS=id>%s:%d</TD>%s",
-        [s(FileName), i(LineNumber), s(HTML0)]),
-    LineGroup =
-        line_group(FileName, LineNumber, Name, Own, Desc, HTML, LaterLines).
-
-add_ranks(LineGroups0) = add_ranks_2(1, LineGroups0).
-
-:- func add_ranks_2(int, list(line_group(one_id, LL)))
-    = list(line_group(two_id, LL)).
-
-add_ranks_2(_Rank, []) = [].
-add_ranks_2(Rank, [LineGroup0 | LineGroups0]) = [LineGroup | LineGroups] :-
-    LineGroup0 =
-        line_group(FileName, LineNumber, Name, Own, Desc, HTML0, LaterLines),
-    HTML = string.format("<TD CLASS=id>%d</TD>%s", [i(Rank), s(HTML0)]),
-    LineGroup =
-        line_group(FileName, LineNumber, Name, Own, Desc, HTML, LaterLines),
-    LineGroups = add_ranks_2(Rank + 1, LineGroups0).
-
-line_to_one_id_subline_group(LineGroup0) = LineGroup :-
-    LineGroup0 =
-        line_group(FileName, LineNumber, Name, Own, Desc, HTML, unit),
-    LineGroup =
-        line_group(FileName, LineNumber, Name, Own, Desc, HTML,
-            sub_lines(one_id, [])).
-
-line_to_two_id_subline_group(LineGroup0) = LineGroup :-
-    LineGroup0 = line_group(FileName, LineNumber, Name, Own, Desc,
-        HTML, unit),
-    LineGroup = line_group(FileName, LineNumber, Name, Own, Desc,
-        HTML, sub_lines(two_id, [])).
-
-%-----------------------------------------------------------------------------%
-
-one_id_line_to_html(Pref, Deep, TotalsDisp, LineGroup) =
-    "<TR>\n" ++
-    LineGroup ^ group_first_line_id ++
-    own_and_desc_to_html(LineGroup ^ group_own, LineGroup ^ group_desc,
-        Pref, Deep, TotalsDisp) ++
-    "</TR>\n".
-
-one_id_line_group_to_html(Pref, Deep, TotalsDisp, LineGroup) =
-    "<TR>\n" ++
-    LineGroup ^ group_first_line_id ++
-    own_and_desc_to_html(LineGroup ^ group_own, LineGroup ^ group_desc,
-        Pref, Deep, TotalsDisp) ++
-    "</TR>\n" ++
-    string.append_list(
-        list.map(one_id_line_to_html(Pref, Deep, TotalsDisp),
-            LineGroup ^ group_later_lines ^ sub_line_list)).
-
-two_id_line_to_html(Pref, Deep, TotalsDisp, LineGroup) =
-    "<TR>\n" ++
-    LineGroup ^ group_first_line_id ++
-    own_and_desc_to_html(LineGroup ^ group_own, LineGroup ^ group_desc,
-        Pref, Deep, TotalsDisp) ++
-    "</TR>\n".
-
-two_id_line_group_to_html(Pref, Deep, TotalsDisp, LineGroup) =
-    "<TR>\n" ++
-    LineGroup ^ group_first_line_id ++
-    own_and_desc_to_html(LineGroup ^ group_own, LineGroup ^ group_desc,
-        Pref, Deep, TotalsDisp) ++
-    "</TR>\n" ++
-    string.append_list(
-        list.map(two_id_line_to_html(Pref, Deep, TotalsDisp),
-            LineGroup ^ group_later_lines ^ sub_line_list)).
-
-%-----------------------------------------------------------------------------%
-
-own_and_desc_to_html(Own, Desc, Pref, Deep, TotalsDisp) = HTML :-
-    add_own_to_inherit(Own, Desc) = OwnPlusDesc,
-    Root = root_total_info(Deep),
-    Calls = calls(Own),
-    Exits = exits(Own),
-    Fails = fails(Own),
-    Redos = redos(Own),
-    Excps = excps(Own),
-
-    OwnQuanta = quanta(Own),
-    TotalQuanta = inherit_quanta(OwnPlusDesc),
-    RootQuanta = inherit_quanta(Root),
-    OwnQuantaProp = percentage(OwnQuanta, RootQuanta),
-    TotalQuantaProp = percentage(TotalQuanta, RootQuanta),
-
-    OwnCallSeqs = callseqs(Own),
-    TotalCallSeqs = inherit_callseqs(OwnPlusDesc),
-    RootCallSeqs = inherit_callseqs(Root),
-    OwnCallSeqsProp = percentage(OwnCallSeqs, RootCallSeqs),
-    TotalCallSeqsProp = percentage(TotalCallSeqs, RootCallSeqs),
-
-    OwnAllocs = allocs(Own),
-    TotalAllocs = inherit_allocs(OwnPlusDesc),
-    RootAllocs = inherit_allocs(Root),
-    OwnAllocProp = percentage(OwnAllocs, RootAllocs),
-    TotalAllocProp = percentage(TotalAllocs, RootAllocs),
-
-    OwnWords = words(Own),
-    TotalWords = inherit_words(OwnPlusDesc),
-    RootWords = inherit_words(Root),
-    OwnMemoryProp = percentage(OwnWords, RootWords),
-    TotalMemoryProp = percentage(TotalWords, RootWords),
-
-    Fields = Pref ^ pref_fields,
-
-    ShowPortCounts = show_port_counts(Fields),
-    (
-        ShowPortCounts = yes,
-        PortHTML =
-            string.format("<TD CLASS=port ALIGN=RIGHT>%s</TD>\n",
-                [s(commas(Calls))]) ++
-            string.format("<TD CLASS=port ALIGN=RIGHT>%s</TD>\n",
-                [s(commas(Exits))]) ++
-            string.format("<TD CLASS=port ALIGN=RIGHT>%s</TD>\n",
-                [s(commas(Fails))]) ++
-            string.format("<TD CLASS=port ALIGN=RIGHT>%s</TD>\n",
-                [s(commas(Redos))]) ++
-            string.format("<TD CLASS=port ALIGN=RIGHT>%s</TD>\n",
-                [s(commas(Excps))])
-    ;
-        ShowPortCounts = no,
-        PortHTML = ""
-    ),
-
-    ShowQuanta = show_quanta(Fields),
-    (
-        ShowQuanta = yes,
-        QuantaSelfHTML =
-            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
-                [s(commas(OwnQuanta))]),
-        QuantaTotalHTML =
-            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
-                [s(commas(TotalQuanta))])
-    ;
-        ShowQuanta = no,
-        QuantaSelfHTML = "",
-        QuantaTotalHTML = ""
-    ),
-    ShowTimes = show_times(Fields),
-    (
-        ShowTimes = yes,
-        TimeSelfHTML =
-            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
-                [s(overall_time(Pref, Deep, OwnQuanta))]),
-        TimeTotalHTML =
-            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
-                [s(overall_time(Pref, Deep, TotalQuanta))])
-    ;
-        ShowTimes = no,
-        TimeSelfHTML = "",
-        TimeTotalHTML = ""
-    ),
-    ShowTimeFraction = bool.or(ShowQuanta, ShowTimes),
-    (
-        ShowTimeFraction = yes,
-        QuantaPropSelfHTML =
-            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
-                [s(OwnQuantaProp)]),
-        QuantaPropTotalHTML =
-            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
-                [s(TotalQuantaProp)])
-    ;
-        ShowTimeFraction = no,
-        QuantaPropSelfHTML = "",
-        QuantaPropTotalHTML = ""
-    ),
-    ShowTimesPerCall = show_times_per_call(Fields),
-    (
-        ShowTimesPerCall = yes,
-        TimePerCallSelfHTML =
-            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
-                [s(per_call_time(Pref, Deep, OwnQuanta, Calls))]),
-        TimePerCallTotalHTML =
-            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
-                [s(per_call_time(Pref, Deep, TotalQuanta, Calls))])
-    ;
-        ShowTimesPerCall = no,
-        TimePerCallSelfHTML = "",
-        TimePerCallTotalHTML = ""
-    ),
-
-    ShowCallSeqs = show_callseqs(Fields),
-    (
-        ShowCallSeqs = yes,
-        CallSeqsSelfHTML =
-            string.format("<TD CLASS=callseqs ALIGN=RIGHT>%s</TD>\n",
-                [s(commas(OwnCallSeqs))]),
-        CallSeqsTotalHTML =
-            string.format("<TD CLASS=callseqs ALIGN=RIGHT>%s</TD>\n",
-                [s(commas(TotalCallSeqs))]),
-        CallSeqsPropSelfHTML =
-            string.format("<TD CLASS=callseqs ALIGN=RIGHT>%s</TD>\n",
-                [s(OwnCallSeqsProp)]),
-        CallSeqsPropTotalHTML =
-            string.format("<TD CLASS=callseqs ALIGN=RIGHT>%s</TD>\n",
-                [s(TotalCallSeqsProp)])
-    ;
-        ShowCallSeqs = no,
-        CallSeqsSelfHTML = "",
-        CallSeqsTotalHTML = "",
-        CallSeqsPropSelfHTML = "",
-        CallSeqsPropTotalHTML = ""
-    ),
-    ShowCallSeqsPerCall = show_callseqs_per_call(Fields),
-    (
-        ShowCallSeqsPerCall = yes,
-        ( Calls = 0 ->
-            OwnCallSeqsPerCall = "N/A",
-            TotalCallSeqsPerCall = "N/A"
-        ;
-            OwnCallSeqsPerCall =
-                one_decimal_fraction(float(OwnCallSeqs) / float(Calls)),
-            TotalCallSeqsPerCall =
-                one_decimal_fraction(float(TotalCallSeqs) / float(Calls))
-        ),
-        CallSeqsPerCallSelfHTML =
-            string.format("<TD CLASS=callseqs ALIGN=RIGHT>%s</TD>\n",
-                [s(OwnCallSeqsPerCall)]),
-        CallSeqsPerCallTotalHTML =
-            string.format("<TD CLASS=callseqs ALIGN=RIGHT>%s</TD>\n",
-                [s(TotalCallSeqsPerCall)])
-    ;
-        ShowCallSeqsPerCall = no,
-        CallSeqsPerCallSelfHTML = "",
-        CallSeqsPerCallTotalHTML = ""
-    ),
-
-    ShowAlloc = show_alloc(Fields),
-    (
-        ShowAlloc = yes,
-        AllocSelfHTML =
-            string.format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
-                [s(commas(OwnAllocs))]) ++
-            string.format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
-                [s(OwnAllocProp)]),
-        AllocTotalHTML =
-            string.format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
-                [s(commas(TotalAllocs))]) ++
-            string.format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
-                [s(TotalAllocProp)])
-    ;
-        ShowAlloc = no,
-        AllocSelfHTML = "",
-        AllocTotalHTML = ""
-    ),
-    ShowAllocPerCall = show_alloc_per_call(Fields),
-    (
-        ShowAllocPerCall = yes,
-        AllocPerCallSelfHTML =
-            string.format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
-                [s(count_per_call(OwnAllocs, Calls))]),
-        AllocPerCallTotalHTML =
-            string.format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
-                [s(count_per_call(TotalAllocs, Calls))])
-    ;
-        ShowAllocPerCall = no,
-        AllocPerCallSelfHTML = "",
-        AllocPerCallTotalHTML = ""
-    ),
-
-    ShowMemory = show_memory(Fields),
-    (
-        ShowMemory = yes(Unit),
-        (
-            Unit = units_words,
-            OwnMemory = OwnWords,
-            TotalMemory = TotalWords
-        ;
-            Unit = units_bytes,
-            WordSize = Deep ^ profile_stats ^ word_size,
-            OwnMemory = OwnWords * WordSize,
-            TotalMemory = TotalWords * WordSize
-        )
-    ;
-        ShowMemory = no,
-        % These values won't be used.
-        OwnMemory = 0,
-        TotalMemory = 0
-    ),
-    (
-        ShowMemory = yes(_),
-        MemorySelfHTML =
-            string.format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
-                [s(commas(OwnMemory))]) ++
-            string.format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
-                [s(OwnMemoryProp)]),
-        MemoryTotalHTML =
-            string.format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
-                [s(commas(TotalMemory))]) ++
-            string.format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
-                [s(TotalMemoryProp)])
-    ;
-        ShowMemory = no,
-        MemorySelfHTML = "",
-        MemoryTotalHTML = ""
-    ),
-    ShowMemoryPerCall = show_memory_per_call(Fields),
-    (
-        ShowMemoryPerCall = yes(_),
-        MemoryPerCallSelfHTML =
-            string.format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
-                [s(count_per_call(OwnMemory, Calls))]),
-        MemoryPerCallTotalHTML =
-            string.format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
-                [s(count_per_call(TotalMemory, Calls))])
-    ;
-        ShowMemoryPerCall = no,
-        MemoryPerCallSelfHTML = "",
-        MemoryPerCallTotalHTML = ""
-    ),
-
-    (
-        TotalsDisp = totals_meaningful,
-        HTML =
-            PortHTML ++
-
-            QuantaSelfHTML ++
-            TimeSelfHTML ++
-            QuantaPropSelfHTML ++
-            TimePerCallSelfHTML ++
-            QuantaTotalHTML ++
-            TimeTotalHTML ++
-            QuantaPropTotalHTML ++
-            TimePerCallTotalHTML ++
-
-            CallSeqsSelfHTML ++
-            CallSeqsPropSelfHTML ++
-            CallSeqsPerCallSelfHTML ++
-            CallSeqsTotalHTML ++
-            CallSeqsPropTotalHTML ++
-            CallSeqsPerCallTotalHTML ++
-
-            AllocSelfHTML ++
-            AllocPerCallSelfHTML ++
-            AllocTotalHTML ++
-            AllocPerCallTotalHTML ++
-
-            MemorySelfHTML ++
-            MemoryPerCallSelfHTML ++
-            MemoryTotalHTML ++
-            MemoryPerCallTotalHTML
-    ;
-        TotalsDisp = totals_not_meaningful,
-        HTML =
-            PortHTML ++
-
-            QuantaSelfHTML ++
-            TimeSelfHTML ++
-            QuantaPropSelfHTML ++
-            TimePerCallSelfHTML ++
-
-            CallSeqsSelfHTML ++
-            CallSeqsPropSelfHTML ++
-            CallSeqsPerCallSelfHTML ++
-
-            AllocSelfHTML ++
-            AllocPerCallSelfHTML ++
-
-            MemorySelfHTML ++
-            MemoryPerCallSelfHTML
-    ).
-
-%-----------------------------------------------------------------------------%
-
-:- func overall_time(preferences, deep, int) = string.
-
-overall_time(Pref, Deep, Quanta) = TimeStr :-
-    lookup_ticks_per_sec(Deep ^ profile_stats, TicksPerSec, _Assumed),
-    % We display Time as seconds, with two digits after the decimal point.
-    % This is the most we can do, given clock granularity.
-    Time = float(Quanta) / float(TicksPerSec),
-    TimeStr = format_time(Pref, Time).
-
-:- func per_call_time(preferences, deep, int, int) = string.
-
-per_call_time(Pref, Deep, Quanta, Calls) = TimeStr :-
-    lookup_ticks_per_sec(Deep ^ profile_stats, TicksPerSec, _Assumed),
-    % We display Time as seconds, with two digits after the decimal point.
-    % This is the most we can do, given clock granularity.
-    Time = float(Quanta) / float(TicksPerSec),
-    ( Calls \= 0 ->
-        TimePerCall = Time / float(Calls)
-    ;
-        TimePerCall = 0.0
-    ),
-    TimeStr = format_time(Pref, TimePerCall).
-
-:- func format_time(preferences, float) = string.
-
-format_time(Pref, Time) = TimeStr :-
-    (
-        Pref ^ pref_time = no_scale,
-        TimeStr0 = four_decimal_fraction(Time),
-        Unit = "s"
-    ;
-        Pref ^ pref_time = scale_by_millions,
-        ( Time >= 0.001 ->
-            ScaledTime = Time,
-            Unit = "s"
-        ;
-            ScaledTime = 1000000.0 * Time,
-            Unit = "us"
-        ),
-        TimeStr0 = four_decimal_fraction(ScaledTime)
-    ;
-        Pref ^ pref_time = scale_by_thousands,
-        ( Time >= 1.0 ->
-            ScaledTime = Time,
-            Unit = "s"
-        ; Time >= 0.001 ->
-            ScaledTime = 1000.0 * Time,
-            Unit = "ms"
-        ; Time >= 0.000001 ->
-            ScaledTime = 1000000.0 * Time,
-            Unit = "us"
-        ;
-            ScaledTime = 1000000000.0 * Time,
-            Unit = "ns"
-        ),
-        TimeStr0 = two_decimal_fraction(ScaledTime)
-    ),
-    TimeStr = TimeStr0 ++ Unit.
-
-:- func one_decimal_fraction(float) = string.
-
-one_decimal_fraction(Measure) = decimal_fraction("%.1f", Measure).
-
-:- func two_decimal_fraction(float) = string.
-
-two_decimal_fraction(Measure) = decimal_fraction("%.2f", Measure).
-
-:- func four_decimal_fraction(float) = string.
-
-four_decimal_fraction(Measure) = decimal_fraction("%.4f", Measure).
-
-:- func percentage(int, int) = string.
-
-percentage(Fraction, Whole) = PercentageStr :-
-    ( Whole = 0 ->
-        PercentageStr = "N/A"
-    ;
-        Percentage = 100.0 * float(Fraction) / float(Whole),
-        PercentageStr = string.format("%5.2f", [f(Percentage)])
-    ).
-
-lookup_ticks_per_sec(Stats, TicksPerSec, Assumed) :-
-    TicksPerSec0 = Stats ^ ticks_per_sec,
-    ( TicksPerSec0 = 0 ->
-        TicksPerSec = default_ticks_per_sec,
-        Assumed = yes
-    ;
-        TicksPerSec = TicksPerSec0,
-        Assumed = no
-    ).
-
-    % The number of ticks per sec to assume if the profiling data file does
-    % not record the actual tick rate.
-    %
-:- func default_ticks_per_sec = int.
-
-default_ticks_per_sec = 100.
-
-%-----------------------------------------------------------------------------%
-
-:- func count_per_call(int, int) = string.
-
-count_per_call(Count, Calls) =
-    ( Calls = 0 ->
-        two_decimal_fraction(0.0)
-    ;
-        two_decimal_fraction(float(Count) / float(Calls))
-    ).
-
-%-----------------------------------------------------------------------------%
-
-:- func show_port_counts(fields) = bool.
-
-show_port_counts(Fields) = ShowPorts :-
-    PortFields = Fields ^ port_fields,
-    ( PortFields = no_port, ShowPorts = no
-    ; PortFields = port, ShowPorts = yes
-    ).
-
-:- func show_quanta(fields) = bool.
-
-show_quanta(Fields) = ShowQuanta :-
-    TimeFields = Fields ^ time_fields,
-    ( TimeFields = no_time, ShowQuanta = no
-    ; TimeFields = ticks, ShowQuanta = yes
-    ; TimeFields = time, ShowQuanta = no
-    ; TimeFields = ticks_and_time, ShowQuanta = yes
-    ; TimeFields = time_and_percall, ShowQuanta = no
-    ; TimeFields = ticks_and_time_and_percall, ShowQuanta = yes
-    ).
-
-:- func show_times(fields) = bool.
-
-show_times(Fields) = ShowTimes :-
-    TimeFields = Fields ^ time_fields,
-    ( TimeFields = no_time, ShowTimes = no
-    ; TimeFields = ticks, ShowTimes = no
-    ; TimeFields = time, ShowTimes = yes
-    ; TimeFields = ticks_and_time, ShowTimes = yes
-    ; TimeFields = time_and_percall, ShowTimes = yes
-    ; TimeFields = ticks_and_time_and_percall, ShowTimes = yes
-    ).
-
-:- func show_times_per_call(fields) = bool.
-
-show_times_per_call(Fields) = ShowTimesPerCall :-
-    TimeFields = Fields ^ time_fields,
-    ( TimeFields = no_time, ShowTimesPerCall = no
-    ; TimeFields = ticks, ShowTimesPerCall = no
-    ; TimeFields = time, ShowTimesPerCall = no
-    ; TimeFields = ticks_and_time, ShowTimesPerCall = no
-    ; TimeFields = time_and_percall, ShowTimesPerCall = yes
-    ; TimeFields = ticks_and_time_and_percall, ShowTimesPerCall = yes
-    ).
-
-:- func show_callseqs(fields) = bool.
-
-show_callseqs(Fields) = ShowCallSeqs :-
-    CallSeqsField = Fields ^ callseqs_fields,
-    ( CallSeqsField = no_callseqs, ShowCallSeqs = no
-    ; CallSeqsField = callseqs, ShowCallSeqs = yes
-    ; CallSeqsField = callseqs_and_percall, ShowCallSeqs = yes
-    ).
-
-:- func show_callseqs_per_call(fields) = bool.
-
-show_callseqs_per_call(Fields) = ShowCallSeqsPerCall :-
-    CallSeqsField = Fields ^ callseqs_fields,
-    ( CallSeqsField = no_callseqs, ShowCallSeqsPerCall = no
-    ; CallSeqsField = callseqs, ShowCallSeqsPerCall = no
-    ; CallSeqsField = callseqs_and_percall, ShowCallSeqsPerCall = yes
-    ).
-
-:- func show_alloc(fields) = bool.
-
-show_alloc(Fields) = ShowAlloc :-
-    AllocFields = Fields ^ alloc_fields,
-    ( AllocFields = no_alloc, ShowAlloc = no
-    ; AllocFields = alloc, ShowAlloc = yes
-    ; AllocFields = alloc_and_percall, ShowAlloc = yes
-    ).
-
-:- func show_alloc_per_call(fields) = bool.
-
-show_alloc_per_call(Fields) = ShowPerAlloc :-
-    AllocFields = Fields ^ alloc_fields,
-    ( AllocFields = no_alloc, ShowPerAlloc = no
-    ; AllocFields = alloc, ShowPerAlloc = no
-    ; AllocFields = alloc_and_percall, ShowPerAlloc = yes
-    ).
-
-:- func show_memory(fields) = maybe(memory_units).
-
-show_memory(Fields) = ShowMemory :-
-    MemoryFields = Fields ^ memory_fields,
-    ( MemoryFields = no_memory, ShowMemory = no
-    ; MemoryFields = memory(Unit), ShowMemory = yes(Unit)
-    ; MemoryFields = memory_and_percall(Unit), ShowMemory = yes(Unit)
-    ).
-
-:- func show_memory_per_call(fields) = maybe(memory_units).
-
-show_memory_per_call(Fields) = ShowPerMemory :-
-    MemoryFields = Fields ^ memory_fields,
-    ( MemoryFields = no_memory, ShowPerMemory = no
-    ; MemoryFields = memory(_Unit), ShowPerMemory = no
-    ; MemoryFields = memory_and_percall(Unit), ShowPerMemory = yes(Unit)
-    ).
-
-%-----------------------------------------------------------------------------%
-
-proc_dynamic_name(Deep, PDPtr) = Name :-
-    deep_lookup_proc_dynamics(Deep, PDPtr, PD),
-    PSPtr = PD ^ pd_proc_static,
-    deep_lookup_proc_statics(Deep, PSPtr, PS),
-    Name = PS ^ ps_refined_id.
-
-proc_static_name(Deep, PSPtr) = Name :-
-    deep_lookup_proc_statics(Deep, PSPtr, PS),
-    Name = PS ^ ps_refined_id.
-
-%-----------------------------------------------------------------------------%
-
-proc_dynamic_context(Deep, PDPtr, FileName, LineNumber) :-
-    deep_lookup_proc_dynamics(Deep, PDPtr, PD),
-    PSPtr = PD ^ pd_proc_static,
-    deep_lookup_proc_statics(Deep, PSPtr, PS),
-    FileName = PS ^ ps_file_name,
-    LineNumber = PS ^ ps_line_number.
-
-proc_static_context(Deep, PSPtr, FileName, LineNumber) :-
-    deep_lookup_proc_statics(Deep, PSPtr, PS),
-    FileName = PS ^ ps_file_name,
-    LineNumber = PS ^ ps_line_number.
-
-call_site_context(Deep, CSSPtr, FileName, LineNumber) :-
-    deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
-    CSS = call_site_static(PSPtr, _SlotNum, _Kind, LineNumber, _GoalPath),
-    deep_lookup_proc_statics(Deep, PSPtr, PS),
-    FileName = PS ^ ps_file_name.
-
-%-----------------------------------------------------------------------------%
-
-proc_static_to_line_group_info(Pref, Deep, PSPtr, FileName, LineNumber,
-        Name, HTML) :-
-    ( valid_proc_static_ptr(Deep, PSPtr) ->
-        deep_lookup_proc_statics(Deep, PSPtr, PS),
-        FileName = PS ^ ps_file_name,
-        LineNumber = PS ^ ps_line_number,
-        Name = PS ^ ps_refined_id,
-        HTML = proc_static_to_html_ref(Pref, Deep, PSPtr)
-    ;
-        FileName = "",
-        LineNumber = 0,
-        Name = "mercury_runtime",
-        HTML = Name
-    ).
-
-proc_static_to_html_ref(Pref, Deep, PSPtr) = HTML :-
-    URL = deep_cmd_pref_to_url(Pref, Deep, deep_cmd_proc(PSPtr)),
-    deep_lookup_proc_statics(Deep, PSPtr, PS),
-    ProcName = PS ^ ps_refined_id,
-    HTML = string.format("<A HREF=""%s"">%s</A>",
-        [s(URL), s(escape_break_html_string(ProcName))]).
-
-module_name_to_html_ref(Pref, Deep, ModuleName) = HTML :-
-    URL = deep_cmd_pref_to_url(Pref, Deep, deep_cmd_module(ModuleName)),
-    HTML = string.format("<A HREF=""%s"">%s</A>",
-        [s(URL), s(escape_break_html_string(ModuleName))]).
-
-clique_ptr_to_html_ref(Pref, Deep, ProcName, CliquePtr) = HTML :-
-    URL = deep_cmd_pref_to_url(Pref, Deep, deep_cmd_clique(CliquePtr)),
-    HTML = string.format("<A HREF=""%s"">%s</A>",
-        [s(URL), s(escape_break_html_string(ProcName))]).
-
-deep_cmd_pref_to_url(Pref, Deep, Cmd) =
-    machine_datafile_cmd_pref_to_url(Deep ^ server_name_port,
-        Deep ^ script_name, Deep ^ data_file_name, Cmd, Pref).
-
-%-----------------------------------------------------------------------------%
-
-plural(N) = Plural :-
-    ( N = 1 ->
-        Plural = ""
-    ;
-        Plural = "s"
-    ).
-
-%-----------------------------------------------------------------------------%
 
 escape_html_string(String) =
     replace_special_chars(special_html_char, String).
@@ -3208,16 +1012,6 @@
 % zero_width_space = "​".
 zero_width_space = "<wbr />".
 
-:- func machine_datafile_cmd_pref_to_url(string, string, string, cmd,
-    preferences) = string.
-
-machine_datafile_cmd_pref_to_url(Machine, ScriptName, DeepFileName, Cmd,
-        Preferences) =
-    "http://" ++
-    Machine ++
-    ScriptName ++ "?" ++
-    query_to_string(deep_query(yes(Cmd), DeepFileName, yes(Preferences))). 
-
 %-----------------------------------------------------------------------------%
 :- end_module html_format.
 %-----------------------------------------------------------------------------%
Index: measurement_units.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/measurement_units.m,v
retrieving revision 1.4
diff -u -b -r1.4 measurement_units.m
--- measurement_units.m	25 Sep 2008 03:47:03 -0000	1.4
+++ measurement_units.m	25 Sep 2008 07:04:29 -0000
@@ -119,10 +119,14 @@
 :- func commas(int) = string.
 
     % Format a floating point number, placing commas between groups of three
-    % digits in the integer part.
+    % digits in the integer part. The first argument is a format string.
     %
 :- func decimal_fraction(string, float) = string.
 
+:- func one_decimal_fraction(float) = string.
+:- func two_decimal_fraction(float) = string.
+:- func four_decimal_fraction(float) = string.
+
 %-----------------------------------------------------------------------------%
 %-----------------------------------------------------------------------------%
 
@@ -279,6 +283,12 @@
         error("decimal_fraction: Didn't split on decimal point properly")
     ).
 
+one_decimal_fraction(Measure) = decimal_fraction("%.1f", Measure).
+
+two_decimal_fraction(Measure) = decimal_fraction("%.2f", Measure).
+
+four_decimal_fraction(Measure) = decimal_fraction("%.4f", Measure).
+
 %-----------------------------------------------------------------------------%
 
 :- pred add_commas_intstr(string::in, string::out) is det.
Index: old_html_format.m
===================================================================
RCS file: old_html_format.m
diff -N old_html_format.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ old_html_format.m	25 Sep 2008 07:12:55 -0000
@@ -0,0 +1,2237 @@
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%-----------------------------------------------------------------------------%
+% Copyright (C) 2001-2002, 2004-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: old_html_format.m.
+%
+% This module contains code that helps old_query.m generate HTML.
+% The approach whose implementation it forms a part of has been superseded,
+% so it shouldn't be actively modified in the future.
+%
+%-----------------------------------------------------------------------------%
+
+:- module old_html_format.
+:- interface.
+
+:- import_module measurements.
+:- import_module profile.
+:- import_module query.
+:- import_module top_procs.
+
+:- import_module bool.
+:- import_module list.
+:- import_module unit.
+
+%-----------------------------------------------------------------------------%
+
+:- func table_start(preferences) = string.
+:- func table_end(preferences) = string.
+
+:- func page_banner(cmd, preferences) = string.
+:- func page_footer(cmd, preferences, deep) = string.
+
+:- func toggle_cost_criteria_in_top_procs_cmd(preferences, deep, display_limit,
+    cost_kind, include_descendants, measurement_scope) = string.
+
+:- func criteria_to_description(order_criteria) = string.
+:- func cost_criteria_to_description(cost_kind, include_descendants,
+    measurement_scope) = string.
+
+:- type id_fields
+    --->    source_proc
+    ;       rank_module
+    ;       rank_proc
+    ;       proc.
+
+:- type totals_disposition
+    --->    totals_meaningful
+    ;       totals_not_meaningful.
+
+:- type header_wrap_func == (func(string, order_criteria) = string).
+
+:- func fields_header(preferences, id_fields, totals_disposition,
+    header_wrap_func) = string.
+
+:- func header_row(string, preferences, id_fields, totals_disposition)
+    = string.
+:- func separator_row(preferences, id_fields, totals_disposition)
+    = string.
+
+:- type sub_lines(T)
+    --->    sub_lines(
+                sub_line_type   :: T,
+                sub_line_list   :: list(line_group(T, unit))
+             ).
+
+:- type one_id ---> one_id.
+:- type two_id ---> two_id.
+
+:- type one_id_sub_lines == sub_lines(one_id).
+:- type two_id_sub_lines == sub_lines(two_id).
+
+:- type one_id_line == line_group(one_id, unit).
+:- type two_id_line == line_group(two_id, unit).
+:- type one_id_line_group == line_group(one_id, one_id_sub_lines).
+:- type two_id_line_group == line_group(two_id, two_id_sub_lines).
+:- type one_two_id_line_group == line_group(one_id, two_id_sub_lines).
+
+    % This function takes a context description (which may be empty) and a
+    % HTML string describing all fields in a row but the first, and returns
+    % the HTML for the full row.
+    %
+:- func add_context(string, line_group(one_id, LL)) = line_group(two_id, LL).
+
+    % This adds the context from the line group to the HTML as the first field
+    % of the resulting line group.
+    %
+:- func add_self_context(line_group(one_id, LL)) = line_group(two_id, LL).
+
+:- func add_ranks(list(line_group(one_id, LL))) = list(line_group(two_id, LL)).
+
+:- func line_to_one_id_subline_group(line_group(FL, unit))
+    = line_group(FL, one_id_sub_lines).
+
+:- func line_to_two_id_subline_group(line_group(FL, unit))
+    = line_group(FL, two_id_sub_lines).
+
+:- func one_id_line_to_html(preferences, deep, totals_disposition,
+    one_id_line) = string.
+:- func one_id_line_group_to_html(preferences, deep, totals_disposition,
+    one_id_line_group) = string.
+:- func two_id_line_to_html(preferences, deep, totals_disposition,
+    two_id_line) = string.
+:- func two_id_line_group_to_html(preferences, deep, totals_disposition,
+    two_id_line_group) = string.
+
+:- func own_and_desc_to_html(own_prof_info, inherit_prof_info,
+    preferences, deep, totals_disposition) = string.
+
+:- pred lookup_ticks_per_sec(profile_stats::in, int::out, bool::out) is det.
+
+:- func proc_dynamic_name(deep, proc_dynamic_ptr) = string.
+:- func proc_static_name(deep, proc_static_ptr) = string.
+
+:- pred proc_dynamic_context(deep::in, proc_dynamic_ptr::in,
+    string::out, int::out) is det.
+:- pred proc_static_context(deep::in, proc_static_ptr::in,
+    string::out, int::out) is det.
+:- pred call_site_context(deep::in, call_site_static_ptr::in,
+    string::out, int::out) is det.
+
+:- pred proc_static_to_line_group_info(preferences::in, deep::in,
+    proc_static_ptr::in, string::out, int::out, string::out, string::out)
+    is det.
+:- func proc_static_to_html_ref(preferences, deep, proc_static_ptr) = string.
+:- func module_name_to_html_ref(preferences, deep, string) = string.
+:- func clique_ptr_to_html_ref(preferences, deep, string, clique_ptr) = string.
+:- func deep_cmd_pref_to_url(preferences, deep, cmd) = string.
+
+:- func plural(int) = string.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module measurement_units.
+:- import_module html_format.       % for escape_break_html_string
+
+:- import_module float.
+:- import_module int.
+:- import_module maybe.
+:- import_module string.
+
+%-----------------------------------------------------------------------------%
+%
+% Deprecated html_format code.
+%
+
+page_banner(_Cmd, Pref) =
+    "<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.01//EN""\n" ++
+    """http://www.w3.org/TR/html4/strict.dtd"">\n" ++
+    "<HTML>\n" ++
+    "<HEAD>\n" ++
+    "<TITLE>Page created by the Mercury Deep Profiler.</TITLE>\n" ++
+    banner_style(Pref) ++
+    "</HEAD>\n" ++
+    "<BODY>\n".
+
+:- func banner_style(preferences) = string.
+
+banner_style(Pref) = HTML :-
+    Fields = Pref ^ pref_fields,
+
+    some [!GroupNum] (
+        !:GroupNum = 0,
+        IdStyle = string.format("  TD.id       { %s }\n",
+            [s(select_colgroup_background(Pref, !.GroupNum))]),
+        !:GroupNum = !.GroupNum + 1,
+        Fields = fields(PortFields, TimeFields, CallSeqsFields,
+            AllocFields, MemoryFields),
+        (
+            PortFields = no_port,
+            PortStyle = ""
+        ;
+            PortFields = port,
+            PortStyle = string.format("  TD.port     { %s }\n",
+                [s(select_colgroup_background(Pref, !.GroupNum))]),
+            !:GroupNum = !.GroupNum + 1
+        ),
+        (
+            TimeFields = no_time,
+            TimeStyle = ""
+        ;
+            ( TimeFields = ticks
+            ; TimeFields = time
+            ; TimeFields = ticks_and_time
+            ; TimeFields = time_and_percall
+            ; TimeFields = ticks_and_time_and_percall
+            ),
+            TimeStyle = string.format("  TD.time     { %s }\n",
+                [s(select_colgroup_background(Pref, !.GroupNum))]),
+            !:GroupNum = !.GroupNum + 1
+        ),
+        (
+            CallSeqsFields = no_callseqs,
+            CallSeqsStyle = ""
+        ;
+            ( CallSeqsFields = callseqs
+            ; CallSeqsFields = callseqs_and_percall
+            ),
+            CallSeqsStyle = string.format("  TD.callseqs { %s }\n",
+                [s(select_colgroup_background(Pref, !.GroupNum))]),
+            !:GroupNum = !.GroupNum + 1
+        ),
+        (
+            AllocFields = no_alloc,
+            AllocStyle = ""
+        ;
+            ( AllocFields = alloc
+            ; AllocFields = alloc_and_percall
+            ),
+            AllocStyle = string.format("  TD.alloc    { %s }\n",
+                [s(select_colgroup_background(Pref, !.GroupNum))]),
+            !:GroupNum = !.GroupNum + 1
+        ),
+        (
+            MemoryFields = no_memory,
+            MemoryStyle = ""
+        ;
+            ( MemoryFields = memory(_)
+            ; MemoryFields = memory_and_percall(_)
+            ),
+            MemoryStyle = string.format("  TD.memory   { %s }\n",
+                [s(select_colgroup_background(Pref, !.GroupNum))])
+        )
+    ),
+    ButtonStyle = "  A.button { margin: 5px; text-decoration: none; }\n",
+    HTML =
+        "<STYLE TYPE=""text/css"">\n" ++
+        IdStyle ++
+        PortStyle ++
+        TimeStyle ++
+        CallSeqsStyle ++
+        AllocStyle ++
+        MemoryStyle ++
+        ButtonStyle ++
+        "</STYLE>\n".
+
+:- func select_colgroup_background(preferences, int) = string.
+
+select_colgroup_background(Pref, N) = HTML :-
+    (
+        Pref ^ pref_colour = colour_column_groups,
+        ( N /\ 1 = 0 ->
+            Background = even_background
+        ;
+            Background = odd_background
+        ),
+        string.format("background: %s", [s(Background)], HTML)
+    ;
+        Pref ^ pref_colour = do_not_colour_column_groups,
+        HTML = ""
+    ).
+
+:- func even_background = string.
+
+even_background = "rgb(255, 255, 240)".
+
+:- func odd_background = string.
+
+odd_background =  "rgb(240, 240, 255)".
+
+%-----------------------------------------------------------------------------%
+
+table_start(Pref) = HTML :-
+    (
+        Pref ^ pref_box = do_not_box_tables,
+        HTML = "\n<TABLE width=100%>\n"
+    ;
+        Pref ^ pref_box = box_tables,
+        HTML = "\n<TABLE width=100% border=1>\n"
+    ).
+
+table_end(_) = "</TABLE>\n".
+
+%-----------------------------------------------------------------------------%
+
+page_footer(Cmd, Pref, Deep) =
+    "<p>\n" ++
+    footer_pref_toggles(Cmd, Pref, Deep) ++
+    "<br>\n" ++
+    string.format("<A CLASS=""button"" HREF=""%s"">[Menu]</A>\n",
+        [s(deep_cmd_pref_to_url(Pref, Deep, deep_cmd_menu))]) ++
+    string.format("<A CLASS=""button"" HREF=""%s"">[Restart]</A>\n",
+        [s(deep_cmd_pref_to_url(Pref, Deep, deep_cmd_restart))]) ++
+    string.format("<A CLASS=""button"" HREF=""%s"">[Quit]</A>\n",
+        [s(deep_cmd_pref_to_url(Pref, Deep, deep_cmd_quit))]) ++
+    "</BODY>\n" ++
+    "</HTML>\n".
+
+:- func footer_pref_toggles(cmd, preferences, deep) = string.
+
+footer_pref_toggles(Cmd, Pref, Deep) = AllToggles :-
+    RelevantToggles = command_relevant_toggles(Cmd),
+    ( list.member(toggle_fields, RelevantToggles) ->
+        FieldToggle = footer_field_toggle(Cmd, Pref, Deep)
+    ;
+        FieldToggle = ""
+    ),
+    ( list.member(toggle_ancestor_limit, RelevantToggles) ->
+        AncestorToggle =
+            footer_ancestor_toggle(Cmd, Pref, Deep) ++
+            "<br>\n"
+    ;
+        AncestorToggle = ""
+    ),
+    ( list.member(toggle_order_criteria, RelevantToggles) ->
+        OrderToggle =
+            footer_order_criteria_toggle(Cmd, Pref, Deep) ++
+            "<br>\n"
+    ;
+        OrderToggle = ""
+    ),
+    ( list.member(toggle_summarize, RelevantToggles) ->
+        SummarizeToggle = footer_summarize_toggle(Cmd, Pref, Deep)
+    ;
+        SummarizeToggle = ""
+    ),
+    ( list.member(toggle_contour, RelevantToggles) ->
+        ContourToggle = footer_contour_toggle(Cmd, Pref, Deep)
+    ;
+        ContourToggle = ""
+    ),
+    ( list.member(toggle_time_format, RelevantToggles) ->
+        TimeFormatToggle = footer_time_format_toggle(Cmd, Pref, Deep)
+    ;
+        TimeFormatToggle = ""
+    ),
+    ( list.member(toggle_colour, RelevantToggles) ->
+        ColourToggle = footer_colour_toggle(Cmd, Pref, Deep)
+    ;
+        ColourToggle = ""
+    ),
+    ( list.member(toggle_box, RelevantToggles) ->
+        BoxToggle = footer_box_toggle(Cmd, Pref, Deep)
+    ;
+        BoxToggle = ""
+    ),
+    ( list.member(toggle_inactive_modules, RelevantToggles) ->
+        InactiveModuleToggle = footer_inactive_modules_toggle(Cmd, Pref, Deep)
+    ;
+        InactiveModuleToggle = ""
+    ),
+    ( list.member(toggle_inactive_procs, RelevantToggles) ->
+        InactiveProcsToggle = footer_inactive_procs_toggle(Cmd, Pref, Deep)
+    ;
+        InactiveProcsToggle = ""
+    ),
+    AllToggles =
+        FieldToggle ++
+        AncestorToggle ++
+        OrderToggle ++
+        SummarizeToggle ++
+        ContourToggle ++
+        TimeFormatToggle ++
+        ColourToggle ++
+        BoxToggle ++
+        InactiveModuleToggle ++
+        InactiveProcsToggle.
+
+%-----------------------------------------------------------------------------%
+
+:- type toggle_kind
+    --->    toggle_fields
+    ;       toggle_box
+    ;       toggle_colour
+    ;       toggle_ancestor_limit
+    ;       toggle_summarize
+    ;       toggle_order_criteria
+    ;       toggle_contour
+    ;       toggle_time_format
+    ;       toggle_inactive_modules
+    ;       toggle_inactive_procs.
+
+:- func command_relevant_toggles(cmd) = list(toggle_kind).
+
+command_relevant_toggles(deep_cmd_quit) = [].
+command_relevant_toggles(deep_cmd_restart) = [].
+command_relevant_toggles(deep_cmd_timeout(_)) = [].
+command_relevant_toggles(deep_cmd_menu) = [].
+command_relevant_toggles(deep_cmd_root(_)) =
+    % The clique_ptr doesn't matter.
+    command_relevant_toggles(deep_cmd_clique(clique_ptr(1))).
+command_relevant_toggles(deep_cmd_clique(_)) =
+    [toggle_fields, toggle_box, toggle_colour, toggle_ancestor_limit,
+    toggle_summarize, toggle_order_criteria, toggle_time_format].
+command_relevant_toggles(deep_cmd_proc(_)) =
+    [toggle_fields, toggle_box, toggle_colour, toggle_summarize,
+    toggle_order_criteria, toggle_time_format].
+command_relevant_toggles(deep_cmd_proc_callers(_, _, _, _)) =
+    [toggle_fields, toggle_box, toggle_colour, toggle_order_criteria,
+    toggle_contour, toggle_time_format].
+command_relevant_toggles(deep_cmd_program_modules) =
+    [toggle_fields, toggle_box, toggle_colour, toggle_order_criteria,
+    toggle_time_format, toggle_inactive_modules].
+command_relevant_toggles(deep_cmd_module(_)) =
+    [toggle_fields, toggle_box, toggle_colour, toggle_order_criteria,
+    toggle_time_format, toggle_inactive_procs].
+command_relevant_toggles(deep_cmd_top_procs(_, _, _, _)) =
+    [toggle_fields, toggle_box, toggle_colour, toggle_time_format].
+command_relevant_toggles(deep_cmd_procrep_coverage(_)) = [].
+command_relevant_toggles(deep_cmd_dump_proc_static(_)) = [].
+command_relevant_toggles(deep_cmd_dump_proc_dynamic(_)) = [].
+command_relevant_toggles(deep_cmd_dump_call_site_static(_)) = [].
+command_relevant_toggles(deep_cmd_dump_call_site_dynamic(_)) = [].
+command_relevant_toggles(deep_cmd_dump_clique(_)) = [].
+
+:- func footer_field_toggle(cmd, preferences, deep) = string.
+
+footer_field_toggle(Cmd, Pref, Deep) = HTML :-
+    Fields = Pref ^ pref_fields,
+    ( Fields ^ port_fields = no_port ->
+        Port1Toggle = ""
+    ;
+        Port1Fields = Fields ^ port_fields := no_port,
+        Port1Pref = Pref ^ pref_fields := Port1Fields,
+        Port1Msg = "[No port counts]",
+        Port1Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(deep_cmd_pref_to_url(Port1Pref, Deep, Cmd)), s(Port1Msg)])
+    ),
+    ( Fields ^ port_fields = port ->
+        Port2Toggle = ""
+    ;
+        Port2Fields = Fields ^ port_fields := port,
+        Port2Pref = Pref ^ pref_fields := Port2Fields,
+        Port2Msg = "[Port counts]",
+        Port2Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(deep_cmd_pref_to_url(Port2Pref, Deep, Cmd)), s(Port2Msg)])
+    ),
+    ( Fields ^ time_fields = no_time ->
+        Time1Toggle = ""
+    ;
+        Time1Fields = Fields ^ time_fields := no_time,
+        Time1Pref = Pref ^ pref_fields := Time1Fields,
+        Time1Msg = "[No time info]",
+        Time1Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(deep_cmd_pref_to_url(Time1Pref, Deep, Cmd)), s(Time1Msg)])
+    ),
+    ( Fields ^ time_fields = ticks ->
+        Time2Toggle = ""
+    ;
+        Time2Fields = Fields ^ time_fields := ticks,
+        Time2Pref = Pref ^ pref_fields := Time2Fields,
+        Time2Msg = "[Ticks]",
+        Time2Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(deep_cmd_pref_to_url(Time2Pref, Deep, Cmd)), s(Time2Msg)])
+    ),
+    ( Fields ^ time_fields = time ->
+        Time3Toggle = ""
+    ;
+        Time3Fields = Fields ^ time_fields := time,
+        Time3Pref = Pref ^ pref_fields := Time3Fields,
+        Time3Msg = "[Times]",
+        Time3Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(deep_cmd_pref_to_url(Time3Pref, Deep, Cmd)), s(Time3Msg)])
+    ),
+    ( Fields ^ time_fields = ticks_and_time->
+        Time4Toggle = ""
+    ;
+        Time4Fields = Fields ^ time_fields := ticks_and_time,
+        Time4Pref = Pref ^ pref_fields := Time4Fields,
+        Time4Msg = "[Ticks and times]",
+        Time4Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(deep_cmd_pref_to_url(Time4Pref, Deep, Cmd)), s(Time4Msg)])
+    ),
+    ( Fields ^ time_fields = time_and_percall ->
+        Time5Toggle = ""
+    ;
+        Time5Fields = Fields ^ time_fields := time_and_percall,
+        Time5Pref = Pref ^ pref_fields := Time5Fields,
+        Time5Msg = "[Times and per-call times]",
+        Time5Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(deep_cmd_pref_to_url(Time5Pref, Deep, Cmd)), s(Time5Msg)])
+    ),
+    ( Fields ^ time_fields = ticks_and_time_and_percall ->
+        Time6Toggle = ""
+    ;
+        Time6Fields = Fields ^ time_fields := ticks_and_time_and_percall,
+        Time6Pref = Pref ^ pref_fields := Time6Fields,
+        Time6Msg = "[Ticks and times and per-call times]",
+        Time6Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(deep_cmd_pref_to_url(Time6Pref, Deep, Cmd)), s(Time6Msg)])
+    ),
+    ( Fields ^ callseqs_fields = no_callseqs ->
+        CallSeqs1Toggle = ""
+    ;
+        CallSeqs1Fields = Fields ^ callseqs_fields := no_callseqs,
+        CallSeqs1Pref = Pref ^ pref_fields := CallSeqs1Fields,
+        CallSeqs1Msg = "[No call sequence number info]",
+        CallSeqs1Toggle =
+            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+                [s(deep_cmd_pref_to_url(CallSeqs1Pref, Deep, Cmd)),
+                s(CallSeqs1Msg)])
+    ),
+    ( Fields ^ callseqs_fields = callseqs ->
+        CallSeqs2Toggle = ""
+    ;
+        CallSeqs2Fields = Fields ^ callseqs_fields := callseqs,
+        CallSeqs2Pref = Pref ^ pref_fields := CallSeqs2Fields,
+        CallSeqs2Msg = "[Call sequence numbers]",
+        CallSeqs2Toggle =
+            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+                [s(deep_cmd_pref_to_url(CallSeqs2Pref, Deep, Cmd)),
+                s(CallSeqs2Msg)])
+    ),
+    ( Fields ^ callseqs_fields = callseqs_and_percall ->
+        CallSeqs3Toggle = ""
+    ;
+        CallSeqs3Fields = Fields ^ callseqs_fields := callseqs_and_percall,
+        CallSeqs3Pref = Pref ^ pref_fields := CallSeqs3Fields,
+        CallSeqs3Msg = "[Call sequence numbers including per-call]",
+        CallSeqs3Toggle =
+            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+                [s(deep_cmd_pref_to_url(CallSeqs3Pref, Deep, Cmd)),
+                s(CallSeqs3Msg)])
+    ),
+    ( Fields ^ alloc_fields = no_alloc ->
+        Alloc1Toggle = ""
+    ;
+        Alloc1Fields = Fields ^ alloc_fields := no_alloc,
+        Alloc1Pref = Pref ^ pref_fields := Alloc1Fields,
+        Alloc1Msg = "[No allocations]",
+        Alloc1Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(deep_cmd_pref_to_url(Alloc1Pref, Deep, Cmd)), s(Alloc1Msg)])
+    ),
+    ( Fields ^ alloc_fields = alloc ->
+        Alloc2Toggle = ""
+    ;
+        Alloc2Fields = Fields ^ alloc_fields := alloc,
+        Alloc2Pref = Pref ^ pref_fields := Alloc2Fields,
+        Alloc2Msg = "[Allocations]",
+        Alloc2Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(deep_cmd_pref_to_url(Alloc2Pref, Deep, Cmd)), s(Alloc2Msg)])
+    ),
+    ( Fields ^ alloc_fields = alloc_and_percall ->
+        Alloc3Toggle = ""
+    ;
+        Alloc3Fields = Fields ^ alloc_fields := alloc_and_percall,
+        Alloc3Pref = Pref ^ pref_fields := Alloc3Fields,
+        Alloc3Msg = "[Allocations and per-call allocations]",
+        Alloc3Toggle = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(deep_cmd_pref_to_url(Alloc3Pref, Deep, Cmd)), s(Alloc3Msg)])
+    ),
+    ( Fields ^ memory_fields = no_memory ->
+        Memory1Toggle = ""
+    ;
+        Memory1Fields = Fields ^ memory_fields := no_memory,
+        Memory1Pref = Pref ^ pref_fields := Memory1Fields,
+        Memory1Msg = "[No memory info]",
+        Memory1Toggle =
+            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+                [s(deep_cmd_pref_to_url(Memory1Pref, Deep, Cmd)),
+                s(Memory1Msg)])
+    ),
+    ( Fields ^ memory_fields = memory(units_words) ->
+        Memory2Toggle = ""
+    ;
+        Memory2Fields = Fields ^ memory_fields := memory(units_words),
+        Memory2Pref = Pref ^ pref_fields := Memory2Fields,
+        Memory2Msg = "[Words]",
+        Memory2Toggle =
+            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+                [s(deep_cmd_pref_to_url(Memory2Pref, Deep, Cmd)),
+                s(Memory2Msg)])
+    ),
+    ( Fields ^ memory_fields = memory(units_bytes) ->
+        Memory3Toggle = ""
+    ;
+        Memory3Fields = Fields ^ memory_fields := memory(units_bytes),
+        Memory3Pref = Pref ^ pref_fields := Memory3Fields,
+        Memory3Msg = "[Bytes]",
+        Memory3Toggle =
+            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+                [s(deep_cmd_pref_to_url(Memory3Pref, Deep, Cmd)),
+                s(Memory3Msg)])
+    ),
+    ( Fields ^ memory_fields = memory_and_percall(units_words) ->
+        Memory4Toggle = ""
+    ;
+        Memory4Fields = Fields ^ memory_fields :=
+            memory_and_percall(units_words),
+        Memory4Pref = Pref ^ pref_fields := Memory4Fields,
+        Memory4Msg = "[Words and per-call words]",
+        Memory4Toggle =
+            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+                [s(deep_cmd_pref_to_url(Memory4Pref, Deep, Cmd)),
+                s(Memory4Msg)])
+    ),
+    ( Fields ^ memory_fields = memory_and_percall(units_bytes) ->
+        Memory5Toggle = ""
+    ;
+        Memory5Fields = Fields ^ memory_fields :=
+            memory_and_percall(units_bytes),
+        Memory5Pref = Pref ^ pref_fields := Memory5Fields,
+        Memory5Msg = "[Bytes and per-call bytes]",
+        Memory5Toggle =
+            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+                [s(deep_cmd_pref_to_url(Memory5Pref, Deep, Cmd)),
+                s(Memory5Msg)])
+    ),
+    ( Fields = default_fields(Deep) ->
+        DefaultToggle = ""
+    ;
+        DefaultMsg  = "[Restore defaults]",
+        DefaultPref = Pref ^ pref_fields := default_fields(Deep),
+        DefaultToggle =
+            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+                [s(deep_cmd_pref_to_url(DefaultPref, Deep, Cmd)),
+                s(DefaultMsg)])
+    ),
+    HTML =
+        "<strong>Toggle fields:</strong>\n" ++
+        DefaultToggle ++
+        "<br>\n" ++
+        Port1Toggle ++ Port2Toggle ++
+        "<br>\n" ++
+        Time1Toggle ++ Time2Toggle ++ Time3Toggle ++
+        Time4Toggle ++ Time5Toggle ++ Time6Toggle ++
+        "<br>\n" ++
+        CallSeqs1Toggle ++ CallSeqs2Toggle ++ CallSeqs3Toggle ++
+        "<br>\n" ++
+        Alloc1Toggle ++ Alloc2Toggle ++ Alloc3Toggle ++
+        "<br>\n" ++
+        Memory1Toggle ++ Memory2Toggle ++ Memory3Toggle ++
+        Memory4Toggle ++ Memory5Toggle ++
+        "<br>\n".
+
+:- func footer_ancestor_toggle(cmd, preferences, deep) = string.
+
+footer_ancestor_toggle(Cmd, Pref, Deep) = HTML :-
+    (
+        Pref ^ pref_anc = no,
+        Display1 = yes,
+        Display2 = yes,
+        Msg1 = "[One ancestor]",
+        Pref1 = Pref ^ pref_anc := yes(1),
+        Msg2 = "[Two ancestors]",
+        Pref2 = Pref ^ pref_anc := yes(2),
+        Msg3 = "[Three ancestors]",
+        Pref3 = Pref ^ pref_anc := yes(3),
+        Msg4 = "[Five ancestors]",
+        Pref4 = Pref ^ pref_anc := yes(5),
+        Msg5 = "[Ten ancestors]",
+        Pref5 = Pref ^ pref_anc := yes(10)
+    ;
+        Pref ^ pref_anc = yes(OldAncestorLimit),
+        ( OldAncestorLimit > 2 ->
+            Display1 = yes
+        ;
+            Display1 = no
+        ),
+        ( OldAncestorLimit > 1 ->
+            Display2 = yes
+        ;
+            Display2 = no
+        ),
+        Msg1 = "[Halve ancestors]",
+        Pref1 = Pref ^ pref_anc := yes(OldAncestorLimit // 2),
+        Msg2 = "[Remove an ancestor]",
+        Pref2 = Pref ^ pref_anc := yes(OldAncestorLimit - 1),
+        Msg3 = "[Add an ancestor]",
+        Pref3 = Pref ^ pref_anc := yes(OldAncestorLimit + 1),
+        Msg4 = "[Double ancestors]",
+        Pref4 = Pref ^ pref_anc := yes(OldAncestorLimit * 2),
+        Msg5 = "[Unlimited ancestors]",
+        Pref5 = Pref ^ pref_anc := no
+    ),
+    Toggle1 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+        [s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]),
+    Toggle2 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+        [s(deep_cmd_pref_to_url(Pref2, Deep, Cmd)), s(Msg2)]),
+    Toggle3 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+        [s(deep_cmd_pref_to_url(Pref3, Deep, Cmd)), s(Msg3)]),
+    Toggle4 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+        [s(deep_cmd_pref_to_url(Pref4, Deep, Cmd)), s(Msg4)]),
+    Toggle5 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+        [s(deep_cmd_pref_to_url(Pref5, Deep, Cmd)), s(Msg5)]),
+    (
+        Display1 = yes,
+        MaybeToggle1 = Toggle1
+    ;
+        Display1 = no,
+        MaybeToggle1 = ""
+    ),
+    (
+        Display2 = yes,
+        MaybeToggle2 = Toggle2
+    ;
+        Display2 = no,
+        MaybeToggle2 = ""
+    ),
+    HTML =
+        "<strong>Toggle ancestors:</strong><br />\n" ++
+        MaybeToggle1 ++ MaybeToggle2 ++ Toggle3 ++ Toggle4 ++ Toggle5.
+
+:- func footer_box_toggle(cmd, preferences, deep) = string.
+
+footer_box_toggle(Cmd, Pref, Deep) = HTML :-
+    (
+        Pref ^ pref_box = do_not_box_tables,
+        Pref1 = Pref ^ pref_box := box_tables,
+        Msg1 = "[Box]"
+    ;
+        Pref ^ pref_box = box_tables,
+        Pref1 = Pref ^ pref_box := do_not_box_tables,
+        Msg1 = "[Unbox]"
+    ),
+    HTML = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+        [s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]).
+
+:- func footer_colour_toggle(cmd, preferences, deep) = string.
+
+footer_colour_toggle(Cmd, Pref, Deep) = HTML :-
+    (
+        Pref ^ pref_colour = do_not_colour_column_groups,
+        Pref1 = Pref ^ pref_colour := colour_column_groups,
+        Msg1 = "[Colour column groups]"
+    ;
+        Pref ^ pref_colour = colour_column_groups,
+        Pref1 = Pref ^ pref_colour := do_not_colour_column_groups,
+        Msg1 = "[Fade column groups]"
+    ),
+    HTML = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+        [s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]).
+
+:- func footer_summarize_toggle(cmd, preferences, deep) = string.
+
+footer_summarize_toggle(Cmd, Pref, Deep) = HTML :-
+    (
+        Pref ^ pref_summarize = summarize,
+        Pref1 = Pref ^ pref_summarize := do_not_summarize,
+        Msg1 = "[Expand higher order calls]"
+    ;
+        Pref ^ pref_summarize = do_not_summarize,
+        Pref1 = Pref ^ pref_summarize := summarize,
+        Msg1 = "[Summarize higher order calls]"
+    ),
+    HTML = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+        [s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]).
+
+:- func footer_contour_toggle(cmd, preferences, deep) = string.
+
+footer_contour_toggle(Cmd, Pref, Deep) = HTML :-
+    (
+        Pref ^ pref_contour = do_not_apply_contour_exclusion,
+        Pref1 = Pref ^ pref_contour := apply_contour_exclusion,
+        Msg1 = "[Apply contour exclusion]"
+    ;
+        Pref ^ pref_contour = apply_contour_exclusion,
+        Pref1 = Pref ^ pref_contour := do_not_apply_contour_exclusion,
+        Msg1 = "[Don't apply contour exclusion]"
+    ),
+    HTML = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+        [s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]).
+
+:- func footer_time_format_toggle(cmd, preferences, deep) = string.
+
+footer_time_format_toggle(Cmd, Pref, Deep) = HTML :-
+    TimeFields = Pref ^ pref_fields ^ time_fields,
+    (
+        ( TimeFields = no_time
+        ; TimeFields = ticks
+        ),
+        ToggleTimeFormat = no
+    ;
+        ( TimeFields = time
+        ; TimeFields = ticks_and_time
+        ; TimeFields = time_and_percall
+        ; TimeFields = ticks_and_time_and_percall
+        ),
+        ToggleTimeFormat = yes
+    ),
+    (
+        ToggleTimeFormat = no,
+        HTML = ""
+    ;
+        ToggleTimeFormat = yes,
+        (
+            Pref ^ pref_time = no_scale,
+            Pref1 = Pref ^ pref_time := scale_by_millions,
+            Msg1  = "[Time in s, us]",
+            Pref2 = Pref ^ pref_time := scale_by_thousands,
+            Msg2  = "[Time in s, ms, us, ns]"
+        ;
+            Pref ^ pref_time = scale_by_millions,
+            Pref1 = Pref ^ pref_time := no_scale,
+            Msg1  = "[Time in s]",
+            Pref2 = Pref ^ pref_time := scale_by_thousands,
+            Msg2  = "[Time in s, ms, us, ns]"
+        ;
+            Pref ^ pref_time = scale_by_thousands,
+            Pref1 = Pref ^ pref_time := no_scale,
+            Msg1  = "[Time in s]",
+            Pref2 = Pref ^ pref_time := scale_by_millions,
+            Msg2  = "[Time in s, us]"
+        ),
+        HTML =
+            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+                [s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]) ++
+            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+                [s(deep_cmd_pref_to_url(Pref2, Deep, Cmd)), s(Msg2)])
+    ).
+
+%-----------------------------------------------------------------------------%
+
+:- func footer_order_criteria_toggle(cmd, preferences, deep) = string.
+
+footer_order_criteria_toggle(Cmd, Pref, Deep) =
+    toggle_criteria(Pref ^ pref_criteria,
+        update_criteria_in_prefs(Pref, Deep, Cmd),
+        update_cost_criteria_in_prefs(Pref, Deep, Cmd)).
+
+toggle_cost_criteria_in_top_procs_cmd(Pref, Deep, Limit,
+        CostKind, InclDesc, Scope) =
+    toggle_cost_criteria(CostKind, InclDesc, Scope,
+        update_cost_criteria_in_top_procs_cmd(Pref, Deep, Limit)).
+
+%-----------------------------------------------------------------------------%
+
+:- type update_criteria_func == (func(order_criteria) = string).
+
+:- type update_cost_criteria_func ==
+    (func(cost_kind, include_descendants, measurement_scope) = string).
+
+:- func toggle_criteria(order_criteria,
+    update_criteria_func, update_cost_criteria_func) = string.
+
+toggle_criteria(Criteria, UpdateCriteria, UpdateCostCriteria) = HTML :-
+    (
+        Criteria = by_context,
+        Criteria1 = by_name,
+        Msg1 = "[Sort by name]",
+        Criteria2 =
+            by_cost(default_cost_kind, default_incl_desc, default_scope),
+        Msg2 = "[Sort by cost]"
+    ;
+        Criteria = by_name,
+        Criteria1 = by_context,
+        Msg1 = "[Sort by context]",
+        Criteria2 =
+            by_cost(default_cost_kind, default_incl_desc, default_scope),
+        Msg2 = "[Sort by cost]"
+    ;
+        Criteria = by_cost(_, _, _),
+        Criteria1 = by_context,
+        Msg1 = "[Sort by context]",
+        Criteria2 = by_name,
+        Msg2 = "[Sort by name]"
+    ),
+    Toggle1 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+        [s(UpdateCriteria(Criteria1)), s(Msg1)]),
+    Toggle2 = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+        [s(UpdateCriteria(Criteria2)), s(Msg2)]),
+    (
+        Criteria = by_cost(CostKind, InclDesc, Scope),
+        ToggleRest = toggle_cost_criteria(CostKind, InclDesc, Scope,
+            UpdateCostCriteria)
+    ;
+        ( Criteria = by_context
+        ; Criteria = by_name
+        ),
+        ToggleRest = ""
+    ),
+    HTML = "<strong>Toggle ordering criteria:</strong><br />\n" ++
+        Toggle1 ++ Toggle2 ++ ToggleRest.
+
+:- func toggle_cost_criteria(cost_kind, include_descendants, measurement_scope,
+    update_cost_criteria_func) = string.
+
+toggle_cost_criteria(CostKind, InclDesc, Scope, UpdateCriteria) = Toggles :-
+    (
+        ( CostKind = cost_redos
+        ; CostKind = cost_time
+        ; CostKind = cost_callseqs
+        ; CostKind = cost_allocs
+        ; CostKind = cost_words
+        ),
+        MsgCalls = "[Sort by calls]",
+        ToggleCalls = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(UpdateCriteria(cost_calls, InclDesc, Scope)), s(MsgCalls)])
+    ;
+        CostKind = cost_calls,
+        ToggleCalls = ""
+    ),
+    (
+        ( CostKind = cost_calls
+        ; CostKind = cost_time
+        ; CostKind = cost_callseqs
+        ; CostKind = cost_allocs
+        ; CostKind = cost_words
+        ),
+        MsgRedos = "[Sort by redos]",
+        ToggleRedos = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(UpdateCriteria(cost_redos, InclDesc, Scope)), s(MsgRedos)])
+    ;
+        CostKind = cost_redos,
+        ToggleRedos = ""
+    ),
+    (
+        ( CostKind = cost_calls
+        ; CostKind = cost_redos
+        ; CostKind = cost_callseqs
+        ; CostKind = cost_allocs
+        ; CostKind = cost_words
+        ),
+        MsgTime = "[Sort by time]",
+        ToggleTime = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(UpdateCriteria(cost_time, InclDesc, Scope)), s(MsgTime)])
+    ;
+        CostKind = cost_time,
+        ToggleTime = ""
+    ),
+    (
+        ( CostKind = cost_calls
+        ; CostKind = cost_redos
+        ; CostKind = cost_time
+        ; CostKind = cost_allocs
+        ; CostKind = cost_words
+        ),
+        MsgCallSeqs = "[Sort by call sequence numbers]",
+        ToggleCallSeqs =
+            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+                [s(UpdateCriteria(cost_callseqs, InclDesc, Scope)),
+                s(MsgCallSeqs)])
+    ;
+        CostKind = cost_callseqs,
+        ToggleCallSeqs = ""
+    ),
+    (
+        ( CostKind = cost_calls
+        ; CostKind = cost_redos
+        ; CostKind = cost_time
+        ; CostKind = cost_callseqs
+        ; CostKind = cost_words
+        ),
+        MsgAllocs = "[Sort by allocations]",
+        ToggleAllocs =
+            string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+                [s(UpdateCriteria(cost_allocs, InclDesc, Scope)),
+                s(MsgAllocs)])
+    ;
+        CostKind = cost_allocs,
+        ToggleAllocs = ""
+    ),
+    (
+        ( CostKind = cost_calls
+        ; CostKind = cost_redos
+        ; CostKind = cost_time
+        ; CostKind = cost_callseqs
+        ; CostKind = cost_allocs
+        ),
+        MsgWords = "[Sort by words]",
+        ToggleWords = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(UpdateCriteria(cost_words, InclDesc, Scope)), s(MsgWords)])
+    ;
+        CostKind = cost_words,
+        ToggleWords = ""
+    ),
+    (
+        InclDesc = self,
+        MsgDesc = "[Include descendants]",
+        ToggleDesc = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(UpdateCriteria(CostKind, self_and_desc, Scope)), s(MsgDesc)])
+    ;
+        InclDesc = self_and_desc,
+        MsgDesc = "[Exclude descendants]",
+        ToggleDesc = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(UpdateCriteria(CostKind, self, Scope)), s(MsgDesc)])
+    ),
+    (
+        Scope = per_call,
+        MsgScope = "[Count overall cost]",
+        ToggleScope = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(UpdateCriteria(CostKind, InclDesc, overall)), s(MsgScope)])
+    ;
+        Scope = overall,
+        MsgScope = "[Count per-call cost]",
+        ToggleScope = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+            [s(UpdateCriteria(CostKind, InclDesc, per_call)), s(MsgScope)])
+    ),
+    Toggles = ToggleCalls ++ ToggleRedos ++ ToggleTime ++ ToggleCallSeqs ++
+        ToggleAllocs ++ ToggleWords ++
+        "\n<br>\n" ++ ToggleDesc ++ ToggleScope.
+
+%-----------------------------------------------------------------------------%
+%
+% Toggles to control showing/hiding inactive modules/procedures
+%
+
+:- func footer_inactive_modules_toggle(cmd, preferences, deep) = string.
+
+footer_inactive_modules_toggle(Cmd, Pref0, Deep) = HTML :-
+    Pref0 ^ pref_inactive = inactive_items(Procs, Modules),
+    (
+        Modules = inactive_show,
+        Msg  = "[Hide inactive modules]",
+        Pref = Pref0 ^ pref_inactive := inactive_items(Procs, inactive_hide)
+    ;
+        Modules = inactive_hide,
+        Msg  = "[Show inactive modules]",
+        Pref = Pref0 ^ pref_inactive := inactive_items(Procs, inactive_show)
+    ),
+    HTML = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+        [s(deep_cmd_pref_to_url(Pref, Deep, Cmd)), s(Msg)]).
+
+:- func footer_inactive_procs_toggle(cmd, preferences, deep) = string.
+
+footer_inactive_procs_toggle(Cmd, Pref0, Deep) = HTML :-
+    Pref0 ^ pref_inactive = inactive_items(Procs, Modules),
+    (
+        Procs = inactive_show,
+        Msg = "[Hide inactive procedures]",
+        Pref = Pref0 ^ pref_inactive := inactive_items(inactive_hide, Modules)
+    ;
+        Procs = inactive_hide,
+        Msg = "[Show inactive procedures]",
+        Pref = Pref0 ^ pref_inactive := inactive_items(inactive_show, Modules)
+    ),
+    HTML = string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
+        [s(deep_cmd_pref_to_url(Pref, Deep, Cmd)), s(Msg)]).
+
+%-----------------------------------------------------------------------------%
+
+:- func update_criteria_in_prefs(preferences, deep, cmd, order_criteria)
+    = string.
+
+update_criteria_in_prefs(Pref0, Deep, Cmd, Criteria) = HTML :-
+    Pref = Pref0 ^ pref_criteria := Criteria,
+    HTML = deep_cmd_pref_to_url(Pref, Deep, Cmd).
+
+:- func update_cost_criteria_in_prefs(preferences, deep, cmd,
+    cost_kind, include_descendants, measurement_scope) = string.
+
+update_cost_criteria_in_prefs(Pref0, Deep, Cmd, CostKind, InclDesc, Scope)
+        = HTML :-
+    Pref = Pref0 ^ pref_criteria := by_cost(CostKind, InclDesc, Scope),
+    HTML = deep_cmd_pref_to_url(Pref, Deep, Cmd).
+
+:- func update_cost_criteria_in_top_procs_cmd(preferences, deep, display_limit,
+    cost_kind, include_descendants, measurement_scope) = string.
+
+update_cost_criteria_in_top_procs_cmd(Pref, Deep, Limit,
+        CostKind, InclDesc, Scope) = HTML :-
+    Cmd = deep_cmd_top_procs(Limit, CostKind, InclDesc, Scope),
+    HTML = deep_cmd_pref_to_url(Pref, Deep, Cmd).
+
+%-----------------------------------------------------------------------------%
+
+criteria_to_description(by_context) = "ordered by context".
+criteria_to_description(by_name) = "ordered by name".
+criteria_to_description(by_cost(CostKind, InclDesc, Scope)) =
+    cost_criteria_to_description(CostKind, InclDesc, Scope).
+
+cost_criteria_to_description(CostKind, InclDesc, Scope) = Desc :-
+    Desc =
+        "ordered by " ++
+        incl_desc_to_description(InclDesc) ++ " " ++
+        cost_kind_to_description(CostKind) ++ " " ++
+        scope_to_description(Scope).
+
+:- func cost_kind_to_description(cost_kind) = string.
+
+cost_kind_to_description(cost_calls)    = "number of calls".
+cost_kind_to_description(cost_redos)    = "number of redos".
+cost_kind_to_description(cost_time)     = "time".
+cost_kind_to_description(cost_callseqs) = "call sequence numbers".
+cost_kind_to_description(cost_allocs)   = "memory allocations".
+cost_kind_to_description(cost_words)    = "words allocated".
+
+:- func incl_desc_to_description(include_descendants) = string.
+
+incl_desc_to_description(self) = "self".
+incl_desc_to_description(self_and_desc) = "total".
+
+:- func scope_to_description(measurement_scope) = string.
+
+scope_to_description(per_call) = "per call".
+scope_to_description(overall) = "overall".
+
+%-----------------------------------------------------------------------------%
+
+% The predicates banner_style, fields_header, table_width and
+% own_and_desc_to_html all make decisions about what columns each row
+% in the table will have.  They therefore have similar control structures,
+% and a change in one may require changes in the others as well.
+
+fields_header(Pref, IdFields, TotalsDisp, WrapFunc) = HTML :-
+    Fields = Pref ^ pref_fields,
+    ProcName = WrapFunc("Procedure", by_name),
+    ModuleName = WrapFunc("Module", by_name),
+
+    some [!FirstRow, !SecondRow] (
+        (
+            IdFields = source_proc,
+            Source = WrapFunc("Source", by_context),
+            !:FirstRow =
+                "<TR>\n" ++
+                string.format("<TH ALIGN=LEFT ROWSPAN=2>%s\n", [s(Source)]) ++
+                string.format("<TH ALIGN=LEFT ROWSPAN=2>%s\n", [s(ProcName)])
+        ;
+            IdFields = rank_proc,
+            !:FirstRow =
+                "<TR>\n" ++
+                "<TH ALIGN=LEFT ROWSPAN=2>Rank\n" ++
+                string.format("<TH ALIGN=LEFT ROWSPAN=2>%s\n", [s(ProcName)])
+        ;
+            IdFields = rank_module,
+            !:FirstRow =
+                "<TR>\n" ++
+                "<TH ALIGN=LEFT ROWSPAN=2>Rank\n" ++
+                string.format("<TH ALIGN=LEFT ROWSPAN=2>%s\n", [s(ModuleName)])
+        ;
+            IdFields = proc,
+            !:FirstRow =
+                "<TR>\n" ++
+                string.format("<TH ALIGN=LEFT ROWSPAN=2>%s\n", [s(ProcName)])
+        ),
+        !:SecondRow = "<TR>\n",
+
+        ShowPortCounts = show_port_counts(Fields),
+        (
+            ShowPortCounts = yes,
+            Calls = WrapFunc("Calls", by_cost(cost_calls, self, overall)),
+            Redos = WrapFunc("Redos", by_cost(cost_redos, self, overall)),
+            !:FirstRow = !.FirstRow ++
+                "<TH COLSPAN=5>Port counts\n",
+            !:SecondRow = !.SecondRow ++
+                string.format("<TH ALIGN=RIGHT>%s\n", [s(Calls)]) ++
+                "<TH ALIGN=RIGHT>Exits\n" ++
+                "<TH ALIGN=RIGHT>Fails\n" ++
+                string.format("<TH ALIGN=RIGHT>%s\n", [s(Redos)]) ++
+                "<TH ALIGN=RIGHT>Excps\n"
+        ;
+            ShowPortCounts = no
+        ),
+
+        ShowQuanta = show_quanta(Fields),
+        (
+            ShowQuanta = yes,
+            TicksSelfOverall = WrapFunc("Self",
+                by_cost(cost_time, self, overall)),
+            TicksSelfHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n", [s(TicksSelfOverall)]),
+            TicksSelfFields = 1
+        ;
+            ShowQuanta = no,
+            TicksSelfHeading = "",
+            TicksSelfFields = 0
+        ),
+        ShowTimes = show_times(Fields),
+        (
+            ShowTimes = yes,
+            ( show_quanta(Fields) = yes ->
+                TimeSelfOverall = WrapFunc("Time",
+                    by_cost(cost_time, self, overall))
+            ;
+                TimeSelfOverall = WrapFunc("Self",
+                    by_cost(cost_time, self, overall))
+            ),
+            TimeSelfHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n", [s(TimeSelfOverall)]),
+            TimeSelfFields = 1
+        ;
+            ShowTimes = no,
+            TimeSelfHeading = "",
+            TimeSelfFields = 0
+        ),
+        ( ( ShowQuanta = yes ; ShowTimes = yes ) ->
+            TimeSelfPercentHeading = "<TH ALIGN=RIGHT>%\n",
+            TimeSelfPercentFields = 1
+        ;
+            TimeSelfPercentHeading = "",
+            TimeSelfPercentFields = 0
+        ),
+        ShowTimesPerCall = show_times_per_call(Fields),
+        (
+            ShowTimesPerCall = yes,
+            TimeSelfPerCall = WrapFunc("/call",
+                by_cost(cost_time, self, per_call)),
+            TimeSelfPerCallHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n", [s(TimeSelfPerCall)]),
+            TimeSelfPerCallFields = 1
+        ;
+            ShowTimesPerCall = no,
+            TimeSelfPerCallHeading = "",
+            TimeSelfPerCallFields = 0
+        ),
+        ( TotalsDisp = totals_meaningful, ShowQuanta = yes ->
+            TicksTotalOverall = WrapFunc("Total",
+                by_cost(cost_time, self_and_desc, overall)),
+            TicksTotalHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n", [s(TicksTotalOverall)]),
+            TicksTotalFields = 1
+        ;
+            TicksTotalHeading = "",
+            TicksTotalFields = 0
+        ),
+        ( TotalsDisp = totals_meaningful, ShowTimes = yes ->
+            ( show_quanta(Fields) = yes ->
+                TimeTotalOverall = WrapFunc("Time",
+                    by_cost(cost_time, self_and_desc, overall))
+            ;
+                TimeTotalOverall = WrapFunc("Total",
+                    by_cost(cost_time, self_and_desc, overall))
+            ),
+            TimeTotalHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n", [s(TimeTotalOverall)]),
+            TimeTotalFields = 1
+        ;
+            TimeTotalHeading = "",
+            TimeTotalFields = 0
+        ),
+        (
+            TotalsDisp = totals_meaningful,
+            ( ShowQuanta = yes ; ShowTimes = yes )
+        ->
+            TimeTotalPercentHeading = "<TH ALIGN=RIGHT>%\n",
+            TimeTotalPercentFields = 1
+        ;
+            TimeTotalPercentHeading = "",
+            TimeTotalPercentFields = 0
+        ),
+        ( TotalsDisp = totals_meaningful, ShowTimesPerCall = yes ->
+            TimeTotalPerCall = WrapFunc("/call",
+                by_cost(cost_time, self_and_desc, per_call)),
+            TimeTotalPerCallHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n", [s(TimeTotalPerCall)]),
+            TimeTotalPerCallFields = 1
+        ;
+            TimeTotalPerCallHeading = "",
+            TimeTotalPerCallFields = 0
+        ),
+        TimeFields =
+            TicksSelfFields + TimeSelfFields +
+            TimeSelfPercentFields + TimeSelfPerCallFields +
+            TicksTotalFields + TimeTotalFields +
+            TimeTotalPercentFields + TimeTotalPerCallFields,
+        !:SecondRow = !.SecondRow ++
+            TicksSelfHeading ++ TimeSelfHeading ++
+            TimeSelfPercentHeading ++ TimeSelfPerCallHeading ++
+            TicksTotalHeading ++ TimeTotalHeading ++
+            TimeTotalPercentHeading ++ TimeTotalPerCallHeading,
+        (
+            ShowQuanta = yes,
+            ShowTimes = yes,
+            !:FirstRow = !.FirstRow ++
+                string.format("<TH COLSPAN=%d>Clock ticks and times\n",
+                    [i(TimeFields)])
+        ;
+            ShowQuanta = yes,
+            ShowTimes = no,
+            !:FirstRow = !.FirstRow ++
+                string.format("<TH COLSPAN=%d>Clock ticks\n", [i(TimeFields)])
+        ;
+            ShowQuanta = no,
+            ShowTimes = yes,
+            !:FirstRow = !.FirstRow ++
+                string.format("<TH COLSPAN=%d>Time\n", [i(TimeFields)])
+        ;
+            ShowQuanta = no,
+            ShowTimes = no
+        ),
+
+        ShowCallSeqs = show_callseqs(Fields),
+        (
+            ShowCallSeqs = yes,
+            CallSeqsSelfOverall = WrapFunc("Self",
+                by_cost(cost_callseqs, self, overall)),
+            CallSeqsSelfHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n",
+                    [s(CallSeqsSelfOverall)]) ++
+                "<TH ALIGN=RIGHT>%\n",
+            CallSeqsSelfFields = 2
+        ;
+            ShowCallSeqs = no,
+            CallSeqsSelfHeading = "",
+            CallSeqsSelfFields = 0
+        ),
+        ShowCallSeqsPerCall = show_callseqs_per_call(Fields),
+        (
+            ShowCallSeqsPerCall = yes,
+            CallSeqsSelfPerCall = WrapFunc("/call",
+                by_cost(cost_callseqs, self, per_call)),
+            CallSeqsSelfPerCallHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n", [s(CallSeqsSelfPerCall)]),
+            CallSeqsSelfPerCallFields = 1
+        ;
+            ShowCallSeqsPerCall = no,
+            CallSeqsSelfPerCallHeading = "",
+            CallSeqsSelfPerCallFields = 0
+        ),
+        ( TotalsDisp = totals_meaningful, ShowCallSeqs = yes ->
+            CallSeqsTotalOverall = WrapFunc("Total",
+                by_cost(cost_callseqs, self_and_desc, overall)),
+            CallSeqsTotalHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n",
+                    [s(CallSeqsTotalOverall)]) ++
+                "<TH ALIGN=RIGHT>%\n",
+            CallSeqsTotalFields = 2
+        ;
+            CallSeqsTotalHeading = "",
+            CallSeqsTotalFields = 0
+        ),
+        ( TotalsDisp = totals_meaningful, ShowCallSeqsPerCall = yes ->
+            CallSeqsTotalPerCall = WrapFunc("/call",
+                by_cost(cost_callseqs, self_and_desc, per_call)),
+            CallSeqsTotalPerCallHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n",
+                    [s(CallSeqsTotalPerCall)]),
+            CallSeqsTotalPerCallFields = 1
+        ;
+            CallSeqsTotalPerCallHeading = "",
+            CallSeqsTotalPerCallFields = 0
+        ),
+        CallSeqsFields =
+            CallSeqsSelfFields + CallSeqsSelfPerCallFields +
+            CallSeqsTotalFields + CallSeqsTotalPerCallFields,
+        !:SecondRow = !.SecondRow ++
+            CallSeqsSelfHeading ++ CallSeqsSelfPerCallHeading ++
+            CallSeqsTotalHeading ++ CallSeqsTotalPerCallHeading,
+        (
+            ShowCallSeqs = yes,
+            !:FirstRow = !.FirstRow ++
+                string.format("<TH COLSPAN=%d>Call sequence numbers\n",
+                    [i(CallSeqsFields)])
+        ;
+            ShowCallSeqs = no
+        ),
+
+        ShowAlloc = show_alloc(Fields),
+        (
+            ShowAlloc = yes,
+            AllocsSelfOverall = WrapFunc("Self",
+                by_cost(cost_allocs, self, overall)),
+            AllocsSelfHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n",
+                    [s(AllocsSelfOverall)]) ++
+                "<TH ALIGN=RIGHT>%\n",
+            AllocsSelfFields = 2
+        ;
+            ShowAlloc = no,
+            AllocsSelfHeading = "",
+            AllocsSelfFields = 0
+        ),
+        ShowAllocPerCall = show_alloc_per_call(Fields),
+        (
+            ShowAllocPerCall = yes,
+            AllocsSelfPerCall = WrapFunc("/call",
+                by_cost(cost_allocs, self, per_call)),
+            AllocsSelfPerCallHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n", [s(AllocsSelfPerCall)]),
+            AllocsSelfPerCallFields = 1
+        ;
+            ShowAllocPerCall = no,
+            AllocsSelfPerCallHeading = "",
+            AllocsSelfPerCallFields = 0
+        ),
+        ( TotalsDisp = totals_meaningful, ShowAlloc = yes ->
+            AllocsTotalOverall = WrapFunc("Total",
+                by_cost(cost_allocs, self_and_desc, overall)),
+            AllocsTotalHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n",
+                    [s(AllocsTotalOverall)]) ++
+                "<TH ALIGN=RIGHT>%\n",
+            AllocsTotalFields = 2
+        ;
+            AllocsTotalHeading = "",
+            AllocsTotalFields = 0
+        ),
+        ( TotalsDisp = totals_meaningful, ShowAllocPerCall = yes ->
+            AllocsTotalPerCall = WrapFunc("/call",
+                by_cost(cost_allocs, self_and_desc, per_call)),
+            AllocsTotalPerCallHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n", [s(AllocsTotalPerCall)]),
+            AllocsTotalPerCallFields = 1
+        ;
+            AllocsTotalPerCallHeading = "",
+            AllocsTotalPerCallFields = 0
+        ),
+        AllocsFields =
+            AllocsSelfFields + AllocsSelfPerCallFields +
+            AllocsTotalFields + AllocsTotalPerCallFields,
+        !:SecondRow = !.SecondRow ++
+            AllocsSelfHeading ++ AllocsSelfPerCallHeading ++
+            AllocsTotalHeading ++ AllocsTotalPerCallHeading,
+        (
+            ShowAlloc = yes,
+            !:FirstRow = !.FirstRow ++
+                string.format("<TH COLSPAN=%d>Memory allocations\n",
+                    [i(AllocsFields)])
+        ;
+            ShowAlloc = no
+        ),
+
+        ShowMemory = show_memory(Fields),
+        (
+            ShowMemory = yes(_),
+            MemorySelfOverall = WrapFunc("Self",
+                by_cost(cost_words, self, overall)),
+            MemorySelfHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n",
+                    [s(MemorySelfOverall)]) ++
+                "<TH ALIGN=RIGHT>%\n",
+            MemorySelfFields = 2
+        ;
+            ShowMemory = no,
+            MemorySelfHeading = "",
+            MemorySelfFields = 0
+        ),
+        ShowMemoryPerCall = show_memory_per_call(Fields),
+        (
+            ShowMemoryPerCall = yes(_),
+            MemorySelfPerCall = WrapFunc("/call",
+                by_cost(cost_words, self, per_call)),
+            MemorySelfPerCallHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n", [s(MemorySelfPerCall)]),
+            MemorySelfPerCallFields = 1
+        ;
+            ShowMemoryPerCall = no,
+            MemorySelfPerCallHeading = "",
+            MemorySelfPerCallFields = 0
+        ),
+        ( TotalsDisp = totals_meaningful, ShowMemory = yes(_) ->
+            MemoryTotalOverall = WrapFunc("Total",
+                by_cost(cost_words, self_and_desc, overall)),
+            MemoryTotalHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n",
+                    [s(MemoryTotalOverall)]) ++
+                "<TH ALIGN=RIGHT>%\n",
+            MemoryTotalFields = 2
+        ;
+            MemoryTotalHeading = "",
+            MemoryTotalFields = 0
+        ),
+        ( TotalsDisp = totals_meaningful, ShowMemoryPerCall = yes(_) ->
+            MemoryTotalPerCall = WrapFunc("/call",
+                by_cost(cost_words, self_and_desc, per_call)),
+            MemoryTotalPerCallHeading =
+                string.format("<TH ALIGN=RIGHT>%s\n", [s(MemoryTotalPerCall)]),
+            MemoryTotalPerCallFields = 1
+        ;
+            MemoryTotalPerCallHeading = "",
+            MemoryTotalPerCallFields = 0
+        ),
+        MemoryFields =
+            MemorySelfFields + MemorySelfPerCallFields +
+            MemoryTotalFields + MemoryTotalPerCallFields,
+        !:SecondRow = !.SecondRow ++
+            MemorySelfHeading ++ MemorySelfPerCallHeading ++
+            MemoryTotalHeading ++ MemoryTotalPerCallHeading,
+        (
+            ShowMemory = yes(Units),
+            (
+                Units = units_words,
+                !:FirstRow = !.FirstRow ++
+                    string.format("<TH COLSPAN=%d>Memory words\n",
+                        [i(MemoryFields)])
+            ;
+                Units = units_bytes,
+                !:FirstRow = !.FirstRow ++
+                    string.format("<TH COLSPAN=%d>Memory bytes\n",
+                        [i(MemoryFields)])
+            )
+        ;
+            ShowMemory = no
+        ),
+        HTML =
+            "<THEAD>\n" ++
+            !.FirstRow ++
+            !.SecondRow ++
+            "<TBODY>\n" ++
+            separator_row(Pref, IdFields, TotalsDisp)
+    ).
+
+%-----------------------------------------------------------------------------%
+
+header_row(Heading, Pref, IdFields, TotalsDisp) = Separator :-
+    Separator = string.format("<TR><TD COLSPAN=%d>%s</TD></TR>\n",
+        [i(table_width(Pref, IdFields, TotalsDisp)), s(Heading)]).
+
+separator_row(Pref, IdFields, TotalsDisp) = Separator :-
+    Separator = string.format("<TR><TD COLSPAN=%d></TD></TR>\n",
+        [i(table_width(Pref, IdFields, TotalsDisp))]).
+
+:- func table_width(preferences, id_fields, totals_disposition) = int.
+
+table_width(Pref, IdFields, TotalsDisp) = Width :-
+    Fields = Pref ^ pref_fields,
+    (
+        IdFields = source_proc,
+        Id = 2
+    ;
+        IdFields = rank_module,
+        Id = 2
+    ;
+        IdFields = rank_proc,
+        Id = 2
+    ;
+        IdFields = proc,
+        Id = 1
+    ),
+    (
+        Fields ^ port_fields = no_port,
+        Port = 0
+    ;
+        Fields ^ port_fields = port,
+        Port = 5
+    ),
+    (
+        Fields ^ time_fields = no_time,
+        Time = 0
+    ;
+        Fields ^ time_fields = ticks,
+        Time = 2
+    ;
+        Fields ^ time_fields = time,
+        Time = 2
+    ;
+        Fields ^ time_fields = ticks_and_time,
+        Time = 3
+    ;
+        Fields ^ time_fields = time_and_percall,
+        Time = 3
+    ;
+        Fields ^ time_fields = ticks_and_time_and_percall,
+        Time = 4
+    ),
+    (
+        Fields ^ callseqs_fields = no_callseqs,
+        CallSeqs = 0
+    ;
+        Fields ^ callseqs_fields = callseqs,
+        CallSeqs = 2
+    ;
+        Fields ^ callseqs_fields = callseqs_and_percall,
+        CallSeqs = 3
+    ),
+    (
+        Fields ^ alloc_fields = no_alloc,
+        Alloc = 0
+    ;
+        Fields ^ alloc_fields = alloc,
+        Alloc = 2
+    ;
+        Fields ^ alloc_fields = alloc_and_percall,
+        Alloc = 3
+    ),
+    (
+        Fields ^ memory_fields = no_memory,
+        Memory = 0
+    ;
+        Fields ^ memory_fields = memory(_),
+        Memory = 2
+    ;
+        Fields ^ memory_fields = memory_and_percall(_),
+        Memory = 3
+    ),
+    (
+        TotalsDisp = totals_meaningful,
+        Width = Id + Port + Time * 2 + CallSeqs * 2 + Alloc * 2 + Memory * 2
+    ;
+        TotalsDisp = totals_not_meaningful,
+        Width = Id + Port + Time + CallSeqs + Alloc + Memory
+    ).
+
+%-----------------------------------------------------------------------------%
+
+add_context(Context, LineGroup0) = LineGroup :-
+    LineGroup0 =
+        line_group(FileName, LineNumber, Name, Own, Desc, HTML0, LaterLines),
+    HTML = string.format("<TD CLASS=id>%s</TD>%s",
+        [s(Context), s(HTML0)]),
+    LineGroup =
+        line_group(FileName, LineNumber, Name, Own, Desc, HTML, LaterLines).
+
+add_self_context(LineGroup0) = LineGroup :-
+    LineGroup0 =
+        line_group(FileName, LineNumber, Name, Own, Desc, HTML0, LaterLines),
+    HTML = string.format("<TD CLASS=id>%s:%d</TD>%s",
+        [s(FileName), i(LineNumber), s(HTML0)]),
+    LineGroup =
+        line_group(FileName, LineNumber, Name, Own, Desc, HTML, LaterLines).
+
+add_ranks(LineGroups0) = add_ranks_2(1, LineGroups0).
+
+:- func add_ranks_2(int, list(line_group(one_id, LL)))
+    = list(line_group(two_id, LL)).
+
+add_ranks_2(_Rank, []) = [].
+add_ranks_2(Rank, [LineGroup0 | LineGroups0]) = [LineGroup | LineGroups] :-
+    LineGroup0 =
+        line_group(FileName, LineNumber, Name, Own, Desc, HTML0, LaterLines),
+    HTML = string.format("<TD CLASS=id>%d</TD>%s", [i(Rank), s(HTML0)]),
+    LineGroup =
+        line_group(FileName, LineNumber, Name, Own, Desc, HTML, LaterLines),
+    LineGroups = add_ranks_2(Rank + 1, LineGroups0).
+
+line_to_one_id_subline_group(LineGroup0) = LineGroup :-
+    LineGroup0 =
+        line_group(FileName, LineNumber, Name, Own, Desc, HTML, unit),
+    LineGroup =
+        line_group(FileName, LineNumber, Name, Own, Desc, HTML,
+            sub_lines(one_id, [])).
+
+line_to_two_id_subline_group(LineGroup0) = LineGroup :-
+    LineGroup0 = line_group(FileName, LineNumber, Name, Own, Desc,
+        HTML, unit),
+    LineGroup = line_group(FileName, LineNumber, Name, Own, Desc,
+        HTML, sub_lines(two_id, [])).
+
+%-----------------------------------------------------------------------------%
+
+one_id_line_to_html(Pref, Deep, TotalsDisp, LineGroup) =
+    "<TR>\n" ++
+    LineGroup ^ group_first_line_id ++
+    own_and_desc_to_html(LineGroup ^ group_own, LineGroup ^ group_desc,
+        Pref, Deep, TotalsDisp) ++
+    "</TR>\n".
+
+one_id_line_group_to_html(Pref, Deep, TotalsDisp, LineGroup) =
+    "<TR>\n" ++
+    LineGroup ^ group_first_line_id ++
+    own_and_desc_to_html(LineGroup ^ group_own, LineGroup ^ group_desc,
+        Pref, Deep, TotalsDisp) ++
+    "</TR>\n" ++
+    string.append_list(
+        list.map(one_id_line_to_html(Pref, Deep, TotalsDisp),
+            LineGroup ^ group_later_lines ^ sub_line_list)).
+
+two_id_line_to_html(Pref, Deep, TotalsDisp, LineGroup) =
+    "<TR>\n" ++
+    LineGroup ^ group_first_line_id ++
+    own_and_desc_to_html(LineGroup ^ group_own, LineGroup ^ group_desc,
+        Pref, Deep, TotalsDisp) ++
+    "</TR>\n".
+
+two_id_line_group_to_html(Pref, Deep, TotalsDisp, LineGroup) =
+    "<TR>\n" ++
+    LineGroup ^ group_first_line_id ++
+    own_and_desc_to_html(LineGroup ^ group_own, LineGroup ^ group_desc,
+        Pref, Deep, TotalsDisp) ++
+    "</TR>\n" ++
+    string.append_list(
+        list.map(two_id_line_to_html(Pref, Deep, TotalsDisp),
+            LineGroup ^ group_later_lines ^ sub_line_list)).
+
+%-----------------------------------------------------------------------------%
+
+own_and_desc_to_html(Own, Desc, Pref, Deep, TotalsDisp) = HTML :-
+    add_own_to_inherit(Own, Desc) = OwnPlusDesc,
+    Root = root_total_info(Deep),
+    Calls = calls(Own),
+    Exits = exits(Own),
+    Fails = fails(Own),
+    Redos = redos(Own),
+    Excps = excps(Own),
+
+    OwnQuanta = quanta(Own),
+    TotalQuanta = inherit_quanta(OwnPlusDesc),
+    RootQuanta = inherit_quanta(Root),
+    OwnQuantaProp = percentage(OwnQuanta, RootQuanta),
+    TotalQuantaProp = percentage(TotalQuanta, RootQuanta),
+
+    OwnCallSeqs = callseqs(Own),
+    TotalCallSeqs = inherit_callseqs(OwnPlusDesc),
+    RootCallSeqs = inherit_callseqs(Root),
+    OwnCallSeqsProp = percentage(OwnCallSeqs, RootCallSeqs),
+    TotalCallSeqsProp = percentage(TotalCallSeqs, RootCallSeqs),
+
+    OwnAllocs = allocs(Own),
+    TotalAllocs = inherit_allocs(OwnPlusDesc),
+    RootAllocs = inherit_allocs(Root),
+    OwnAllocProp = percentage(OwnAllocs, RootAllocs),
+    TotalAllocProp = percentage(TotalAllocs, RootAllocs),
+
+    OwnWords = words(Own),
+    TotalWords = inherit_words(OwnPlusDesc),
+    RootWords = inherit_words(Root),
+    OwnMemoryProp = percentage(OwnWords, RootWords),
+    TotalMemoryProp = percentage(TotalWords, RootWords),
+
+    Fields = Pref ^ pref_fields,
+
+    ShowPortCounts = show_port_counts(Fields),
+    (
+        ShowPortCounts = yes,
+        PortHTML =
+            string.format("<TD CLASS=port ALIGN=RIGHT>%s</TD>\n",
+                [s(commas(Calls))]) ++
+            string.format("<TD CLASS=port ALIGN=RIGHT>%s</TD>\n",
+                [s(commas(Exits))]) ++
+            string.format("<TD CLASS=port ALIGN=RIGHT>%s</TD>\n",
+                [s(commas(Fails))]) ++
+            string.format("<TD CLASS=port ALIGN=RIGHT>%s</TD>\n",
+                [s(commas(Redos))]) ++
+            string.format("<TD CLASS=port ALIGN=RIGHT>%s</TD>\n",
+                [s(commas(Excps))])
+    ;
+        ShowPortCounts = no,
+        PortHTML = ""
+    ),
+
+    ShowQuanta = show_quanta(Fields),
+    (
+        ShowQuanta = yes,
+        QuantaSelfHTML =
+            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+                [s(commas(OwnQuanta))]),
+        QuantaTotalHTML =
+            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+                [s(commas(TotalQuanta))])
+    ;
+        ShowQuanta = no,
+        QuantaSelfHTML = "",
+        QuantaTotalHTML = ""
+    ),
+    ShowTimes = show_times(Fields),
+    (
+        ShowTimes = yes,
+        TimeSelfHTML =
+            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+                [s(overall_time(Pref, Deep, OwnQuanta))]),
+        TimeTotalHTML =
+            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+                [s(overall_time(Pref, Deep, TotalQuanta))])
+    ;
+        ShowTimes = no,
+        TimeSelfHTML = "",
+        TimeTotalHTML = ""
+    ),
+    ShowTimeFraction = bool.or(ShowQuanta, ShowTimes),
+    (
+        ShowTimeFraction = yes,
+        QuantaPropSelfHTML =
+            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+                [s(OwnQuantaProp)]),
+        QuantaPropTotalHTML =
+            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+                [s(TotalQuantaProp)])
+    ;
+        ShowTimeFraction = no,
+        QuantaPropSelfHTML = "",
+        QuantaPropTotalHTML = ""
+    ),
+    ShowTimesPerCall = show_times_per_call(Fields),
+    (
+        ShowTimesPerCall = yes,
+        TimePerCallSelfHTML =
+            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+                [s(per_call_time(Pref, Deep, OwnQuanta, Calls))]),
+        TimePerCallTotalHTML =
+            string.format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+                [s(per_call_time(Pref, Deep, TotalQuanta, Calls))])
+    ;
+        ShowTimesPerCall = no,
+        TimePerCallSelfHTML = "",
+        TimePerCallTotalHTML = ""
+    ),
+
+    ShowCallSeqs = show_callseqs(Fields),
+    (
+        ShowCallSeqs = yes,
+        CallSeqsSelfHTML =
+            string.format("<TD CLASS=callseqs ALIGN=RIGHT>%s</TD>\n",
+                [s(commas(OwnCallSeqs))]),
+        CallSeqsTotalHTML =
+            string.format("<TD CLASS=callseqs ALIGN=RIGHT>%s</TD>\n",
+                [s(commas(TotalCallSeqs))]),
+        CallSeqsPropSelfHTML =
+            string.format("<TD CLASS=callseqs ALIGN=RIGHT>%s</TD>\n",
+                [s(OwnCallSeqsProp)]),
+        CallSeqsPropTotalHTML =
+            string.format("<TD CLASS=callseqs ALIGN=RIGHT>%s</TD>\n",
+                [s(TotalCallSeqsProp)])
+    ;
+        ShowCallSeqs = no,
+        CallSeqsSelfHTML = "",
+        CallSeqsTotalHTML = "",
+        CallSeqsPropSelfHTML = "",
+        CallSeqsPropTotalHTML = ""
+    ),
+    ShowCallSeqsPerCall = show_callseqs_per_call(Fields),
+    (
+        ShowCallSeqsPerCall = yes,
+        ( Calls = 0 ->
+            OwnCallSeqsPerCall = "N/A",
+            TotalCallSeqsPerCall = "N/A"
+        ;
+            OwnCallSeqsPerCall =
+                one_decimal_fraction(float(OwnCallSeqs) / float(Calls)),
+            TotalCallSeqsPerCall =
+                one_decimal_fraction(float(TotalCallSeqs) / float(Calls))
+        ),
+        CallSeqsPerCallSelfHTML =
+            string.format("<TD CLASS=callseqs ALIGN=RIGHT>%s</TD>\n",
+                [s(OwnCallSeqsPerCall)]),
+        CallSeqsPerCallTotalHTML =
+            string.format("<TD CLASS=callseqs ALIGN=RIGHT>%s</TD>\n",
+                [s(TotalCallSeqsPerCall)])
+    ;
+        ShowCallSeqsPerCall = no,
+        CallSeqsPerCallSelfHTML = "",
+        CallSeqsPerCallTotalHTML = ""
+    ),
+
+    ShowAlloc = show_alloc(Fields),
+    (
+        ShowAlloc = yes,
+        AllocSelfHTML =
+            string.format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
+                [s(commas(OwnAllocs))]) ++
+            string.format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
+                [s(OwnAllocProp)]),
+        AllocTotalHTML =
+            string.format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
+                [s(commas(TotalAllocs))]) ++
+            string.format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
+                [s(TotalAllocProp)])
+    ;
+        ShowAlloc = no,
+        AllocSelfHTML = "",
+        AllocTotalHTML = ""
+    ),
+    ShowAllocPerCall = show_alloc_per_call(Fields),
+    (
+        ShowAllocPerCall = yes,
+        AllocPerCallSelfHTML =
+            string.format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
+                [s(count_per_call(OwnAllocs, Calls))]),
+        AllocPerCallTotalHTML =
+            string.format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
+                [s(count_per_call(TotalAllocs, Calls))])
+    ;
+        ShowAllocPerCall = no,
+        AllocPerCallSelfHTML = "",
+        AllocPerCallTotalHTML = ""
+    ),
+
+    ShowMemory = show_memory(Fields),
+    (
+        ShowMemory = yes(Unit),
+        (
+            Unit = units_words,
+            OwnMemory = OwnWords,
+            TotalMemory = TotalWords
+        ;
+            Unit = units_bytes,
+            WordSize = Deep ^ profile_stats ^ word_size,
+            OwnMemory = OwnWords * WordSize,
+            TotalMemory = TotalWords * WordSize
+        )
+    ;
+        ShowMemory = no,
+        % These values won't be used.
+        OwnMemory = 0,
+        TotalMemory = 0
+    ),
+    (
+        ShowMemory = yes(_),
+        MemorySelfHTML =
+            string.format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
+                [s(commas(OwnMemory))]) ++
+            string.format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
+                [s(OwnMemoryProp)]),
+        MemoryTotalHTML =
+            string.format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
+                [s(commas(TotalMemory))]) ++
+            string.format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
+                [s(TotalMemoryProp)])
+    ;
+        ShowMemory = no,
+        MemorySelfHTML = "",
+        MemoryTotalHTML = ""
+    ),
+    ShowMemoryPerCall = show_memory_per_call(Fields),
+    (
+        ShowMemoryPerCall = yes(_),
+        MemoryPerCallSelfHTML =
+            string.format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
+                [s(count_per_call(OwnMemory, Calls))]),
+        MemoryPerCallTotalHTML =
+            string.format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
+                [s(count_per_call(TotalMemory, Calls))])
+    ;
+        ShowMemoryPerCall = no,
+        MemoryPerCallSelfHTML = "",
+        MemoryPerCallTotalHTML = ""
+    ),
+
+    (
+        TotalsDisp = totals_meaningful,
+        HTML =
+            PortHTML ++
+
+            QuantaSelfHTML ++
+            TimeSelfHTML ++
+            QuantaPropSelfHTML ++
+            TimePerCallSelfHTML ++
+            QuantaTotalHTML ++
+            TimeTotalHTML ++
+            QuantaPropTotalHTML ++
+            TimePerCallTotalHTML ++
+
+            CallSeqsSelfHTML ++
+            CallSeqsPropSelfHTML ++
+            CallSeqsPerCallSelfHTML ++
+            CallSeqsTotalHTML ++
+            CallSeqsPropTotalHTML ++
+            CallSeqsPerCallTotalHTML ++
+
+            AllocSelfHTML ++
+            AllocPerCallSelfHTML ++
+            AllocTotalHTML ++
+            AllocPerCallTotalHTML ++
+
+            MemorySelfHTML ++
+            MemoryPerCallSelfHTML ++
+            MemoryTotalHTML ++
+            MemoryPerCallTotalHTML
+    ;
+        TotalsDisp = totals_not_meaningful,
+        HTML =
+            PortHTML ++
+
+            QuantaSelfHTML ++
+            TimeSelfHTML ++
+            QuantaPropSelfHTML ++
+            TimePerCallSelfHTML ++
+
+            CallSeqsSelfHTML ++
+            CallSeqsPropSelfHTML ++
+            CallSeqsPerCallSelfHTML ++
+
+            AllocSelfHTML ++
+            AllocPerCallSelfHTML ++
+
+            MemorySelfHTML ++
+            MemoryPerCallSelfHTML
+    ).
+
+%-----------------------------------------------------------------------------%
+
+:- func overall_time(preferences, deep, int) = string.
+
+overall_time(Pref, Deep, Quanta) = TimeStr :-
+    lookup_ticks_per_sec(Deep ^ profile_stats, TicksPerSec, _Assumed),
+    % We display Time as seconds, with two digits after the decimal point.
+    % This is the most we can do, given clock granularity.
+    Time = float(Quanta) / float(TicksPerSec),
+    TimeStr = format_time(Pref, Time).
+
+:- func per_call_time(preferences, deep, int, int) = string.
+
+per_call_time(Pref, Deep, Quanta, Calls) = TimeStr :-
+    lookup_ticks_per_sec(Deep ^ profile_stats, TicksPerSec, _Assumed),
+    % We display Time as seconds, with two digits after the decimal point.
+    % This is the most we can do, given clock granularity.
+    Time = float(Quanta) / float(TicksPerSec),
+    ( Calls \= 0 ->
+        TimePerCall = Time / float(Calls)
+    ;
+        TimePerCall = 0.0
+    ),
+    TimeStr = format_time(Pref, TimePerCall).
+
+:- func format_time(preferences, float) = string.
+
+format_time(Pref, Time) = TimeStr :-
+    (
+        Pref ^ pref_time = no_scale,
+        TimeStr0 = four_decimal_fraction(Time),
+        Unit = "s"
+    ;
+        Pref ^ pref_time = scale_by_millions,
+        ( Time >= 0.001 ->
+            ScaledTime = Time,
+            Unit = "s"
+        ;
+            ScaledTime = 1000000.0 * Time,
+            Unit = "us"
+        ),
+        TimeStr0 = four_decimal_fraction(ScaledTime)
+    ;
+        Pref ^ pref_time = scale_by_thousands,
+        ( Time >= 1.0 ->
+            ScaledTime = Time,
+            Unit = "s"
+        ; Time >= 0.001 ->
+            ScaledTime = 1000.0 * Time,
+            Unit = "ms"
+        ; Time >= 0.000001 ->
+            ScaledTime = 1000000.0 * Time,
+            Unit = "us"
+        ;
+            ScaledTime = 1000000000.0 * Time,
+            Unit = "ns"
+        ),
+        TimeStr0 = two_decimal_fraction(ScaledTime)
+    ),
+    TimeStr = TimeStr0 ++ Unit.
+
+:- func percentage(int, int) = string.
+
+percentage(Fraction, Whole) = PercentageStr :-
+    ( Whole = 0 ->
+        PercentageStr = "N/A"
+    ;
+        Percentage = 100.0 * float(Fraction) / float(Whole),
+        PercentageStr = string.format("%5.2f", [f(Percentage)])
+    ).
+
+lookup_ticks_per_sec(Stats, TicksPerSec, Assumed) :-
+    TicksPerSec0 = Stats ^ ticks_per_sec,
+    ( TicksPerSec0 = 0 ->
+        TicksPerSec = default_ticks_per_sec,
+        Assumed = yes
+    ;
+        TicksPerSec = TicksPerSec0,
+        Assumed = no
+    ).
+
+    % The number of ticks per sec to assume if the profiling data file does
+    % not record the actual tick rate.
+    %
+:- func default_ticks_per_sec = int.
+
+default_ticks_per_sec = 100.
+
+%-----------------------------------------------------------------------------%
+
+:- func count_per_call(int, int) = string.
+
+count_per_call(Count, Calls) =
+    ( Calls = 0 ->
+        two_decimal_fraction(0.0)
+    ;
+        two_decimal_fraction(float(Count) / float(Calls))
+    ).
+
+%-----------------------------------------------------------------------------%
+
+:- func show_port_counts(fields) = bool.
+
+show_port_counts(Fields) = ShowPorts :-
+    PortFields = Fields ^ port_fields,
+    ( PortFields = no_port, ShowPorts = no
+    ; PortFields = port, ShowPorts = yes
+    ).
+
+:- func show_quanta(fields) = bool.
+
+show_quanta(Fields) = ShowQuanta :-
+    TimeFields = Fields ^ time_fields,
+    ( TimeFields = no_time, ShowQuanta = no
+    ; TimeFields = ticks, ShowQuanta = yes
+    ; TimeFields = time, ShowQuanta = no
+    ; TimeFields = ticks_and_time, ShowQuanta = yes
+    ; TimeFields = time_and_percall, ShowQuanta = no
+    ; TimeFields = ticks_and_time_and_percall, ShowQuanta = yes
+    ).
+
+:- func show_times(fields) = bool.
+
+show_times(Fields) = ShowTimes :-
+    TimeFields = Fields ^ time_fields,
+    ( TimeFields = no_time, ShowTimes = no
+    ; TimeFields = ticks, ShowTimes = no
+    ; TimeFields = time, ShowTimes = yes
+    ; TimeFields = ticks_and_time, ShowTimes = yes
+    ; TimeFields = time_and_percall, ShowTimes = yes
+    ; TimeFields = ticks_and_time_and_percall, ShowTimes = yes
+    ).
+
+:- func show_times_per_call(fields) = bool.
+
+show_times_per_call(Fields) = ShowTimesPerCall :-
+    TimeFields = Fields ^ time_fields,
+    ( TimeFields = no_time, ShowTimesPerCall = no
+    ; TimeFields = ticks, ShowTimesPerCall = no
+    ; TimeFields = time, ShowTimesPerCall = no
+    ; TimeFields = ticks_and_time, ShowTimesPerCall = no
+    ; TimeFields = time_and_percall, ShowTimesPerCall = yes
+    ; TimeFields = ticks_and_time_and_percall, ShowTimesPerCall = yes
+    ).
+
+:- func show_callseqs(fields) = bool.
+
+show_callseqs(Fields) = ShowCallSeqs :-
+    CallSeqsField = Fields ^ callseqs_fields,
+    ( CallSeqsField = no_callseqs, ShowCallSeqs = no
+    ; CallSeqsField = callseqs, ShowCallSeqs = yes
+    ; CallSeqsField = callseqs_and_percall, ShowCallSeqs = yes
+    ).
+
+:- func show_callseqs_per_call(fields) = bool.
+
+show_callseqs_per_call(Fields) = ShowCallSeqsPerCall :-
+    CallSeqsField = Fields ^ callseqs_fields,
+    ( CallSeqsField = no_callseqs, ShowCallSeqsPerCall = no
+    ; CallSeqsField = callseqs, ShowCallSeqsPerCall = no
+    ; CallSeqsField = callseqs_and_percall, ShowCallSeqsPerCall = yes
+    ).
+
+:- func show_alloc(fields) = bool.
+
+show_alloc(Fields) = ShowAlloc :-
+    AllocFields = Fields ^ alloc_fields,
+    ( AllocFields = no_alloc, ShowAlloc = no
+    ; AllocFields = alloc, ShowAlloc = yes
+    ; AllocFields = alloc_and_percall, ShowAlloc = yes
+    ).
+
+:- func show_alloc_per_call(fields) = bool.
+
+show_alloc_per_call(Fields) = ShowPerAlloc :-
+    AllocFields = Fields ^ alloc_fields,
+    ( AllocFields = no_alloc, ShowPerAlloc = no
+    ; AllocFields = alloc, ShowPerAlloc = no
+    ; AllocFields = alloc_and_percall, ShowPerAlloc = yes
+    ).
+
+:- func show_memory(fields) = maybe(memory_units).
+
+show_memory(Fields) = ShowMemory :-
+    MemoryFields = Fields ^ memory_fields,
+    ( MemoryFields = no_memory, ShowMemory = no
+    ; MemoryFields = memory(Unit), ShowMemory = yes(Unit)
+    ; MemoryFields = memory_and_percall(Unit), ShowMemory = yes(Unit)
+    ).
+
+:- func show_memory_per_call(fields) = maybe(memory_units).
+
+show_memory_per_call(Fields) = ShowPerMemory :-
+    MemoryFields = Fields ^ memory_fields,
+    ( MemoryFields = no_memory, ShowPerMemory = no
+    ; MemoryFields = memory(_Unit), ShowPerMemory = no
+    ; MemoryFields = memory_and_percall(Unit), ShowPerMemory = yes(Unit)
+    ).
+
+%-----------------------------------------------------------------------------%
+
+proc_dynamic_name(Deep, PDPtr) = Name :-
+    deep_lookup_proc_dynamics(Deep, PDPtr, PD),
+    PSPtr = PD ^ pd_proc_static,
+    deep_lookup_proc_statics(Deep, PSPtr, PS),
+    Name = PS ^ ps_refined_id.
+
+proc_static_name(Deep, PSPtr) = Name :-
+    deep_lookup_proc_statics(Deep, PSPtr, PS),
+    Name = PS ^ ps_refined_id.
+
+%-----------------------------------------------------------------------------%
+
+proc_dynamic_context(Deep, PDPtr, FileName, LineNumber) :-
+    deep_lookup_proc_dynamics(Deep, PDPtr, PD),
+    PSPtr = PD ^ pd_proc_static,
+    deep_lookup_proc_statics(Deep, PSPtr, PS),
+    FileName = PS ^ ps_file_name,
+    LineNumber = PS ^ ps_line_number.
+
+proc_static_context(Deep, PSPtr, FileName, LineNumber) :-
+    deep_lookup_proc_statics(Deep, PSPtr, PS),
+    FileName = PS ^ ps_file_name,
+    LineNumber = PS ^ ps_line_number.
+
+call_site_context(Deep, CSSPtr, FileName, LineNumber) :-
+    deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
+    CSS = call_site_static(PSPtr, _SlotNum, _Kind, LineNumber, _GoalPath),
+    deep_lookup_proc_statics(Deep, PSPtr, PS),
+    FileName = PS ^ ps_file_name.
+
+%-----------------------------------------------------------------------------%
+
+proc_static_to_line_group_info(Pref, Deep, PSPtr, FileName, LineNumber,
+        Name, HTML) :-
+    ( valid_proc_static_ptr(Deep, PSPtr) ->
+        deep_lookup_proc_statics(Deep, PSPtr, PS),
+        FileName = PS ^ ps_file_name,
+        LineNumber = PS ^ ps_line_number,
+        Name = PS ^ ps_refined_id,
+        HTML = proc_static_to_html_ref(Pref, Deep, PSPtr)
+    ;
+        FileName = "",
+        LineNumber = 0,
+        Name = "mercury_runtime",
+        HTML = Name
+    ).
+
+proc_static_to_html_ref(Pref, Deep, PSPtr) = HTML :-
+    URL = deep_cmd_pref_to_url(Pref, Deep, deep_cmd_proc(PSPtr)),
+    deep_lookup_proc_statics(Deep, PSPtr, PS),
+    ProcName = PS ^ ps_refined_id,
+    HTML = string.format("<A HREF=""%s"">%s</A>",
+        [s(URL), s(escape_break_html_string(ProcName))]).
+
+module_name_to_html_ref(Pref, Deep, ModuleName) = HTML :-
+    URL = deep_cmd_pref_to_url(Pref, Deep, deep_cmd_module(ModuleName)),
+    HTML = string.format("<A HREF=""%s"">%s</A>",
+        [s(URL), s(escape_break_html_string(ModuleName))]).
+
+clique_ptr_to_html_ref(Pref, Deep, ProcName, CliquePtr) = HTML :-
+    URL = deep_cmd_pref_to_url(Pref, Deep, deep_cmd_clique(CliquePtr)),
+    HTML = string.format("<A HREF=""%s"">%s</A>",
+        [s(URL), s(escape_break_html_string(ProcName))]).
+
+deep_cmd_pref_to_url(Pref, Deep, Cmd) =
+    machine_datafile_cmd_pref_to_url(Deep ^ server_name_port,
+        Deep ^ script_name, Deep ^ data_file_name, Cmd, Pref).
+
+%-----------------------------------------------------------------------------%
+
+plural(N) = Plural :-
+    ( N = 1 ->
+        Plural = ""
+    ;
+        Plural = "s"
+    ).
+
+%-----------------------------------------------------------------------------%
+
+:- func machine_datafile_cmd_pref_to_url(string, string, string, cmd,
+    preferences) = string.
+
+machine_datafile_cmd_pref_to_url(Machine, ScriptName, DeepFileName, Cmd,
+        Preferences) =
+    "http://" ++
+    Machine ++
+    ScriptName ++ "?" ++
+    query_to_string(deep_query(yes(Cmd), DeepFileName, yes(Preferences))).
+
+%-----------------------------------------------------------------------------%
+:- end_module old_html_format.
+%-----------------------------------------------------------------------------%
Index: old_query.m
===================================================================
RCS file: old_query.m
diff -N old_query.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ old_query.m	25 Sep 2008 07:06:59 -0000
@@ -0,0 +1,1874 @@
+%-----------------------------------------------------------------------------%
+% 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 code to answer deep profiler queries by directly
+% generating HTML. This approach has been superseded by another (implemented in
+% create_report.m and display_report.m) that answers queries by first creating
+% a high level report, and then converting that report to a display structure
+% and finally to HTML. The code in this module is therefore present only as a
+% backup; it should not be actively modified.
+%
+%-----------------------------------------------------------------------------%
+
+:- module old_query.
+
+:- interface.
+
+:- import_module profile.
+:- import_module query.
+
+:- import_module io.
+
+    % Old deep profiler cgi code.  This should remain supported until all cmds
+    % have been updated to use the data structures in report.m.
+    %
+:- pred old_exec(cmd::in, preferences::in, deep::in, string::out, 
+    io::di, io::uo) is det.
+
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module apply_exclusion.
+:- import_module html_format.       % for escape_break_html_string
+:- import_module measurements.
+:- import_module old_html_format.
+:- import_module top_procs.
+
+:- import_module array.
+:- import_module assoc_list.
+:- import_module bool.
+:- 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 unit.
+
+old_exec(deep_cmd_restart, _Pref, _Deep, _HTML, !IO) :-
+    % Our caller is supposed to filter out restart commands.
+    error("exec: found restart command").
+old_exec(deep_cmd_quit, _Pref, Deep, HTML, !IO) :-
+    HTML = string.format(
+        "<H3>Shutting down deep profile server for %s.</H3>\n",
+        [s(Deep ^ data_file_name)]).
+old_exec(deep_cmd_timeout(TimeOut), _Pref, _Deep, HTML, !IO) :-
+    HTML = string.format("<H3>Timeout set to %d minutes</H3>\n", [i(TimeOut)]).
+old_exec(Cmd, Pref, Deep, HTML, !IO) :-
+    Cmd = deep_cmd_menu,
+    HTML = generate_menu_page(Cmd, Pref, Deep).
+old_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).
+old_exec(deep_cmd_dump_proc_static(PSPtr), _Pref, Deep, HTML, !IO) :-
+    HTML = generate_proc_static_debug_page(PSPtr, Deep).
+old_exec(deep_cmd_dump_proc_dynamic(PDPtr), _Pref, Deep, HTML, !IO) :-
+    HTML = generate_proc_dynamic_debug_page(PDPtr, Deep).
+old_exec(deep_cmd_dump_call_site_static(CSSPtr), _Pref, Deep, HTML, !IO) :-
+    HTML = generate_call_site_static_debug_page(CSSPtr, Deep).
+old_exec(deep_cmd_dump_call_site_dynamic(CSDPtr), _Pref, Deep, HTML, !IO) :-
+    HTML = generate_call_site_dynamic_debug_page(CSDPtr, Deep).
+old_exec(Cmd, Pref, Deep, HTML, !IO) :-
+    Cmd = deep_cmd_proc(PSPtr),
+    ( 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)
+    ).
+old_exec(Cmd, Pref, Deep, HTML, !IO) :-
+    Cmd = deep_cmd_root(MaybePercent),
+    deep_lookup_clique_index(Deep, Deep ^ root, RootCliquePtr),
+    (
+        MaybePercent = yes(Percent),
+        HTML = chase_the_action(Cmd, RootCliquePtr, Pref, Deep, Percent)
+    ;
+        MaybePercent = no,
+        generate_clique_page(Cmd, RootCliquePtr, Pref, Deep, HTML, 100, _)
+    ).
+old_exec(Cmd, Pref, Deep, HTML, !IO) :-
+    Cmd = deep_cmd_clique(CliquePtr),
+    ( valid_clique_ptr(Deep, CliquePtr) ->
+        generate_clique_page(Cmd, CliquePtr, Pref, Deep, HTML, 100, _)
+    ;
+        HTML =
+            page_banner(Cmd, Pref) ++
+            "There is no clique with that number.\n" ++
+            page_footer(Cmd, Pref, Deep)
+    ).
+old_exec(Cmd, Pref0, Deep, HTML, !IO) :-
+    Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, Contour),
+    Pref = Pref0 ^ pref_contour := Contour,
+    ( 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)
+    ).
+old_exec(Cmd, Pref, Deep, HTML, !IO) :-
+    Cmd = deep_cmd_program_modules,
+    HTML = generate_modules_page(Cmd, Pref, Deep).
+old_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)
+    ).
+old_exec(deep_cmd_dump_clique(CliquePtr), _Pref, Deep, HTML, !IO) :-
+    HTML = generate_clique_debug_page(CliquePtr, Deep).
+old_exec(deep_cmd_procrep_coverage(_), _, _, HTML, !IO) :-
+    HTML = "query.m: deep_cmd_procrep_coverage is unsupported by old_exec\n". 
+
+%-----------------------------------------------------------------------------%
+
+:- func generate_proc_static_debug_page(proc_static_ptr, deep) = string.
+
+generate_proc_static_debug_page(PSPtr, Deep) = HTML :-
+    ( 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(proc_dynamic_ptr, deep) = string.
+
+generate_proc_dynamic_debug_page(PDPtr, Deep) = HTML :-
+    ( 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(call_site_static_ptr, deep)
+    = string.
+
+generate_call_site_static_debug_page(CSSPtr, Deep) = HTML :-
+    ( 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(call_site_dynamic_ptr, deep)
+    = string.
+
+generate_call_site_dynamic_debug_page(CSDPtr, Deep) = HTML :-
+    ( 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(clique_ptr, deep) = string.
+
+generate_clique_debug_page(CliquePtr, Deep) = HTML :-
+    ( valid_clique_ptr(Deep, CliquePtr) ->
+        deep_lookup_clique_parents(Deep, CliquePtr, ParentCSDPtr),
+        ParentCSDPtr = call_site_dynamic_ptr(ParentCSDI),
+        ParentStr = string.format("%d ->", [i(ParentCSDI)]),
+        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_program_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, clique_ptr, preferences, deep, int) = string.
+
+chase_the_action(Cmd, CliquePtr, Pref, Deep, Percent) = HTML :-
+    generate_clique_page(Cmd, CliquePtr, Pref, Deep, HTML0,
+        Percent, ActionPtrs),
+    ( ActionPtrs = [ActionCliquePtr] ->
+        HTML = chase_the_action(Cmd, ActionCliquePtr, Pref, Deep, Percent)
+    ;
+        HTML = HTML0
+    ).
+
+%-----------------------------------------------------------------------------%
+
+:- pred generate_clique_page(cmd::in, clique_ptr::in, preferences::in,
+    deep::in, string::out, int::in, list(clique_ptr)::out) is det.
+
+generate_clique_page(Cmd, CliquePtr, Pref, Deep, HTML, Percent, ActionPtrs) :-
+    clique_to_html(Pref, Deep, CliquePtr, CliqueHTML, Percent, ActionPtrs),
+    CliquePtr = clique_ptr(CliqueNum),
+    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(CliquePtr, 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, !IO) :-
+    proc_callers_to_html(Pref, Deep, PSPtr, CallerGroups, BunchNum,
+        MaybePage, !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,
+        compute_is_active(Own) = is_not_active
+    ),
+    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,
+        compute_is_active(Own) = is_not_active
+    ),
+    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 = do_not_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 = do_not_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 = do_not_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 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).
+
+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
+    ),
+    WrappedProcName = string.format("<A HREF=""%s"">%s</A>",
+        [s(deep_cmd_pref_to_url(Pref, Deep, deep_cmd_clique(ChosenCliquePtr))),
+        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),
+    PrefContour = Pref ^ pref_contour,
+    (
+        PrefContour = do_not_apply_contour_exclusion,
+        CallerCSDPtrPairs = list.map(pair_self, CallerCSDPtrs),
+        MaybeErrorMsg = no
+    ;
+        PrefContour = apply_contour_exclusion,
+        MaybeMaybeExcludeFile = Deep ^ exclude_contour_file,
+        (
+            MaybeMaybeExcludeFile = no,
+            % There is no contour exclusion file, so do the same as for
+            % do_not_apply_contour_exclusion.
+            CallerCSDPtrPairs = list.map(pair_self, CallerCSDPtrs),
+            MaybeErrorMsg = no
+        ;
+            MaybeMaybeExcludeFile = yes(MaybeExcludeFile),
+            (
+                MaybeExcludeFile = ok(ExcludeSpec),
+                CallerCSDPtrPairs = list.map(pair_contour(Deep, ExcludeSpec),
+                    CallerCSDPtrs),
+                MaybeErrorMsg = no
+            ;
+                MaybeExcludeFile = error(ErrorMsg),
+                MaybeErrorMsg = yes(ErrorMsg ++ "\n<br>"),
+                CallerCSDPtrPairs = list.map(pair_self, CallerCSDPtrs)
+            )
+        )
+    ),
+    ProcName = proc_static_name(Deep, PSPtr),
+    CmdSite    = deep_cmd_proc_callers(PSPtr, group_by_call_site, 1,
+        PrefContour),
+    CmdProc    = deep_cmd_proc_callers(PSPtr, group_by_proc, 1,
+        PrefContour),
+    CmdModule  = deep_cmd_proc_callers(PSPtr, group_by_module, 1,
+        PrefContour),
+    CmdClique  = deep_cmd_proc_callers(PSPtr, group_by_clique, 1,
+        PrefContour),
+    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,
+        GroupList = group_csds_by_call_site(Deep, CallerCSDPtrPairs),
+        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,
+        GroupList = group_csds_by_procedure(Deep, CallerCSDPtrPairs),
+        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,
+        GroupList = group_csds_by_module(Deep, CallerCSDPtrPairs),
+        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,
+        GroupList = group_csds_by_clique(Deep, CallerCSDPtrPairs),
+        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(PSPtr, 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(PSPtr, CallerGroups, 1, PrefContour),
+        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(PSPtr, CallerGroups, BunchNum - 1,
+            PrefContour),
+        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(PSPtr, CallerGroups, BunchNum + 1,
+            PrefContour),
+        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(proc_static_ptr, string, preferences, deep,
+    int, int, int, string) = string.
+
+proc_callers_banner(PSPtr, ProcName, Pref, Deep, NumLines, BunchSize, BunchNum,
+        Parent) = HTML :-
+    Cmd = deep_cmd_proc(PSPtr),
+    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,
+    compute_parent_csd_prof_info(Deep, CalleePSPtr, CSDPtrs, Own, 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,
+    compute_parent_csd_prof_info(Deep, CalleePSPtr, CSDPtrs, Own, 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 :-
+    compute_parent_csd_prof_info(Deep, CalleePSPtr, CSDPtrs, Own, 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 :-
+    compute_parent_csd_prof_info(Deep, CalleePSPtr, CSDPtrs, Own, 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 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 :-
+    PrefContour = Pref ^ pref_contour,
+    Msg1 = "[Parent call sites]",
+    Cmd1 = deep_cmd_proc_callers(PSPtr, group_by_call_site, 1, PrefContour),
+    Msg2 = "[Parent procedures]",
+    Cmd2 = deep_cmd_proc_callers(PSPtr, group_by_proc, 1, PrefContour),
+    Msg3 = "[Parent modules]",
+    Cmd3 = deep_cmd_proc_callers(PSPtr, group_by_module, 1, PrefContour),
+    Msg4 = "[Parent cliques]",
+    Cmd4 = deep_cmd_proc_callers(PSPtr, group_by_clique, 1, PrefContour),
+    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 :-
+    Cmd = deep_cmd_clique(CliquePtr),
+    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 :-
+    Cmd = deep_cmd_proc(PSPtr),
+    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 :-
+    PrefContour = Pref0 ^ pref_contour,
+    Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, PrefContour),
+    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_program_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))])
+    ).
Index: query.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/query.m,v
retrieving revision 1.29
diff -u -b -r1.29 query.m
--- query.m	25 Sep 2008 03:47:03 -0000	1.29
+++ query.m	25 Sep 2008 04:01:33 -0000
@@ -323,28 +323,20 @@
 :- import_module apply_exclusion.
 :- import_module create_report.
 :- import_module display_report.
-:- import_module exclude.
 :- import_module html_format.
 :- import_module measurements.
+:- import_module old_query.
 :- import_module report.
-:- import_module top_procs.
 :- import_module util.
 
-:- import_module array.
-:- import_module assoc_list.
-:- import_module bool.
 :- import_module char.
 :- import_module exception.
-:- import_module float.
 :- import_module int.
 :- import_module list.
-:- import_module map.
 :- import_module math.
 :- import_module maybe.
-:- import_module pair.
 :- import_module require.
 :- import_module string.
-:- import_module unit.
 :- import_module univ.
 
 %-----------------------------------------------------------------------------%
@@ -438,1828 +430,6 @@
     HTML = htmlize_display(Deep, Prefs, Display),
     HTMLStr = html_to_string(HTML).
 
-    % Old deep profiler cgi code.  This should remain supported until all cmds
-    % have been updated to use the data structures in report.m.
-    %
-:- pred old_exec(cmd::in, preferences::in, deep::in, string::out, 
-    io::di, io::uo) is det.
-
-old_exec(deep_cmd_restart, _Pref, _Deep, _HTML, !IO) :-
-    % Our caller is supposed to filter out restart commands.
-    error("exec: found restart command").
-old_exec(deep_cmd_quit, _Pref, Deep, HTML, !IO) :-
-    HTML = string.format(
-        "<H3>Shutting down deep profile server for %s.</H3>\n",
-        [s(Deep ^ data_file_name)]).
-old_exec(deep_cmd_timeout(TimeOut), _Pref, _Deep, HTML, !IO) :-
-    HTML = string.format("<H3>Timeout set to %d minutes</H3>\n", [i(TimeOut)]).
-old_exec(Cmd, Pref, Deep, HTML, !IO) :-
-    Cmd = deep_cmd_menu,
-    HTML = generate_menu_page(Cmd, Pref, Deep).
-old_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).
-old_exec(deep_cmd_dump_proc_static(PSPtr), _Pref, Deep, HTML, !IO) :-
-    HTML = generate_proc_static_debug_page(PSPtr, Deep).
-old_exec(deep_cmd_dump_proc_dynamic(PDPtr), _Pref, Deep, HTML, !IO) :-
-    HTML = generate_proc_dynamic_debug_page(PDPtr, Deep).
-old_exec(deep_cmd_dump_call_site_static(CSSPtr), _Pref, Deep, HTML, !IO) :-
-    HTML = generate_call_site_static_debug_page(CSSPtr, Deep).
-old_exec(deep_cmd_dump_call_site_dynamic(CSDPtr), _Pref, Deep, HTML, !IO) :-
-    HTML = generate_call_site_dynamic_debug_page(CSDPtr, Deep).
-old_exec(Cmd, Pref, Deep, HTML, !IO) :-
-    Cmd = deep_cmd_proc(PSPtr),
-    ( 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)
-    ).
-old_exec(Cmd, Pref, Deep, HTML, !IO) :-
-    Cmd = deep_cmd_root(MaybePercent),
-    deep_lookup_clique_index(Deep, Deep ^ root, RootCliquePtr),
-    (
-        MaybePercent = yes(Percent),
-        HTML = chase_the_action(Cmd, RootCliquePtr, Pref, Deep, Percent)
-    ;
-        MaybePercent = no,
-        generate_clique_page(Cmd, RootCliquePtr, Pref, Deep, HTML, 100, _)
-    ).
-old_exec(Cmd, Pref, Deep, HTML, !IO) :-
-    Cmd = deep_cmd_clique(CliquePtr),
-    ( valid_clique_ptr(Deep, CliquePtr) ->
-        generate_clique_page(Cmd, CliquePtr, Pref, Deep, HTML, 100, _)
-    ;
-        HTML =
-            page_banner(Cmd, Pref) ++
-            "There is no clique with that number.\n" ++
-            page_footer(Cmd, Pref, Deep)
-    ).
-old_exec(Cmd, Pref0, Deep, HTML, !IO) :-
-    Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, Contour),
-    Pref = Pref0 ^ pref_contour := Contour,
-    ( 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)
-    ).
-old_exec(Cmd, Pref, Deep, HTML, !IO) :-
-    Cmd = deep_cmd_program_modules,
-    HTML = generate_modules_page(Cmd, Pref, Deep).
-old_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)
-    ).
-old_exec(deep_cmd_dump_clique(CliquePtr), _Pref, Deep, HTML, !IO) :-
-    HTML = generate_clique_debug_page(CliquePtr, Deep).
-old_exec(deep_cmd_procrep_coverage(_), _, _, HTML, !IO) :-
-    HTML = "query.m: deep_cmd_procrep_coverage is unsupported by old_exec\n". 
-
-%-----------------------------------------------------------------------------%
-
-:- func generate_proc_static_debug_page(proc_static_ptr, deep) = string.
-
-generate_proc_static_debug_page(PSPtr, Deep) = HTML :-
-    ( 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(proc_dynamic_ptr, deep) = string.
-
-generate_proc_dynamic_debug_page(PDPtr, Deep) = HTML :-
-    ( 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(call_site_static_ptr, deep)
-    = string.
-
-generate_call_site_static_debug_page(CSSPtr, Deep) = HTML :-
-    ( 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(call_site_dynamic_ptr, deep)
-    = string.
-
-generate_call_site_dynamic_debug_page(CSDPtr, Deep) = HTML :-
-    ( 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(clique_ptr, deep) = string.
-
-generate_clique_debug_page(CliquePtr, Deep) = HTML :-
-    ( valid_clique_ptr(Deep, CliquePtr) ->
-        deep_lookup_clique_parents(Deep, CliquePtr, ParentCSDPtr),
-        ParentCSDPtr = call_site_dynamic_ptr(ParentCSDI),
-        ParentStr = string.format("%d ->", [i(ParentCSDI)]),
-        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_program_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, clique_ptr, preferences, deep, int) = string.
-
-chase_the_action(Cmd, CliquePtr, Pref, Deep, Percent) = HTML :-
-    generate_clique_page(Cmd, CliquePtr, Pref, Deep, HTML0,
-        Percent, ActionPtrs),
-    ( ActionPtrs = [ActionCliquePtr] ->
-        HTML = chase_the_action(Cmd, ActionCliquePtr, Pref, Deep, Percent)
-    ;
-        HTML = HTML0
-    ).
-
-%-----------------------------------------------------------------------------%
-
-:- pred generate_clique_page(cmd::in, clique_ptr::in, preferences::in,
-    deep::in, string::out, int::in, list(clique_ptr)::out) is det.
-
-generate_clique_page(Cmd, CliquePtr, Pref, Deep, HTML, Percent, ActionPtrs) :-
-    clique_to_html(Pref, Deep, CliquePtr, CliqueHTML, Percent, ActionPtrs),
-    CliquePtr = clique_ptr(CliqueNum),
-    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(CliquePtr, 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, !IO) :-
-    proc_callers_to_html(Pref, Deep, PSPtr, CallerGroups, BunchNum,
-        MaybePage, !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,
-        compute_is_active(Own) = is_not_active
-    ),
-    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,
-        compute_is_active(Own) = is_not_active
-    ),
-    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 = do_not_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 = do_not_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 = do_not_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 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).
-
-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
-    ),
-    WrappedProcName = string.format("<A HREF=""%s"">%s</A>",
-        [s(deep_cmd_pref_to_url(Pref, Deep, deep_cmd_clique(ChosenCliquePtr))),
-        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),
-    PrefContour = Pref ^ pref_contour,
-    (
-        PrefContour = do_not_apply_contour_exclusion,
-        CallerCSDPtrPairs = list.map(pair_self, CallerCSDPtrs),
-        MaybeErrorMsg = no
-    ;
-        PrefContour = apply_contour_exclusion,
-        MaybeMaybeExcludeFile = Deep ^ exclude_contour_file,
-        (
-            MaybeMaybeExcludeFile = no,
-            % There is no contour exclusion file, so do the same as for
-            % do_not_apply_contour_exclusion.
-            CallerCSDPtrPairs = list.map(pair_self, CallerCSDPtrs),
-            MaybeErrorMsg = no
-        ;
-            MaybeMaybeExcludeFile = yes(MaybeExcludeFile),
-            (
-                MaybeExcludeFile = ok(ExcludeSpec),
-                CallerCSDPtrPairs = list.map(pair_contour(Deep, ExcludeSpec),
-                    CallerCSDPtrs),
-                MaybeErrorMsg = no
-            ;
-                MaybeExcludeFile = error(ErrorMsg),
-                MaybeErrorMsg = yes(ErrorMsg ++ "\n<br>"),
-                CallerCSDPtrPairs = list.map(pair_self, CallerCSDPtrs)
-            )
-        )
-    ),
-    ProcName = proc_static_name(Deep, PSPtr),
-    CmdSite    = deep_cmd_proc_callers(PSPtr, group_by_call_site, 1,
-        PrefContour),
-    CmdProc    = deep_cmd_proc_callers(PSPtr, group_by_proc, 1,
-        PrefContour),
-    CmdModule  = deep_cmd_proc_callers(PSPtr, group_by_module, 1,
-        PrefContour),
-    CmdClique  = deep_cmd_proc_callers(PSPtr, group_by_clique, 1,
-        PrefContour),
-    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,
-        GroupList = group_csds_by_call_site(Deep, CallerCSDPtrPairs),
-        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,
-        GroupList = group_csds_by_procedure(Deep, CallerCSDPtrPairs),
-        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,
-        GroupList = group_csds_by_module(Deep, CallerCSDPtrPairs),
-        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,
-        GroupList = group_csds_by_clique(Deep, CallerCSDPtrPairs),
-        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(PSPtr, 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(PSPtr, CallerGroups, 1, PrefContour),
-        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(PSPtr, CallerGroups, BunchNum - 1,
-            PrefContour),
-        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(PSPtr, CallerGroups, BunchNum + 1,
-            PrefContour),
-        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(proc_static_ptr, string, preferences, deep,
-    int, int, int, string) = string.
-
-proc_callers_banner(PSPtr, ProcName, Pref, Deep, NumLines, BunchSize, BunchNum,
-        Parent) = HTML :-
-    Cmd = deep_cmd_proc(PSPtr),
-    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,
-    compute_parent_csd_prof_info(Deep, CalleePSPtr, CSDPtrs, Own, 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,
-    compute_parent_csd_prof_info(Deep, CalleePSPtr, CSDPtrs, Own, 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 :-
-    compute_parent_csd_prof_info(Deep, CalleePSPtr, CSDPtrs, Own, 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 :-
-    compute_parent_csd_prof_info(Deep, CalleePSPtr, CSDPtrs, Own, 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 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 :-
-    PrefContour = Pref ^ pref_contour,
-    Msg1 = "[Parent call sites]",
-    Cmd1 = deep_cmd_proc_callers(PSPtr, group_by_call_site, 1, PrefContour),
-    Msg2 = "[Parent procedures]",
-    Cmd2 = deep_cmd_proc_callers(PSPtr, group_by_proc, 1, PrefContour),
-    Msg3 = "[Parent modules]",
-    Cmd3 = deep_cmd_proc_callers(PSPtr, group_by_module, 1, PrefContour),
-    Msg4 = "[Parent cliques]",
-    Cmd4 = deep_cmd_proc_callers(PSPtr, group_by_clique, 1, PrefContour),
-    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 :-
-    Cmd = deep_cmd_clique(CliquePtr),
-    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 :-
-    Cmd = deep_cmd_proc(PSPtr),
-    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 :-
-    PrefContour = Pref0 ^ pref_contour,
-    Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, PrefContour),
-    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_program_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
cvs diff: Diffing notes
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list