[m-rev.] For post-commit review: Deep profiler enhancements.

Paul Bone pbone at csse.unimelb.edu.au
Thu Aug 26 16:32:36 AEST 2010


For post commit review by Zoltan.

Deep profiler enhancements.

Create two new deep profiler reports: the first calculates the costs of
recursive calls at any depth of recursion for a clique.  The second summarises
the types of recursion seen in a program.

deep_profiler/query.m:
    Introduce new cmd types.

    Conform to changes of the cmd type.

    Add a new preference: the maximum number of proc statics to display for
    each recursion type.

    Memoize the creation of the recursion types histogram report as it can take
    a while to generate, 39 minutes on a Core 4 when generating a report for
    the compiler it's self.

deep_profiler/report.m:
    Define the new report structures.

deep_profiler/create_report.m:
    Handle creation of the new reports.

    Export describe_proc and own_and_inherit_to_perf_row_data so that the
    recursion_patterns module can use them.

    Write a find_clique_first_and_other_procs predicate to find the first
    procedure in a clique, it also returns a list of the remaining procedures.
    This is exported so that recursion_patterns can use it but it belongs here
    as it is generally used for creating reports.

    Refactor the retrieval of the progrep data from the deep data structure.

    Make a note about a change that could be made to speed up large analyses.

deep_profiler/profile.m:
    Refactor the retrieval of the progrep data from the deep data structure.

deep_profiler/display_report.m:
    Handle translation of the new reports to the display data type.

    Link to this report from clique reports, and to clique reports from the new
    clique recursion costs report.

    Refactor the code that constructs the lists of related reports.

deep_profiler/recursion_patterns.m:
    Create a new module to contain the recursion pattern analysis code.

deep_profiler/old_html_format.m:
deep_profiler/old_query.m:
    Conform to changes in query.m

deep_profiler/mdprof_fb.automatic_parallelism.m:
    Remove add_call_site_to_map it is a duplicate of add_call_site_report_to_map.

    Move add_call_site_report_to_map and proc_label_from_proc_desc to report.m

deep_profiler/measurement_units.m:
    Include functions to manipulate probabilities of conjoint and disjoint events.

    Allow percentages to be greater than 100% and less than 0% this can occur
    legitimately in some cases.

deep_profiler/measurements.m:
deep_profiler/var_use_analysis.m:
    Move weighted_average/3 from var_use_analysis.m to measurements.m where it
    can be used globally.

deep_profiler/mdprof_test.m:
    Modify the mdprof_test program so that it can generate the recursion types
    histogram report, this is useful for profiling and debugging.

Index: deep_profiler/create_report.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/create_report.m,v
retrieving revision 1.20
diff -u -p -b -r1.20 create_report.m
--- deep_profiler/create_report.m	4 Aug 2010 02:25:01 -0000	1.20
+++ deep_profiler/create_report.m	26 Aug 2010 06:21:36 -0000
@@ -16,11 +16,14 @@
 :- module create_report.
 :- interface.
 
-:- import_module maybe.
+:- import_module measurements.
 :- import_module profile.
 :- import_module query.
 :- import_module report.
 
+:- import_module list.
+:- import_module maybe.
+
 %-----------------------------------------------------------------------------%
 
 :- pred create_report(cmd::in, deep::in, deep_report::out) is det.
@@ -55,18 +58,35 @@
 :- pred create_clique_report(deep::in, clique_ptr::in,
     maybe_error(clique_report)::out) is det.
 
+    % Instead of using the clique report above to find proc dynamics for a
+    % clique, use this as it is much faster.
+    %
+:- pred find_clique_first_and_other_procs(deep::in, clique_ptr::in, 
+    maybe(proc_dynamic_ptr)::out, list(proc_dynamic_ptr)::out) is det.
+    
+    % Create a proc_desc structure for a given proc static pointer.
+    %
+:- func describe_proc(deep, proc_static_ptr) = proc_desc.
+
+%----------------------------------------------------------------------------%
+
+    % Convert own and inherit perf information to row data used for reports.
+    %
+:- pred own_and_inherit_to_perf_row_data(deep::in, T::in,
+    own_prof_info::in, inherit_prof_info::in, perf_row_data(T)::out) is det.
+
 %----------------------------------------------------------------------------%
 
 :- implementation.
 
 :- import_module apply_exclusion.
-:- import_module exclude.
 :- import_module coverage.
+:- import_module exclude.
 :- import_module mdbcomp.
 :- import_module mdbcomp.program_representation.
 :- import_module measurement_units.
-:- import_module measurements.
 :- import_module program_representation_utils.
+:- import_module recursion_patterns.
 :- import_module top_procs.
 :- import_module var_use_analysis.
 
@@ -76,7 +96,6 @@
 :- import_module exception.
 :- import_module float.
 :- import_module int.
-:- import_module list.
 :- import_module map.
 :- import_module math.
 :- import_module pair.
@@ -121,6 +140,15 @@ create_report(Cmd, Deep, Report) :-
         create_clique_report(Deep, CliquePtr, MaybeCliqueReport),
         Report = report_clique(MaybeCliqueReport)
     ;
+        Cmd = deep_cmd_clique_recursive_costs(CliquePtr),
+        create_clique_recursion_costs_report(Deep, CliquePtr,
+            MaybeCliqueRecursionReport),
+        Report = report_clique_recursion_costs(MaybeCliqueRecursionReport)
+    ;
+        Cmd = deep_cmd_recursion_types_frequency,
+        create_recursion_types_frequency_report(Deep, MaybeRecTypesFreqReport),
+        Report = report_recursion_types_frequency(MaybeRecTypesFreqReport)
+    ;
         Cmd = deep_cmd_program_modules,
         create_program_modules_report(Deep, MaybeProgramModulesReport),
         Report = report_program_modules(MaybeProgramModulesReport)
@@ -305,6 +333,21 @@ create_clique_report(Deep, CliquePtr, Ma
     CliqueReport = clique_report(CliquePtr, AncestorRowDatas, CliqueProcs),
     MaybeCliqueReport = ok(CliqueReport).
 
+find_clique_first_and_other_procs(Deep, CliquePtr, MaybeFirstPDPtr,
+        OtherPDPtrs) :-
+    deep_lookup_clique_members(Deep, CliquePtr, PDPtrs),
+    deep_lookup_clique_parents(Deep, CliquePtr, EntryCSDPtr),
+    ( valid_call_site_dynamic_ptr(Deep, EntryCSDPtr) ->
+        deep_lookup_call_site_dynamics(Deep, EntryCSDPtr, EntryCSD),
+        FirstPDPtr = EntryCSD ^ csd_callee,
+        MaybeFirstPDPtr = yes(FirstPDPtr),
+        list.negated_filter(unify(FirstPDPtr), PDPtrs,
+            OtherPDPtrs)
+    ;
+        MaybeFirstPDPtr = no,
+        OtherPDPtrs = PDPtrs
+    ).
+
 :- func find_clique_ancestors(deep, clique_ptr) =
     list(perf_row_data(ancestor_desc)).
 
@@ -1075,17 +1118,12 @@ create_proc_caller_cliques(Deep, CalleeP
 %
 
 create_procrep_coverage_report(Deep, PSPtr, MaybeReport) :-
-    MaybeProgRepResult = Deep ^ procrep_file,
+    deep_get_maybe_progrep(Deep, MaybeProgRep),
     (
-        MaybeProgRepResult = no,
-        MaybeReport = error("There is no readable " ++
-            "procedure representation information file.")
-    ;
-        MaybeProgRepResult = yes(error(Error)),
-        MaybeReport = error("Error reading procedure representation " ++
-            "information file: " ++ Error)
+        MaybeProgRep = error(Error),
+        MaybeReport = error(Error)
     ;
-        MaybeProgRepResult = yes(ok(ProgRep)),
+        MaybeProgRep = ok(ProgRep),
         ( valid_proc_static_ptr(Deep, PSPtr) ->
             deep_lookup_proc_statics(Deep, PSPtr, PS),
             ProcLabel = PS ^ ps_id,
@@ -1128,6 +1166,10 @@ create_procrep_coverage_report(Deep, PSP
     map(goal_path, call_site_perf)) =  map(goal_path, call_site_perf).
 
 create_cs_summary_add_to_map(Deep, CSStatic, Map0) = Map :-
+    % TODO: Most uses of this predicate don't need all the information that a
+    % call site summary provides.  Additionally creating a call site summary is
+    % reasonably expensive, consider using more specialised code instead as it
+    % will be faster.
     create_call_site_summary(Deep, CSStatic) = CSSummary,
     GoalPath = CSSummary ^ csf_summary_perf ^ perf_row_subject
         ^ csdesc_goal_path,
@@ -1269,9 +1311,6 @@ psi_to_perf_row_data(Deep, PSI, RowData)
     deep_lookup_ps_desc(Deep, PSPtr, Desc),
     own_and_inherit_to_perf_row_data(Deep, ProcDesc, Own, Desc, RowData).
 
-:- pred own_and_inherit_to_perf_row_data(deep::in, T::in,
-    own_prof_info::in, inherit_prof_info::in, perf_row_data(T)::out) is det.
-
 own_and_inherit_to_perf_row_data(Deep, Subject, Own, Desc, RowData) :-
     own_and_maybe_inherit_to_perf_row_data(Deep, Subject, Own, yes(Desc),
         RowData).
@@ -1394,10 +1433,6 @@ percent_from_ints(Nom, Denom) = Percent 
 
 %-----------------------------------------------------------------------------%
 
-    % Create a proc_desc structure for a given proc static pointer.
-    %
-:- func describe_proc(deep, proc_static_ptr) = proc_desc.
-
 describe_proc(Deep, PSPtr) = ProcDesc :-
     ( valid_proc_static_ptr(Deep, PSPtr) ->
         deep_lookup_proc_statics(Deep, PSPtr, PS),
Index: deep_profiler/display_report.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/display_report.m,v
retrieving revision 1.24
diff -u -p -b -r1.24 display_report.m
--- deep_profiler/display_report.m	4 Jul 2010 10:24:09 -0000	1.24
+++ deep_profiler/display_report.m	26 Aug 2010 06:21:36 -0000
@@ -55,6 +55,8 @@
 :- import_module maybe.
 :- import_module pair.
 :- import_module require.
+:- import_module set.
+:- import_module solutions.
 :- import_module string.
 :- import_module unit.
 
@@ -83,6 +85,26 @@ report_to_display(Deep, Prefs, Report) =
             Display = display(no, [display_heading(Msg)])
         )
     ;
+        Report = report_clique_recursion_costs(MaybeCliqueRecursionReport),
+        (
+            MaybeCliqueRecursionReport = ok(CliqueRecursionReport),
+            display_report_clique_recursion(Prefs, CliqueRecursionReport,
+                Display)
+        ;
+            MaybeCliqueRecursionReport = error(Msg),
+            Display = display(no, [display_heading(Msg)])
+        )
+    ;
+        Report = report_recursion_types_frequency(MaybeRecTypesFreqReport),
+        (
+            MaybeRecTypesFreqReport = ok(RecTypesFreqReport),
+            display_report_recursion_types_frequency(Prefs, RecTypesFreqReport,
+                Display)
+        ;
+            MaybeRecTypesFreqReport = error(Msg),
+            Display = display(no, [display_heading(Msg)])
+        )
+    ;
         Report = report_program_modules(MaybeProgramModulesReport),
         (
             MaybeProgramModulesReport = ok(ProgramModulesReport),
@@ -330,9 +352,17 @@ display_report_menu(Deep, Prefs, MenuRep
             "Procedures above 2M words threshold: words, self+descendants.")
     ],
 
+    RecursionTypeFrequenciesCmd = deep_cmd_recursion_types_frequency,
+
+    LinksSummaryReports = [
+        link_base(RecursionTypeFrequenciesCmd, no,
+            "Frequencies of different types of recursion used in the program.")
+    ],
+
     LinkCmds = LinksExploration ++
         LinksTopProcsByLimitTime ++ LinksTopProcsByLimit ++
-        LinksTopProcsByPercentTime ++ LinksTopProcsByPercent,
+        LinksTopProcsByPercentTime ++ LinksTopProcsByPercent ++
+        LinksSummaryReports,
     list.map(make_link, LinkCmds, LinksList),
     Links = display_list(list_class_vertical_bullets,
         yes("You can start exploring the deep profile at the following" ++
@@ -460,6 +490,7 @@ display_report_clique(Prefs, CliqueRepor
     ModuleQualControls = module_qual_controls(Prefs, Cmd),
     FieldControls = field_controls(Prefs, Cmd),
     FormatControls = format_controls(Prefs, Cmd),
+    CliqueReportControls = clique_reports_controls(Prefs, CliquePtr, Cmd),
     MenuRestartQuitControls = cmds_menu_restart_quit(yes(Prefs)),
 
     Display = display(yes(Title),
@@ -469,6 +500,7 @@ display_report_clique(Prefs, CliqueRepor
         display_paragraph_break, ModuleQualControls,
         display_paragraph_break, FieldControls,
         display_paragraph_break, FormatControls,
+        display_paragraph_break, CliqueReportControls,
         display_paragraph_break, MenuRestartQuitControls]).
 
 :- func clique_proc_report_module_name(clique_proc_report) = string.
@@ -656,6 +688,302 @@ clique_call_site_callee_to_row(MaybeCurM
 
 %-----------------------------------------------------------------------------%
 %
+% Code to display a clique recursion report.
+%
+
+:- pred display_report_clique_recursion(preferences::in,
+    clique_recursion_report::in, display::out) is det.
+
+display_report_clique_recursion(Prefs, CliqueRecursionReport, Display) :-
+    CliqueRecursionReport = clique_recursion_report(CliquePtr, RecursionType, 
+        _NumProcs),
+    Cmd = deep_cmd_clique_recursive_costs(CliquePtr),
+    CliquePtr = clique_ptr(CliqueNum),
+    Title = string.format("The recursion information for clique %d:",
+        [i(CliqueNum)]),
+
+    display_recursion_type(RecursionType, DisplayRecursionType),
+
+    CliqueReportsControls = clique_reports_controls(Prefs, CliquePtr, Cmd), 
+    
+    MenuRestartQuitControls = cmds_menu_restart_quit(yes(Prefs)),
+    Display = display(yes(Title), DisplayRecursionType ++
+        [display_paragraph_break, CliqueReportsControls,
+         display_paragraph_break, MenuRestartQuitControls]).
+
+:- pred display_recursion_type(recursion_type::in, list(display_item)::out) 
+    is det.
+
+display_recursion_type(RecursionType, Items) :-
+    (
+        (
+            RecursionType = rt_not_recursive,
+            Text = "Clique is non-recursive"
+        ;
+            RecursionType = rt_mutual_recursion(NumProcs),
+            Text = format("Mutual recursion between %d procedures", 
+                [i(NumProcs)])
+        ),
+        Items = [display_text(Text)]
+    ;
+        RecursionType = rt_errors(Errors),
+        ErrorItems = map((func(Text) = display_text(Text)), Errors),
+        Items = [display_list(list_class_vertical_no_bullets,
+            yes("Unknown, error(s) occured"), ErrorItems)]
+    ;
+        (
+            RecursionType = rt_single(BaseLevel, RecLevel),
+            RowData = [
+                {"Base case", BaseLevel}, 
+                {"Recursive case", RecLevel}],
+            Text = "Single-recursion:"
+        ;
+            RecursionType = rt_divide_and_conquer(BaseLevel, RecLevel),
+            RowData = [
+                {"Base case", BaseLevel},
+                {"Doubly-recursive case", RecLevel}],
+            Text = "Double-recursion (Probably Divide and Conquer):"
+        ; 
+            RecursionType = rt_other(Levels),
+            RowData = map((func(Level) = {Label, Level} :-
+                    Label = format("Case for %d recursive calls", 
+                        [i(Level ^ rlr_level)])
+                ), Levels), 
+            Text = "Unknown recursion type:"
+        ),
+        Rows = map(make_recursion_table_row, RowData),
+        Header = table_header(map(
+            (func({Name, Class}) = 
+                table_header_group(table_header_group_single(td_s(Name)),
+                    Class, column_do_not_colour)
+            ), 
+            [{"Recursion type", table_column_class_field_name},
+             {"Exec count", table_column_class_number},
+             {"Non recursive calls cost", table_column_class_callseqs},
+             {"Recursive calls cost (ex children)", 
+                table_column_class_callseqs}])),
+        Table = display_table(table(table_class_box_if_pref, 4, yes(Header), 
+            Rows)),
+        Description = display_text(Text),
+        Items = [Description, display_paragraph_break, Table]
+    ).
+
+:- func make_recursion_table_row({string, recursion_level_report}) = table_row.
+
+make_recursion_table_row({Label, Report}) = 
+    table_row([table_cell(td_s(Label)),
+        table_cell(td_i(Report ^ rlr_calls)),
+        table_cell(td_f(Report ^ rlr_non_rec_calls_cost)),
+        table_cell(td_f(Report ^ rlr_rec_calls_ex_chld_cost))]).
+
+:- pred display_report_recursion_types_frequency(preferences::in,
+    recursion_types_frequency_report::in, display::out) is det.
+
+display_report_recursion_types_frequency(Prefs, Report, Display) :-
+    Cmd = deep_cmd_recursion_types_frequency,
+    Report = recursion_types_frequency_report(Histogram0),
+
+    Title = "Frequencies of recognized recursion types",
+
+    % Build the table.
+    RecursionTypeLink = deep_link(Cmd, yes(Prefs ^ pref_criteria := by_context),
+        attr_str([], "Recursion Type"), link_class_link), 
+    RecursionTypeHeaderGroup = make_single_table_header_group(
+        td_l(RecursionTypeLink), table_column_class_no_class,
+        column_do_not_colour),
+    FreqHeaderGroup = make_single_table_header_group(
+        td_s("Frequency"), table_column_class_no_class,
+        column_do_not_colour),
+    PercentageHeaderGroup = make_single_table_header_group(
+        td_s("Percentage"), table_column_class_no_class,
+        column_do_not_colour),
+    perf_table_header(total_columns_meaningful, Prefs, 
+        override_order_criteria_header_data(Cmd), PerfHeaderGroups),
+    AllHeaderGroups = [RecursionTypeHeaderGroup, FreqHeaderGroup,
+        PercentageHeaderGroup] ++ PerfHeaderGroups,
+    header_groups_to_header(AllHeaderGroups, NumColumns, Header),
+
+    Histogram1 = map.to_assoc_list(Histogram0),
+    sort_recursion_types_by_preferences(Prefs, Histogram1, Histogram),
+    map(display_report_rec_type_freq_rows(Prefs, NumColumns), Histogram, Rowss),
+    list.condense(Rowss, Rows),
+    RecursionTypesTable = display_table(table(table_class_box_if_pref,
+        NumColumns, yes(Header), Rows)),
+
+    ModuleQualControls = module_qual_controls(Prefs, Cmd),
+    FieldControls = field_controls(Prefs, Cmd),
+    FormatControls = format_controls(Prefs, Cmd),
+    MenuRestartQuitControls = cmds_menu_restart_quit(yes(Prefs)),
+    
+    Display = display(yes(Title), [RecursionTypesTable,
+        display_paragraph_break, ModuleQualControls,
+        display_paragraph_break, FieldControls,
+        display_paragraph_break, FormatControls,
+        display_paragraph_break, MenuRestartQuitControls]).
+
+:- pred sort_recursion_types_by_preferences(preferences::in,
+    assoc_list(recursion_type_simple, recursion_type_freq_data)::in,
+    assoc_list(recursion_type_simple, recursion_type_freq_data)::out) is det.
+
+sort_recursion_types_by_preferences(Prefs, !RecursionTypes) :-
+    OrderCriteria = Prefs ^ pref_criteria,
+    (
+        ( OrderCriteria = by_context
+        ; OrderCriteria = by_name
+        ),
+        % Sort by the type of recursion.
+        list.sort(compare_recursion_type_row_by_rec_type, !RecursionTypes)
+    ;
+        OrderCriteria = by_cost(CostKind, InclDesc, Scope),
+        list.sort(compare_rec_type_row_datas_by_cost(CostKind, InclDesc, Scope),
+            !RecursionTypes),
+        % We want the most expensive rows to appear first.
+        list.reverse(!RecursionTypes)
+    ).
+
+:- pred sort_recursion_type_procs_by_preferences(preferences::in,
+    list(recursion_type_proc_freq_data)::in,
+    list(recursion_type_proc_freq_data)::out) is det. 
+
+sort_recursion_type_procs_by_preferences(Prefs, !RecursionTypeProcs) :-
+    OrderCriteria = Prefs ^ pref_criteria,
+    (
+        ( OrderCriteria = by_context
+        ; OrderCriteria = by_name
+        ),
+        % Since in this case we sort recursion type rows by their recursion
+        % type It makes sense to sort the procs within each type by their
+        % frequency.
+        sort(compare_recursion_proc_row_by_frequency_rev, !RecursionTypeProcs)
+    ;
+        OrderCriteria = by_cost(CostKind, InclDesc, Scope),
+        sort(compare_rec_proc_row_datas_by_cost(CostKind, InclDesc, Scope),
+            !RecursionTypeProcs),
+
+        % We want the most expensive rows to appear first.
+        reverse(!RecursionTypeProcs)
+    ).
+
+:- pred compare_recursion_type_row_by_rec_type(
+    pair(recursion_type_simple, T)::in,
+    pair(recursion_type_simple, T)::in, comparison_result::out) is det.
+
+compare_recursion_type_row_by_rec_type(RTA - _, RTB - _, Result) :-
+    compare(Result, RTA, RTB).
+
+    % This is reversed so that entries with larger frequencies are listed first.
+    %
+:- pred compare_recursion_proc_row_by_frequency_rev(
+    recursion_type_proc_freq_data::in, recursion_type_proc_freq_data::in,
+    comparison_result::out) is det.
+
+compare_recursion_proc_row_by_frequency_rev(
+        recursion_type_proc_freq_data(FreqA, _, _),
+        recursion_type_proc_freq_data(FreqB, _, _), Result) :-
+    compare(Result, FreqB, FreqA).
+
+:- pred compare_rec_type_row_datas_by_cost(cost_kind::in,
+    include_descendants::in, measurement_scope::in,
+    pair(T, recursion_type_freq_data)::in,
+    pair(T, recursion_type_freq_data)::in,
+    comparison_result::out) is det.
+
+compare_rec_type_row_datas_by_cost(CostKind, IncDesc, Scope, _ - DataA, 
+        _ - DataB, Result) :- 
+    MaybePerfA = DataA ^ rtfd_maybe_summary,
+    MaybePerfB = DataB ^ rtfd_maybe_summary,
+    (
+        MaybePerfA = yes(PerfA),
+        MaybePerfB = yes(PerfB),
+        compare_perf_row_datas_by_cost(CostKind, IncDesc, Scope, 
+            PerfA, PerfB, Result)
+    ;
+        MaybePerfA = yes(_),
+        MaybePerfB = no,
+        Result = (>)
+    ;
+        MaybePerfA = no,
+        MaybePerfB = yes(_),
+        Result = (=)
+    ;
+        MaybePerfA = no,
+        MaybePerfB = no,
+        Result = (=)
+    ).
+
+:- pred compare_rec_proc_row_datas_by_cost(cost_kind::in,
+    include_descendants::in, measurement_scope::in,
+    recursion_type_proc_freq_data::in, recursion_type_proc_freq_data::in,
+    comparison_result::out) is det.
+
+compare_rec_proc_row_datas_by_cost(CostKind, InclDesc, Scope, 
+        recursion_type_proc_freq_data(_, _, PerfA),
+        recursion_type_proc_freq_data(_, _, PerfB), Result) :-
+    compare_perf_row_datas_by_cost(CostKind, InclDesc, Scope, PerfA, PerfB,
+        Result).
+
+:- pred display_report_rec_type_freq_rows(preferences::in, int::in,
+    pair(recursion_type_simple, recursion_type_freq_data)::in, 
+    list(table_row)::out) is det.
+
+display_report_rec_type_freq_rows(Prefs, NumColumns, Type - FreqData, Rows) :-
+    FreqData = recursion_type_freq_data(Frequency, Percent, MaybeSummary, 
+        ProcsMap),
+
+    % Make the header row.
+    display_report_recursion_type_simple(Type, TypeStr),
+    HeaderRow = table_section_header(td_s(TypeStr)),
+
+    % Make the row summarising the recursion type.
+    (
+        MaybeSummary = yes(Summary),
+        perf_table_row(total_columns_meaningful, Prefs ^ pref_fields, Summary,
+            SummaryCells)  
+    ;
+        MaybeSummary = no,
+        duplicate(NumColumns - 3, table_empty_cell, SummaryCells)
+    ),
+    Row = table_row([table_cell(td_s("Totals")), table_cell(td_i(Frequency)),
+        table_cell(td_p(Percent))] ++ SummaryCells),
+
+    % Make rows for the top proc statics.
+    Procs0 = values(ProcsMap),
+    sort_recursion_type_procs_by_preferences(Prefs, Procs0, Procs1),
+    take_upto(Prefs ^ pref_proc_statics_per_rec_type, Procs1, Procs),
+    map(display_report_rec_type_proc_rows(Prefs), Procs, ProcRows),
+
+    Rows = [HeaderRow | [Row | ProcRows ++ 
+        [table_separator_row]]].
+
+:- pred display_report_rec_type_proc_rows(preferences::in,
+    recursion_type_proc_freq_data::in, table_row::out) is det.
+
+display_report_rec_type_proc_rows(Prefs, 
+        recursion_type_proc_freq_data(Freq, Percent, Summary), Row) :-
+    perf_table_row(total_columns_meaningful, Prefs ^ pref_fields, Summary,
+        SummaryCells),
+    ProcDesc = Summary ^ perf_row_subject,
+    ProcCell = proc_desc_to_proc_name_cell(no, Prefs ^ pref_module_qual, Prefs, 
+        ProcDesc),
+    Row = table_row([ProcCell, table_cell(td_i(Freq)),
+        table_cell(td_p(Percent))] ++ SummaryCells).
+
+:- pred display_report_recursion_type_simple(recursion_type_simple::in,
+    string::out) is det.
+
+display_report_recursion_type_simple(rts_not_recursive, "Not recursive").
+display_report_recursion_type_simple(rts_single, "Single-recursion").
+display_report_recursion_type_simple(rts_divide_and_conquer, "Divide and conquer").
+display_report_recursion_type_simple(rts_mutual_recursion(NumProcs), String) :-
+    format("Mutual recursion between %d procs", [i(NumProcs)], String).
+display_report_recursion_type_simple(rts_other(Levels), String) :-
+    LevelsStr = join_list(", ", map(string, set.to_sorted_list(Levels))),
+    format("Other recursion with levels: %s", [s(LevelsStr)], String).
+display_report_recursion_type_simple(rts_total_error_instances, "Total errors").
+display_report_recursion_type_simple(rts_error(Error), "Error: " ++ Error). 
+
+%-----------------------------------------------------------------------------%
+%
 % Code to display a program_modules report.
 %
 
@@ -3392,36 +3720,42 @@ set_box_tables(Box, !Prefs) :-
 
 %----------------------------------------------------------------------------%
 %
-% Controls for related procedure reports.
+% Controls for related procedure and clique reports.
 %
 
 :- func proc_reports_controls(preferences, proc_static_ptr, cmd) = display_item.
 
 proc_reports_controls(Prefs, Proc, NotCmd) = ControlsItem :-
-    make_cmd_controls_item(Prefs, proc_reports(Proc, NotCmd),
-        ProcReportControls),
+    solutions((pred(Control::out) is nondet :-
+            (
+                Cmd = deep_cmd_proc(Proc),
+                Label = "Procedure"
+            ;
+                Cmd = deep_cmd_procrep_coverage(Proc),
+                Label = "Coverage annotated procedure representation"
+            ),
+            Cmd \= NotCmd,
+            make_control(yes(Prefs), Cmd - Label, Control)
+        ), ProcReportControls),
     ControlsItem = display_list(list_class_vertical_no_bullets,
-        yes("Related procedure reports:"), [ProcReportControls]).
+        yes("Related procedure reports:"), ProcReportControls).
 
-:- func proc_reports(proc_static_ptr, cmd) = assoc_list(cmd, string).
+:- func clique_reports_controls(preferences, clique_ptr, cmd) = display_item. 
 
-proc_reports(Proc, NotCmd) = Reports :-
-    Reports0 = [
-        deep_cmd_proc(Proc) -
-            "Procedure",
-        deep_cmd_procrep_coverage(Proc) -
-            "Coverage annotated procedure representation"
-    ],
-    list.filter((pred((Cmd - _)::in) is semidet :-
-            Cmd \= NotCmd
-        ), Reports0, Reports).
-
-:- pred make_cmd_controls_item(preferences::in, assoc_list(cmd, string)::in,
-    display_item::out) is det.
-
-make_cmd_controls_item(Prefs, LabeledCmds, Item) :-
-    list.map(make_control(yes(Prefs)), LabeledCmds, CmdItems),
-    Item = display_list(list_class_horizontal, no, CmdItems).
+clique_reports_controls(Perfs, CliquePtr, NotCmd) = ControlsItem :-
+    solutions((pred(Control::out) is nondet :-
+            (
+                Cmd = deep_cmd_clique(CliquePtr),
+                Label = "Clique"
+            ;
+                Cmd = deep_cmd_clique_recursive_costs(CliquePtr),
+                Label = "Clique's recursion information"
+            ),
+            Cmd \= NotCmd,
+            make_control(yes(Perfs), Cmd - Label, Control)
+        ), CliqueReportControls),
+    ControlsItem = display_list(list_class_vertical_no_bullets,
+        yes("Related clique reports:"), CliqueReportControls).
 
 %-----------------------------------------------------------------------------%
 %
Index: deep_profiler/mdprof_fb.automatic_parallelism.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/mdprof_fb.automatic_parallelism.m,v
retrieving revision 1.13
diff -u -p -b -r1.13 mdprof_fb.automatic_parallelism.m
--- deep_profiler/mdprof_fb.automatic_parallelism.m	24 Aug 2010 00:01:47 -0000	1.13
+++ deep_profiler/mdprof_fb.automatic_parallelism.m	26 Aug 2010 06:21:36 -0000
@@ -653,7 +653,8 @@ build_recursive_call_site_cost_map(Deep,
         (
             MaybeCoverageReport = ok(procrep_coverage_info(_, ProcRep)),
             Goal = ProcRep ^ pr_defn ^ pdr_goal,
-            foldl(add_call_site_to_map, CallSites, map.init, CallSiteMap),
+            foldl(add_call_site_report_to_map, CallSites,
+                map.init, CallSiteMap),
             goal_get_callsite_cost_info(CliquePtr, CallSiteMap, Goal,
                 empty_goal_path, Info),
             Info = callsite_cost_info(NonRecCSCost, RecursiveCSCalls,
@@ -704,15 +705,6 @@ format_recursive_call_site_cost(GoalPath
     PerCallCost = cs_cost_get_percall(Cost),
     Calls = cs_cost_get_calls(Cost).
 
-:- pred add_call_site_to_map(clique_call_site_report::in, 
-    map(goal_path, clique_call_site_report)::in,
-    map(goal_path, clique_call_site_report)::out) is det.
-
-add_call_site_to_map(CallSite, !Map) :-
-    GoalPath = CallSite ^ ccsr_call_site_summary ^ perf_row_subject 
-        ^ csdesc_goal_path,
-    svmap.det_insert(GoalPath, CallSite, !Map).
-
     % The stateful data of the goal_get_callsite_cost_info code below.
     %
 :- type callsite_cost_info
@@ -3523,23 +3515,6 @@ css_to_call(Deep, CSS, Call) :-
 % Useful utility predicates.
 %
 
-:- pred proc_label_from_proc_desc(deep::in, proc_desc::in,
-    string_proc_label::out) is det.
-
-proc_label_from_proc_desc(Deep, ProcDesc, ProcLabel) :-
-    PSPtr = ProcDesc ^ pdesc_ps_ptr,
-    deep_lookup_proc_statics(Deep, PSPtr, ProcStatic),
-    ProcLabel = ProcStatic ^ ps_id.
-
-:- pred add_call_site_report_to_map(clique_call_site_report::in, 
-    map(goal_path, clique_call_site_report)::in, 
-    map(goal_path, clique_call_site_report)::out) is det.
-
-add_call_site_report_to_map(CallSite, !Map) :-
-    GoalPath = CallSite ^ ccsr_call_site_summary ^ perf_row_subject 
-        ^ csdesc_goal_path,
-    svmap.det_insert(GoalPath, CallSite, !Map).
-
 :- func this_file = string.
 
 this_file = "mdprof_fb.automatic_parallelism.m: ".
Index: deep_profiler/mdprof_test.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/mdprof_test.m,v
retrieving revision 1.21
diff -u -p -b -r1.21 mdprof_test.m
--- deep_profiler/mdprof_test.m	3 Oct 2008 07:39:04 -0000	1.21
+++ deep_profiler/mdprof_test.m	26 Aug 2010 06:21:36 -0000
@@ -213,7 +213,11 @@ write_help_message(ProgName) -->
         "--test-dir <dirname>\n" ++
         "\t\t\tPut the generated web pages into <dirname>.\n" ++
         "--no-compress\n" ++
-        "\t\t\tDon't compress the resulting files, this speeds the test.").
+        "\t\t\tDon't compress the resulting files, this speeds the test.\n" ++
+        "--procrep-coverage\n" ++
+        "\t\t\tRun the procrep coverage query on every static procedure\n" ++
+        "--recursion-types-histogram\n" ++
+        "\t\t\tRun the recursion types histogram query\n").
     % --canonical-clique is not documented because it is not yet supported
 
 %-----------------------------------------------------------------------------%
@@ -232,8 +236,22 @@ test_server(Pref, Deep, Options, !IO) :-
     % test_cliques(1, NumCliques, DirName, Pref, Deep, !IO),
     % test_procs(1, NumProcStatics, DirName, Pref, Deep, !IO).
     
+    lookup_bool_option(Options, procrep_coverage, ProcrepCoverage),
+    (
+        ProcrepCoverage = yes,
     array.max(Deep ^ proc_statics, NumProcStatics),
-    test_procrep_coverages(1, NumProcStatics, Pref, Deep, Options, !IO).
+        test_procrep_coverages(1, NumProcStatics, Pref, Deep, Options, !IO)
+    ;
+        ProcrepCoverage = no
+    ),
+
+    lookup_bool_option(Options, recursion_types_histogram, RecTypesHistogram),
+    (
+        RecTypesHistogram = yes,
+        test_recursion_types_histogram(Pref, Deep, Options, !IO)
+    ;
+        RecTypesHistogram = no
+    ).
 
 :- pred test_cliques(int::in, int::in, option_table::in, preferences::in,
     deep::in, io::di, io::uo) is cc_multi.
@@ -272,6 +290,15 @@ test_procrep_coverages(Cur, Max, Pref, D
         true
     ).
 
+:- pred test_recursion_types_histogram(preferences::in, deep::in, 
+    option_table::in, io::di, io::uo) is det.
+
+test_recursion_types_histogram(Pref, Deep, Options, !IO) :-
+    promise_equivalent_solutions [!:IO] (
+        try_exec(deep_cmd_recursion_types_frequency, Pref, Deep, HTML, !IO),
+        write_test_html(Options, "recursion_types_histogram", 1, HTML, !IO)
+    ).
+
 :- func progrep_error = maybe_error(prog_rep).
 
 progrep_error = 
@@ -331,7 +358,9 @@ write_test_html(Options, BaseName, Num, 
     ;       test_dir
     ;       verbose
     ;       version
-    ;       verify_profile.
+    ;       verify_profile
+    ;       procrep_coverage
+    ;       recursion_types_histogram.
 
 :- type options ---> options.
 :- type option_table == (option_table(option)).
@@ -356,6 +385,8 @@ long("test-dir",            test_dir).
 long("verbose",             verbose).
 long("version",             version).
 long("verify-profile",      verify_profile).
+long("procrep-coverage",            procrep_coverage).
+long("recursion-types-histogram",   recursion_types_histogram).
 
 :- pred defaults(option::out, option_data::out) is multi.
 
@@ -369,6 +400,8 @@ defaults(test_dir,          string("deep
 defaults(verbose,           bool(no)).
 defaults(version,           bool(no)).
 defaults(verify_profile,    bool(no)).
+defaults(procrep_coverage,          bool(no)).
+defaults(recursion_types_histogram, bool(no)).
 
 %-----------------------------------------------------------------------------%
 :- end_module mdprof_test.
Index: deep_profiler/measurement_units.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/measurement_units.m,v
retrieving revision 1.6
diff -u -p -b -r1.6 measurement_units.m
--- deep_profiler/measurement_units.m	16 Dec 2009 23:47:30 -0000	1.6
+++ deep_profiler/measurement_units.m	26 Aug 2010 06:21:36 -0000
@@ -133,6 +133,10 @@
     %
 :- func probability_to_float(probability) = float.
 
+    % Combine probabilities.
+:- func or(probability, probability) = probability.
+:- func and(probability, probability) = probability.
+
 %-----------------------------------------------------------------------------%
 %
 % Code for formatting numbers.
@@ -204,15 +208,7 @@ compare_memory(MemoryA, MemoryB, Result)
 :- type percent
     --->    percent_float(float).
 
-percent(P) = PF :-
-    (
-        0.0 =< P,
-        P =< 1.0
-    ->
-        PF = percent_float(P)
-    ;
-        error("Percentage value out of range 0.0 to 1.0 (inclusive)")
-    ).
+percent(P) = percent_float(P).
 
 format_percent(percent_float(P)) = String :-
     string.format("%.2f", [f(P * 100.0)], String).
@@ -304,6 +300,12 @@ probable(Prob) = Prob :-
 
 probability_to_float(Prob) = Prob.
 
+    % Combine disjoint probabilities with addition.
+or(A, B) = A + B.
+
+    % Combine conjoint probabilities with multiplication.
+and(A, B) = A * B.
+
 %-----------------------------------------------------------------------------%
 %
 % Code for formatting numbers.
Index: deep_profiler/measurements.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/measurements.m,v
retrieving revision 1.19
diff -u -p -b -r1.19 measurements.m
--- deep_profiler/measurements.m	24 Aug 2010 00:01:47 -0000	1.19
+++ deep_profiler/measurements.m	26 Aug 2010 06:21:36 -0000
@@ -224,6 +224,10 @@
     = parallel_exec_metrics.
 
 %-----------------------------------------------------------------------------%
+
+:- pred weighted_average(list(float)::in, list(float)::in, float::out) is det.
+
+%-----------------------------------------------------------------------------%
 %-----------------------------------------------------------------------------%
 
 :- implementation.
@@ -887,6 +891,21 @@ pem_get_par_overheads(pem_additional(Lef
 
 %----------------------------------------------------------------------------%
 
+weighted_average(Weights, Values, Average) :-
+    list.foldl2_corresponding(
+        (pred(Value::in, Weight::in, Sum0::in, Sum::out,
+                WeightSum0::in, WeightSum::out) is det :-
+            Sum = Sum0 + (Value * Weight),
+            WeightSum = WeightSum0 + Weight
+        ), Values, Weights, 0.0, Total, 0.0, TotalWeight),
+    ( abs(TotalWeight) < epsilon ->
+        Average = 0.0
+    ;
+        Average = Total / TotalWeight
+    ).
+
+%----------------------------------------------------------------------------%
+
 :- func this_file = string.
 
 this_file = "measurements.m".
Index: deep_profiler/old_html_format.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/old_html_format.m,v
retrieving revision 1.5
diff -u -p -b -r1.5 old_html_format.m
--- deep_profiler/old_html_format.m	2 Oct 2009 04:37:57 -0000	1.5
+++ deep_profiler/old_html_format.m	26 Aug 2010 06:21:36 -0000
@@ -409,9 +409,12 @@ command_relevant_toggles(deep_cmd_dump_c
 command_relevant_toggles(deep_cmd_dump_call_site_dynamic(_)) = [].
 command_relevant_toggles(deep_cmd_dump_clique(_)) = [].
 command_relevant_toggles(deep_cmd_dump_proc_var_use(_)) = [].
-command_relevant_toggles(deep_cmd_procrep_coverage(_)) = [] :-
-    error("unexpected command in command_relevant_toggles").
-command_relevant_toggles(deep_cmd_module_getter_setters(_)) = [] :-
+command_relevant_toggles(Cmd) = [] :-
+    ( Cmd = deep_cmd_procrep_coverage(_)
+    ; Cmd = deep_cmd_module_getter_setters(_)
+    ; Cmd = deep_cmd_clique_recursive_costs(_)
+    ; Cmd = deep_cmd_recursion_types_frequency
+    ),
     error("unexpected command in command_relevant_toggles").
 
 :- func footer_field_toggle(cmd, preferences, deep) = string.
Index: deep_profiler/old_query.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/old_query.m,v
retrieving revision 1.5
diff -u -p -b -r1.5 old_query.m
--- deep_profiler/old_query.m	2 Oct 2009 04:37:57 -0000	1.5
+++ deep_profiler/old_query.m	26 Aug 2010 06:21:36 -0000
@@ -148,6 +148,12 @@ old_exec(deep_cmd_dump_proc_var_use(_), 
 old_exec(deep_cmd_module_getter_setters(_), _, _, HTML, !IO) :-
     HTML = "old_query.m: " ++
         "deep_cmd_module_getter_setters is unsupported by old_exec\n". 
+old_exec(deep_cmd_clique_recursive_costs(_), _, _, HTML, !IO) :-
+    HTML = "old_query.m: " ++
+        "deep_cmd_clique_recursive_costs is unsupported by old_exec\n".
+old_exec(deep_cmd_recursion_types_frequency, _, _, HTML, !IO) :-
+    HTML = "old_query.m: " ++
+        "deep_cmd_recursion_types_frequency is unsupported by old_exec\n".
 
 %-----------------------------------------------------------------------------%
 
Index: deep_profiler/profile.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/profile.m,v
retrieving revision 1.28
diff -u -p -b -r1.28 profile.m
--- deep_profiler/profile.m	2 Oct 2009 04:37:57 -0000	1.28
+++ deep_profiler/profile.m	26 Aug 2010 06:21:36 -0000
@@ -488,7 +488,7 @@
     %
 :- pred deep_get_progrep_det(deep::in, prog_rep::out) is det.
 
-:- pred deep_get_progrep(deep::in, prog_rep::out) is semidet.
+:- pred deep_get_maybe_progrep(deep::in, maybe_error(prog_rep)::out) is det.
 
 %-----------------------------------------------------------------------------%
 %-----------------------------------------------------------------------------%
@@ -1003,19 +1003,27 @@ root_own_info(Deep) = RootOwn :-
 %-----------------------------------------------------------------------------%
 
 deep_get_progrep_det(Deep, ProgRep) :-
-    ( deep_get_progrep(Deep, ProgRepPrime) ->
-        ProgRep = ProgRepPrime
+    deep_get_maybe_progrep(Deep, MaybeProgRep),
+    (
+        MaybeProgRep = ok(ProgRep)
     ;
-        error(this_file ++ "Could not open Deep.procrep")
+        MaybeProgRep = error(Error),
+        error(this_file ++ Error)
     ).
 
-deep_get_progrep(Deep, ProgRep) :-
-    Deep ^ procrep_file = yes(MaybeProgRep1),
+deep_get_maybe_progrep(Deep, MaybeProgRep) :-
+    MaybeProgRep0 = Deep ^ procrep_file,
     (
-        MaybeProgRep1 = ok(ProgRep)
+        MaybeProgRep0 = no,
+        MaybeProgRep = error("There is no readable " ++
+            "procedure representation information file.")
+    ;
+        MaybeProgRep0 = yes(error(Error)),
+        MaybeProgRep = error("Error reading procedure representation " ++
+            "information file: " ++ Error)
     ;
-        MaybeProgRep1 = error(Error),
-        error(this_file ++ Error)
+        MaybeProgRep0 = yes(ok(ProgRep)),
+        MaybeProgRep = ok(ProgRep)
     ).
 
 %-----------------------------------------------------------------------------%
Index: deep_profiler/query.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/query.m,v
retrieving revision 1.35
diff -u -p -b -r1.35 query.m
--- deep_profiler/query.m	2 Oct 2009 04:37:57 -0000	1.35
+++ deep_profiler/query.m	26 Aug 2010 06:21:36 -0000
@@ -88,6 +88,17 @@
     ;       deep_cmd_clique(
                 cmd_clique_clique_id            :: clique_ptr
             )
+
+                % Construct formulas for calculating the costs of the recursive
+                % calls within this clique.
+                %
+    ;       deep_cmd_clique_recursive_costs(
+                cmd_crc_clique_id               :: clique_ptr
+            )
+                % Generate a frequency table about how often each recursion
+                % type occurs in the program.
+                %
+    ;       deep_cmd_recursion_types_frequency
     ;       deep_cmd_proc(
                 cmd_proc_proc_id                :: proc_static_ptr
             )
@@ -185,6 +196,11 @@
                 % The max number of ancestors to display.
                 pref_anc            :: maybe(int),
 
+                % The max number of proc statics to display for each recursion
+                % type group in the recursion type frequency report.
+                pref_proc_statics_per_rec_type 
+                                    :: int,
+
                 % Whether pages should summarize at higher order call sites.
                 pref_summarize      :: summarize_ho_call_sites,
 
@@ -319,6 +335,7 @@
 :- func default_box_tables = box_tables.
 :- func default_colour_column_groups = colour_column_groups.
 :- func default_ancestor_limit = maybe(int).
+:- func default_proc_statics_per_rec_type_limit = int.
 :- func default_summarize_ho_call_sites = summarize_ho_call_sites.
 :- func default_order_criteria = order_criteria.
 :- func default_cost_kind = cost_kind.
@@ -432,6 +449,8 @@ exec(Cmd, Prefs, Deep, HTMLStr, !IO) :-
         ( Cmd = deep_cmd_procrep_coverage(_)
         ; Cmd = deep_cmd_module_getter_setters(_)
         ; Cmd = deep_cmd_dump_proc_var_use(_)
+        ; Cmd = deep_cmd_clique_recursive_costs(_)
+        ; Cmd = deep_cmd_recursion_types_frequency
         ),
         new_exec(Cmd, Prefs, Deep, HTMLStr, !IO)
     ).
@@ -442,11 +461,29 @@ exec(Cmd, Prefs, Deep, HTMLStr, !IO) :-
     io::di, io::uo) is det.
 
 new_exec(Cmd, Prefs, Deep, HTMLStr, !IO) :-
-    create_report(Cmd, Deep, Report),
+    ( slow_cmd(Cmd) ->
+        create_and_memoize_report(Cmd, Deep, Report)
+    ;
+        create_report(Cmd, Deep, Report)
+    ),
     Display = report_to_display(Deep, Prefs, Report),
     HTML = htmlize_display(Deep, Prefs, Display),
     HTMLStr = html_to_string(HTML).
 
+    % slow_cmd(Cmd) is slow for any command that is slow and is probably going
+    % to be executed more than once.
+    %
+:- pred slow_cmd(cmd::in) is semidet.
+
+slow_cmd(deep_cmd_recursion_types_frequency).
+
+:- pragma memo(create_and_memoize_report(in, in, out), 
+    [specified([value, addr, output])]).
+:- pred create_and_memoize_report(cmd::in, deep::in, deep_report::out) is det.
+
+create_and_memoize_report(Cmd, Deep, Report) :-
+    create_report(Cmd, Deep, Report).
+
 %-----------------------------------------------------------------------------%
 
     % Display times only if the profile was derived from a run that ran for
@@ -482,6 +519,7 @@ default_preferences(Deep) =
         default_box_tables,
         default_colour_column_groups,
         default_ancestor_limit,
+        default_proc_statics_per_rec_type_limit,
         default_summarize_ho_call_sites,
         default_order_criteria,
         default_contour_exclusion,
@@ -507,6 +545,7 @@ all_fields = fields(port, ticks_and_time
 default_box_tables = box_tables.
 default_colour_column_groups = colour_column_groups.
 default_ancestor_limit = yes(5).
+default_proc_statics_per_rec_type_limit = 20.
 default_summarize_ho_call_sites = do_not_summarize_ho_call_sites.
 default_order_criteria = by_context.
 default_cost_kind = cost_callseqs.
@@ -564,10 +603,19 @@ cmd_to_string(Cmd) = CmdStr :-
                 [s(cmd_str_root), c(cmd_separator_char), s("no")])
         )
     ;
+        (
         Cmd = deep_cmd_clique(CliquePtr),
+            Name = cmd_str_clique
+        ;
+            Cmd = deep_cmd_clique_recursive_costs(CliquePtr),
+            Name = cmd_str_clique_recursive_costs
+        ),
         CliquePtr = clique_ptr(CliqueNum),
         CmdStr = string.format("%s%c%d",
-            [s(cmd_str_clique), c(cmd_separator_char), i(CliqueNum)])
+            [s(Name), c(cmd_separator_char), i(CliqueNum)])
+    ;
+        Cmd = deep_cmd_recursion_types_frequency,
+        CmdStr = cmd_str_recursion_types_frequency
     ;
         Cmd = deep_cmd_proc(PSPtr),
         PSPtr = proc_static_ptr(PSI),
@@ -651,7 +699,8 @@ cmd_to_string(Cmd) = CmdStr :-
 
 preferences_to_string(Pref) = PrefStr :-
     Pref = preferences(Fields, Box, Colour, MaybeAncestorLimit,
-        SummarizeHoCallSites, Order, Contour, Time, ModuleQual, InactiveItems),
+        ProcStaticsPerRecTypeLimit, SummarizeHoCallSites, Order, Contour,
+        Time, ModuleQual, InactiveItems),
     (
         MaybeAncestorLimit = yes(AncestorLimit),
         MaybeAncestorLimitStr =
@@ -660,11 +709,12 @@ preferences_to_string(Pref) = PrefStr :-
         MaybeAncestorLimit = no,
         MaybeAncestorLimitStr = "no"
     ),
-    PrefStr = string.format("%s%c%s%c%s%c%s%c%s%c%s%c%s%c%s%c%s%c%s",
+    PrefStr = string.format("%s%c%s%c%s%c%s%c%d%c%s%c%s%c%s%c%s%c%s%c%s",
         [s(fields_to_string(Fields)),
         c(pref_separator_char), s(box_to_string(Box)),
         c(pref_separator_char), s(colour_scheme_to_string(Colour)),
         c(pref_separator_char), s(MaybeAncestorLimitStr),
+        c(pref_separator_char), i(ProcStaticsPerRecTypeLimit),
         c(pref_separator_char), s(summarize_to_string(SummarizeHoCallSites)),
         c(pref_separator_char), s(order_criteria_to_string(Order)),
         c(pref_separator_char), s(contour_exclusion_to_string(Contour)),
@@ -706,6 +756,18 @@ string_to_maybe_cmd(QueryString) = Maybe
         Cmd = deep_cmd_clique(CliquePtr),
         MaybeCmd = yes(Cmd)
     ;
+        Pieces = [cmd_str_clique_recursive_costs, CliqueNumStr],
+        string.to_int(CliqueNumStr, CliqueNum)
+    ->
+        CliquePtr = clique_ptr(CliqueNum),
+        Cmd = deep_cmd_clique_recursive_costs(CliquePtr),
+        MaybeCmd = yes(Cmd)
+    ;
+        Pieces = [cmd_str_recursion_types_frequency]
+    ->
+        Cmd = deep_cmd_recursion_types_frequency,
+        MaybeCmd = yes(Cmd)
+    ;
         Pieces = [cmd_str_proc, PSIStr],
         string.to_int(PSIStr, PSI)
     ->
@@ -826,7 +888,8 @@ string_to_maybe_cmd(QueryString) = Maybe
 string_to_maybe_pref(QueryString) = MaybePreferences :-
     split(QueryString, pref_separator_char, Pieces),
     (
-        Pieces = [FieldsStr, BoxStr, ColourStr, MaybeAncestorLimitStr,
+        Pieces = [FieldsStr, BoxStr, ColourStr,
+            MaybeAncestorLimitStr, ProcStaticsPerRecTypeLimitStr, 
             SummarizeHoCallSitesStr, OrderStr, ContourStr, TimeStr,
             ModuleQualStr, InactiveItemsStr],
         string_to_fields(FieldsStr, Fields),
@@ -839,6 +902,8 @@ string_to_maybe_pref(QueryString) = Mayb
         ;
             fail
         ),
+        string.to_int(ProcStaticsPerRecTypeLimitStr,
+            ProcStaticsPerRecTypeLimit),
         string_to_summarize(SummarizeHoCallSitesStr, SummarizeHoCallSites),
         string_to_order_criteria(OrderStr, Order),
         string_to_contour_exclusion(ContourStr, Contour),
@@ -847,8 +912,8 @@ string_to_maybe_pref(QueryString) = Mayb
         string_to_inactive_items(InactiveItemsStr, InactiveItems)
     ->
         Preferences = preferences(Fields, Box, Colour, MaybeAncestorLimit,
-            SummarizeHoCallSites, Order, Contour, Time, ModuleQual,
-            InactiveItems),
+            ProcStaticsPerRecTypeLimit, SummarizeHoCallSites, Order, Contour,
+            Time, ModuleQual, InactiveItems),
         MaybePreferences = yes(Preferences)
     ;
         MaybePreferences = no
@@ -1256,6 +1321,12 @@ cmd_str_root = "root".
 :- func cmd_str_clique = string.
 cmd_str_clique = "clique".
 
+:- func cmd_str_clique_recursive_costs = string.
+cmd_str_clique_recursive_costs = "clique_rc".
+        
+:- func cmd_str_recursion_types_frequency = string.
+cmd_str_recursion_types_frequency = "recursion_type_freq".
+
 :- func cmd_str_proc = string.
 cmd_str_proc = "proc".
 
Index: deep_profiler/report.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/report.m,v
retrieving revision 1.21
diff -u -p -b -r1.21 report.m
--- deep_profiler/report.m	2 Oct 2009 04:37:57 -0000	1.21
+++ deep_profiler/report.m	26 Aug 2010 06:21:36 -0000
@@ -34,6 +34,7 @@
 :- import_module list.
 :- import_module map.
 :- import_module maybe.
+:- import_module set.
 :- import_module string.
 :- import_module unit.
 
@@ -49,6 +50,12 @@
     ;       report_clique(
                 maybe_error(clique_report)
             )
+    ;       report_clique_recursion_costs(
+                maybe_error(clique_recursion_report)
+            )
+    ;       report_recursion_types_frequency(
+                maybe_error(recursion_types_frequency_report)
+            )
     ;       report_program_modules(
                 maybe_error(program_modules_report)
             )
@@ -197,6 +204,88 @@
                 ccsr_callee_perfs           :: list(perf_row_data(clique_desc))
             ).
 
+:- type clique_recursion_report
+    --->    clique_recursion_report(
+                % The clique the report is for.
+                crr_clique_ptr              :: clique_ptr,
+
+                % The type of recursion in this clique.
+                crr_recursion_type          :: recursion_type,
+
+                % The number of procuedures in the clique.
+                crr_num_procs               :: int
+            ).
+
+:- type recursion_type
+    --->    rt_not_recursive
+    ;       rt_single(
+                rts_base                    :: recursion_level_report,
+                rts_recursive               :: recursion_level_report
+            )
+    ;       rt_divide_and_conquer(
+                rtdsc_base                  :: recursion_level_report,
+                rtdsc_recursive             :: recursion_level_report
+            )
+    ;       rt_mutual_recursion(
+                rtml_num_procs              :: int
+            )
+    ;       rt_other(
+                rto_all_levels              :: list(recursion_level_report)
+            )
+    ;       rt_errors(
+                rte_errors                  :: list(string)
+            ).
+
+:- type recursion_level_report
+    --->    recursion_level_report(
+                rlr_level                       :: int,
+                rlr_calls                       :: int,
+                rlr_prob                        :: probability,
+                rlr_non_rec_calls_cost          :: float,
+                rlr_rec_calls_ex_chld_cost      :: float
+            ).
+ 
+:- type recursion_types_frequency_report
+    --->    recursion_types_frequency_report(
+                rtfr_histogram                  :: recursion_type_histogram
+            ).
+
+:- type recursion_type_histogram == 
+    map(recursion_type_simple, recursion_type_freq_data). 
+
+:- type recursion_type_simple
+    --->    rts_not_recursive
+    ;       rts_single
+    ;       rts_divide_and_conquer
+    ;       rts_mutual_recursion(
+                rtsmr_num_procs                 :: int
+            )
+    ;       rts_other(
+                rtso_levels                     :: set(int)
+            )
+    ;       rts_error(
+                rtse_error                      :: string
+            )
+    ;       rts_total_error_instances.
+
+:- type recursion_type_freq_data
+    --->    recursion_type_freq_data(
+                rtfd_freq                       :: int,
+                rtfd_percent                    :: percent,
+                rtfd_maybe_summary              :: maybe(perf_row_data(unit)),
+                rtfd_entry_procs                :: recursion_type_proc_map
+            ).
+
+:- type recursion_type_proc_map ==
+    map(proc_static_ptr, recursion_type_proc_freq_data).
+
+:- type recursion_type_proc_freq_data
+    --->    recursion_type_proc_freq_data(
+                rtpfd_freq                      :: int,
+                rtpfd_percent                   :: percent,
+                rtpfd_summary                   :: perf_row_data(proc_desc)
+            ).
+
 :- type program_modules_report
     --->    program_modules_report(
                 % Summary information about all the modules of the program.
@@ -554,5 +643,41 @@
             ).
 
 %-----------------------------------------------------------------------------%
+%
+% Predicates to make common actions on these types easier.
+%
+
+:- pred proc_label_from_proc_desc(deep::in, proc_desc::in,
+    string_proc_label::out) is det.
+    
+    % foldl(add_call_site_report_to_map(CallSiteReports, map.init,
+    %   CallSitesMap).
+    %
+    % Build a map from goal paths to call site reports.
+    %
+:- pred add_call_site_report_to_map(clique_call_site_report::in, 
+    map(goal_path, clique_call_site_report)::in, 
+    map(goal_path, clique_call_site_report)::out) is det.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module svmap.
+
+%----------------------------------------------------------------------------%
+
+proc_label_from_proc_desc(Deep, ProcDesc, ProcLabel) :-
+    PSPtr = ProcDesc ^ pdesc_ps_ptr,
+    deep_lookup_proc_statics(Deep, PSPtr, ProcStatic),
+    ProcLabel = ProcStatic ^ ps_id.
+
+add_call_site_report_to_map(CallSite, !Map) :-
+    GoalPath = CallSite ^ ccsr_call_site_summary ^ perf_row_subject 
+        ^ csdesc_goal_path,
+    svmap.det_insert(GoalPath, CallSite, !Map).
+
+%-----------------------------------------------------------------------------%
 :- end_module report.
 %-----------------------------------------------------------------------------%
Index: deep_profiler/var_use_analysis.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/var_use_analysis.m,v
retrieving revision 1.2
diff -u -p -b -r1.2 var_use_analysis.m
--- deep_profiler/var_use_analysis.m	5 Nov 2008 23:10:02 -0000	1.2
+++ deep_profiler/var_use_analysis.m	26 Aug 2010 06:21:36 -0000
@@ -77,6 +77,7 @@
 
 :- import_module coverage.
 :- import_module create_report.
+:- import_module measurements.
 :- import_module program_representation_utils.
 
 :- import_module float.
@@ -652,21 +653,6 @@ ite_var_first_use(GoalPath, Cond, Then, 
         )
     ).
 
-:- pred weighted_average(list(float)::in, list(float)::in, float::out) is det.
-
-weighted_average(Weights, Values, Average) :-
-    list.foldl2_corresponding(
-        (pred(Value::in, Weight::in, Sum0::in, Sum::out,
-                WeightSum0::in, WeightSum::out) is det :-
-            Sum = Sum0 + (Value * Weight),
-            WeightSum = WeightSum0 + Weight
-        ), Values, Weights, 0.0, Total, 0.0, TotalWeight),
-    ( abs(TotalWeight) < epsilon ->
-        Average = 0.0
-    ;
-        Average = Total / TotalWeight
-    ).
-
 :- pred ffu_to_float(float::in, found_first_use::in, float::out) is det.
 
 ffu_to_float(Default, have_not_found_first_use, Default).
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 489 bytes
Desc: Digital signature
URL: <http://lists.mercurylang.org/archives/reviews/attachments/20100826/ac30521e/attachment.sig>


More information about the reviews mailing list