[m-rev.] for post-commit review: improve deep profiler usability

Zoltan Somogyi zs at csse.unimelb.edu.au
Fri Oct 2 14:34:37 AEST 2009


For review by anyone. The most interesting thing is the additional
documentation on the deep profiler in the user guide. Paul, you should
add to it some documentation of coverage profiling.

Zoltan.

Fix some usability problems in the deep profiler.

deep_profiler/exclude.m:
	Provide a better, more explicit representation of the contour exclusion
	file. This representation allows the application of contour exclusion
	even if the contour exclusion file contains some unrecognized module
	names. This is useful, because some modules of the Mercury standard
	library, such as profiling_builtin and mer_std, are unrecognized.
	They are unrecognized because either they export no predicates,
	or because deep profiling is turned off for them.

deep_profiler/query.m:
	Make a provision for controlling the number of a procedure's callers
	displayed at a time.

deep_profiler/create_report.m:
	Take advantage of the new capabilities listed above.

deep_profiler/display_report.m:
	When listing a procedure's callers, print any warning messages
	generated at startup about recognized modules.

	When listing a procedure's callers, list the application of contour
	exclusion among the links only if contour exclusion is in fact
	applicable.

	When listing a procedure's callers, allow the user to control how
	many callers are shown at a time. Make the default bunch size 20,
	instead of previous hardwired (and too small) 5.

	Fix some counterintuitive behavior. When clicking on one of the fields
	in the header of a table, in all but one of the displayed reports, this
	sorts the data being displayed on the selected column. The exception
	was top_procs, in which it changed the criterion on which the top_procs
	were selected, and thus changed the procedures being displayed as well.
	This diff eliminates the exception. The links in the header cells now
	do the expected thing, obeying the law of least astonishment. The
	criterion for selected what procs are top can still be changed using
	the already existing (and previously mostly redundant) links below
	the table.

deep_profiler/startup.m:
	Look for contour exclusion files not in Deep.data.contour, but in
	Deep.contour, since this is more consistent with what we do for other
	auxiliary files, like Data.procrep.

	Make the reading of the exclusion file and the program representation
	file part of the normal startup process, instead of setting their
	fields to dummies and filling them in with real values later.

	If there is a program representation file, make sure that the names
	of the modules in it are not unrecognized by contour exclusion.

	Fix a minor old bug: don't count the time spent reading Deep.contour
	as part of the propagation pass.

	Print meaningful messages (to the log file in /var/tmp) about
	any problems in reading Deep.contour files.

deep_profiler/apply_exclusion.m:
deep_profiler/old_html_format.m:
deep_profiler/old_query.m:
deep_profiler/profile.m:
deep_profiler/report.m:
	Conform to the changes above.

doc/user_guide.texi:
	Document contour exclusion, and to make this possible, expand
	considerably the documentation of the deep profiler as a whole.

doc/Mmakefile:
	Ensure that invocations of latex cannot hang waiting for user input.

cvs diff: Diffing .
cvs diff: Diffing analysis
cvs diff: Diffing bindist
cvs diff: Diffing boehm_gc
cvs diff: Diffing boehm_gc/Mac_files
cvs diff: Diffing boehm_gc/cord
cvs diff: Diffing boehm_gc/cord/private
cvs diff: Diffing boehm_gc/doc
cvs diff: Diffing boehm_gc/include
cvs diff: Diffing boehm_gc/include/private
cvs diff: Diffing boehm_gc/libatomic_ops-1.2
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/doc
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops/sysdeps
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops/sysdeps/gcc
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops/sysdeps/hpc
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops/sysdeps/ibmc
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops/sysdeps/icc
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops/sysdeps/msftc
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops/sysdeps/sunc
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/tests
cvs diff: Diffing boehm_gc/tests
cvs diff: Diffing boehm_gc/windows-untested
cvs diff: Diffing boehm_gc/windows-untested/vc60
cvs diff: Diffing boehm_gc/windows-untested/vc70
cvs diff: Diffing boehm_gc/windows-untested/vc71
cvs diff: Diffing browser
cvs diff: Diffing bytecode
cvs diff: Diffing compiler
cvs diff: Diffing compiler/notes
cvs diff: Diffing debian
cvs diff: Diffing debian/patches
cvs diff: Diffing deep_profiler
Index: deep_profiler/apply_exclusion.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/apply_exclusion.m,v
retrieving revision 1.1
diff -u -b -r1.1 apply_exclusion.m
--- deep_profiler/apply_exclusion.m	25 Aug 2008 07:19:39 -0000	1.1
+++ deep_profiler/apply_exclusion.m	1 Oct 2009 06:11:39 -0000
@@ -41,7 +41,7 @@
 
 :- func pair_self(call_site_dynamic_ptr) = pair(call_site_dynamic_ptr).
 
-:- func pair_contour(deep, exclude_file, call_site_dynamic_ptr)
+:- func pair_contour(deep, excluded_modules, call_site_dynamic_ptr)
     = pair(call_site_dynamic_ptr).
 
 %-----------------------------------------------------------------------------%
Index: deep_profiler/create_report.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/create_report.m,v
retrieving revision 1.18
diff -u -b -r1.18 create_report.m
--- deep_profiler/create_report.m	8 Sep 2009 02:37:14 -0000	1.18
+++ deep_profiler/create_report.m	1 Oct 2009 07:20:26 -0000
@@ -60,6 +60,7 @@
 :- implementation.
 
 :- import_module apply_exclusion.
+:- import_module exclude.
 :- import_module coverage.
 :- import_module mdbcomp.
 :- import_module mdbcomp.program_representation.
@@ -147,9 +148,10 @@
         create_proc_report(Deep, PSPtr, MaybeProcReport),
         Report = report_proc(MaybeProcReport)
     ;
-        Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, Contour),
+        Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum,
+            CallersPerBunch, Contour),
         create_proc_callers_report(Deep, PSPtr, CallerGroups, BunchNum,
-            Contour, MaybeProcCallersReport),
+            CallersPerBunch, Contour, MaybeProcCallersReport),
         Report = report_proc_callers(MaybeProcCallersReport)
     ;
         Cmd = deep_cmd_dump_proc_static(PSPtr),
@@ -929,46 +931,61 @@
 %
 
 :- pred create_proc_callers_report(deep::in, proc_static_ptr::in,
-    caller_groups::in, int::in, contour_exclusion::in,
+    caller_groups::in, int::in, int::in, contour_exclusion::in,
     maybe_error(proc_callers_report)::out) is det.
 
-create_proc_callers_report(Deep, PSPtr, CallerGroups, BunchNum, Contour,
-        MaybeProcCallersReport) :-
+create_proc_callers_report(Deep, PSPtr, CallerGroups, BunchNum,
+        CallersPerBunch, Contour, MaybeProcCallersReport) :-
     ( valid_proc_static_ptr(Deep, PSPtr) ->
         ProcDesc = describe_proc(Deep, PSPtr),
 
-        deep_lookup_proc_callers(Deep, PSPtr, CallerCSDPtrs),
-        MaybeMaybeExcludeFile = Deep ^ exclude_contour_file,
+        deep_lookup_proc_callers(Deep, PSPtr, CallerCSDPtrs0),
         (
             Contour = do_not_apply_contour_exclusion,
-            CallerCSDPtrPairs = list.map(pair_self, CallerCSDPtrs),
-            Messages = []
+            CallerCSDPtrPairs0 = list.map(pair_self, CallerCSDPtrs0),
+            MaybeCallerCSDPtrPairs = ok(CallerCSDPtrPairs0),
+            MaybeWarnMessage = no
         ;
             Contour = apply_contour_exclusion,
+            ExcludeFile = Deep ^ exclude_contour_file,
+            ExcludeFile = exclude_file(ExcludeFileName, ExcludeContents),
             (
-                MaybeMaybeExcludeFile = no,
+                ExcludeContents = no_exclude_file,
                 % There is no contour exclusion file, so do the same as for
                 % do_not_apply_contour_exclusion, but add a message to the
                 % report.
-                CallerCSDPtrPairs = list.map(pair_self, CallerCSDPtrs),
-                Message = "There is no readable contour exclusion file.",
-                Messages = [Message]
-            ;
-                MaybeMaybeExcludeFile = yes(MaybeExcludeFile),
-                (
-                    MaybeExcludeFile = ok(ExcludeSpec),
-                    CallerCSDPtrPairs = list.map(
-                        pair_contour(Deep, ExcludeSpec), CallerCSDPtrs),
-                    Messages = []
-                ;
-                    MaybeExcludeFile = error(ErrorMsg),
-                    CallerCSDPtrPairs = list.map(pair_self, CallerCSDPtrs),
-                    MessagePrefix = "The contour exclusion file has an error:",
-                    Messages = [MessagePrefix, ErrorMsg]
+                string.format("Could not read contour exclusion file `%s'.",
+                    [s(ExcludeFileName)], ErrorMessage0),
+                MaybeCallerCSDPtrPairs = error(ErrorMessage0),
+                MaybeWarnMessage = no
+            ;
+                ExcludeContents = unreadable_exclude_file(ErrorMsg),
+                string.format(
+                    "The contour exclusion file `%s' has an error: %s.",
+                    [s(ExcludeFileName), s(ErrorMsg)], ErrorMessage0),
+                MaybeCallerCSDPtrPairs = error(ErrorMessage0),
+                MaybeWarnMessage = no
+            ;
+                ExcludeContents = readable_exclude_file(ExcludeModules,
+                    MaybeWarnMsg),
+                CallerCSDPtrPairs0 = list.map(
+                    pair_contour(Deep, ExcludeModules), CallerCSDPtrs0),
+                MaybeCallerCSDPtrPairs = ok(CallerCSDPtrPairs0),
+                (
+                    MaybeWarnMsg = no,
+                    MaybeWarnMessage = no
+                ;
+                    MaybeWarnMsg = yes(WarnMessage),
+                    MaybeWarnMessage = yes(WarnMessage)
                 )
             )
         ),
         (
+            MaybeCallerCSDPtrPairs = error(ErrorMessage),
+            MaybeProcCallersReport = error(ErrorMessage)
+        ;
+            MaybeCallerCSDPtrPairs = ok(CallerCSDPtrPairs),
+            (
             CallerGroups = group_by_call_site,
             CallSiteCallerGroups = group_csds_by_call_site(Deep,
                 CallerCSDPtrPairs),
@@ -1001,10 +1018,10 @@
                 CliqueCallerGroups),
             Callers = proc_caller_cliques(ProcCallerCliques)
         ),
-
         ProcCallersReport = proc_callers_report(ProcDesc, Callers,
-            BunchNum, Contour, Messages),
+                BunchNum, CallersPerBunch, Contour, MaybeWarnMessage),
         MaybeProcCallersReport = ok(ProcCallersReport)
+        )
     ;
         MaybeProcCallersReport = error("invalid proc_static index")
     ).
Index: deep_profiler/display_report.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/display_report.m,v
retrieving revision 1.22
diff -u -b -r1.22 display_report.m
--- deep_profiler/display_report.m	8 Sep 2009 02:37:14 -0000	1.22
+++ deep_profiler/display_report.m	1 Oct 2009 09:52:22 -0000
@@ -36,6 +36,7 @@
 :- implementation.
 
 :- import_module coverage.
+:- import_module exclude.
 :- import_module mdbcomp.
 :- import_module mdbcomp.program_representation.
 :- import_module measurement_units.
@@ -123,7 +124,7 @@
         Report = report_proc(MaybeProcReport),
         (
             MaybeProcReport = ok(ProcReport),
-            display_report_proc(Prefs, ProcReport, Display)
+            display_report_proc(Deep, Prefs, ProcReport, Display)
         ;
             MaybeProcReport = error(Msg),
             Display = display(no, [display_heading(Msg)])
@@ -142,7 +143,7 @@
         Report = report_proc_callers(MaybeProcCallersReport),
         (
             MaybeProcCallersReport = ok(ProcCallersReport),
-            display_report_proc_callers(Prefs, ProcCallersReport, Display)
+            display_report_proc_callers(Deep, Prefs, ProcCallersReport, Display)
         ;
             MaybeProcCallersReport = error(Msg),
             Display = display(no, [display_heading(Msg)])
@@ -470,7 +471,6 @@
         display_paragraph_break, FormatControls,
         display_paragraph_break, MenuRestartQuitControls]).
 
-
 :- func clique_proc_report_module_name(clique_proc_report) = string.
 
 clique_proc_report_module_name(CliqueProc) =
@@ -947,27 +947,30 @@
     display::out) is det.
 
 display_report_top_procs(Prefs, TopProcsReport, Display) :-
-    TopProcsReport = top_procs_report(Ordering, TopProcs),
+    TopProcsReport = top_procs_report(Ordering, TopProcRowDatas),
     Ordering = report_ordering(DisplayLimit, CostKind, InclDesc, Scope),
     Desc = cost_criteria_to_description(CostKind, InclDesc, Scope),
     Title = "Top procedures " ++ Desc,
 
     % Build the table of the top procedures.
-    MakeHeaderData = top_procs_order_criteria_header_data(DisplayLimit,
-        CostKind, InclDesc, Scope),
+    Cmd = deep_cmd_top_procs(DisplayLimit, CostKind, InclDesc, Scope),
+    MakeHeaderData = override_order_criteria_header_data(Cmd),
     maybe_ranked_proc_table_header(Prefs, ranked, MakeHeaderData,
         NumColumns, Header),
+
+    MaybeCurModuleName = no,
     ModuleQual = Prefs ^ pref_module_qual,
+    sort_proc_desc_rows_by_preferences(MaybeCurModuleName, ModuleQual,
+        Prefs, TopProcRowDatas, OrderedTopProcRowDatas),
     list.map_foldl(
         maybe_ranked_subject_perf_table_row(Prefs, ranked,
             total_columns_meaningful,
             proc_desc_to_proc_name_cell(no, ModuleQual)),
-        TopProcs, Rows, 1, _),
+        OrderedTopProcRowDatas, Rows, 1, _),
     Table = table(table_class_box_if_pref, NumColumns, yes(Header), Rows),
     DisplayTable = display_table(Table),
 
     % Build controls at the bottom of the page.
-    Cmd = deep_cmd_top_procs(DisplayLimit, CostKind, InclDesc, Scope),
     TopProcsControls = top_procs_controls(Prefs,
         DisplayLimit, CostKind, InclDesc, Scope),
     FieldControls = field_controls(Prefs, Cmd),
@@ -1025,10 +1028,10 @@
 
     % Create a display_report structure for a proc report.
     %
-:- pred display_report_proc(preferences::in, proc_report::in,
+:- pred display_report_proc(deep::in, preferences::in, proc_report::in,
     display::out) is det.
 
-display_report_proc(Prefs, ProcReport, Display) :-
+display_report_proc(Deep, Prefs, ProcReport, Display) :-
     ProcReport = proc_report(ProcSummaryRowData, CallSitePerfs0),
     ProcDesc = ProcSummaryRowData ^ perf_row_subject,
     RefinedName = ProcDesc ^ pdesc_q_refined_name,
@@ -1083,8 +1086,9 @@
     DisplayTable = display_table(Table),
 
     % Build the controls at the bottom of the page.
-    ProcCallersControls = proc_callers_group_controls(Prefs, Cmd,
-        PSPtr, group_by_call_site, Prefs ^ pref_contour),
+    ProcCallersControls = proc_callers_group_controls(Deep, Prefs, Cmd,
+        PSPtr, group_by_call_site, default_callers_per_bunch,
+        Prefs ^ pref_contour),
     SummarizeControls = summarize_controls(Prefs, Cmd),
     InactiveCallSitesControls = inactive_call_site_controls(Prefs, Cmd),
     ModuleQualControls = module_qual_controls(Prefs, Cmd),
@@ -1106,6 +1110,10 @@
         display_paragraph_break, ProcReportControls,
         display_paragraph_break, MenuRestartQuitControls]).
 
+:- func default_callers_per_bunch = int.
+
+default_callers_per_bunch = 20.
+
 :- func report_proc_call_site(maybe(string), module_qual, preferences,
     call_site_perf) = list(table_row).
 
@@ -1195,12 +1203,22 @@
 
     % Create a display_report structure for a proc_callers report.
     %
-:- pred display_report_proc_callers(preferences::in, proc_callers_report::in,
-    display::out) is det.
+:- pred display_report_proc_callers(deep::in, preferences::in,
+    proc_callers_report::in, display::out) is det.
 
-display_report_proc_callers(Prefs0, ProcCallersReport, Display) :-
+display_report_proc_callers(Deep, Prefs0, ProcCallersReport, Display) :-
     ProcCallersReport = proc_callers_report(ProcDesc, CallerRowDatas,
-        BunchNum, ContourExcl, _ContourErrorMessages),
+        BunchNum, BunchSize, ContourExcl, MaybeContourWarnMessage),
+
+    (
+        MaybeContourWarnMessage = no,
+        WarnItems = []
+    ;
+        MaybeContourWarnMessage = yes(ContourWarnMessage),
+        ContourWarnItem = display_text(ContourWarnMessage),
+        WarnItems = [display_paragraph_break, ContourWarnItem]
+    ),
+
     RefinedName = ProcDesc ^ pdesc_q_refined_name,
 
     MaybeCurModuleName = yes(ProcDesc ^ pdesc_module_name),
@@ -1214,12 +1232,12 @@
     (
         CallerRowDatas = proc_caller_call_sites(CallSiteRowDatas),
         CallerGroups = group_by_call_site,
-        Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum,
+        Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, BunchSize,
             ContourExcl),
         Title = "The call sites calling " ++ RefinedName,
         sort_call_site_desc_rows_by_preferences(MaybeCurModuleName, ModuleQual,
             Prefs, CallSiteRowDatas, SortedCallSiteRowDatas),
-        select_displayed_rows(SortedCallSiteRowDatas, BunchNum,
+        select_displayed_rows(SortedCallSiteRowDatas, BunchNum, BunchSize,
             DisplayedCallSiteRowDatas, TotalNumRows, FirstRowNum, LastRowNum,
             DisplayedBunchNum, MaybeFirstAndLastBunchNum),
         list.map_foldl(
@@ -1241,12 +1259,12 @@
     ;
         CallerRowDatas = proc_caller_procedures(ProcRowDatas),
         CallerGroups = group_by_proc,
-        Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum,
+        Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, BunchSize,
             ContourExcl),
         Title = "The procedures calling " ++ RefinedName,
         sort_proc_desc_rows_by_preferences(MaybeCurModuleName, ModuleQual,
             Prefs, ProcRowDatas, SortedProcRowDatas),
-        select_displayed_rows(SortedProcRowDatas, BunchNum,
+        select_displayed_rows(SortedProcRowDatas, BunchNum, BunchSize,
             DisplayedProcRowDatas, TotalNumRows, FirstRowNum, LastRowNum,
             DisplayedBunchNum, MaybeFirstAndLastBunchNum),
         list.map_foldl(
@@ -1268,12 +1286,12 @@
     ;
         CallerRowDatas = proc_caller_modules(ModuleRowDatas),
         CallerGroups = group_by_module,
-        Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum,
+        Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, BunchSize,
             ContourExcl),
         Title = "The modules calling " ++ RefinedName,
         sort_module_name_rows_by_preferences(Prefs, ModuleRowDatas,
             SortedModuleRowDatas),
-        select_displayed_rows(SortedModuleRowDatas, BunchNum,
+        select_displayed_rows(SortedModuleRowDatas, BunchNum, BunchSize,
             DisplayedModuleRowDatas, TotalNumRows, FirstRowNum, LastRowNum,
             DisplayedBunchNum, MaybeFirstAndLastBunchNum),
         list.map_foldl(display_caller_module(Prefs),
@@ -1289,12 +1307,12 @@
     ;
         CallerRowDatas = proc_caller_cliques(CliqueRowDatas),
         CallerGroups = group_by_clique,
-        Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum,
+        Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, BunchSize,
             ContourExcl),
         Title = "The cliques calling " ++ RefinedName,
         sort_clique_rows_by_preferences(Prefs, CliqueRowDatas,
             SortedCliqueRowDatas),
-        select_displayed_rows(SortedCliqueRowDatas, BunchNum,
+        select_displayed_rows(SortedCliqueRowDatas, BunchNum, BunchSize,
             DisplayedCliqueRowDatas, TotalNumRows, FirstRowNum, LastRowNum,
             DisplayedBunchNum, MaybeFirstAndLastBunchNum),
         list.map_foldl(
@@ -1343,28 +1361,28 @@
             ( BunchNum > FirstBunchNum ->
                 BunchControlsFirst = [make_proc_callers_link(Prefs,
                     "First group", PSPtr, CallerGroups, FirstBunchNum,
-                    ContourExcl)]
+                    BunchSize, ContourExcl)]
             ;
                 BunchControlsFirst = []
             ),
             ( BunchNum - 1 > FirstBunchNum ->
                 BunchControlsPrev = [make_proc_callers_link(Prefs,
                     "Previous group", PSPtr, CallerGroups, BunchNum - 1,
-                    ContourExcl)]
+                    BunchSize, ContourExcl)]
             ;
                 BunchControlsPrev = []
             ),
             ( BunchNum + 1 < LastBunchNum ->
                 BunchControlsNext = [make_proc_callers_link(Prefs,
                     "Next group", PSPtr, CallerGroups, BunchNum + 1,
-                    ContourExcl)]
+                    BunchSize, ContourExcl)]
             ;
                 BunchControlsNext = []
             ),
             ( BunchNum < LastBunchNum ->
                 BunchControlsLast = [make_proc_callers_link(Prefs,
                     "Last group", PSPtr, CallerGroups, LastBunchNum,
-                    ContourExcl)]
+                    BunchSize, ContourExcl)]
             ;
                 BunchControlsLast = []
             ),
@@ -1375,36 +1393,122 @@
                     yes("Show other groups:"), BunchControlList)]
         ),
 
+        (
+            BunchSize > 5,
+            HalfBunchSize = BunchSize / 2,
+            HalfBunchSize \= 10,
+            HalfBunchSize \= 20,
+            HalfBunchSize \= 50,
+            HalfBunchSize \= 100
+        ->
+            BunchSizeControlPairsHalf = [(BunchSize / 2) -
+                make_proc_callers_link(Prefs,
+                    "Halve group size", PSPtr, CallerGroups, BunchNum * 2,
+                    BunchSize / 2, ContourExcl)]
+        ;
+            BunchSizeControlPairsHalf = []
+        ),
+        (
+            DoubleBunchSize = BunchSize * 2,
+            DoubleBunchSize \= 10,
+            DoubleBunchSize \= 20,
+            DoubleBunchSize \= 50,
+            DoubleBunchSize \= 100
+        ->
+            BunchSizeControlPairsDouble = [(BunchSize * 2) -
+                make_proc_callers_link(Prefs,
+                    "Double group size", PSPtr, CallerGroups, BunchNum / 2,
+                    BunchSize * 2, ContourExcl)]
+        ;
+            BunchSizeControlPairsDouble = []
+        ),
+        (
+            TotalNumRows > 10,
+            BunchSize \= 10
+        ->
+            BunchSizeControlPairs10 = [10 -
+                make_proc_callers_link(Prefs,
+                    "Set group size to 10", PSPtr, CallerGroups,
+                    (BunchNum * BunchSize) / 10, 10, ContourExcl)]
+        ;
+            BunchSizeControlPairs10 = []
+        ),
+        (
+            TotalNumRows > 20,
+            BunchSize \= 20
+        ->
+            BunchSizeControlPairs20 = [20 -
+                make_proc_callers_link(Prefs,
+                    "Set group size to 20", PSPtr, CallerGroups,
+                    (BunchNum * BunchSize) / 20, 20, ContourExcl)]
+        ;
+            BunchSizeControlPairs20 = []
+        ),
+        (
+            TotalNumRows > 50,
+            BunchSize \= 50
+        ->
+            BunchSizeControlPairs50 = [50 -
+                make_proc_callers_link(Prefs,
+                    "Set group size to 50", PSPtr, CallerGroups,
+                    (BunchNum * BunchSize) / 50, 50, ContourExcl)]
+        ;
+            BunchSizeControlPairs50 = []
+        ),
+        (
+            TotalNumRows > 100,
+            BunchSize \= 100
+        ->
+            BunchSizeControlPairs100 = [100 -
+                make_proc_callers_link(Prefs,
+                    "Set group size to 100", PSPtr, CallerGroups,
+                    (BunchNum * BunchSize) / 100, 100, ContourExcl)]
+        ;
+            BunchSizeControlPairs100 = []
+        ),
+        BunchSizeControlPairs =
+            BunchSizeControlPairsHalf ++ BunchSizeControlPairsDouble ++
+            BunchSizeControlPairs10 ++ BunchSizeControlPairs20 ++
+            BunchSizeControlPairs50 ++ BunchSizeControlPairs100,
+        list.sort(BunchSizeControlPairs, SortedBunchSizeControlPairs),
+        assoc_list.values(SortedBunchSizeControlPairs, BunchSizeControlList),
+        BunchSizeControls = [display_paragraph_break,
+            display_list(list_class_horizontal_except_title,
+            yes("Change group size:"), BunchSizeControlList)],
+
         Table = table(table_class_box_if_pref, NumColumns, yes(Header), Rows),
         DisplayTable = display_table(Table),
 
         % Build the controls at the bottom of the page.
-        FirstCmd = deep_cmd_proc_callers(PSPtr, CallerGroups, 1, ContourExcl),
-        CallerGroupControls = proc_callers_group_controls(Prefs, FirstCmd,
-            PSPtr, CallerGroups, ContourExcl),
+        FirstCmd = deep_cmd_proc_callers(PSPtr, CallerGroups, 1, BunchSize,
+            ContourExcl),
+        CallerGroupControls = proc_callers_group_controls(Deep, Prefs,
+            FirstCmd, PSPtr, CallerGroups, BunchSize, ContourExcl),
         FieldControls = field_controls(Prefs, Cmd),
         FormatControls = format_controls(Prefs, Cmd),
         MenuRestartQuitControls = cmds_menu_restart_quit(yes(Prefs)),
 
         Display = display(yes(Title),
-            [display_text(Message),
-            display_paragraph_break, DisplayTable] ++
+            [display_text(Message)] ++
+            WarnItems ++
+            [display_paragraph_break, DisplayTable] ++
             BunchControls ++
+            BunchSizeControls ++
             [display_paragraph_break, CallerGroupControls,
             display_paragraph_break, FieldControls,
             display_paragraph_break, FormatControls,
             display_paragraph_break, MenuRestartQuitControls])
     ).
 
-:- pred select_displayed_rows(list(perf_row_data(T))::in, int::in,
+:- pred select_displayed_rows(list(perf_row_data(T))::in, int::in, int::in,
     list(perf_row_data(T))::out, int::out, int::out, int::out,
     int::out, maybe({int, int})::out) is det.
 
-select_displayed_rows(RowDatas, BunchNum, DisplayRowDatas,
+select_displayed_rows(RowDatas, BunchNum, BunchSize, DisplayRowDatas,
         TotalNumRows, FirstRowNum, LastRowNum,
         DisplayedBunchNum, MaybeFirstAndLastBunchNum) :-
     list.length(RowDatas, TotalNumRows),
-    NumRowsToDelete = (BunchNum - 1) * bunch_size,
+    NumRowsToDelete = (BunchNum - 1) * BunchSize,
     (
         list.drop(NumRowsToDelete, RowDatas, RemainingRowDatasPrime),
         RemainingRowDatasPrime = [_ | _]
@@ -1420,24 +1524,20 @@
         RemainingRowDatas = RowDatas,
         NumRemainingRows = TotalNumRows
     ),
-    ( NumRemainingRows > bunch_size ->
-        list.take_upto(bunch_size, RemainingRowDatas, DisplayRowDatas)
+    ( NumRemainingRows > BunchSize ->
+        list.take_upto(BunchSize, RemainingRowDatas, DisplayRowDatas)
     ;
         DisplayRowDatas = RemainingRowDatas
     ),
     LastRowNum = FirstRowNum - 1 + list.length(DisplayRowDatas),
     ( TotalNumRows > 0 ->
         FirstBunchNum = 1,
-        LastBunchNum = (TotalNumRows + bunch_size - 1) / bunch_size,
+        LastBunchNum = (TotalNumRows + BunchSize - 1) / BunchSize,
         MaybeFirstAndLastBunchNum = yes({FirstBunchNum, LastBunchNum})
     ;
         MaybeFirstAndLastBunchNum = no
     ).
 
-:- func bunch_size = int.
-
-bunch_size = 5.
-
 :- pred display_caller_call_site(maybe(string)::in, module_qual::in,
     preferences::in, perf_row_data(call_site_desc)::in, table_row::out,
     int::in, int::out) is det.
@@ -1523,11 +1623,12 @@
     Row = table_row(Cells).
 
 :- func make_proc_callers_link(preferences, string, proc_static_ptr,
-    caller_groups, int, contour_exclusion) = display_item.
+    caller_groups, int, int, contour_exclusion) = display_item.
 
-make_proc_callers_link(Prefs, Label, PSPtr, CallerGroups, BunchNum,
+make_proc_callers_link(Prefs, Label, PSPtr, CallerGroups, BunchNum, BunchSize,
         ContourExcl) = Item :-
-    Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, ContourExcl),
+    Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, BunchSize,
+        ContourExcl),
     Link = deep_link(Cmd, yes(Prefs), attr_str([], Label), link_class_control),
     Item = display_link(Link).
 
@@ -1916,35 +2017,6 @@
         TableData = td_l(Link)
     ).
 
-:- func top_procs_order_criteria_header_data(display_limit, cost_kind,
-    include_descendants, measurement_scope, preferences,
-    order_criteria, string) = table_data.
-
-top_procs_order_criteria_header_data(DisplayLimit0, CostKind0, InclDesc0,
-        Scope0, Prefs0, Criteria, Label) = TableData :-
-    Cmd0 = deep_cmd_top_procs(DisplayLimit0, CostKind0, InclDesc0, Scope0),
-    (
-        ( Criteria = by_context
-        ; Criteria = by_name
-        ),
-        TableData =
-            override_order_criteria_header_data(Cmd0, Prefs0, Criteria, Label)
-    ;
-        Criteria = by_cost(CostKind, InclDesc, Scope),
-        Cmd = deep_cmd_top_procs(DisplayLimit0, CostKind, InclDesc, Scope),
-        ( Cmd = Cmd0 ->
-            % Should we display a simple string to indicate that this link
-            % leads back to the same page, or would that be confusing?
-            Link = deep_link(Cmd, yes(Prefs0), attr_str([], Label),
-                link_class_link),
-            TableData = td_l(Link)
-        ;
-            Link = deep_link(Cmd, yes(Prefs0), attr_str([], Label),
-                link_class_link),
-            TableData = td_l(Link)
-        )
-    ).
-
 :- func dummy_order_criteria_header_data(preferences, order_criteria, string)
     = table_data.
 
@@ -2764,7 +2836,7 @@
 % The basic predicates for creating the controls at the bottoms of pages.
 %
 
-:- pred make_top_procs_cmd_items(preferences::in, display_limit::in,
+:- pred make_top_procs_cmd_items(preferences::in, bool::in, display_limit::in,
     cost_kind::in, include_descendants::in, measurement_scope::in,
     maybe(string)::in,
     assoc_list(string,
@@ -2773,15 +2845,16 @@
         in(list_skel(pair(ground, (pred(in, in, in, in, out) is det)))),
     display_item::out) is det.
 
-make_top_procs_cmd_items(Prefs, DisplayLimit, CostKind, InclDesc, Scope,
-        MaybeLabel, LabelsCmdMakers, Item) :-
+make_top_procs_cmd_items(Prefs, SetPrefs, DisplayLimit, CostKind, InclDesc,
+        Scope, MaybeLabel, LabelsCmdMakers, Item) :-
     list.map(
-        make_top_procs_cmd_item(Prefs, DisplayLimit, CostKind, InclDesc, Scope),
+        make_top_procs_cmd_item(Prefs, SetPrefs, DisplayLimit, CostKind,
+            InclDesc, Scope),
         LabelsCmdMakers, ControlItemLists),
     list.condense(ControlItemLists, ControlItems),
     Item = display_list(list_class_horizontal, MaybeLabel, ControlItems).
 
-:- pred make_top_procs_cmd_item(preferences::in, display_limit::in,
+:- pred make_top_procs_cmd_item(preferences::in, bool::in, display_limit::in,
     cost_kind::in, include_descendants::in, measurement_scope::in,
     pair(string,
         (pred(display_limit, cost_kind, include_descendants, measurement_scope,
@@ -2789,8 +2862,8 @@
         in(pair(ground, (pred(in, in, in, in, out) is det))),
     list(display_item)::out) is det.
 
-make_top_procs_cmd_item(Prefs, DisplayLimit, CostKind, InclDesc, Scope,
-        Label - CmdMaker, Items) :-
+make_top_procs_cmd_item(Prefs0, SetPrefs, DisplayLimit, CostKind, InclDesc,
+        Scope, Label - CmdMaker, Items) :-
     Cmd0 = deep_cmd_top_procs(DisplayLimit, CostKind, InclDesc, Scope),
     CmdMaker(DisplayLimit, CostKind, InclDesc, Scope, Cmd),
     ( Cmd = Cmd0 ->
@@ -2801,6 +2874,24 @@
         % Item = display_pseudo_link(PseudoLink),
         % Items = [Item]
     ;
+        (
+            SetPrefs = no,
+            Prefs = Prefs0
+        ;
+            SetPrefs = yes,
+            % By default, sort the top procedures by the same criteria
+            % by which they were selected. Users can override this choice
+            % later if they wish.
+            (
+                Cmd = deep_cmd_top_procs(_, CmdCostKind, CmdInclDesc,
+                    CmdScope)
+            ->
+                Prefs = Prefs0 ^ pref_criteria :=
+                    by_cost(CmdCostKind, CmdInclDesc, CmdScope)
+            ;
+                Prefs = Prefs0
+            )
+        ),
         Link = deep_link(Cmd, yes(Prefs), attr_str([], Label),
             link_class_control),
         Item = display_link(Link),
@@ -2808,34 +2899,34 @@
     ).
 
 :- pred make_first_proc_callers_cmds_item(preferences::in, cmd::in,
-    proc_static_ptr::in, caller_groups::in, contour_exclusion::in,
+    proc_static_ptr::in, caller_groups::in, int::in, contour_exclusion::in,
     maybe(string)::in,
     assoc_list(string,
-        (pred(proc_static_ptr, caller_groups, contour_exclusion, cmd)))::
-        in(list_skel(pair(ground, (pred(in, in, in, out) is det)))),
+        (pred(proc_static_ptr, caller_groups, int, contour_exclusion, cmd)))::
+        in(list_skel(pair(ground, (pred(in, in, in, in, out) is det)))),
     display_item::out) is det.
 
-make_first_proc_callers_cmds_item(Prefs, Cmd, PSPtr, CallerGroups, ContourExcl,
-        MaybeLabel, LabelsCmdMakers, Item) :-
+make_first_proc_callers_cmds_item(Prefs, Cmd, PSPtr, CallerGroups, BunchSize,
+        ContourExcl, MaybeLabel, LabelsCmdMakers, Item) :-
     list.map(
         make_first_proc_callers_cmd_item(Prefs, Cmd, PSPtr, CallerGroups,
-            ContourExcl),
+            BunchSize, ContourExcl),
         LabelsCmdMakers, ControlItemLists),
     list.condense(ControlItemLists, ControlItems),
     Item = display_list(list_class_horizontal, MaybeLabel, ControlItems).
 
 :- pred make_first_proc_callers_cmd_item(preferences::in, cmd::in,
-    proc_static_ptr::in, caller_groups::in, contour_exclusion::in,
+    proc_static_ptr::in, caller_groups::in, int::in, contour_exclusion::in,
     pair(string,
-        (pred(proc_static_ptr, caller_groups, contour_exclusion, cmd)))::
-        in(pair(ground, (pred(in, in, in, out) is det))),
+        (pred(proc_static_ptr, caller_groups, int, contour_exclusion, cmd)))::
+        in(pair(ground, (pred(in, in, in, in, out) is det))),
     list(display_item)::out) is det.
 
-make_first_proc_callers_cmd_item(Prefs, Cmd0, PSPtr, CallerGroups, ContourExcl,
-        Label - CmdMaker, Items) :-
+make_first_proc_callers_cmd_item(Prefs, Cmd0, PSPtr, CallerGroups, BunchSize,
+        ContourExcl, Label - CmdMaker, Items) :-
     % When we just to a different kind of grouping or toggle contour exclusion,
     % we always go to the first bunch of the resulting rows.
-    CmdMaker(PSPtr, CallerGroups, ContourExcl, Cmd),
+    CmdMaker(PSPtr, CallerGroups, BunchSize, ContourExcl, Cmd),
     ( Cmd = Cmd0 ->
         Items = []
         % We could use this code instead.
@@ -2892,14 +2983,14 @@
 
 top_procs_controls(Prefs, DisplayLimit, CostKind, InclDesc, Scope) =
         ControlsItem :-
-    make_top_procs_cmd_items(Prefs, DisplayLimit, CostKind, InclDesc, Scope,
-        no, top_procs_limit_toggles, LimitControls),
-    make_top_procs_cmd_items(Prefs, DisplayLimit, CostKind, InclDesc, Scope,
-        no, top_procs_sort_toggles, SortControls),
-    make_top_procs_cmd_items(Prefs, DisplayLimit, CostKind, InclDesc, Scope,
-        no, top_procs_incl_desc_toggles, InclDescControls),
-    make_top_procs_cmd_items(Prefs, DisplayLimit, CostKind, InclDesc, Scope,
-        no, top_procs_scope_toggles, ScopeControls),
+    make_top_procs_cmd_items(Prefs, no, DisplayLimit, CostKind, InclDesc,
+        Scope, no, top_procs_limit_toggles, LimitControls),
+    make_top_procs_cmd_items(Prefs, yes, DisplayLimit, CostKind, InclDesc,
+        Scope, no, top_procs_sort_toggles, SortControls),
+    make_top_procs_cmd_items(Prefs, no, DisplayLimit, CostKind, InclDesc,
+        Scope, no, top_procs_incl_desc_toggles, InclDescControls),
+    make_top_procs_cmd_items(Prefs, no, DisplayLimit, CostKind, InclDesc,
+        Scope, no, top_procs_scope_toggles, ScopeControls),
     ControlsItem = display_list(list_class_vertical_no_bullets,
         yes("Toggle sorting criteria:"),
         [LimitControls, SortControls, InclDescControls, ScopeControls]).
@@ -3012,18 +3103,32 @@
 % Control how the proc command displays the procedure's callers.
 %
 
-:- func proc_callers_group_controls(preferences, cmd, proc_static_ptr,
-    caller_groups, contour_exclusion) = display_item.
+:- func proc_callers_group_controls(deep, preferences, cmd, proc_static_ptr,
+    caller_groups, int, contour_exclusion) = display_item.
 
-proc_callers_group_controls(Prefs, Cmd, PSPtr, CallerGroups, ContourExcl) =
-        ControlsItem :-
+proc_callers_group_controls(Deep, Prefs, Cmd, PSPtr, CallerGroups,
+        BunchSize, ContourExcl) = ControlsItem :-
     make_first_proc_callers_cmds_item(Prefs, Cmd, PSPtr, CallerGroups,
-        ContourExcl, no, proc_callers_group_toggles, GroupControls),
-    ( Cmd = deep_cmd_proc_callers(_, _, _, _) ->
+        BunchSize, ContourExcl, no, proc_callers_group_toggles, GroupControls),
+    ExcludeFile = Deep ^ exclude_contour_file,
+    ( Cmd = deep_cmd_proc_callers(_, _, _, _, _) ->
+        ExcludeFile = exclude_file(ExcludeFileName, ExcludeContents),
+        (
+            ExcludeContents = no_exclude_file,
+            List = [GroupControls]
+        ;
+            ExcludeContents = unreadable_exclude_file(ErrorMsg),
+            ContourExclMsg = "Cannot apply contour exclusion:\n"
+                ++ ExcludeFileName ++ ": " ++ ErrorMsg,
+            ContourExclItem = display_text(ContourExclMsg),
+            List = [GroupControls, ContourExclItem]
+        ;
+            ExcludeContents = readable_exclude_file(_, _),
         make_first_proc_callers_cmds_item(Prefs, Cmd, PSPtr, CallerGroups,
-            ContourExcl, no, proc_callers_contour_excl_toggles,
+                BunchSize, ContourExcl, no, proc_callers_contour_excl_toggles,
             ContourExclControls),
         List = [GroupControls, ContourExclControls]
+        )
     ;
         List = [GroupControls]
     ),
@@ -3032,8 +3137,8 @@
 
 :- func proc_callers_group_toggles =
     (assoc_list(string,
-        (pred(proc_static_ptr, caller_groups, contour_exclusion, cmd)))::
-        out(list_skel(pair(ground, (pred(in, in, in, out) is det)))))
+        (pred(proc_static_ptr, caller_groups, int, contour_exclusion, cmd)))::
+        out(list_skel(pair(ground, (pred(in, in, in, in, out) is det)))))
     is det.
 
 proc_callers_group_toggles = [
@@ -3049,8 +3154,8 @@
 
 :- func proc_callers_contour_excl_toggles =
     (assoc_list(string,
-        (pred(proc_static_ptr, caller_groups, contour_exclusion, cmd)))::
-        out(list_skel(pair(ground, (pred(in, in, in, out) is det)))))
+        (pred(proc_static_ptr, caller_groups, int, contour_exclusion, cmd)))::
+        out(list_skel(pair(ground, (pred(in, in, in, in, out) is det)))))
     is det.
 
 proc_callers_contour_excl_toggles = [
@@ -3061,19 +3166,21 @@
 ].
 
 :- pred set_proc_callers_caller_groups(caller_groups::in, proc_static_ptr::in,
-    caller_groups::in, contour_exclusion::in, cmd::out) is det.
+    caller_groups::in, int::in, contour_exclusion::in, cmd::out) is det.
 
-set_proc_callers_caller_groups(CallerGroups, PSPtr, _CallerGroups, ContourExcl,
-        Cmd) :-
-    Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, 1, ContourExcl).
+set_proc_callers_caller_groups(CallerGroups, PSPtr, _CallerGroups, BunchSize,
+        ContourExcl, Cmd) :-
+    Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, 1, BunchSize,
+        ContourExcl).
 
 :- pred set_proc_callers_contour_excl(contour_exclusion::in,
-    proc_static_ptr::in, caller_groups::in, contour_exclusion::in, cmd::out)
-    is det.
+    proc_static_ptr::in, caller_groups::in, int::in, contour_exclusion::in,
+    cmd::out) is det.
 
-set_proc_callers_contour_excl(ContourExcl, PSPtr, CallerGroups, _ContourExcl,
-        Cmd) :-
-    Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, 1, ContourExcl).
+set_proc_callers_contour_excl(ContourExcl, PSPtr, CallerGroups, BunchSize,
+        _ContourExcl, Cmd) :-
+    Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, 1, BunchSize,
+        ContourExcl).
 
 %-----------------------------------------------------------------------------%
 %
Index: deep_profiler/exclude.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/exclude.m,v
retrieving revision 1.12
diff -u -b -r1.12 exclude.m
--- deep_profiler/exclude.m	25 Aug 2008 07:19:39 -0000	1.12
+++ deep_profiler/exclude.m	1 Oct 2009 09:22:51 -0000
@@ -46,16 +46,33 @@
 :- import_module profile.
 
 :- import_module io.
+:- import_module map.
 :- import_module maybe.
 
 %-----------------------------------------------------------------------------%
 
-:- type exclude_file.
+:- type exclude_file
+    --->    exclude_file(
+                exclude_filename        :: string,
+                exclude_file_contents   :: exclude_contents
+            ).
+
+:- type exclude_contents
+    --->    no_exclude_file
+    ;       unreadable_exclude_file(
+                exclude_syntax_error    :: string
+            )
+    ;       readable_exclude_file(
+                exclude_specs           :: excluded_modules,
+                exclude_maybe_error     :: maybe(string)
+            ).
+
+:- type excluded_modules.
 
-:- pred read_exclude_file(string::in, deep::in,
-    maybe(maybe_error(exclude_file))::out, io::di, io::uo) is det.
+:- pred read_exclude_file(string::in, map(string, module_data)::in,
+    exclude_file::out, io::di, io::uo) is det.
 
-:- func apply_contour_exclusion(deep, exclude_file, call_site_dynamic_ptr)
+:- func apply_contour_exclusion(deep, excluded_modules, call_site_dynamic_ptr)
     = call_site_dynamic_ptr.
 
 %-----------------------------------------------------------------------------%
@@ -66,13 +83,12 @@
 :- import_module bool.
 :- import_module char.
 :- import_module list.
-:- import_module map.
 :- import_module set.
 :- import_module string.
 
 %-----------------------------------------------------------------------------%
 
-:- type exclude_file == set(exclude_spec).
+:- type excluded_modules == set(exclude_spec).
 
 :- type exclude_spec
     --->    exclude_spec(
@@ -90,7 +106,7 @@
 
 %-----------------------------------------------------------------------------%
 
-read_exclude_file(FileName, Deep, MaybeMaybeExcludeFile, !IO) :-
+read_exclude_file(FileName, ModuleDataMap, ExcludeFile, !IO) :-
     io.open_input(FileName, MaybeStream, !IO),
     (
         MaybeStream = ok(InputStream),
@@ -98,18 +114,19 @@
         io.close_input(InputStream, !IO),
         (
             MaybeSpecs = ok(Specs),
-            validate_exclude_lines(FileName, Specs, Deep, MaybeExcludeFile),
-            MaybeMaybeExcludeFile = yes(MaybeExcludeFile)
+            validate_exclude_lines(FileName, Specs, ModuleDataMap,
+                ExcludeContents)
         ;
             MaybeSpecs = error(Msg),
-            MaybeMaybeExcludeFile = yes(error(Msg))
+            ExcludeContents = unreadable_exclude_file(Msg)
         )
     ;
         MaybeStream = error(_),
         % If we cannot open the file, simply return `no' as an indication
         % that there is no exclude file there, at least not a readable one.
-        MaybeMaybeExcludeFile = no
-    ).
+        ExcludeContents = no_exclude_file
+    ),
+    ExcludeFile = exclude_file(FileName, ExcludeContents).
 
 :- pred read_exclude_lines(string::in, io.input_stream::in,
     list(exclude_spec)::in, maybe_error(list(exclude_spec))::out,
@@ -152,28 +169,48 @@
         Res = error(Msg)
     ).
 
-:- pred validate_exclude_lines(string::in, list(exclude_spec)::in, deep::in,
-    maybe_error(set(exclude_spec))::out) is det.
+:- pred validate_exclude_lines(string::in, list(exclude_spec)::in,
+    map(string, module_data)::in, exclude_contents::out) is det.
 
-validate_exclude_lines(FileName, Specs, Deep, Res) :-
-    list.filter(has_valid_module_name(Deep), Specs, ValidSpecs, InvalidSpecs),
+validate_exclude_lines(FileName, Specs, ModuleDataMap, ExcludeContents) :-
+    list.filter(has_valid_module_name(ModuleDataMap), Specs,
+        ValidSpecs, InvalidSpecs),
+    set.list_to_set(ValidSpecs, ModuleSpecs),
     (
         InvalidSpecs = [],
-        set.list_to_set(ValidSpecs, ModuleSpecSet),
-        Res = ok(ModuleSpecSet)
+        MaybeErrorMsg = no,
+        ExcludeContents = readable_exclude_file(ModuleSpecs, MaybeErrorMsg)
     ;
         InvalidSpecs = [_ | _],
+        (
+            ValidSpecs = [_ | _],
         InvalidModuleNames = list.map(spec_to_module_name, InvalidSpecs),
         BadNames = string.join_list(", ", InvalidModuleNames),
-        Msg = string.format("file %s contains bad module names: %s",
+            Msg1 = string.format(
+                "Warning: %s contains unrecognized module names: %s.",
             [s(FileName), s(BadNames)]),
-        Res = error(Msg)
+            Msg2 = "These modules either do not exist " ++
+                "or have no deep profiled procedures.",
+            Msg = Msg1 ++ Msg2,
+            MaybeErrorMsg = yes(Msg),
+            ExcludeContents = readable_exclude_file(ModuleSpecs, MaybeErrorMsg)
+        ;
+            ValidSpecs = [],
+            Msg1 = string.format(
+                "Error: file %s contains only unrecognized module names.",
+                [s(FileName)]),
+            Msg2 = "These modules either do not exist " ++
+                "or have no deep profiled procedures.",
+            Msg = Msg1 ++ Msg2,
+            ExcludeContents = unreadable_exclude_file(Msg)
+        )
     ).
 
-:- pred has_valid_module_name(deep::in, exclude_spec::in) is semidet.
+:- pred has_valid_module_name(map(string, module_data)::in, exclude_spec::in)
+    is semidet.
 
-has_valid_module_name(Deep, Spec) :-
-    map.search(Deep ^ module_data, spec_to_module_name(Spec), _).
+has_valid_module_name(ModuleDataMap, Spec) :-
+    map.search(ModuleDataMap, spec_to_module_name(Spec), _).
 
 :- func spec_to_module_name(exclude_spec) = string.
 
Index: deep_profiler/old_html_format.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/old_html_format.m,v
retrieving revision 1.4
diff -u -b -r1.4 old_html_format.m
--- deep_profiler/old_html_format.m	8 Sep 2009 02:37:15 -0000	1.4
+++ deep_profiler/old_html_format.m	1 Oct 2009 07:21:53 -0000
@@ -392,7 +392,7 @@
 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(_, _, _, _)) =
+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) =
Index: deep_profiler/old_query.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/old_query.m,v
retrieving revision 1.4
diff -u -b -r1.4 old_query.m
--- deep_profiler/old_query.m	8 Sep 2009 02:37:15 -0000	1.4
+++ deep_profiler/old_query.m	1 Oct 2009 07:24:20 -0000
@@ -38,6 +38,7 @@
 :- implementation.
 
 :- import_module apply_exclusion.
+:- import_module exclude.
 :- import_module html_format.       % for escape_break_html_string
 :- import_module measurements.
 :- import_module old_html_format.
@@ -111,7 +112,8 @@
             page_footer(Cmd, Pref, Deep)
     ).
 old_exec(Cmd, Pref0, Deep, HTML, !IO) :-
-    Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, Contour),
+    Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum,
+        _CallersPerBunch, Contour),
     Pref = Pref0 ^ pref_contour := Contour,
     ( valid_proc_static_ptr(Deep, PSPtr) ->
         generate_proc_callers_page(Cmd, PSPtr, CallerGroups, BunchNum,
@@ -1464,35 +1466,33 @@
         MaybeErrorMsg = no
     ;
         PrefContour = apply_contour_exclusion,
-        MaybeMaybeExcludeFile = Deep ^ exclude_contour_file,
+        ExcludeFile = Deep ^ exclude_contour_file,
+        ExcludeFile = exclude_file(_ExcludeFileName, ExcludeContents),
         (
-            MaybeMaybeExcludeFile = no,
+            ExcludeContents = no_exclude_file,
             % 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),
+            ExcludeContents = unreadable_exclude_file(ErrorMsg),
                 MaybeErrorMsg = yes(ErrorMsg ++ "\n<br>"),
                 CallerCSDPtrPairs = list.map(pair_self, CallerCSDPtrs)
-            )
+        ;
+            ExcludeContents = readable_exclude_file(ExcludedModules, _Warn),
+            CallerCSDPtrPairs = list.map(pair_contour(Deep, ExcludedModules),
+                CallerCSDPtrs),
+            MaybeErrorMsg = no
         )
     ),
     ProcName = proc_static_name(Deep, PSPtr),
-    CmdSite    = deep_cmd_proc_callers(PSPtr, group_by_call_site, 1,
+    CmdSite    = deep_cmd_proc_callers(PSPtr, group_by_call_site, 1, 100,
         PrefContour),
-    CmdProc    = deep_cmd_proc_callers(PSPtr, group_by_proc, 1,
+    CmdProc    = deep_cmd_proc_callers(PSPtr, group_by_proc, 1, 100,
         PrefContour),
-    CmdModule  = deep_cmd_proc_callers(PSPtr, group_by_module, 1,
+    CmdModule  = deep_cmd_proc_callers(PSPtr, group_by_module, 1, 100,
         PrefContour),
-    CmdClique  = deep_cmd_proc_callers(PSPtr, group_by_clique, 1,
+    CmdClique  = deep_cmd_proc_callers(PSPtr, group_by_clique, 1, 100,
         PrefContour),
     LinkSite   = "[Group callers by call site]",
     LinkProc   = "[Group callers by procedure]",
@@ -1590,7 +1590,8 @@
         DisplayedLines),
     HTML = string.append_list(DisplayedHTMLs),
     ( BunchNum > 1 ->
-        FirstCmd = deep_cmd_proc_callers(PSPtr, CallerGroups, 1, PrefContour),
+        FirstCmd = deep_cmd_proc_callers(PSPtr, CallerGroups, 1, 100,
+            PrefContour),
         FirstLink = "First group",
         FirstToggle =
             string.format("<A CLASS=""button"" HREF=""%s"">%s</A>\n",
@@ -1599,7 +1600,7 @@
         FirstToggle = ""
     ),
     ( BunchNum > 2 ->
-        PrevCmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum - 1,
+        PrevCmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum - 1, 100,
             PrefContour),
         PrevLink = "Previous group",
         PrevToggle =
@@ -1609,7 +1610,7 @@
         PrevToggle = ""
     ),
     ( NumLines > BunchNum * BunchSize ->
-        NextCmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum + 1,
+        NextCmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum + 1, 100,
             PrefContour),
         NextLink = "Next group",
         NextToggle =
@@ -1788,13 +1789,14 @@
 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),
+    Cmd1 = deep_cmd_proc_callers(PSPtr, group_by_call_site, 1, 100,
+        PrefContour),
     Msg2 = "[Parent procedures]",
-    Cmd2 = deep_cmd_proc_callers(PSPtr, group_by_proc, 1, PrefContour),
+    Cmd2 = deep_cmd_proc_callers(PSPtr, group_by_proc, 1, 100, PrefContour),
     Msg3 = "[Parent modules]",
-    Cmd3 = deep_cmd_proc_callers(PSPtr, group_by_module, 1, PrefContour),
+    Cmd3 = deep_cmd_proc_callers(PSPtr, group_by_module, 1, 100, PrefContour),
     Msg4 = "[Parent cliques]",
-    Cmd4 = deep_cmd_proc_callers(PSPtr, group_by_clique, 1, PrefContour),
+    Cmd4 = deep_cmd_proc_callers(PSPtr, group_by_clique, 1, 100, 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",
@@ -1837,7 +1839,8 @@
 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),
+    Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, 100,
+        PrefContour),
     Pref = Pref0 ^ pref_criteria := Criteria,
     URL = deep_cmd_pref_to_url(Pref, Deep, Cmd),
     Str = string.format("<A HREF=%s>%s</A>",
Index: deep_profiler/profile.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/profile.m,v
retrieving revision 1.27
diff -u -b -r1.27 profile.m
--- deep_profiler/profile.m	8 Sep 2009 02:37:15 -0000	1.27
+++ deep_profiler/profile.m	1 Oct 2009 05:56:40 -0000
@@ -124,7 +124,7 @@
                 % MaybeExcludeFile = ok(ExcludeFile). The other case is
                 % MaybeExcludeFile = error(ErrorMsg), which shows that the
                 % contour exclusion file was malformed.
-                exclude_contour_file    :: maybe(maybe_error(exclude_file)),
+                exclude_contour_file    :: exclude_file,
 
                 % If this field is `no', then there is no (readable) proc
                 % representation file. If this field is yes(MaybeProcRepFile),
Index: deep_profiler/query.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/query.m,v
retrieving revision 1.34
diff -u -b -r1.34 query.m
--- deep_profiler/query.m	8 Sep 2009 02:37:15 -0000	1.34
+++ deep_profiler/query.m	1 Oct 2009 07:19:16 -0000
@@ -95,6 +95,7 @@
                 cmd_pc_proc_id                  :: proc_static_ptr,
                 cmd_pc_called_groups            :: caller_groups,
                 cmd_pc_bunch_number             :: int,
+                cmd_pc_callers_per_bunch        :: int,
                 cmd_pc_contour_exclusion        :: contour_exclusion
             )
     ;       deep_cmd_program_modules
@@ -413,7 +414,7 @@
         ; Cmd = deep_cmd_module(_)
         ; Cmd = deep_cmd_top_procs(_, _, _, _)
         ; Cmd = deep_cmd_proc(_)
-        ; Cmd = deep_cmd_proc_callers(_, _, _, _)
+        ; Cmd = deep_cmd_proc_callers(_, _, _, _, _)
         ; Cmd = deep_cmd_dump_proc_static(_)
         ; Cmd = deep_cmd_dump_proc_dynamic(_)
         ; Cmd = deep_cmd_dump_call_site_static(_)
@@ -573,15 +574,17 @@
         CmdStr = string.format("%s%c%d",
             [s(cmd_str_proc), c(cmd_separator_char), i(PSI)])
     ;
-        Cmd = deep_cmd_proc_callers(PSPtr, GroupCallers, BunchNum, Contour),
+        Cmd = deep_cmd_proc_callers(PSPtr, GroupCallers, BunchNum,
+            CallersPerBunch, Contour),
         PSPtr = proc_static_ptr(PSI),
         GroupCallersStr = caller_groups_to_string(GroupCallers),
         ContourStr = contour_exclusion_to_string(Contour),
-        CmdStr = string.format("%s%c%d%c%s%c%d%c%s",
+        CmdStr = string.format("%s%c%d%c%s%c%d%c%d%c%s",
             [s(cmd_str_proc_callers),
             c(cmd_separator_char), i(PSI),
             c(cmd_separator_char), s(GroupCallersStr),
             c(cmd_separator_char), i(BunchNum),
+            c(cmd_separator_char), i(CallersPerBunch),
             c(cmd_separator_char), s(ContourStr)])
     ;
         Cmd = deep_cmd_program_modules,
@@ -711,14 +714,16 @@
         MaybeCmd = yes(Cmd)
     ;
         Pieces = [cmd_str_proc_callers, PSIStr, GroupCallersStr, BunchNumStr, 
-            ContourStr],
+            CallersPerBunchStr, ContourStr],
         string.to_int(PSIStr, PSI),
         string_to_caller_groups(GroupCallersStr, GroupCallers),
         string.to_int(BunchNumStr, BunchNum),
+        string.to_int(CallersPerBunchStr, CallersPerBunch),
         string_to_contour_exclusion(ContourStr, Contour)
     ->
         PSPtr = proc_static_ptr(PSI),
-        Cmd = deep_cmd_proc_callers(PSPtr, GroupCallers, BunchNum, Contour),
+        Cmd = deep_cmd_proc_callers(PSPtr, GroupCallers, BunchNum,
+            CallersPerBunch, Contour),
         MaybeCmd = yes(Cmd)
     ;
         Pieces = [cmd_str_program_modules]
Index: deep_profiler/report.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/report.m,v
retrieving revision 1.20
diff -u -b -r1.20 report.m
--- deep_profiler/report.m	8 Sep 2009 02:37:15 -0000	1.20
+++ deep_profiler/report.m	1 Oct 2009 07:21:36 -0000
@@ -320,13 +320,18 @@
                 % from the request.
                 pc_batch_number             :: int,
 
+                % How many callers per batch, also transmitted without
+                % processing from the request.
+                pc_callers_per_batch        :: int,
+
                 % Whether contour exclusion was applied in computing the
                 % pc_callers field.
                 pc_contour_exclusion        :: contour_exclusion,
 
                 % If contour exclusion was asked for, this field may contain
-                % the pieces of an error message.
-                pc_contour_error_messages   :: list(string)
+                % a warning message about nonfatal problems with the exclusion
+                % specification file.
+                pc_contour_warn_message     :: maybe(string)
             ).
 
 :- type proc_callers
Index: deep_profiler/startup.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/startup.m,v
retrieving revision 1.23
diff -u -b -r1.23 startup.m
--- deep_profiler/startup.m	3 Oct 2008 07:39:04 -0000	1.23
+++ deep_profiler/startup.m	1 Oct 2009 06:40:14 -0000
@@ -80,36 +80,7 @@
         DataFileResult = ok(InitDeep),
         startup(Machine, ScriptName, DataFileName,
             Canonical, MaybeOutputStream, DumpStages, DumpOptions,
-            InitDeep, Deep0, !IO),
-        ProgRepFileName = make_progrep_filename(DataFileName),
-        maybe_report_msg(MaybeOutputStream,
-            "% Reading program representation...\n", !IO),
-        read_prog_rep_file(ProgRepFileName, ProgRepResult, !IO),
-        (
-            ProgRepResult = ok(ProgRep),
-            maybe_report_msg(MaybeOutputStream,
-                "% Done.\n", !IO),
-            Deep = Deep0 ^ procrep_file := yes(ok(ProgRep))
-        ;
-            ProgRepResult = error(Error),
-            (
-                io.open_input(ProgRepFileName, OpenProgRepResult, !IO),
-                (
-                    OpenProgRepResult = ok(ProgRepStream),
-                    % The file exists, so the error message describes something
-                    % wrong with the file.
-                    io.close_input(ProgRepStream, !IO),
-                    ErrorMessage = io.error_message(Error),
-                    maybe_report_msg(MaybeOutputStream,
-                        "% Error: " ++ ErrorMessage ++ "\n", !IO),
-                    Deep = Deep0 ^ procrep_file := yes(error(ErrorMessage))
-                ;
-                    OpenProgRepResult = error(_),
-                    % The file does not exist.
-                    Deep = Deep0 ^ procrep_file := no
-                )
-            )
-        ),
+            InitDeep, Deep, !IO),
         Result = ok(Deep)
     ;
         DataFileResult = error(Error),
@@ -140,6 +111,7 @@
         "% Mapping static call sites to containing procedures...\n", !IO),
     array_foldl2_from_1(record_css_containers_module_procs, ProcStatics0,
         u(CallSiteStatics0), CallSiteStatics, map.init, ModuleProcs),
+    ModuleDataMap0 = map.map_values_only(initialize_module_data, ModuleProcs),
     maybe_report_msg(MaybeOutputStream,
         "% Done.\n", !IO),
     maybe_report_stats(MaybeOutputStream, !IO),
@@ -243,6 +215,71 @@
         "% Done.\n", !IO),
     maybe_report_stats(MaybeOutputStream, !IO),
 
+    ProgRepFileName = make_progrep_filename(DataFileName),
+    maybe_report_msg(MaybeOutputStream,
+        "% Reading program representation...\n", !IO),
+    read_prog_rep_file(ProgRepFileName, ProgRepResult, !IO),
+    (
+        ProgRepResult = ok(ProgRep),
+        maybe_report_msg(MaybeOutputStream,
+            "% Done.\n", !IO),
+        MaybeProcRepFile = yes(ok(ProgRep)),
+
+        ProgRep = prog_rep(ModuleMap),
+        map.keys(ModuleMap, ProgRepModules),
+        (
+            MaybeOutputStream = yes(OutputStream),
+            io.write(OutputStream, ProgRepModules, !IO),
+            io.nl(OutputStream, !IO)
+        ;
+            MaybeOutputStream = no
+        ),
+        list.foldl(ensure_module_has_module_data, ProgRepModules,
+            ModuleDataMap0, ModuleDataMap)
+    ;
+        ProgRepResult = error(Error),
+        (
+            io.open_input(ProgRepFileName, OpenProgRepResult, !IO),
+            (
+                OpenProgRepResult = ok(ProgRepStream),
+                % The file exists, so the error message describes something
+                % wrong with the file.
+                io.close_input(ProgRepStream, !IO),
+                ErrorMessage = io.error_message(Error),
+                maybe_report_msg(MaybeOutputStream,
+                    "% Error: " ++ ErrorMessage ++ "\n", !IO),
+                MaybeProcRepFile = yes(error(ErrorMessage)),
+                ModuleDataMap = ModuleDataMap0
+            ;
+                OpenProgRepResult = error(_),
+                % The file does not exist.
+                MaybeProcRepFile = no,
+                ModuleDataMap = ModuleDataMap0
+            )
+        )
+    ),
+
+    ContourFileName = contour_file_name(DataFileName),
+    string.format("%% Trying to read contour exclusion file `%s'...\n",
+        [s(ContourFileName)], TryMsg),
+    maybe_report_msg(MaybeOutputStream, TryMsg, !IO),
+    read_exclude_file(ContourFileName, ModuleDataMap, ExcludeFile, !IO),
+    ExcludeFile = exclude_file(_, ExcludeContents),
+    (
+        ExcludeContents = no_exclude_file,
+        maybe_report_msg(MaybeOutputStream,
+            "% Couldn't open file.\n", !IO)
+    ;
+        ExcludeContents = unreadable_exclude_file(ExcludeError),
+        string.format("%% File had unrecoverable errors:\n%% %s.\n",
+            [s(ExcludeError)], ExcludeErrorMsg),
+        maybe_report_msg(MaybeOutputStream, ExcludeErrorMsg, !IO)
+    ;
+        ExcludeContents = readable_exclude_file(_, _),
+        maybe_report_msg(MaybeOutputStream,
+            "% Done.\n", !IO)
+    ),
+
     maybe_report_msg(MaybeOutputStream,
         "% Propagating measurements up call graph...\n", !IO),
 
@@ -258,35 +295,21 @@
     array.init(NPDs, map.init, PDCompTable0),
     array.init(NCSDs, map.init, CSDCompTable0),
 
-    ModuleData = map.map_values(initialize_module_data, ModuleProcs),
-    % The field holding DummyMaybeExcludeFile is given its proper,
-    % non-dummy value a few calls below.
-    DummyMaybeExcludeFile = no,
-    % The field holding DummyMaybeProcRepFile is given its proper,
-    % non-dummy value outside this predicate.
-    DummyMaybeProcRepFile = no,
     !:Deep = deep(InitStats, Machine, ScriptName, DataFileName, Root,
         CallSiteDynamics, ProcDynamics, CallSiteStatics, ProcStatics,
         CliqueIndex, Cliques, CliqueParents, CliqueMaybeChildren,
         ProcCallers, CallSiteStaticMap, CallSiteCalls,
         PDOwn, PDDesc0, CSDDesc0,
         PSOwn0, PSDesc0, CSSOwn0, CSSDesc0,
-        PDCompTable0, CSDCompTable0, ModuleData,
-        DummyMaybeExcludeFile, DummyMaybeProcRepFile),
-
-    read_exclude_file(contour_file_name(DataFileName), !.Deep,
-        MaybeMaybeExcludeFile, !IO),
-    !Deep ^ exclude_contour_file := MaybeMaybeExcludeFile,
-
-    maybe_dump(DataFileName, DumpStages, 30,
-        dump_deep(!.Deep, DumpOptions), !IO),
+        PDCompTable0, CSDCompTable0, ModuleDataMap,
+        ExcludeFile, MaybeProcRepFile),
 
     array_foldl_from_1(propagate_to_clique, Cliques, !Deep),
     maybe_report_msg(MaybeOutputStream,
         "% Done.\n", !IO),
     maybe_report_stats(MaybeOutputStream, !IO),
 
-    maybe_dump(DataFileName, DumpStages, 40,
+    maybe_dump(DataFileName, DumpStages, 30,
         dump_deep(!.Deep, DumpOptions), !IO),
 
     maybe_report_msg(MaybeOutputStream,
@@ -298,24 +321,39 @@
         "% Done.\n", !IO),
     maybe_report_stats(MaybeOutputStream, !IO),
 
-    maybe_dump(DataFileName, DumpStages, 50,
+    maybe_dump(DataFileName, DumpStages, 40,
         dump_deep(!.Deep, DumpOptions), !IO).
 
 :- func contour_file_name(string) = string.
 
-contour_file_name(DataFileName) =
-    DataFileName ++ ".contour".
+contour_file_name(DataFileName) = ContourFileName :-
+    ( remove_suffix(DataFileName, ".data", BaseFileName) ->
+        ContourFileName = BaseFileName ++ ".contour"
+    ;
+        error("Couldn't remove suffix from deep file name: " ++ DataFileName)
+    ).
 
 :- pred count_quanta(int::in, call_site_dynamic::in, int::in, int::out) is det.
 
 count_quanta(_N, CSD, Quanta0, Quanta) :-
     Quanta = Quanta0 + quanta(CSD ^ csd_own_prof).
 
-:- func initialize_module_data(string, list(proc_static_ptr)) = module_data.
+:- func initialize_module_data(list(proc_static_ptr)) = module_data.
 
-initialize_module_data(_ModuleName, PSPtrs) =
+initialize_module_data(PSPtrs) =
     module_data(zero_own_prof_info, zero_inherit_prof_info, PSPtrs).
 
+:- pred ensure_module_has_module_data(string::in,
+    map(string, module_data)::in, map(string, module_data)::out) is det.
+
+ensure_module_has_module_data(Module, !ModuleDataMap) :-
+    ( map.search(!.ModuleDataMap, Module, _) ->
+        true
+    ;
+        Data = module_data(zero_own_prof_info, zero_inherit_prof_info, []),
+        svmap.det_insert(Module, Data, !ModuleDataMap)
+    ).
+
 :- pred maybe_dump(string::in, list(string)::in, int::in,
     pred(io, io)::in(pred(di, uo) is det), io::di, io::uo) is det.
 
@@ -744,12 +782,13 @@
 
 summarize_modules(Deep0, Deep) :-
     ModuleData0 = Deep0 ^ module_data,
-    ModuleData = map.map_values(summarize_module_costs(Deep0), ModuleData0),
+    ModuleData =
+        map.map_values_only(summarize_module_costs(Deep0), ModuleData0),
     Deep = Deep0 ^ module_data := ModuleData.
 
-:- func summarize_module_costs(deep, string, module_data) = module_data.
+:- func summarize_module_costs(deep, module_data) = module_data.
 
-summarize_module_costs(Deep, _ModuleName, ModuleData0) = ModuleData :-
+summarize_module_costs(Deep, ModuleData0) = ModuleData :-
     ModuleData0 = module_data(Own0, Desc0, PSPtrs),
     list.foldl2(accumulate_ps_costs(Deep), PSPtrs, Own0, Own, Desc0, Desc),
     ModuleData = module_data(Own, Desc, PSPtrs).
cvs diff: Diffing deep_profiler/notes
cvs diff: Diffing doc
Index: doc/Mmakefile
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/doc/Mmakefile,v
retrieving revision 1.52
diff -u -b -r1.52 Mmakefile
--- doc/Mmakefile	9 Mar 2008 09:39:24 -0000	1.52
+++ doc/Mmakefile	1 Oct 2009 12:07:43 -0000
@@ -75,13 +75,13 @@
 .SUFFIXES: .in .texi_pp .texi .dvi_log .ps .pdf .text
 
 %.dvi_log: %.texi_pp
-	-$(TEXI2DVI) $< > $*.dvi_log
+	-$(TEXI2DVI) $< < /dev/null > $*.dvi_log
 
 %.ps: %.dvi
 	$(DVIPS) -f < $< > $@
 
 %.pdf: %.texi_pp
-	$(PDFTEX) $< > $*.pdf_log
+	$(PDFTEX) $< < /dev/null > $*.pdf_log
 
 %.text: %.texi_pp
 	$(MAKEINFO) --no-headers -o $@ $<
Index: doc/user_guide.texi
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/doc/user_guide.texi,v
retrieving revision 1.594
diff -u -b -r1.594 user_guide.texi
--- doc/user_guide.texi	14 Sep 2009 03:30:34 -0000	1.594
+++ doc/user_guide.texi	1 Oct 2009 12:07:46 -0000
@@ -5977,6 +5977,7 @@
 @pindex mdprof
 @cindex Deep profiling
 
+The user interface of the deep profiler is a browser.
 To display the information contained in a deep profiling data file
 (which will be called @file{Deep.data} unless you renamed it),
 start up your browser and give it a URL of the form
@@ -5986,10 +5987,109 @@
 it should have a web server running on it,
 and it should have the @samp{mdprof_cgi} program installed in
 the web server's CGI program directory.
+(On many Linux systems, this directory is @file{/usr/lib/cgi-bin}.)
 The @file{/full/path/name/Deep.data} part
 should be the full path name of the deep profiling data file
 whose data you wish to explore.
-The name of this file must not have percent signs in it.
+The name of this file must not have percent signs in it,
+and it must end in the suffix @file{.data}.
+
+When you start up @samp{mdprof} using the command above,
+you will see a list of the usual places
+where you may want to start looking at the profile.
+Each place is represented by a link.
+Clicking on and following that link will give you a web page
+that contains both the profile information you asked for
+and other links,
+some of which present the same information in a different form
+and some of which lead to further information.
+You explore the profile
+by clicking on links and looking at the resulting pages.
+
+The deep profiler can generate several kinds of pages.
+
+ at table @asis
+
+ at item The menu page
+The menu page gives summary information about the profile,
+and the usual starting points for exploration.
+
+ at item Clique pages
+Clique pages are the most fundamental pages of the deep profiler.
+Each clique page presents performance information about a clique,
+which is either a single procedure or a group of mutually recursive procedures,
+in a given ancestor context,
+which in turn is a list of other cliques
+starting with the caller of the entry point of the clique
+and ending with the clique of the @samp{main} predicate.
+
+Each clique page lists the closest ancestor cliques,
+and then the procedures of the clique.
+It gives the cost of each call site in each procedure,
+as well as the cost of each procedure in total.
+These costs will be just those incurred in the given ancestor context;
+the costs incurrent by these call sites
+and procedures in other ancestor contexts
+will be shown on other clique pages.
+
+ at item Procedure pages
+Procedure pages give the total cost of a procedure and its call sites
+in all ancestor contexts.
+
+ at item Module pages
+Module pages give the total cost of all the procedures of a module.
+
+ at item Module getters and setters pages
+These pages identifies the getter and setter procedures in a module.
+Getters and setters are simply predicates and functions
+that contain @samp{_get_} and @samp{_set_} respectively in their names;
+they are usually used to access fields of data structures.
+
+ at item Program modules page
+The program modules page gives the list of the program's modules.
+
+ at item Top procedure pages
+Top procedure pages identify the procedures that are
+most expensive as measured by various criteria.
+
+ at item Procedure caller pages
+A procedure caller page lists the call sites, procedures, modules or cliques
+that call the given procedure.
+
+ at end table
+
+When exploring a procedure's callers,
+you often want only the ancestors
+that are at or above a certain level of abstraction.
+Effectively you want to draw a line through the procedures of the program,
+such that you are interested in the procedures on or above the line
+but those below the line.
+Since we want to exclude procedures below the line
+from procedure caller pages,
+we call this line an @emph{exclusion contour}.
+
+You can tell the deep profiler where you want to draw this line
+by giving it a @samp{exclusion contour file}.
+The name of this file should be the same
+as the name of the deep profiling data file,
+but with the suffix @samp{.data} replaced with @samp{.contour}.
+This file should consist of a sequence of lines,
+and each line should contain two words.
+The first word should be either @samp{all} or @samp{internal};
+the second should the name of a module.
+If the first word is @samp{all}, then
+all procedures in the named module are below the exclusion contour;
+if the first word is @samp{internal}, then
+all internal (non-exported) procedures in the named module
+are below the exclusion contour.
+Here is an example of an exclusion contour file.
+
+ at example
+all		bag
+all		list
+all		map
+internal	set
+ at end example
 
 @node Profiling and shared libraries
 @section Profiling and shared libraries
cvs diff: Diffing extras
cvs diff: Diffing extras/base64
cvs diff: Diffing extras/cgi
cvs diff: Diffing extras/complex_numbers
cvs diff: Diffing extras/complex_numbers/samples
cvs diff: Diffing extras/complex_numbers/tests
cvs diff: Diffing extras/concurrency
cvs diff: Diffing extras/curs
cvs diff: Diffing extras/curs/samples
cvs diff: Diffing extras/curses
cvs diff: Diffing extras/curses/sample
cvs diff: Diffing extras/dynamic_linking
cvs diff: Diffing extras/error
cvs diff: Diffing extras/fixed
cvs diff: Diffing extras/gator
cvs diff: Diffing extras/gator/generations
cvs diff: Diffing extras/gator/generations/1
cvs diff: Diffing extras/graphics
cvs diff: Diffing extras/graphics/easyx
cvs diff: Diffing extras/graphics/easyx/samples
cvs diff: Diffing extras/graphics/mercury_allegro
cvs diff: Diffing extras/graphics/mercury_allegro/examples
cvs diff: Diffing extras/graphics/mercury_allegro/samples
cvs diff: Diffing extras/graphics/mercury_allegro/samples/demo
cvs diff: Diffing extras/graphics/mercury_allegro/samples/mandel
cvs diff: Diffing extras/graphics/mercury_allegro/samples/pendulum2
cvs diff: Diffing extras/graphics/mercury_allegro/samples/speed
cvs diff: Diffing extras/graphics/mercury_glut
cvs diff: Diffing extras/graphics/mercury_opengl
cvs diff: Diffing extras/graphics/mercury_tcltk
cvs diff: Diffing extras/graphics/samples
cvs diff: Diffing extras/graphics/samples/calc
cvs diff: Diffing extras/graphics/samples/gears
cvs diff: Diffing extras/graphics/samples/maze
cvs diff: Diffing extras/graphics/samples/pent
cvs diff: Diffing extras/lazy_evaluation
cvs diff: Diffing extras/lex
cvs diff: Diffing extras/lex/samples
cvs diff: Diffing extras/lex/tests
cvs diff: Diffing extras/log4m
cvs diff: Diffing extras/logged_output
cvs diff: Diffing extras/moose
cvs diff: Diffing extras/moose/samples
cvs diff: Diffing extras/moose/tests
cvs diff: Diffing extras/mopenssl
cvs diff: Diffing extras/morphine
cvs diff: Diffing extras/morphine/non-regression-tests
cvs diff: Diffing extras/morphine/scripts
cvs diff: Diffing extras/morphine/source
cvs diff: Diffing extras/net
cvs diff: Diffing extras/odbc
cvs diff: Diffing extras/posix
cvs diff: Diffing extras/posix/samples
cvs diff: Diffing extras/quickcheck
cvs diff: Diffing extras/quickcheck/tutes
cvs diff: Diffing extras/references
cvs diff: Diffing extras/references/samples
cvs diff: Diffing extras/references/tests
cvs diff: Diffing extras/solver_types
cvs diff: Diffing extras/solver_types/library
cvs diff: Diffing extras/trailed_update
cvs diff: Diffing extras/trailed_update/samples
cvs diff: Diffing extras/trailed_update/tests
cvs diff: Diffing extras/windows_installer_generator
cvs diff: Diffing extras/windows_installer_generator/sample
cvs diff: Diffing extras/windows_installer_generator/sample/images
cvs diff: Diffing extras/xml
cvs diff: Diffing extras/xml/samples
cvs diff: Diffing extras/xml_stylesheets
cvs diff: Diffing java
cvs diff: Diffing java/runtime
cvs diff: Diffing library
cvs diff: Diffing mdbcomp
cvs diff: Diffing profiler
cvs diff: Diffing robdd
cvs diff: Diffing runtime
cvs diff: Diffing runtime/GETOPT
cvs diff: Diffing runtime/machdeps
cvs diff: Diffing samples
cvs diff: Diffing samples/c_interface
cvs diff: Diffing samples/c_interface/c_calls_mercury
cvs diff: Diffing samples/c_interface/cplusplus_calls_mercury
cvs diff: Diffing samples/c_interface/mercury_calls_c
cvs diff: Diffing samples/c_interface/mercury_calls_cplusplus
cvs diff: Diffing samples/c_interface/mercury_calls_fortran
cvs diff: Diffing samples/c_interface/simpler_c_calls_mercury
cvs diff: Diffing samples/c_interface/simpler_cplusplus_calls_mercury
cvs diff: Diffing samples/c_interface/standalone_c
cvs diff: Diffing samples/diff
cvs diff: Diffing samples/muz
cvs diff: Diffing samples/rot13
cvs diff: Diffing samples/solutions
cvs diff: Diffing samples/solver_types
cvs diff: Diffing samples/tests
cvs diff: Diffing samples/tests/c_interface
cvs diff: Diffing samples/tests/c_interface/c_calls_mercury
cvs diff: Diffing samples/tests/c_interface/cplusplus_calls_mercury
cvs diff: Diffing samples/tests/c_interface/mercury_calls_c
cvs diff: Diffing samples/tests/c_interface/mercury_calls_cplusplus
cvs diff: Diffing samples/tests/c_interface/mercury_calls_fortran
cvs diff: Diffing samples/tests/c_interface/simpler_c_calls_mercury
cvs diff: Diffing samples/tests/c_interface/simpler_cplusplus_calls_mercury
cvs diff: Diffing samples/tests/diff
cvs diff: Diffing samples/tests/muz
cvs diff: Diffing samples/tests/rot13
cvs diff: Diffing samples/tests/solutions
cvs diff: Diffing samples/tests/toplevel
cvs diff: Diffing scripts
cvs diff: Diffing slice
cvs diff: Diffing ssdb
cvs diff: Diffing tests
cvs diff: Diffing tests/analysis
cvs diff: Diffing tests/analysis/ctgc
cvs diff: Diffing tests/analysis/excp
cvs diff: Diffing tests/analysis/ext
cvs diff: Diffing tests/analysis/sharing
cvs diff: Diffing tests/analysis/table
cvs diff: Diffing tests/analysis/trail
cvs diff: Diffing tests/analysis/unused_args
cvs diff: Diffing tests/benchmarks
cvs diff: Diffing tests/debugger
cvs diff: Diffing tests/debugger/declarative
cvs diff: Diffing tests/dppd
cvs diff: Diffing tests/general
cvs diff: Diffing tests/general/accumulator
cvs diff: Diffing tests/general/string_format
cvs diff: Diffing tests/general/structure_reuse
cvs diff: Diffing tests/grade_subdirs
cvs diff: Diffing tests/hard_coded
cvs diff: Diffing tests/hard_coded/exceptions
cvs diff: Diffing tests/hard_coded/purity
cvs diff: Diffing tests/hard_coded/sub-modules
cvs diff: Diffing tests/hard_coded/typeclasses
cvs diff: Diffing tests/invalid
cvs diff: Diffing tests/invalid/purity
cvs diff: Diffing tests/misc_tests
cvs diff: Diffing tests/mmc_make
cvs diff: Diffing tests/mmc_make/lib
cvs diff: Diffing tests/par_conj
cvs diff: Diffing tests/recompilation
cvs diff: Diffing tests/stm
cvs diff: Diffing tests/stm/orig
cvs diff: Diffing tests/stm/orig/stm-compiler
cvs diff: Diffing tests/stm/orig/stm-compiler/test1
cvs diff: Diffing tests/stm/orig/stm-compiler/test10
cvs diff: Diffing tests/stm/orig/stm-compiler/test2
cvs diff: Diffing tests/stm/orig/stm-compiler/test3
cvs diff: Diffing tests/stm/orig/stm-compiler/test4
cvs diff: Diffing tests/stm/orig/stm-compiler/test5
cvs diff: Diffing tests/stm/orig/stm-compiler/test6
cvs diff: Diffing tests/stm/orig/stm-compiler/test7
cvs diff: Diffing tests/stm/orig/stm-compiler/test8
cvs diff: Diffing tests/stm/orig/stm-compiler/test9
cvs diff: Diffing tests/stm/orig/stm-compiler-par
cvs diff: Diffing tests/stm/orig/stm-compiler-par/bm1
cvs diff: Diffing tests/stm/orig/stm-compiler-par/bm2
cvs diff: Diffing tests/stm/orig/stm-compiler-par/stmqueue
cvs diff: Diffing tests/stm/orig/stm-compiler-par/test1
cvs diff: Diffing tests/stm/orig/stm-compiler-par/test10
cvs diff: Diffing tests/stm/orig/stm-compiler-par/test11
cvs diff: Diffing tests/stm/orig/stm-compiler-par/test2
cvs diff: Diffing tests/stm/orig/stm-compiler-par/test3
cvs diff: Diffing tests/stm/orig/stm-compiler-par/test4
cvs diff: Diffing tests/stm/orig/stm-compiler-par/test5
cvs diff: Diffing tests/stm/orig/stm-compiler-par/test6
cvs diff: Diffing tests/stm/orig/stm-compiler-par/test7
cvs diff: Diffing tests/stm/orig/stm-compiler-par/test8
cvs diff: Diffing tests/stm/orig/stm-compiler-par/test9
cvs diff: Diffing tests/stm/orig/stm-compiler-par-asm_fast
cvs diff: Diffing tests/stm/orig/stm-compiler-par-asm_fast/test1
cvs diff: Diffing tests/stm/orig/stm-compiler-par-asm_fast/test2
cvs diff: Diffing tests/stm/orig/stm-compiler-par-asm_fast/test3
cvs diff: Diffing tests/stm/orig/stm-compiler-par-asm_fast/test4
cvs diff: Diffing tests/stm/orig/stm-compiler-par-asm_fast/test5
cvs diff: Diffing tests/stm/orig/stm-compiler-par-asm_fast/test6
cvs diff: Diffing tests/stm/orig/stm-compiler-par-asm_fast/test7
cvs diff: Diffing tests/stm/orig/stm-compiler-par-asm_fast/test8
cvs diff: Diffing tests/stm/orig/stm-compiler-par-asm_fast/test9
cvs diff: Diffing tests/tabling
cvs diff: Diffing tests/term
cvs diff: Diffing tests/trailing
cvs diff: Diffing tests/valid
cvs diff: Diffing tests/warnings
cvs diff: Diffing tools
cvs diff: Diffing trace
cvs diff: Diffing util
cvs diff: Diffing vim
cvs diff: Diffing vim/after
cvs diff: Diffing vim/ftplugin
cvs diff: Diffing vim/syntax
--------------------------------------------------------------------------
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