[m-rev.] diff/review: deep profiler enhancements

Zoltan Somogyi zs at cs.mu.OZ.AU
Tue Jul 3 13:24:40 AEST 2001


This change includes the stuff I showed at the meeting on friday, as well as
some things requested at the meeting on friday.

I intend to commit this sometime this evening, to allow people to play
with the profiler on their own. The changes outside the profiler directory
are minimal, and have passed bootcheck. I expect Tom will review the changes
in the profiler directory sometime later, however that can wait until after
I add some documentation.

Reviews today would be welcome. Reviews later will be unread until monday,
because I am taking annual leave for three days from tomorrow.

Zoltan.

Major enhancements of the deep profiler. The most important ones are:

- The addition of several new classes of preferences, including: the use of
  colour, boxing, sorting, summarizing of higher order call sites, and time
  formats.

- Making preferences persistent across different queries, so that once a
  preference is set, it "sticks" until the user changes it. Previously,
  preferences stuck during query sequences of the same command type.

- Several new command types:

	- listing all the modules of the program
	- listing all the procedures in a module
	- listing all the callers of a procedure,
		grouped by calling call site, procedure, module, or clique,
		and filtering out certain classes of ancestors
	- jumping into the call graph close to the action
	- restarting the server (useful when the data file changes)

- New optional columns showing time per call, allocations per call and words
  allocated per call.

- Can now display memory consumption in bytes as well as words.

- More robustness in the face of external events, e.g. machine shutdowns.

- Fix a bug reported by Tom in the summaries of procedures that make higher
  order calls.

The new functionality required adding some fields to ProcStatic structures;
as a result, compilers and runtime systems that have this change are
incompatible with compilers and runtime systems before this change in deep
profiling grades. (They of course remain compatible in other grades.)

compiler/deep_profiling.m:
compiler/layout.m:
compiler/layout_out.m:
	Add two new fields to ProcStatic structures, one giving the line number
	of procedure's context and one stating whether the procedure is
	exported from its module.

compiler/layout.m:
	Be consistent about filename vs file_name in field names.

compiler/*.m:
	Minor changes to handle the new fields.

deep_profiler/interface.m:
	Define new command types, modify some of the parameters of existing
	ones, and delete obsolete ones. Define the types and predicates used
	by the new system of preferences, Update the predicates for recognizing
	and generating queries accordingly.

	Make the order of declarations and definitions more consistent.

deep_profiler/split.m:
	Complete rewrite of the only predicate of this module. The old split
	predicate deleted any empty substrings resulting from the breakup of
	the original string. The new one preserves them, because they are
	needed by the new encoding scheme used in interface.m.

deep_profiler/query.m:
	New module, containing code dealing with the computational issues of
	queries. Some of its code is old (from server.m), much of it is new.

deep_profiler/html_format.m:
	New module, containing code dealing with HTML formatting. Some of its
	code is old (from server.m), much of it is new.

deep_profiler/top_procs.m:
	New module, containing code dealing with issues involving sorting
	by various criteria. Some of its code is old (from server.m),
	much of it is new.

deep_profiler/exclude.m:
	New module to handle contour exclusion. This means that when listing
	the callers of a procedure, we display the nearest parent that is *not*
	excluded by virtue of being in a given module. The set of modules so
	excluded forms a contour drawn through the program.

deep_profiler/mdprof_cgi.m:
deep_profiler/mdprof_server.m:
deep_profiler/server.m:
	Minor changes to adapt to the new system of preferences.

deep_profiler/array_util.m:
	Add a mode to foldl2.

deep_profiler/io_combinator.m:
	Add predicates for reading in sequences of ten things.

deep_profiler/measurements.m:
	Add a function needed by new code.

deep_profiler/timeout.m:
	Make the profiler robust in the face of signals.

deep_profiler/canonical.m:
	Some more work towards working canonicalization; not there yet.

	Move some procedures to profile.m, since other modules also need them
	now.

deep_profiler/profile.m:
	Add the new fields to ProcStatic structures.

	Record the word size.

	Record more information about procedures whose activation counters are
	ever zeroed, in order to allow query.m to avoid giving misleading
	information in cases where a procedure calls itself through a higher
	order call site.

	Record information about the modules of the program.

	Add a bunch of lookup predicates, some moved from canonical.m.

deep_profiler/call_graph.m:
	Minor changes to conform to changes in profile.m.

deep_profiler/startup.m:
	Fill in the new parts of the profile data structure.

deep_profiler/read_profile.m:
	Read the new fields in ProcStatic structures.

	Read in the id of the root node as a fixed part of the header,
	not as a node.

	Read in the word size.

	Make it easier to find all the debugging output sites.

	Record, for each call site which can call more than one procedure,
	whether this causes the caller's ProcStatic structure's activation
	count to be zeroed.

runtime/mercury_deep_profiling.h:
	Add the new fields to ProcStatic structures.

runtime/mercury_deep_profiling.c:
	Write out the new fields to ProcStatic structures.

	Write out the id of the root node as a fixed part of the header,
	not as a node.

	Write out the word size.

	Remove incorrect L suffixes on constants.

	Record that the artificial procedure "parent of main" is called once,
	not zero times, to avoid division by zero when computing per-call
	statistics.

runtime/mercury_deep_profiling_hand.h:
	Add the new fields to the macros for creating ProcStatic structures.

runtime/mercury_ho_call.c:
library/array.m:
library/builtin.m:
library/exception.m:
library/std_util.m:
	Add the new fields to the invocations of those macros.

cvs diff: Diffing .
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/tests
cvs diff: Diffing browser
cvs diff: Diffing bytecode
cvs diff: Diffing compiler
Index: compiler/deep_profiling.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/deep_profiling.m,v
retrieving revision 1.3
diff -u -b -r1.3 deep_profiling.m
--- compiler/deep_profiling.m	2001/06/05 04:57:05	1.3
+++ compiler/deep_profiling.m	2001/07/01 15:21:19
@@ -31,7 +31,7 @@
 :- import_module quantification, dependency_graph, rtti, trace.
 :- import_module options, globals.
 :- import_module bool, int, list, assoc_list, map, require, set.
-:- import_module exception, std_util, string, term, varset, counter.
+:- import_module std_util, string, term, varset, counter.
 
 apply_deep_profiling_transformation(ModuleInfo0, ModuleInfo, ProcStatics) -->
 	{ module_info_globals(ModuleInfo0, Globals) },
@@ -567,8 +567,9 @@
 		VarTypes5 = VarTypes3,
 		MaybeActivationPtr = no
 	),
-	goal_info_get_context(GoalInfo0, Context),
+	proc_info_context(Proc0, Context),
 	FileName = term__context_file(Context),
+	LineNumber = term__context_line(Context),
 
 	proc_info_get_maybe_deep_profile_info(Proc0, MaybeRecInfo),
 	DeepInfo0 = deep_info(ModuleInfo, PredProcId, MiddleCSD,
@@ -591,7 +592,9 @@
 	),
 
 	RttiProcLabel = rtti__make_proc_label(ModuleInfo, PredId, ProcId),
-	ProcStatic = proc_static_data(RttiProcLabel, FileName, CallSites),
+	IsInInterface = is_proc_in_interface(ModuleInfo, PredId, ProcId),
+	ProcStatic = proc_static_data(RttiProcLabel, FileName, LineNumber,
+		IsInInterface, CallSites),
 	ProcStaticConsId = deep_profiling_proc_static(RttiProcLabel),
 	generate_unify(ProcStaticConsId, ProcStaticVar, BindProcStaticVarGoal),
 
@@ -651,8 +654,9 @@
 		VarTypes5 = VarTypes3,
 		MaybeActivationPtr = no
 	),
-	goal_info_get_context(GoalInfo0, Context),
+	proc_info_context(Proc0, Context),
 	FileName = term__context_file(Context),
+	LineNumber = term__context_line(Context),
 
 	proc_info_get_maybe_deep_profile_info(Proc0, MaybeRecInfo),
 	DeepInfo0 = deep_info(ModuleInfo, PredProcId, MiddleCSD,
@@ -675,7 +679,9 @@
 	),
 
 	RttiProcLabel = rtti__make_proc_label(ModuleInfo, PredId, ProcId),
-	ProcStatic = proc_static_data(RttiProcLabel, FileName, CallSites),
+	IsInInterface = is_proc_in_interface(ModuleInfo, PredId, ProcId),
+	ProcStatic = proc_static_data(RttiProcLabel, FileName, LineNumber,
+		IsInInterface, CallSites),
 	ProcStaticConsId = deep_profiling_proc_static(RttiProcLabel),
 	generate_unify(ProcStaticConsId, ProcStaticVar, BindProcStaticVarGoal),
 
@@ -757,8 +763,9 @@
 			VarTypes5),
 		MaybeOldActivationPtr = no
 	),
-	goal_info_get_context(GoalInfo0, Context),
+	proc_info_context(Proc0, Context),
 	FileName = term__context_file(Context),
+	LineNumber = term__context_line(Context),
 
 	proc_info_get_maybe_deep_profile_info(Proc0, MaybeRecInfo),
 	DeepInfo0 = deep_info(ModuleInfo, PredProcId, MiddleCSD,
@@ -773,7 +780,9 @@
 
 	PredProcId = proc(PredId, ProcId),
 	RttiProcLabel = rtti__make_proc_label(ModuleInfo, PredId, ProcId),
-	ProcStatic = proc_static_data(RttiProcLabel, FileName, CallSites),
+	IsInInterface = is_proc_in_interface(ModuleInfo, PredId, ProcId),
+	ProcStatic = proc_static_data(RttiProcLabel, FileName, LineNumber,
+		IsInInterface, CallSites),
 	ProcStaticConsId = deep_profiling_proc_static(RttiProcLabel),
 	generate_unify(ProcStaticConsId, ProcStaticVar, BindProcStaticVarGoal),
 
@@ -878,6 +887,22 @@
 	proc_info_set_varset(Proc0, Vars, Proc1),
 	proc_info_set_vartypes(Proc1, VarTypes, Proc2),
 	proc_info_set_goal(Proc2, TransformedGoal, Proc).
+
+%-----------------------------------------------------------------------------%
+
+:- func is_proc_in_interface(module_info, pred_id, proc_id) = bool.
+
+is_proc_in_interface(ModuleInfo, PredId, _ProcId) = IsInInterface :-
+	module_info_pred_info(ModuleInfo, PredId, PredInfo),
+	(
+		( pred_info_is_exported(PredInfo)
+		; pred_info_is_pseudo_exported(PredInfo)
+		)
+	->
+		IsInInterface = yes
+	;
+		IsInInterface = no
+	).
 
 %-----------------------------------------------------------------------------%
 
Index: compiler/layout.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/layout.m,v
retrieving revision 1.2
diff -u -b -r1.2 layout.m
--- compiler/layout.m	2001/05/31 05:59:40	1.2
+++ compiler/layout.m	2001/06/28 13:27:42
@@ -33,7 +33,7 @@
 :- interface.
 
 :- import_module prog_data, trace_params, llds, rtti, hlds_goal.
-:- import_module std_util, list, assoc_list.
+:- import_module bool, std_util, list, assoc_list.
 
 :- type layout_data
 	--->	label_layout_data(		% defines MR_Label_Layout
@@ -68,6 +68,8 @@
 	;	proc_static_data(		% defines MR_ProcStatic
 			proc_static_id		:: rtti_proc_label,
 			proc_static_file_name	:: string,
+			proc_static_line_number :: int,
+			proc_is_in_interface	:: bool,
 			call_site_statics	:: list(call_site_static_data)
 		).
 
@@ -75,27 +77,27 @@
 	--->	normal_call(
 			normal_callee		:: rtti_proc_label,
 			normal_type_subst	:: string,
-			normal_filename		:: string,
+			normal_file_name	:: string,
 			normal_line_number	:: int,
 			normal_goal_path	:: goal_path
 		)
 	;	special_call(
-			special_filename	:: string,
+			special_file_name	:: string,
 			special_line_number	:: int,
 			special_goal_path	:: goal_path
 		)
 	;	higher_order_call(
-			higher_order_filename	:: string,
+			higher_order_file_name	:: string,
 			ho_line_number		:: int,
 			ho_goal_path		:: goal_path
 		)
 	;	method_call(
-			method_filename		:: string,
+			method_file_name	:: string,
 			method_line_number	:: int,
 			method_goal_path	:: goal_path
 		)
 	;	callback(
-			callback_filename	:: string,
+			callback_file_name	:: string,
 			callback_line_number	:: int,
 			callback_goal_path	:: goal_path
 		).
Index: compiler/layout_out.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/layout_out.m,v
retrieving revision 1.4
diff -u -b -r1.4 layout_out.m
--- compiler/layout_out.m	2001/06/15 04:46:19	1.4
+++ compiler/layout_out.m	2001/06/28 14:05:29
@@ -100,10 +100,10 @@
 	output_module_layout_data_defn(ModuleName, StringTableSize,
 		StringTable, ProcLayoutNames, FileLayouts, TraceLevel,
 		DeclSet0, DeclSet).
-output_layout_data_defn(proc_static_data(RttiProcLabel, FileName, CallSites),
-		DeclSet0, DeclSet) -->
-	output_proc_static_data_defn(RttiProcLabel, FileName, CallSites,
-		DeclSet0, DeclSet).
+output_layout_data_defn(proc_static_data(RttiProcLabel, FileName, LineNumber,
+		IsInInterface, CallSites), DeclSet0, DeclSet) -->
+	output_proc_static_data_defn(RttiProcLabel, FileName, LineNumber,
+		IsInInterface, CallSites, DeclSet0, DeclSet).
 
 %-----------------------------------------------------------------------------%
 
@@ -141,7 +141,7 @@
 		closure_proc_id(CallerProcLabel, SeqNo, ClosureProcLabel)).
 extract_layout_name(module_layout_data(ModuleName, _,_,_,_,_), LayoutName) :-
 	LayoutName = module_layout(ModuleName).
-extract_layout_name(proc_static_data(RttiProcLabel, _, _), LayoutName) :-
+extract_layout_name(proc_static_data(RttiProcLabel, _, _, _, _), LayoutName) :-
 	LayoutName = proc_static(RttiProcLabel).
 
 :- pred output_layout_decls(list(layout_name)::in, decl_set::in, decl_set::out,
@@ -1053,11 +1053,11 @@
 %-----------------------------------------------------------------------------%
 
 :- pred output_proc_static_data_defn(rtti_proc_label::in, string::in,
-	list(call_site_static_data)::in, decl_set::in, decl_set::out,
-	io__state::di, io__state::uo) is det.
+	int::in, bool::in, list(call_site_static_data)::in,
+	decl_set::in, decl_set::out, io__state::di, io__state::uo) is det.
 
-output_proc_static_data_defn(RttiProcLabel, FileName, CallSites,
-		DeclSet0, DeclSet) -->
+output_proc_static_data_defn(RttiProcLabel, FileName, LineNumber,
+		IsInInterface, CallSites, DeclSet0, DeclSet) -->
 	list__foldl2(output_call_site_static_decl, CallSites,
 		DeclSet0, DeclSet1),
 	output_call_site_static_array(RttiProcLabel, CallSites,
@@ -1070,6 +1070,16 @@
 	output_layout_proc_id_group(ProcLabel),
 	io__write_string("\t"),
 	quote_and_write_string(FileName),
+	io__write_string(",\n\t"),
+	io__write_int(LineNumber),
+	io__write_string(",\n\t"),
+	(
+		{ IsInInterface = yes },
+		io__write_string("TRUE")
+	;
+		{ IsInInterface = no },
+		io__write_string("FALSE")
+	),
 	io__write_string(",\n\t"),
 	io__write_int(list__length(CallSites)),
 	io__write_string(",\n\t"),
Index: compiler/llds_common.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/llds_common.m,v
retrieving revision 1.38
diff -u -b -r1.38 llds_common.m
--- compiler/llds_common.m	2001/05/31 05:59:42	1.38
+++ compiler/llds_common.m	2001/06/28 14:05:36
@@ -189,7 +189,7 @@
 	LayoutData0 = module_layout_data(_, _, _, _, _, _),
 	LayoutData = LayoutData0.
 llds_common__process_layout_data(LayoutData0, LayoutData, Info, Info) :-
-	LayoutData0 = proc_static_data(_, _, _),
+	LayoutData0 = proc_static_data(_, _, _, _, _),
 	LayoutData = LayoutData0.
 
 :- pred llds_common__process_exec_trace(proc_layout_exec_trace::in,
Index: compiler/llds_out.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/llds_out.m,v
retrieving revision 1.176
diff -u -b -r1.176 llds_out.m
--- compiler/llds_out.m	2001/05/31 05:59:42	1.176
+++ compiler/llds_out.m	2001/06/28 14:05:46
@@ -780,7 +780,7 @@
 output_write_proc_static_list_decls([Data | Datas], DeclSet0, DeclSet) -->
 	(
 		{ Data = layout_data(LayoutData) },
-		{ LayoutData = proc_static_data(_, _, _) }
+		{ LayoutData = proc_static_data(_, _, _, _, _) }
 	->
 		output_maybe_layout_data_decl(LayoutData, DeclSet0, DeclSet1)
 	;
@@ -796,7 +796,7 @@
 output_write_proc_static_list([Data | Datas]) -->
 	(
 		{ Data = layout_data(LayoutData) },
-		{ LayoutData = proc_static_data(RttiProcLabel, _, _) }
+		{ LayoutData = proc_static_data(RttiProcLabel, _, _, _, _) }
 	->
 		io__write_string("\tMR_write_out_proc_static(fp, "),
 		io__write_string("(MR_ProcStatic *)\n\t\t&"),
cvs diff: Diffing compiler/notes
cvs diff: Diffing debian
cvs diff: Diffing deep_profiler
Index: deep_profiler/array_util.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/array_util.m,v
retrieving revision 1.2
diff -u -b -r1.2 array_util.m
--- deep_profiler/array_util.m	2001/06/05 04:47:31	1.2
+++ deep_profiler/array_util.m	2001/06/21 15:06:17
@@ -55,6 +55,8 @@
 :- mode array_foldl2_from_1(pred(in, in, array_di, array_uo,
 	array_di, array_uo) is det,
 	in, array_di, array_uo, array_di, array_uo) is det.
+:- mode array_foldl2_from_1(pred(in, in, array_di, array_uo, in, out) is det,
+	in, array_di, array_uo, in, out) is det.
 :- mode array_foldl2_from_1(pred(in, in, in, out, di, uo) is det,
 	in, in, out, di, uo) is det.
 
@@ -67,6 +69,9 @@
 :- mode array_foldl2(in, in, pred(in, in,
 	array_di, array_uo, array_di, array_uo) is det, in,
 	array_di, array_uo, array_di, array_uo) is det.
+:- mode array_foldl2(in, in, pred(in, in,
+	array_di, array_uo, in, out) is det, in,
+	array_di, array_uo, in, out) is det.
 :- mode array_foldl2(in, in, pred(in, in, in, out, di, uo) is det, in,
 	in, out, di, uo) is det.
 
Index: deep_profiler/callgraph.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/callgraph.m,v
retrieving revision 1.1
diff -u -b -r1.1 callgraph.m
--- deep_profiler/callgraph.m	2001/06/05 07:42:06	1.1
+++ deep_profiler/callgraph.m	2001/06/19 11:28:26
@@ -86,7 +86,7 @@
 		CallSiteSlot = normal(CSDPtr),
 		add_csd_arcs(InitDeep, FromPDI, CSDPtr, Graph0, Graph)
 	;
-		CallSiteSlot = multi(CSDPtrArray),
+		CallSiteSlot = multi(_, CSDPtrArray),
 		array__to_list(CSDPtrArray, CSDPtrs),
 		list__foldl(add_csd_arcs(InitDeep, FromPDI), CSDPtrs,
 			Graph0, Graph)
Index: deep_profiler/canonical.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/canonical.m,v
retrieving revision 1.1
diff -u -b -r1.1 canonical.m
--- deep_profiler/canonical.m	2001/06/05 04:47:31	1.1
+++ deep_profiler/canonical.m	2001/07/02 07:12:54
@@ -9,7 +9,7 @@
 % This module contains code for recursively merging sets of ProcDynamic and
 % CallSiteDynamic nodes.
 
-:- module merge.
+:- module canonical.
 
 :- interface.
 
@@ -72,19 +72,156 @@
 	merge_info::in, initial_deep::in, redirect::in,
 	initial_deep::out, redirect::out) is det.
 
-merge_clique(CliquePDs, MergeInfo, InitDeep0, Redirect0, InitDeep, Redirect) :-
+merge_clique(CliquePDs0, MergeInfo, InitDeep0, Redirect0,
+		InitDeep, Redirect) :-
+	( CliquePDs0 = [_, _ | _] ->
+		map__init(ProcMap0),
+		list__foldl(cluster_pds_by_ps(InitDeep0), CliquePDs0,
+			ProcMap0, ProcMap1),
+		map__values(ProcMap1, PDsList1),
+		list__filter(two_or_more, PDsList1, ToMergePDsList1),
+		( ToMergePDsList1 = [_ | _] ->
+			complete_clique(InitDeep0, Redirect0,
+				ProcMap1, ProcMap, Clique),
+			map__values(ProcMap, PDsList),
+			list__filter(two_or_more, PDsList, ToMergePDsList),
+			list__foldl2(merge_proc_dynamics_ignore_chosen(
+				MergeInfo, Clique),
+				ToMergePDsList, InitDeep0, InitDeep,
+				Redirect0, Redirect)
+		;
+			InitDeep = InitDeep0,
+			Redirect = Redirect0
+		)
+	;
+		InitDeep = InitDeep0,
+		Redirect = Redirect0
+	).
+
+:- pred insert_pds(list(T)::in, set(T)::in, set(T)::out) is det.
+
+insert_pds(List, Set0, Set) :-
+	set__insert_list(Set0, List, Set).
+
 	% find set of proc_statics in the CliquePDs
 	% for all (first order) calls in CliquePDs, if call is to a procedure
 	%	that CliquePDs contains a call to, add its PD to the set
 
-	map__init(ProcMap0),
-	set__list_to_set(CliquePDs, Clique),
-	list__foldl(cluster_pds_by_ps(InitDeep0), CliquePDs,
-		ProcMap0, ProcMap),
-	map__values(ProcMap, PDsList),
-	list__filter(two_or_more, PDsList, ToMergePDsList),
-	list__foldl2(merge_proc_dynamics(MergeInfo, Clique),
-		ToMergePDsList, InitDeep0, InitDeep, Redirect0, Redirect).
+:- pred complete_clique(initial_deep::in, redirect::in,
+	map(proc_static_ptr, list(proc_dynamic_ptr))::in,
+	map(proc_static_ptr, list(proc_dynamic_ptr))::out,
+	set(proc_dynamic_ptr)::out) is det.
+
+complete_clique(InitDeep, Redirect, ProcMap0, ProcMap, Clique) :-
+	map__values(ProcMap0, PDsList0),
+	list__foldl(insert_pds, PDsList0, set__init, Clique0),
+	complete_clique_pass(InitDeep, Redirect, Clique0, ProcMap0, ProcMap1,
+		no, AddedPD),
+	(
+		AddedPD = yes,
+		complete_clique(InitDeep, Redirect, ProcMap1, ProcMap, Clique)
+	;
+		AddedPD = no,
+		ProcMap = ProcMap1,
+		Clique = Clique0
+	).
+
+:- pred complete_clique_pass(initial_deep::in, redirect::in,
+	set(proc_dynamic_ptr)::in,
+	map(proc_static_ptr, list(proc_dynamic_ptr))::in,
+	map(proc_static_ptr, list(proc_dynamic_ptr))::out,
+	bool::in, bool::out) is det.
+
+complete_clique_pass(InitDeep, _Redirect, Clique, ProcMap0, ProcMap,
+		AddedPD0, AddedPD) :-
+	map__to_assoc_list(ProcMap0, PSPDs0),
+	list__foldl2(complete_clique_ps(InitDeep, Clique),
+		PSPDs0, ProcMap0, ProcMap, AddedPD0, AddedPD).
+
+:- pred complete_clique_ps(initial_deep::in,
+	set(proc_dynamic_ptr)::in,
+	pair(proc_static_ptr, list(proc_dynamic_ptr))::in,
+	map(proc_static_ptr, list(proc_dynamic_ptr))::in,
+	map(proc_static_ptr, list(proc_dynamic_ptr))::out,
+	bool::in, bool::out) is det.
+
+complete_clique_ps(InitDeep, Clique, PSPtr - PDPtrs, ProcMap0, ProcMap,
+		AddedPD0, AddedPD) :-
+	( PDPtrs = [_, _ | _] ->
+		lookup_proc_statics(InitDeep ^ init_proc_statics, PSPtr, PS),
+		list__map(lookup_pd_site(InitDeep), PDPtrs, PDSites),
+		complete_clique_slots(array__max(PS ^ ps_sites), InitDeep,
+			Clique, PS ^ ps_sites, PDSites, ProcMap0, ProcMap,
+			AddedPD0, AddedPD)
+	;
+		ProcMap = ProcMap0,
+		AddedPD = AddedPD0
+	).
+
+:- pred lookup_pd_site(initial_deep::in, proc_dynamic_ptr::in,
+	array(call_site_array_slot)::out) is det.
+
+lookup_pd_site(InitDeep, PDPtr, Sites) :-
+	lookup_proc_dynamics(InitDeep ^ init_proc_dynamics, PDPtr, PD),
+	Sites = PD ^ pd_sites.
+
+:- pred complete_clique_slots(int::in, initial_deep::in,
+	set(proc_dynamic_ptr)::in, array(call_site_static_ptr)::in,
+	list(array(call_site_array_slot))::in,
+	map(proc_static_ptr, list(proc_dynamic_ptr))::in,
+	map(proc_static_ptr, list(proc_dynamic_ptr))::out,
+	bool::in, bool::out) is det.
+
+complete_clique_slots(SlotNum, InitDeep, Clique, PSSites, PDSites,
+		ProcMap0, ProcMap, AddedPD0, AddedPD) :-
+	( SlotNum >= 0 ->
+		array__lookup(PSSites, SlotNum, CSSPtr),
+		lookup_call_site_statics(InitDeep ^ init_call_site_statics,
+			CSSPtr, CSS),
+		( CSS ^ css_kind = normal_call(_, _) ->
+			lookup_normal_sites(PDSites, SlotNum, CSDPtrs)
+		;
+			lookup_multi_sites(PDSites, SlotNum, CSDPtrLists),
+			list__condense(CSDPtrLists, CSDPtrs)
+		),
+		list__filter(valid_call_site_dynamic_ptr_raw(
+			InitDeep ^ init_call_site_dynamics), CSDPtrs,
+			ValidCSDPtrs),
+		list__map(extract_csdptr_callee(InitDeep), ValidCSDPtrs,
+			CalleePDPtrs),
+		CalleePDPtrSet = set__list_to_set(CalleePDPtrs),
+		set__intersect(CalleePDPtrSet, Clique, Common),
+		( set__empty(Common) ->
+			ProcMap1 = ProcMap0,
+			AddedPD1 = AddedPD0
+		;
+			set__difference(CalleePDPtrSet, Clique, NewMembers),
+			( set__empty(NewMembers) ->
+				ProcMap1 = ProcMap0,
+				AddedPD1 = no
+			;
+				set__to_sorted_list(NewMembers, NewMemberList),
+				list__foldl(cluster_pds_by_ps(InitDeep),
+					NewMemberList, ProcMap0, ProcMap1),
+				AddedPD1 = yes
+			)
+		),
+		complete_clique_slots(SlotNum - 1, InitDeep, Clique,
+			PSSites, PDSites, ProcMap1, ProcMap, AddedPD1, AddedPD)
+	;
+		ProcMap = ProcMap0,
+		AddedPD = AddedPD0
+	).
+
+:- pred merge_proc_dynamics_ignore_chosen(merge_info::in,
+	set(proc_dynamic_ptr)::in, list(proc_dynamic_ptr)::in,
+	initial_deep::in, initial_deep::out, redirect::in, redirect::out)
+	is det.
+
+merge_proc_dynamics_ignore_chosen(MergeInfo, Clique, CandidatePDPtrs,
+		InitDeep0, InitDeep, Redirect0, Redirect) :-
+	merge_proc_dynamics(MergeInfo, Clique, CandidatePDPtrs, _ChosenPDPtr,
+		InitDeep0, InitDeep, Redirect0, Redirect).
 
 :- pred merge_proc_dynamics(merge_info::in, set(proc_dynamic_ptr)::in,
 	list(proc_dynamic_ptr)::in, proc_dynamic_ptr::out,
@@ -95,7 +232,9 @@
 		InitDeep0, InitDeep, Redirect0, Redirect) :-
 	ProcDynamics0 = InitDeep0 ^ init_proc_dynamics,
 	list__filter(valid_proc_dynamic_ptr_raw(ProcDynamics0),
-		CandidatePDPtrs, ValidPDPtrs),
+		CandidatePDPtrs, ValidPDPtrs, InvalidPDPtrs),
+	require(unify(InvalidPDPtrs, []),
+		"merge_proc_dynamics: invalid pdptrs"),
 	( ValidPDPtrs = [PrimePDPtr | RestPDPtrs] ->
 		record_pd_redirect(RestPDPtrs, PrimePDPtr,
 			Redirect0, Redirect1),
@@ -115,11 +254,13 @@
 		InitDeep = InitDeep1 ^ init_proc_dynamics := ProcDynamics,
 		ChosenPDPtr = PrimePDPtr
 	;
-		% This can happen when merging the callees of CSDs
-		% representing special calls.
-		ChosenPDPtr = proc_dynamic_ptr(0),
-		InitDeep = InitDeep0,
-		Redirect = Redirect0
+		% This could happen when merging the callees of CSDs
+		% representing special calls, but only before we added callcode
+		% to the unify/compare routines of builtin types.
+		% ChosenPDPtr = proc_dynamic_ptr(0),
+		% InitDeep = InitDeep0,
+		% Redirect = Redirect0
+		error("merge_proc_dynamics: no valid pdptrs")
 	).
 
 :- pred merge_proc_dynamic_slots(merge_info::in, int::in,
@@ -144,7 +285,7 @@
 			array__set(PrimeSiteArray0, SlotNum,
 				normal(PrimeCSDPtr), PrimeSiteArray1)
 		;
-			PrimeSite0 = multi(PrimeCSDPtrArray0),
+			PrimeSite0 = multi(IsZeroed, PrimeCSDPtrArray0),
 			array__to_list(PrimeCSDPtrArray0, PrimeCSDPtrList0),
 			merge_proc_dynamic_multi_slot(MergeInfo, SlotNum,
 				Clique, PrimePDPtr, PrimeCSDPtrList0,
@@ -152,7 +293,8 @@
 				InitDeep0, InitDeep1, Redirect0, Redirect1),
 			PrimeCSDPtrArray = array(PrimeCSDPtrList),
 			array__set(PrimeSiteArray0, SlotNum,
-				multi(PrimeCSDPtrArray), PrimeSiteArray1)
+				multi(IsZeroed, PrimeCSDPtrArray),
+				PrimeSiteArray1)
 		),
 		merge_proc_dynamic_slots(MergeInfo, SlotNum - 1, Clique,
 			PrimePDPtr, PrimeSiteArray1, RestSiteArrays,
@@ -363,7 +505,7 @@
 	(
 		Slot = normal(CSDPtr)
 	;
-		Slot = multi(_),
+		Slot = multi(_, _),
 		error("lookup_normal_sites: found multi")
 	),
 	lookup_normal_sites(RestArrays, SlotNum, CSDPtrs).
@@ -378,7 +520,7 @@
 		Slot = normal(_),
 		error("lookup_multi_sites: found normal")
 	;
-		Slot = multi(CSDArray),
+		Slot = multi(_, CSDArray),
 		array__to_list(CSDArray, CSDList)
 	),
 	lookup_multi_sites(RestArrays, SlotNum, CSDLists).
@@ -453,16 +595,6 @@
 	Redirect1 = Redirect0 ^ csd_redirect := CallSiteRedirect,
 	record_csd_redirect_2(RestCSDPtrs, PrimeCSDPtr, Redirect1, Redirect).
 
-:- pred extract_pd_sites(proc_dynamic::in, array(call_site_array_slot)::out)
-	is det.
-
-extract_pd_sites(PD, PD ^ pd_sites).
-
-:- pred extract_csd_callee(call_site_dynamic::in, proc_dynamic_ptr::out)
-	is det.
-
-extract_csd_callee(CSD, CSD ^ csd_callee).
-
 :- pred two_or_more(list(proc_dynamic_ptr)::in) is semidet.
 
 two_or_more([_, _ | _]).
@@ -660,7 +792,7 @@
 
 subst_in_slot(Redirect, normal(CSDPtr0), normal(CSDPtr)) :-
 	lookup_csd_redirect(Redirect ^ csd_redirect, CSDPtr0, CSDPtr).
-subst_in_slot(Redirect, multi(CSDPtrs0), multi(CSDPtrs)) :-
+subst_in_slot(Redirect, multi(IsZeroed, CSDPtrs0), multi(IsZeroed, CSDPtrs)) :-
 	array__map(lookup_csd_redirect(Redirect ^ csd_redirect),
 		u(CSDPtrs0), CSDPtrs).
 
@@ -735,9 +867,10 @@
 	list__map(extract_user_quanta, AllInitDeeps, UserQuantas),
 	list__foldl(int_add, InstrumentQuantas, 0, InstrumentQuanta),
 	list__foldl(int_add, UserQuantas, 0, UserQuanta),
+	WordSize = BaseInitDeep ^ init_profile_stats ^ word_size,
 	ConcatProfileStats = profile_stats(
 		ConcatMaxCSD, BaseMaxCSS, ConcatMaxPD, BaseMaxPS,
-		BaseTicksPerSec, InstrumentQuanta, UserQuanta),
+		BaseTicksPerSec, InstrumentQuanta, UserQuanta, WordSize, yes),
 	% The root part is a temporary lie.
 	MergedInitDeep = initial_deep(ConcatProfileStats,
 		BaseInitDeep ^ init_root,
@@ -760,11 +893,11 @@
 		ConcatProcDynamics0, ConcatProcDynamics) :-
 	concatenate_profile(InitDeep,
 		PrevMaxCSD, PrevMaxPD, NextMaxCSD, NextMaxPD,
-		ConcatCallSiteDynamics0, ConcatCallSiteDynamics,
-		ConcatProcDynamics0, ConcatProcDynamics),
+		ConcatCallSiteDynamics0, ConcatCallSiteDynamics1,
+		ConcatProcDynamics0, ConcatProcDynamics1),
 	concatenate_profiles(InitDeeps, NextMaxCSD, NextMaxPD,
-		ConcatCallSiteDynamics0, ConcatCallSiteDynamics,
-		ConcatProcDynamics0, ConcatProcDynamics).
+		ConcatCallSiteDynamics1, ConcatCallSiteDynamics,
+		ConcatProcDynamics1, ConcatProcDynamics).
 
 :- pred concatenate_profile(initial_deep::in,
 	int::in, int::in, int::out, int::out,
@@ -841,11 +974,11 @@
 				CSDPtr0, CSDPtr),
 			Slot = normal(CSDPtr)
 		;
-			Slot0 = multi(CSDPtrs0),
+			Slot0 = multi(IsZeroed, CSDPtrs0),
 			array_map_from_0(
 				concat_call_site_dynamic_ptr(PrevMaxCSD),
-				CSDPtrs0, CSDPtrs),
-			Slot = multi(CSDPtrs)
+				u(CSDPtrs0), CSDPtrs),
+			Slot = multi(IsZeroed, CSDPtrs)
 		),
 		array__set(Sites0, Cur, Slot, Sites1),
 		concatenate_profile_slots(Cur + 1, Max, PrevMaxCSD, PrevMaxPD,
@@ -877,66 +1010,6 @@
 	).
 
 %-----------------------------------------------------------------------------%
-
-:- pred extract_ticks_per_sec(initial_deep::in, int::out) is det.
-
-extract_ticks_per_sec(InitDeep,
-	InitDeep ^ init_profile_stats ^ ticks_per_sec).
-
-:- pred extract_instrument_quanta(initial_deep::in, int::out) is det.
-
-extract_instrument_quanta(InitDeep,
-	InitDeep ^ init_profile_stats ^ instrument_quanta).
-
-:- pred extract_user_quanta(initial_deep::in, int::out) is det.
-
-extract_user_quanta(InitDeep,
-	InitDeep ^ init_profile_stats ^ user_quanta).
-
-:- pred extract_max_css(initial_deep::in, int::out) is det.
-
-extract_max_css(InitDeep, MaxCSS) :-
-	array__max(InitDeep ^ init_call_site_statics, MaxCSS).
-
-:- pred extract_max_ps(initial_deep::in, int::out) is det.
-
-extract_max_ps(InitDeep, MaxPS) :-
-	array__max(InitDeep ^ init_proc_statics, MaxPS).
-
-:- pred extract_max_csd(initial_deep::in, int::out) is det.
-
-extract_max_csd(InitDeep, MaxCSD) :-
-	array__max(InitDeep ^ init_call_site_dynamics, MaxCSD).
-
-:- pred extract_max_pd(initial_deep::in, int::out) is det.
-
-extract_max_pd(InitDeep, MaxPD) :-
-	array__max(InitDeep ^ init_proc_dynamics, MaxPD).
-
-:- pred extract_init_call_site_dynamics(initial_deep::in,
-	call_site_dynamics::out) is det.
-
-extract_init_call_site_dynamics(InitDeep, InitDeep ^ init_call_site_dynamics).
-
-:- pred extract_init_call_site_statics(initial_deep::in,
-	call_site_statics::out) is det.
-
-extract_init_call_site_statics(InitDeep, InitDeep ^ init_call_site_statics).
-
-:- pred extract_init_proc_dynamics(initial_deep::in,
-	proc_dynamics::out) is det.
-
-extract_init_proc_dynamics(InitDeep, InitDeep ^ init_proc_dynamics).
-
-:- pred extract_init_proc_statics(initial_deep::in,
-	proc_statics::out) is det.
-
-extract_init_proc_statics(InitDeep, InitDeep ^ init_proc_statics).
-
-:- pred extract_init_root(initial_deep::in,
-	proc_dynamic_ptr::out) is det.
-
-extract_init_root(InitDeep, InitDeep ^ init_root).
 
 	% list__all_true(P, L) succeeds iff P is true for all elements of the
 	% list L.
Index: deep_profiler/exclude.m
===================================================================
RCS file: exclude.m
diff -N exclude.m
--- /dev/null	Fri Dec  1 02:25:58 2000
+++ exclude.m	Fri Jun 29 01:24:53 2001
@@ -0,0 +1,164 @@
+%-----------------------------------------------------------------------------%
+% Copyright (C) 2001 The University of Melbourne.
+% This file may only be copied under the terms of the GNU General
+% Public License - see the file COPYING in the Mercury distribution.
+%-----------------------------------------------------------------------------%
+%
+% Author: zs.
+%
+% XXX
+
+:- module exclude.
+
+:- interface.
+
+:- import_module profile.
+:- import_module std_util, io, set.
+
+:- type exclude_spec
+	--->	all_procedures(string)		% Exclude all procedures in the
+						% named module.
+	;	internal_procedures(string).	% Exclude all procedures in the 
+						% named module, except those
+						% which are exported from the
+						% module.
+					
+
+:- pred read_exclude_file(string::in, deep::in,
+	maybe_error(set(exclude_spec))::out,
+	io__state::di, io__state::uo) is det.
+
+:- func apply_contour_exclusion(deep, set(exclude_spec), call_site_dynamic_ptr)
+	= call_site_dynamic_ptr.
+
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module bool, char, string, list, map, require.
+
+read_exclude_file(FileName, Deep, Res) -->
+	io__open_input(FileName, Res0),
+	(
+		{ Res0 = ok(InputStream) },
+		read_exclude_lines(FileName, InputStream, [], Res1),
+		io__close_input(InputStream),
+		(
+			{ Res1 = ok(Specs) },
+			{ validate_exclude_lines(FileName, Specs, Deep, Res) }
+		;
+			{ Res1 = error(Msg) },
+			{ Res = error(Msg) }
+		)
+	;
+		{ Res0 = error(Err) },
+		{ io__error_message(Err, Msg) },
+		{ Res = error(Msg) }
+	).
+
+:- pred read_exclude_lines(string::in, io__input_stream::in,
+	list(exclude_spec)::in, maybe_error(list(exclude_spec))::out,
+	io__state::di, io__state::uo) is det.
+
+read_exclude_lines(FileName, InputStream, RevSpecs0, Res) -->
+	io__read_line_as_string(InputStream, Res0),
+	(
+		{ Res0 = ok(Line0) },
+		{ string__remove_suffix(Line0, "\n", LinePrime) ->
+			Line = LinePrime
+		;
+			Line = Line0
+		},
+		(
+			{ Words = string__words(char__is_whitespace, Line) },
+			{ Words = [Scope, ModuleName] },
+			{
+				Scope = "all",
+				Spec = all_procedures(ModuleName)
+			;
+				Scope = "internal",
+				Spec = internal_procedures(ModuleName)
+			}
+		->
+			{ RevSpecs1 = [Spec | RevSpecs0] },
+			read_exclude_lines(FileName, InputStream, RevSpecs1,
+				Res)
+		;
+			{ Msg = string__format(
+				"file %s contains badly formatted line: %s",
+				[s(FileName), s(Line)]) },
+			{ Res = error(Msg) }
+		)
+	;
+		{ Res0 = eof },
+		{ Res = ok(RevSpecs0) }
+	;
+		{ Res0 = error(Err) },
+		{ io__error_message(Err, Msg) },
+		{ Res = error(Msg) }
+	).
+
+:- pred validate_exclude_lines(string::in, list(exclude_spec)::in, deep::in,
+	maybe_error(set(exclude_spec))::out) is det.
+
+validate_exclude_lines(FileName, Specs, Deep, Res) :-
+	list__filter(has_valid_module_name(Deep), Specs,
+		ValidSpecs, InvalidSpecs),
+	( InvalidSpecs = [] ->
+		set__list_to_set(ValidSpecs, ModuleSpecSet),
+		Res = ok(ModuleSpecSet)
+	;
+		InvalidModuleNames = list__map(spec_to_module_name,
+			InvalidSpecs),
+		BadNames = string__join_list(", ", InvalidModuleNames),
+		Msg = string__format("file %s contains bad module names: %s",
+			[s(FileName), s(BadNames)]),
+		Res = error(Msg)
+	).
+
+:- pred has_valid_module_name(deep::in, exclude_spec::in) is semidet.
+
+has_valid_module_name(Deep, Spec) :-
+	map__search(Deep ^ module_data, spec_to_module_name(Spec), _).
+
+:- func spec_to_module_name(exclude_spec) = string.
+
+spec_to_module_name(all_procedures(ModuleName)) = ModuleName.
+spec_to_module_name(internal_procedures(ModuleName)) = ModuleName.
+
+%-----------------------------------------------------------------------------%
+
+apply_contour_exclusion(Deep, ExcludedSpec, CSDPtr0) = CSDPtr :-
+	( valid_call_site_dynamic_ptr(Deep, CSDPtr0) ->
+		deep_lookup_call_site_dynamics(Deep, CSDPtr0, CSD),
+		PDPtr = CSD ^ csd_caller,
+		deep_lookup_proc_dynamics(Deep, PDPtr, PD),
+		PSPtr = PD ^ pd_proc_static,
+		deep_lookup_proc_statics(Deep, PSPtr, PS),
+		ModuleName = PS ^ ps_decl_module,
+		(
+			set__member(all_procedures(ModuleName),
+				ExcludedSpec)
+		->
+			deep_lookup_clique_index(Deep, PDPtr, CliquePtr),
+			deep_lookup_clique_parents(Deep, CliquePtr,
+				EntryCSDPtr),
+			CSDPtr = apply_contour_exclusion(Deep, ExcludedSpec,
+				EntryCSDPtr)
+		;
+			set__member(internal_procedures(ModuleName),
+				ExcludedSpec),
+			PS ^ ps_in_interface = no
+		->
+			deep_lookup_clique_index(Deep, PDPtr, CliquePtr),
+			deep_lookup_clique_parents(Deep, CliquePtr,
+				EntryCSDPtr),
+			CSDPtr = apply_contour_exclusion(Deep, ExcludedSpec,
+				EntryCSDPtr)
+		;
+			CSDPtr = CSDPtr0
+		)
+	;
+		CSDPtr = CSDPtr0
+	).
+
Index: deep_profiler/html_format.m
===================================================================
RCS file: html_format.m
diff -N html_format.m
--- /dev/null	Fri Dec  1 02:25:58 2000
+++ html_format.m	Mon Jul  2 23:21:50 2001
@@ -0,0 +1,1853 @@
+%-----------------------------------------------------------------------------%
+% Copyright (C) 2001 The University of Melbourne.
+% This file may only be copied under the terms of the GNU General
+% Public License - see the file COPYING in the Mercury distribution.
+%-----------------------------------------------------------------------------%
+%
+% Author: zs.
+%
+% This module contains code that sets the format of the HTML tables
+% we generate for individual queries.
+
+:- module html_format.
+
+:- interface.
+
+:- import_module profile, interface, measurements, top_procs.
+:- import_module std_util, bool, list.
+
+:- func table_start(preferences) = string.
+:- func table_end(preferences) = string.
+
+:- func page_banner(cmd, preferences) = string.
+:- func page_footer(cmd, preferences, deep) = string.
+
+:- func toggle_cost_criteria_in_top_procs_cmd(preferences, deep, display_limit,
+	cost_kind, include_descendants, measurement_scope) = string.
+
+:- func criteria_to_description(order_criteria) = string.
+:- func cost_criteria_to_description(cost_kind, include_descendants,
+	measurement_scope) = string.
+
+:- type id_fields
+	--->	source_proc
+	;	rank_module
+	;	rank_proc
+	;	proc.
+
+:- type totals_disposition
+	--->	totals_meaningful
+	;	totals_not_meaningful.
+
+:- type header_wrap_func == (func(string, order_criteria) = string).
+
+:- func fields_header(preferences, id_fields, totals_disposition,
+	header_wrap_func) = string.
+
+:- func header_row(string, preferences, id_fields, totals_disposition) = string.
+:- func separator_row(preferences, id_fields, totals_disposition) = string.
+
+:- type sub_lines(T)
+	--->	sub_lines(
+			sub_line_type	:: T,
+			sub_line_list	:: list(line_group(T, unit))
+		).
+
+:- type one_id			--->	one_id.
+:- type two_id			--->	two_id.
+
+:- type one_id_sub_lines	==	sub_lines(one_id).
+:- type two_id_sub_lines	==	sub_lines(two_id).
+
+:- type one_id_line		== line_group(one_id, unit).
+:- type two_id_line		== line_group(two_id, unit).
+:- type one_id_line_group	== line_group(one_id, one_id_sub_lines).
+:- type two_id_line_group	== line_group(two_id, two_id_sub_lines).
+:- type one_two_id_line_group	== line_group(one_id, two_id_sub_lines).
+
+	% This function takes a context description (which may be empty)
+	% and a HTML string describing all fields on in row but the first,
+	% and returns the HTML for the full row.
+:- func add_context(string, line_group(one_id, LL)) = line_group(two_id, LL).
+
+	% This adds the context from the line group to the HTML as the first
+	% field of the resulting line group.
+:- func add_self_context(line_group(one_id, LL)) = line_group(two_id, LL).
+
+:- func add_ranks(list(line_group(one_id, LL))) = list(line_group(two_id, LL)).
+
+:- func	line_to_one_id_subline_group(line_group(FL, unit))
+	= line_group(FL, one_id_sub_lines).
+
+:- func	line_to_two_id_subline_group(line_group(FL, unit))
+	= line_group(FL, two_id_sub_lines).
+
+:- func one_id_line_to_html(preferences, deep, totals_disposition,
+	one_id_line) = string.
+:- func one_id_line_group_to_html(preferences, deep, totals_disposition,
+	one_id_line_group) = string.
+:- func two_id_line_to_html(preferences, deep, totals_disposition,
+	two_id_line) = string.
+:- func two_id_line_group_to_html(preferences, deep, totals_disposition,
+	two_id_line_group) = string.
+
+:- func own_and_desc_to_html(own_prof_info, inherit_prof_info,
+	preferences, deep, totals_disposition) = string.
+
+:- pred lookup_ticks_per_sec(profile_stats::in, int::out, bool::out) is det.
+
+:- func proc_dynamic_name(deep, proc_dynamic_ptr) = string.
+:- func proc_static_name(deep, proc_static_ptr) = string.
+
+:- pred proc_dynamic_context(deep::in, proc_dynamic_ptr::in,
+	string::out, int::out) is det.
+:- pred proc_static_context(deep::in, proc_static_ptr::in,
+	string::out, int::out) is det.
+:- pred call_site_context(deep::in, call_site_static_ptr::in,
+	string::out, int::out) is det.
+
+:- pred proc_static_to_line_group_info(preferences::in, deep::in,
+	proc_static_ptr::in, string::out, int::out, string::out, string::out)
+	is det.
+:- func proc_static_to_html_ref(preferences, deep, proc_static_ptr) = string.
+:- func module_name_to_html_ref(preferences, deep, string) = string.
+:- func clique_ptr_to_html_ref(preferences, deep, string, clique_ptr) = string.
+:- func deep_cmd_pref_to_url(preferences, deep, cmd) = string.
+
+:- func plural(int) = string.
+
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module top_procs.
+:- import_module int, float, char, string, map, require.
+
+%-----------------------------------------------------------------------------%
+
+page_banner(_Cmd, Pref) =
+	"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.01//EN""\n" ++
+	"""http://www.w3.org/TR/html4/strict.dtd"">\n" ++
+	"<HTML>\n" ++
+	"<HEAD>\n" ++
+	"<TITLE>Page created by the Mercury Deep Profiler.</TITLE>\n" ++
+	banner_style(Pref) ++
+	"</HEAD>\n" ++
+	"<BODY>\n".
+
+:- func banner_style(preferences) = string.
+
+banner_style(Pref) = HTML :-
+	Fields = Pref ^ pref_fields,
+
+	GroupNum0 = 0,
+	IdStyle = string__format("  TD.id     { %s }\n",
+		[s(select_colgroup_background(Pref, GroupNum0))]),
+	GroupNum1 = GroupNum0 + 1,
+	( Fields ^ port_fields = no_port ->
+		PortStyle = "",
+		GroupNum2 = GroupNum1
+	;
+		PortStyle = string__format("  TD.port   { %s }\n",
+			[s(select_colgroup_background(Pref, GroupNum1))]),
+		GroupNum2 = GroupNum1 + 1
+	),
+	( Fields ^ time_fields = no_time ->
+		TimeStyle = "",
+		GroupNum3 = GroupNum2
+	;
+		TimeStyle = string__format("  TD.time   { %s }\n",
+			[s(select_colgroup_background(Pref, GroupNum2))]),
+		GroupNum3 = GroupNum2 + 1
+	),
+	( Fields ^ alloc_fields = no_alloc ->
+		AllocStyle = "",
+		GroupNum4 = GroupNum3
+	;
+		AllocStyle = string__format("  TD.alloc  { %s }\n",
+			[s(select_colgroup_background(Pref, GroupNum3))]),
+		GroupNum4 = GroupNum3 + 1
+	),
+	( Fields ^ memory_fields = no_memory ->
+		MemoryStyle = ""
+	;
+		MemoryStyle = string__format("  TD.memory { %s }\n",
+			[s(select_colgroup_background(Pref, GroupNum4))])
+	),
+	HTML =
+		"<STYLE TYPE=""text/css"">\n" ++
+		IdStyle ++
+		PortStyle ++
+		TimeStyle ++
+		AllocStyle ++
+		MemoryStyle ++
+		"</STYLE>\n".
+
+:- func select_colgroup_background(preferences, int) = string.
+
+select_colgroup_background(Pref, N) = HTML :-
+	(
+		Pref ^ pref_colour = column_groups,
+		( N /\ 1 = 0 ->
+			Background = even_background
+		;
+			Background = odd_background
+		),
+		string__format("background: %s", [s(Background)], HTML)
+	;
+		Pref ^ pref_colour = none,
+		HTML = ""
+	).
+
+:- func even_background = string.
+:- func odd_background = string.
+
+even_background = "rgb(255, 255, 240)".
+odd_background =  "rgb(240, 240, 255)".
+
+%-----------------------------------------------------------------------------%
+
+table_start(Pref) = HTML :-
+	(
+		Pref ^ pref_box = nobox,
+		HTML = "\n<TABLE width=100%>\n"
+	;
+		Pref ^ pref_box = box,
+		HTML = "\n<TABLE width=100% border=1>\n"
+	).
+
+table_end(_) = "</TABLE>\n".
+
+%-----------------------------------------------------------------------------%
+
+page_footer(Cmd, Pref, Deep) =
+	"<p>\n" ++
+	footer_pref_toggles(Cmd, Pref, Deep) ++
+	"<br>\n" ++
+	string__format("<A HREF=""%s"">Menu</A>\n",
+		[s(deep_cmd_pref_to_url(Pref, Deep, menu))]) ++
+	string__format("<A HREF=""%s"">Restart</A>\n",
+		[s(deep_cmd_pref_to_url(Pref, Deep, restart))]) ++
+	string__format("<A HREF=""%s"">Quit</A>\n",
+		[s(deep_cmd_pref_to_url(Pref, Deep, quit))]) ++
+	"</BODY>\n" ++
+	"</HTML>\n".
+
+:- func footer_pref_toggles(cmd, preferences, deep) = string.
+
+footer_pref_toggles(Cmd, Pref, Deep) = AllToggles :-
+	RelevantToggles = command_relevant_toggles(Cmd),
+	( list__member(toggle_fields, RelevantToggles) ->
+		FieldToggle = footer_field_toggle(Cmd, Pref, Deep)
+	;
+		FieldToggle = ""
+	),
+	( list__member(toggle_ancestor_limit, RelevantToggles) ->
+		AncestorToggle =
+			footer_ancestor_toggle(Cmd, Pref, Deep) ++
+			"<br>\n"
+	;
+		AncestorToggle = ""
+	),
+	( list__member(toggle_order_criteria, RelevantToggles) ->
+		OrderToggle =
+			footer_order_criteria_toggle(Cmd, Pref, Deep) ++
+			"<br>\n"
+	;
+		OrderToggle = ""
+	),
+	( list__member(toggle_summarize, RelevantToggles) ->
+		SummarizeToggle = footer_summarize_toggle(Cmd, Pref, Deep)
+	;
+		SummarizeToggle = ""
+	),
+	( list__member(toggle_contour, RelevantToggles) ->
+		ContourToggle = footer_contour_toggle(Cmd, Pref, Deep)
+	;
+		ContourToggle = ""
+	),
+	( list__member(toggle_time_format, RelevantToggles) ->
+		TimeFormatToggle = footer_time_format_toggle(Cmd, Pref, Deep)
+	;
+		TimeFormatToggle = ""
+	),
+	( list__member(toggle_colour, RelevantToggles) ->
+		ColourToggle = footer_colour_toggle(Cmd, Pref, Deep)
+	;
+		ColourToggle = ""
+	),
+	( list__member(toggle_box, RelevantToggles) ->
+		BoxToggle = footer_box_toggle(Cmd, Pref, Deep)
+	;
+		BoxToggle = ""
+	),
+	AllToggles =
+		FieldToggle ++
+		AncestorToggle ++
+		OrderToggle ++
+		SummarizeToggle ++
+		ContourToggle ++
+		TimeFormatToggle ++
+		ColourToggle ++
+		BoxToggle.
+
+%-----------------------------------------------------------------------------%
+
+:- type toggle_kind
+	--->	toggle_fields
+	;	toggle_box
+	;	toggle_colour
+	;	toggle_ancestor_limit
+	;	toggle_summarize
+	;	toggle_order_criteria
+	;	toggle_contour
+	;	toggle_time_format.
+
+:- func command_relevant_toggles(cmd) = list(toggle_kind).
+
+command_relevant_toggles(quit) = [].
+command_relevant_toggles(restart) = [].
+command_relevant_toggles(timeout(_)) = [].
+command_relevant_toggles(menu) = [].
+command_relevant_toggles(root(_)) =
+	command_relevant_toggles(clique(1)). % The clique num doesn't matter.
+command_relevant_toggles(clique(_)) =
+	[toggle_fields, toggle_box, toggle_colour, toggle_ancestor_limit,
+	toggle_summarize, toggle_order_criteria, toggle_time_format].
+command_relevant_toggles(proc(_)) =
+	[toggle_fields, toggle_box, toggle_colour, toggle_summarize,
+	toggle_order_criteria, toggle_time_format].
+command_relevant_toggles(proc_callers(_, _, _)) =
+	[toggle_fields, toggle_box, toggle_colour, toggle_order_criteria,
+	toggle_contour, toggle_time_format].
+command_relevant_toggles(modules) =
+	[toggle_fields, toggle_box, toggle_colour, toggle_order_criteria,
+	toggle_time_format].
+command_relevant_toggles(module(_)) =
+	[toggle_fields, toggle_box, toggle_colour, toggle_order_criteria,
+	toggle_time_format].
+command_relevant_toggles(top_procs(_, _, _, _)) =
+	[toggle_fields, toggle_box, toggle_colour, toggle_time_format].
+command_relevant_toggles(proc_static(_)) = [].
+command_relevant_toggles(proc_dynamic(_)) = [].
+command_relevant_toggles(call_site_static(_)) = [].
+command_relevant_toggles(call_site_dynamic(_)) = [].
+command_relevant_toggles(raw_clique(_)) = [].
+
+:- func footer_field_toggle(cmd, preferences, deep) = string.
+
+footer_field_toggle(Cmd, Pref, Deep) = HTML :-
+	Fields = Pref ^ pref_fields,
+	( Fields ^ port_fields = no_port ->
+		Port1Toggle = ""
+	;
+		Port1Fields = Fields ^ port_fields := no_port,
+		Port1Pref = Pref ^ pref_fields := Port1Fields,
+		Port1Msg = "No port counts",
+		Port1Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Port1Pref, Deep, Cmd)),
+			s(Port1Msg)])
+	),
+	( Fields ^ port_fields = port ->
+		Port2Toggle = ""
+	;
+		Port2Fields = Fields ^ port_fields := port,
+		Port2Pref = Pref ^ pref_fields := Port2Fields,
+		Port2Msg = "Port counts",
+		Port2Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Port2Pref, Deep, Cmd)),
+			s(Port2Msg)])
+	),
+	( Fields ^ time_fields = no_time ->
+		Time1Toggle = ""
+	;
+		Time1Fields = Fields ^ time_fields := no_time,
+		Time1Pref = Pref ^ pref_fields := Time1Fields,
+		Time1Msg = "No time info",
+		Time1Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Time1Pref, Deep, Cmd)),
+			s(Time1Msg)])
+	),
+	( Fields ^ time_fields = ticks ->
+		Time2Toggle = ""
+	;
+		Time2Fields = Fields ^ time_fields := ticks,
+		Time2Pref = Pref ^ pref_fields := Time2Fields,
+		Time2Msg = "Ticks",
+		Time2Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Time2Pref, Deep, Cmd)),
+			s(Time2Msg)])
+	),
+	( Fields ^ time_fields = time ->
+		Time3Toggle = ""
+	;
+		Time3Fields = Fields ^ time_fields := time,
+		Time3Pref = Pref ^ pref_fields := Time3Fields,
+		Time3Msg = "Ticks and times",
+		Time3Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Time3Pref, Deep, Cmd)),
+			s(Time3Msg)])
+	),
+	( Fields ^ time_fields = ticks_and_time ->
+		Time4Toggle = ""
+	;
+		Time4Fields = Fields ^ time_fields := ticks_and_time,
+		Time4Pref = Pref ^ pref_fields := Time4Fields,
+		Time4Msg = "Ticks and times",
+		Time4Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Time4Pref, Deep, Cmd)),
+			s(Time4Msg)])
+	),
+	( Fields ^ time_fields = time_and_percall ->
+		Time5Toggle = ""
+	;
+		Time5Fields = Fields ^ time_fields := time_and_percall,
+		Time5Pref = Pref ^ pref_fields := Time5Fields,
+		Time5Msg = "Times and per-call times",
+		Time5Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Time5Pref, Deep, Cmd)),
+			s(Time5Msg)])
+	),
+	( Fields ^ time_fields = ticks_and_time_and_percall ->
+		Time6Toggle = ""
+	;
+		Time6Fields = Fields ^ time_fields :=
+			ticks_and_time_and_percall,
+		Time6Pref = Pref ^ pref_fields := Time6Fields,
+		Time6Msg = "Ticks and times and per-call times",
+		Time6Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Time6Pref, Deep, Cmd)),
+			s(Time6Msg)])
+	),
+	( Fields ^ alloc_fields = no_alloc ->
+		Alloc1Toggle = ""
+	;
+		Alloc1Fields = Fields ^ alloc_fields := no_alloc,
+		Alloc1Pref = Pref ^ pref_fields := Alloc1Fields,
+		Alloc1Msg = "No allocations",
+		Alloc1Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Alloc1Pref, Deep, Cmd)),
+			s(Alloc1Msg)])
+	),
+	( Fields ^ alloc_fields = alloc ->
+		Alloc2Toggle = ""
+	;
+		Alloc2Fields = Fields ^ alloc_fields := alloc,
+		Alloc2Pref = Pref ^ pref_fields := Alloc2Fields,
+		Alloc2Msg = "Allocations",
+		Alloc2Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Alloc2Pref, Deep, Cmd)),
+			s(Alloc2Msg)])
+	),
+	( Fields ^ alloc_fields = alloc_and_percall ->
+		Alloc3Toggle = ""
+	;
+		Alloc3Fields = Fields ^ alloc_fields := alloc_and_percall,
+		Alloc3Pref = Pref ^ pref_fields := Alloc3Fields,
+		Alloc3Msg = "Allocations and per-call allocations",
+		Alloc3Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Alloc3Pref, Deep, Cmd)),
+			s(Alloc3Msg)])
+	),
+	( Fields ^ memory_fields = no_memory ->
+		Memory1Toggle = ""
+	;
+		Memory1Fields = Fields ^ memory_fields := no_memory,
+		Memory1Pref = Pref ^ pref_fields := Memory1Fields,
+		Memory1Msg = "No memory info",
+		Memory1Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Memory1Pref, Deep, Cmd)),
+			s(Memory1Msg)])
+	),
+	( Fields ^ memory_fields = memory(words) ->
+		Memory2Toggle = ""
+	;
+		Memory2Fields = Fields ^ memory_fields := memory(words),
+		Memory2Pref = Pref ^ pref_fields := Memory2Fields,
+		Memory2Msg = "Words",
+		Memory2Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Memory2Pref, Deep, Cmd)),
+			s(Memory2Msg)])
+	),
+	( Fields ^ memory_fields = memory(bytes) ->
+		Memory3Toggle = ""
+	;
+		Memory3Fields = Fields ^ memory_fields := memory(bytes),
+		Memory3Pref = Pref ^ pref_fields := Memory3Fields,
+		Memory3Msg = "Bytes",
+		Memory3Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Memory3Pref, Deep, Cmd)),
+			s(Memory3Msg)])
+	),
+	( Fields ^ memory_fields = memory_and_percall(words) ->
+		Memory4Toggle = ""
+	;
+		Memory4Fields = Fields ^ memory_fields :=
+			memory_and_percall(words),
+		Memory4Pref = Pref ^ pref_fields := Memory4Fields,
+		Memory4Msg = "Words and per-call words",
+		Memory4Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Memory4Pref, Deep, Cmd)),
+			s(Memory4Msg)])
+	),
+	( Fields ^ memory_fields = memory_and_percall(bytes) ->
+		Memory5Toggle = ""
+	;
+		Memory5Fields = Fields ^ memory_fields :=
+			memory_and_percall(bytes),
+		Memory5Pref = Pref ^ pref_fields := Memory5Fields,
+		Memory5Msg = "Bytes and per-call bytes",
+		Memory5Toggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Memory5Pref, Deep, Cmd)),
+			s(Memory5Msg)])
+	),
+	( Fields = default_fields ->
+		DefaultMsg  = "Restore defaults",
+		DefaultPref = Pref ^ pref_fields := default_fields,
+		DefaultToggle = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(DefaultPref, Deep, Cmd)),
+			s(DefaultMsg)])
+	;
+		DefaultToggle = ""
+	),
+	HTML =
+		"Toggle fields:\n" ++
+		DefaultToggle ++
+		Port1Toggle ++ Port2Toggle ++
+		Time1Toggle ++ Time2Toggle ++ Time3Toggle ++
+		Time4Toggle ++ Time5Toggle ++ Time6Toggle ++
+		"<br>\n" ++
+		Alloc1Toggle ++ Alloc2Toggle ++ Alloc3Toggle ++
+		Memory1Toggle ++ Memory2Toggle ++ Memory3Toggle ++
+		Memory4Toggle ++ Memory5Toggle ++
+		"<br>\n".
+
+:- func footer_ancestor_toggle(cmd, preferences, deep) = string.
+
+footer_ancestor_toggle(Cmd, Pref, Deep) = HTML :-
+	(
+		Pref ^ pref_anc = no,
+		Display1 = yes,
+		Display2 = yes,
+		Msg1 = "One ancestor",
+		Pref1 = Pref ^ pref_anc := yes(1),
+		Msg2 = "Two ancestors",
+		Pref2 = Pref ^ pref_anc := yes(2),
+		Msg3 = "Three ancestors",
+		Pref3 = Pref ^ pref_anc := yes(3),
+		Msg4 = "Five ancestors",
+		Pref4 = Pref ^ pref_anc := yes(5),
+		Msg5 = "Ten ancestors",
+		Pref5 = Pref ^ pref_anc := yes(10)
+	;
+		Pref ^ pref_anc = yes(OldAncestorLimit),
+		( OldAncestorLimit > 2 ->
+			Display1 = yes
+		;
+			Display1 = no
+		),
+		( OldAncestorLimit > 1 ->
+			Display2 = yes
+		;
+			Display2 = no
+		),
+		Msg1 = "Halve ancestors",
+		Pref1 = Pref ^ pref_anc := yes(OldAncestorLimit // 2),
+		Msg2 = "Remove an ancestor",
+		Pref2 = Pref ^ pref_anc := yes(OldAncestorLimit - 1),
+		Msg3 = "Add an ancestor",
+		Pref3 = Pref ^ pref_anc := yes(OldAncestorLimit + 1),
+		Msg4 = "Double ancestors",
+		Pref4 = Pref ^ pref_anc := yes(OldAncestorLimit * 2),
+		Msg5 = "Unlimited ancestors",
+		Pref5 = Pref ^ pref_anc := no
+	),
+	Toggle1 = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]),
+	Toggle2 = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Pref2, Deep, Cmd)), s(Msg2)]),
+	Toggle3 = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Pref3, Deep, Cmd)), s(Msg3)]),
+	Toggle4 = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Pref4, Deep, Cmd)), s(Msg4)]),
+	Toggle5 = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Pref5, Deep, Cmd)), s(Msg5)]),
+	(
+		Display1 = yes,
+		MaybeToggle1 = Toggle1
+	;
+		Display1 = no,
+		MaybeToggle1 = ""
+	),
+	(
+		Display2 = yes,
+		MaybeToggle2 = Toggle2
+	;
+		Display2 = no,
+		MaybeToggle2 = ""
+	),
+	HTML =
+		"Toggle ancestors:\n" ++
+		MaybeToggle1 ++ MaybeToggle2 ++ Toggle3 ++ Toggle4 ++ Toggle5.
+
+:- func footer_box_toggle(cmd, preferences, deep) = string.
+
+footer_box_toggle(Cmd, Pref, Deep) = HTML :-
+	(
+		Pref ^ pref_box = nobox,
+		Pref1 = Pref ^ pref_box := box,
+		Msg1 = "Box"
+	;
+		Pref ^ pref_box = box,
+		Pref1 = Pref ^ pref_box := nobox,
+		Msg1 = "Unbox"
+	),
+	HTML = string__format("<A HREF=""%s"">%s</A>\n",
+		[s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]).
+
+:- func footer_colour_toggle(cmd, preferences, deep) = string.
+
+footer_colour_toggle(Cmd, Pref, Deep) = HTML :-
+	(
+		Pref ^ pref_colour = none,
+		Pref1 = Pref ^ pref_colour := column_groups,
+		Msg1 = "Colour column groups"
+	;
+		Pref ^ pref_colour = column_groups,
+		Pref1 = Pref ^ pref_colour := none,
+		Msg1 = "Fade column groups"
+	),
+	HTML = string__format("<A HREF=""%s"">%s</A>\n",
+		[s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]).
+
+:- func footer_summarize_toggle(cmd, preferences, deep) = string.
+
+footer_summarize_toggle(Cmd, Pref, Deep) = HTML :-
+	(
+		Pref ^ pref_summarize = summarize,
+		Pref1 = Pref ^ pref_summarize := dont_summarize,
+		Msg1 = "Expand higher order calls"
+	;
+		Pref ^ pref_summarize = dont_summarize,
+		Pref1 = Pref ^ pref_summarize := summarize,
+		Msg1 = "Summarize higher order calls"
+	),
+	HTML = string__format("<A HREF=""%s"">%s</A>\n",
+		[s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]).
+
+:- func footer_contour_toggle(cmd, preferences, deep) = string.
+
+footer_contour_toggle(Cmd, Pref, Deep) = HTML :-
+	(
+		Pref ^ pref_contour = no_contour,
+		Pref1 = Pref ^ pref_contour := apply_contour,
+		Msg1 = "Apply contour exclusion"
+	;
+		Pref ^ pref_contour = apply_contour,
+		Pref1 = Pref ^ pref_contour := no_contour,
+		Msg1 = "Don't apply contour exclusion"
+	),
+	HTML = string__format("<A HREF=""%s"">%s</A>\n",
+		[s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)), s(Msg1)]).
+
+:- func footer_time_format_toggle(cmd, preferences, deep) = string.
+
+footer_time_format_toggle(Cmd, Pref, Deep) = HTML :-
+	(
+		Pref ^ pref_time = no_scale,
+		Pref1 = Pref ^ pref_time := scale_by_millions,
+		Msg1  = "Time in s, us",
+		Pref2 = Pref ^ pref_time := scale_by_thousands,
+		Msg2  = "Time in s, ms, us, ns"
+	;
+		Pref ^ pref_time = scale_by_millions,
+		Pref1 = Pref ^ pref_time := no_scale,
+		Msg1  = "Time in s",
+		Pref2 = Pref ^ pref_time := scale_by_thousands,
+		Msg2  = "Time in s, ms, us, ns"
+	;
+		Pref ^ pref_time = scale_by_thousands,
+		Pref1 = Pref ^ pref_time := no_scale,
+		Msg1  = "Time in s",
+		Pref2 = Pref ^ pref_time := scale_by_millions,
+		Msg2  = "Time in s, us"
+	),
+	HTML =
+		string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Pref1, Deep, Cmd)),
+			s(Msg1)]) ++
+		string__format("<A HREF=""%s"">%s</A>\n",
+			[s(deep_cmd_pref_to_url(Pref2, Deep, Cmd)),
+			s(Msg2)]).
+
+%-----------------------------------------------------------------------------%
+
+:- func footer_order_criteria_toggle(cmd, preferences, deep) = string.
+
+footer_order_criteria_toggle(Cmd, Pref, Deep) =
+	toggle_criteria(Pref ^ pref_criteria,
+		update_criteria_in_prefs(Pref, Deep, Cmd),
+		update_cost_criteria_in_prefs(Pref, Deep, Cmd)).
+
+toggle_cost_criteria_in_top_procs_cmd(Pref, Deep, Limit,
+		CostKind, InclDesc, Scope) =
+	toggle_cost_criteria(CostKind, InclDesc, Scope,
+		update_cost_criteria_in_top_procs_cmd(Pref, Deep, Limit)).
+
+%-----------------------------------------------------------------------------%
+
+:- type update_criteria_func ==
+	(func(order_criteria) = string).
+
+:- type update_cost_criteria_func ==
+	(func(cost_kind, include_descendants, measurement_scope) = string).
+
+:- func toggle_criteria(order_criteria,
+	update_criteria_func, update_cost_criteria_func) = string.
+
+toggle_criteria(Criteria, UpdateCriteria, UpdateCostCriteria) = HTML :-
+	(
+		Criteria = by_context,
+		Criteria1 = by_name,
+		Msg1 = "Sort by name",
+		Criteria2 = by_cost(default_cost_kind,
+			default_incl_desc, default_scope),
+		Msg2 = "Sort by cost"
+	;
+		Criteria = by_name,
+		Criteria1 = by_context,
+		Msg1 = "Sort by context",
+		Criteria2 = by_cost(default_cost_kind,
+			default_incl_desc, default_scope),
+		Msg2 = "Sort by cost"
+	;
+		Criteria = by_cost(_, _, _),
+		Criteria1 = by_context,
+		Msg1 = "Sort by context",
+		Criteria2 = by_name,
+		Msg2 = "Sort by name"
+	),
+	Toggle1 = string__format("<A HREF=""%s"">%s</A>\n",
+		[s(UpdateCriteria(Criteria1)), s(Msg1)]),
+	Toggle2 = string__format("<A HREF=""%s"">%s</A>\n",
+		[s(UpdateCriteria(Criteria2)), s(Msg2)]),
+	( Criteria = by_cost(CostKind, InclDesc, Scope) ->
+		ToggleRest = toggle_cost_criteria(CostKind, InclDesc, Scope,
+			UpdateCostCriteria)
+	;
+		ToggleRest = ""
+	),
+	HTML =
+		"Toggle ordering criteria:\n" ++
+		Toggle1 ++ Toggle2 ++ ToggleRest.
+
+:- func toggle_cost_criteria(cost_kind, include_descendants, measurement_scope,
+	update_cost_criteria_func) = string.
+
+toggle_cost_criteria(CostKind, InclDesc, Scope, UpdateCriteria) = Toggles :-
+	( CostKind \= calls ->	
+		Msg1 = "Sort by calls",
+		Toggle1 = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(UpdateCriteria(calls, InclDesc, Scope)),
+			s(Msg1)])
+	;
+		Toggle1 = ""
+	),
+	( CostKind \= time ->	
+		Msg2 = "Sort by time",
+		Toggle2 = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(UpdateCriteria(time, InclDesc, Scope)),
+			s(Msg2)])
+	;
+		Toggle2 = ""
+	),
+	( CostKind \= allocs ->	
+		Msg3 = "Sort by allocations",
+		Toggle3 = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(UpdateCriteria(allocs, InclDesc, Scope)),
+			s(Msg3)])
+	;
+		Toggle3 = ""
+	),
+	( CostKind \= words ->	
+		Msg4 = "Sort by words",
+		Toggle4 = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(UpdateCriteria(words, InclDesc, Scope)),
+			s(Msg4)])
+	;
+		Toggle4 = ""
+	),
+	(
+		InclDesc = self,
+		Msg5 = "Include descendants",
+		Toggle5 = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(UpdateCriteria(CostKind, self_and_desc, Scope)),
+			s(Msg5)])
+	;
+		InclDesc = self_and_desc,
+		Msg5 = "Exclude descendants",
+		Toggle5 = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(UpdateCriteria(CostKind, self, Scope)),
+			s(Msg5)])
+	),
+	(
+		Scope = per_call,
+		Msg6 = "Count overall cost",
+		Toggle6 = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(UpdateCriteria(CostKind, InclDesc, overall)),
+			s(Msg6)])
+	;
+		Scope = overall,
+		Msg6 = "Count per-call cost",
+		Toggle6 = string__format("<A HREF=""%s"">%s</A>\n",
+			[s(UpdateCriteria(CostKind, InclDesc, per_call)),
+			s(Msg6)])
+	),
+	Toggles = Toggle1 ++ Toggle2 ++ Toggle3 ++ Toggle4
+		++ Toggle5 ++ Toggle6.
+
+%-----------------------------------------------------------------------------%
+
+:- func update_criteria_in_prefs(preferences, deep, cmd, order_criteria)
+	= string.
+
+update_criteria_in_prefs(Pref0, Deep, Cmd, Criteria) = HTML :-
+	Pref = Pref0 ^ pref_criteria := Criteria,
+	HTML = deep_cmd_pref_to_url(Pref, Deep, Cmd).
+
+:- func update_cost_criteria_in_prefs(preferences, deep, cmd,
+	cost_kind, include_descendants, measurement_scope) = string.
+
+update_cost_criteria_in_prefs(Pref0, Deep, Cmd, CostKind, InclDesc, Scope)
+		= HTML :-
+	Pref = Pref0 ^ pref_criteria := by_cost(CostKind, InclDesc, Scope),
+	HTML = deep_cmd_pref_to_url(Pref, Deep, Cmd).
+
+:- func update_cost_criteria_in_top_procs_cmd(preferences, deep, display_limit,
+	cost_kind, include_descendants, measurement_scope) = string.
+
+update_cost_criteria_in_top_procs_cmd(Pref, Deep, Limit,
+		CostKind, InclDesc, Scope) = HTML :-
+	Cmd = top_procs(Limit, CostKind, InclDesc, Scope),
+	HTML = deep_cmd_pref_to_url(Pref, Deep, Cmd).
+
+%-----------------------------------------------------------------------------%
+
+criteria_to_description(by_context) = "ordered by context".
+criteria_to_description(by_name) = "ordered by name".
+criteria_to_description(by_cost(CostKind, InclDesc, Scope)) =
+	cost_criteria_to_description(CostKind, InclDesc, Scope).
+
+cost_criteria_to_description(CostKind, InclDesc, Scope) = Desc :-
+	Desc = 
+		"ordered by " ++
+		incl_desc_to_description(InclDesc) ++ " " ++
+		cost_kind_to_description(CostKind) ++ " " ++
+		scope_to_description(Scope).
+
+:- func cost_kind_to_description(cost_kind) = string.
+
+cost_kind_to_description(calls) = "number of calls".
+cost_kind_to_description(time) = "time".
+cost_kind_to_description(allocs) = "memory allocations".
+cost_kind_to_description(words) = "words allocated".
+
+:- func incl_desc_to_description(include_descendants) = string.
+
+incl_desc_to_description(self) = "self".
+incl_desc_to_description(self_and_desc) = "total".
+
+:- func scope_to_description(measurement_scope) = string.
+
+scope_to_description(per_call) = "per call".
+scope_to_description(overall) = "overall".
+
+%-----------------------------------------------------------------------------%
+
+% The predicates fields_header, table_width and own_and_desc_to_html
+% all make decisions about what columns each row in the table will have.
+% They therefore have similar control structures, and a change in one
+% may require changes in the others as well.
+
+fields_header(Pref, IdFields, TotalsDisp, WrapFunc) = HTML :-
+	Fields = Pref ^ pref_fields,
+	ProcName = WrapFunc("Procedure", by_name),
+	ModuleName = WrapFunc("Module", by_name),
+	(
+		IdFields = source_proc,
+		Source = WrapFunc("Source", by_context),
+		FirstRow0 =
+			"<TR>\n" ++
+			string__format("<TH ALIGN=LEFT ROWSPAN=2>%s\n",
+				[s(Source)]) ++
+			string__format("<TH ALIGN=LEFT ROWSPAN=2>%s\n",
+				[s(ProcName)])
+	;
+		IdFields = rank_proc,
+		FirstRow0 =
+			"<TR>\n" ++
+			"<TH ALIGN=LEFT ROWSPAN=2>Rank\n" ++
+			string__format("<TH ALIGN=LEFT ROWSPAN=2>%s\n",
+				[s(ProcName)])
+	;
+		IdFields = rank_module,
+		FirstRow0 =
+			"<TR>\n" ++
+			"<TH ALIGN=LEFT ROWSPAN=2>Rank\n" ++
+			string__format("<TH ALIGN=LEFT ROWSPAN=2>%s\n",
+				[s(ModuleName)])
+	;
+		IdFields = proc,
+		FirstRow0 =
+			"<TR>\n" ++
+			string__format("<TH ALIGN=LEFT ROWSPAN=2>%s\n",
+				[s(ProcName)])
+	),
+	SecondRow0 = "<TR>\n",
+	( show_port_counts(Fields) = yes ->
+		Calls = WrapFunc("Calls", by_cost(calls, self, overall)),
+		FirstRow1 = FirstRow0 ++
+			"<TH COLSPAN=4>Port counts\n",
+		SecondRow1 = SecondRow0 ++
+			string__format("<TH ALIGN=RIGHT>%s\n",
+				[s(Calls)]) ++
+			"<TH ALIGN=RIGHT>Exits\n" ++
+			"<TH ALIGN=RIGHT>Fails\n" ++
+			"<TH ALIGN=RIGHT>Redos\n"
+	;
+		FirstRow1 = FirstRow0,
+		SecondRow1 = SecondRow0
+	),
+	( show_quanta(Fields) = yes ->
+		TicksSelfOverall = WrapFunc("Self",
+			by_cost(time, self, overall)),
+		TicksSelfHeading =
+			string__format("<TH ALIGN=RIGHT>%s\n",
+				[s(TicksSelfOverall)]),
+		TicksSelfFields = 1
+	;
+		TicksSelfHeading = "",
+		TicksSelfFields = 0
+	),
+	( show_times(Fields) = yes ->
+		( show_quanta(Fields) = yes ->
+			TimeSelfOverall = WrapFunc("Time",
+				by_cost(time, self, overall))
+		;
+			TimeSelfOverall = WrapFunc("Self",
+				by_cost(time, self, overall))
+		),
+		TimeSelfHeading =
+			string__format("<TH ALIGN=RIGHT>%s\n",
+				[s(TimeSelfOverall)]),
+		TimeSelfFields = 1
+	;
+		TimeSelfHeading = "",
+		TimeSelfFields = 0
+	),
+	( ( show_quanta(Fields) = yes ; show_times(Fields) = yes ) ->
+		TimeSelfPercentHeading = "<TH ALIGN=RIGHT>%\n",
+		TimeSelfPercentFields = 1
+	;
+		TimeSelfPercentHeading = "",
+		TimeSelfPercentFields = 0
+	),
+	( show_per_times(Fields) = yes ->
+		TimeSelfPerCall = WrapFunc("/call",
+			by_cost(time, self, per_call)),
+		TimeSelfPerCallHeading =
+			string__format("<TH ALIGN=RIGHT>%s\n",
+				[s(TimeSelfPerCall)]),
+		TimeSelfPerCallFields = 1
+	;
+		TimeSelfPerCallHeading = "",
+		TimeSelfPerCallFields = 0
+	),
+	( TotalsDisp = totals_meaningful, show_quanta(Fields) = yes ->
+		TicksTotalOverall = WrapFunc("Total",
+			by_cost(time, self_and_desc, overall)),
+		TicksTotalHeading =
+			string__format("<TH ALIGN=RIGHT>%s\n",
+				[s(TicksTotalOverall)]),
+		TicksTotalFields = 1
+	;
+		TicksTotalHeading = "",
+		TicksTotalFields = 0
+	),
+	( TotalsDisp = totals_meaningful, show_times(Fields) = yes ->
+		( show_quanta(Fields) = yes ->
+			TimeTotalOverall = WrapFunc("Time",
+				by_cost(time, self_and_desc, overall))
+		;
+			TimeTotalOverall = WrapFunc("Total",
+				by_cost(time, self_and_desc, overall))
+		),
+		TimeTotalHeading =
+			string__format("<TH ALIGN=RIGHT>%s\n",
+				[s(TimeTotalOverall)]),
+		TimeTotalFields = 1
+	;
+		TimeTotalHeading = "",
+		TimeTotalFields = 0
+	),
+	(
+		TotalsDisp = totals_meaningful,
+		( show_quanta(Fields) = yes ; show_times(Fields) = yes )
+	->
+		TimeTotalPercentHeading = "<TH ALIGN=RIGHT>%\n",
+		TimeTotalPercentFields = 1
+	;
+		TimeTotalPercentHeading = "",
+		TimeTotalPercentFields = 0
+	),
+	( TotalsDisp = totals_meaningful, show_per_times(Fields) = yes ->
+		TimeTotalPerCall = WrapFunc("/call",
+			by_cost(time, self_and_desc, per_call)),
+		TimeTotalPerCallHeading =
+			string__format("<TH ALIGN=RIGHT>%s\n",
+				[s(TimeTotalPerCall)]),
+		TimeTotalPerCallFields = 1
+	;
+		TimeTotalPerCallHeading = "",
+		TimeTotalPerCallFields = 0
+	),
+	TimeFields =
+		TicksSelfFields + TimeSelfFields +
+		TimeSelfPercentFields + TimeSelfPerCallFields +
+		TicksTotalFields + TimeTotalFields +
+		TimeTotalPercentFields + TimeTotalPerCallFields,
+	SecondRow2 = SecondRow1 ++
+		TicksSelfHeading ++ TimeSelfHeading ++
+		TimeSelfPercentHeading ++ TimeSelfPerCallHeading ++
+		TicksTotalHeading ++ TimeTotalHeading ++
+		TimeTotalPercentHeading ++ TimeTotalPerCallHeading,
+	( show_quanta(Fields) = yes, show_times(Fields) = yes ->
+		FirstRow2 = FirstRow1 ++
+			string__format(
+				"<TH COLSPAN=%d>Clock ticks and times\n",
+				[i(TimeFields)])
+	; show_quanta(Fields) = yes ->
+		FirstRow2 = FirstRow1 ++
+			string__format(
+				"<TH COLSPAN=%d>Clock ticks\n",
+				[i(TimeFields)])
+	; show_times(Fields) = yes ->
+		FirstRow2 = FirstRow1 ++
+			string__format(
+				"<TH COLSPAN=%d>Time\n",
+				[i(TimeFields)])
+	;
+		FirstRow2 = FirstRow1
+	),
+	( show_alloc(Fields) = yes ->
+		AllocsSelfOverall = WrapFunc("Self",
+			by_cost(allocs, self, overall)),
+		AllocsSelfHeading =
+			string__format("<TH ALIGN=RIGHT>%s\n",
+				[s(AllocsSelfOverall)]) ++
+			"<TH ALIGN=RIGHT>%\n",
+		AllocsSelfFields = 2
+	;
+		AllocsSelfHeading = "",
+		AllocsSelfFields = 0
+	),
+	( show_per_alloc(Fields) = yes ->
+		AllocsSelfPerCall = WrapFunc("/call",
+			by_cost(allocs, self, per_call)),
+		AllocsSelfPerCallHeading =
+			string__format("<TH ALIGN=RIGHT>%s\n",
+				[s(AllocsSelfPerCall)]),
+		AllocsSelfPerCallFields = 1
+	;
+		AllocsSelfPerCallHeading = "",
+		AllocsSelfPerCallFields = 0
+	),
+	( TotalsDisp = totals_meaningful, show_alloc(Fields) = yes ->
+		AllocsTotalOverall = WrapFunc("Total",
+			by_cost(allocs, self_and_desc, overall)),
+		AllocsTotalHeading =
+			string__format("<TH ALIGN=RIGHT>%s\n",
+				[s(AllocsTotalOverall)]) ++
+			"<TH ALIGN=RIGHT>%\n",
+		AllocsTotalFields = 2
+	;
+		AllocsTotalHeading = "",
+		AllocsTotalFields = 0
+	),
+	( TotalsDisp = totals_meaningful, show_per_alloc(Fields) = yes ->
+		AllocsTotalPerCall = WrapFunc("/call",
+			by_cost(allocs, self_and_desc, per_call)),
+		AllocsTotalPerCallHeading =
+			string__format("<TH ALIGN=RIGHT>%s\n",
+				[s(AllocsTotalPerCall)]),
+		AllocsTotalPerCallFields = 1
+	;
+		AllocsTotalPerCallHeading = "",
+		AllocsTotalPerCallFields = 0
+	),
+	AllocsFields =
+		AllocsSelfFields + AllocsSelfPerCallFields +
+		AllocsTotalFields + AllocsTotalPerCallFields,
+	SecondRow3 = SecondRow2 ++
+		AllocsSelfHeading ++ AllocsSelfPerCallHeading ++
+		AllocsTotalHeading ++ AllocsTotalPerCallHeading,
+	( show_alloc(Fields) = yes ->
+		FirstRow3 = FirstRow2 ++
+			string__format(
+				"<TH COLSPAN=%d>Memory allocations\n",
+				[i(AllocsFields)])
+	;
+		FirstRow3 = FirstRow2
+	),
+	( show_memory(Fields) = yes(_) ->
+		MemorySelfOverall = WrapFunc("Self",
+			by_cost(words, self, overall)),
+		MemorySelfHeading =
+			string__format("<TH ALIGN=RIGHT>%s\n",
+				[s(MemorySelfOverall)]) ++
+			"<TH ALIGN=RIGHT>%\n",
+		MemorySelfFields = 2
+	;
+		MemorySelfHeading = "",
+		MemorySelfFields = 0
+	),
+	( show_per_memory(Fields) = yes(_) ->
+		MemorySelfPerCall = WrapFunc("/call",
+			by_cost(words, self, per_call)),
+		MemorySelfPerCallHeading =
+			string__format("<TH ALIGN=RIGHT>%s\n",
+				[s(MemorySelfPerCall)]),
+		MemorySelfPerCallFields = 1
+	;
+		MemorySelfPerCallHeading = "",
+		MemorySelfPerCallFields = 0
+	),
+	( TotalsDisp = totals_meaningful, show_memory(Fields) = yes(_) ->
+		MemoryTotalOverall = WrapFunc("Total",
+			by_cost(words, self_and_desc, overall)),
+		MemoryTotalHeading =
+			string__format("<TH ALIGN=RIGHT>%s\n",
+				[s(MemoryTotalOverall)]) ++
+			"<TH ALIGN=RIGHT>%\n",
+		MemoryTotalFields = 2
+	;
+		MemoryTotalHeading = "",
+		MemoryTotalFields = 0
+	),
+	( TotalsDisp = totals_meaningful, show_per_memory(Fields) = yes(_) ->
+		MemoryTotalPerCall = WrapFunc("/call",
+			by_cost(words, self_and_desc, per_call)),
+		MemoryTotalPerCallHeading =
+			string__format("<TH ALIGN=RIGHT>%s\n",
+				[s(MemoryTotalPerCall)]),
+		MemoryTotalPerCallFields = 1
+	;
+		MemoryTotalPerCallHeading = "",
+		MemoryTotalPerCallFields = 0
+	),
+	MemoryFields =
+		MemorySelfFields + MemorySelfPerCallFields +
+		MemoryTotalFields + MemoryTotalPerCallFields,
+	SecondRow4 = SecondRow3 ++
+		MemorySelfHeading ++ MemorySelfPerCallHeading ++
+		MemoryTotalHeading ++ MemoryTotalPerCallHeading,
+	( show_memory(Fields) = yes(Units) ->
+		(
+			Units = words,
+			FirstRow4 = FirstRow3 ++
+				string__format(
+					"<TH COLSPAN=%d>Memory words\n",
+					[i(MemoryFields)])
+		;
+			Units = bytes,
+			FirstRow4 = FirstRow3 ++
+				string__format(
+					"<TH COLSPAN=%d>Memory bytes\n",
+					[i(MemoryFields)])
+		)
+	;
+		FirstRow4 = FirstRow3
+	),
+	HTML =
+		"<THEAD>\n" ++
+		FirstRow4 ++
+		SecondRow4 ++
+		"<TBODY>\n" ++
+		separator_row(Pref, IdFields, TotalsDisp).
+
+%-----------------------------------------------------------------------------%
+
+header_row(Heading, Pref, IdFields, TotalsDisp) = Separator :-
+	Separator = string__format("<TR><TD COLSPAN=%d>%s</TD></TR>\n",
+		[i(table_width(Pref, IdFields, TotalsDisp)), s(Heading)]).
+
+separator_row(Pref, IdFields, TotalsDisp) = Separator :-
+	Separator = string__format("<TR><TD COLSPAN=%d></TD></TR>\n",
+		[i(table_width(Pref, IdFields, TotalsDisp))]).
+
+:- func table_width(preferences, id_fields, totals_disposition) = int.
+
+table_width(Pref, IdFields, TotalsDisp) = Width :-
+	Fields = Pref ^ pref_fields,
+	(
+		IdFields = source_proc,
+		Id = 2
+	;
+		IdFields = rank_module,
+		Id = 2
+	;
+		IdFields = rank_proc,
+		Id = 2
+	;
+		IdFields = proc,
+		Id = 1
+	),
+	(
+		Fields ^ port_fields = no_port,
+		Port = 0
+	;
+		Fields ^ port_fields = port,
+		Port = 4
+	),
+	(
+		Fields ^ time_fields = no_time,
+		Time = 0
+	;
+		Fields ^ time_fields = ticks,
+		Time = 4
+	;
+		Fields ^ time_fields = time,
+		Time = 4
+	;
+		Fields ^ time_fields = ticks_and_time,
+		Time = 6
+	;
+		Fields ^ time_fields = time_and_percall,
+		Time = 6
+	;
+		Fields ^ time_fields = ticks_and_time_and_percall,
+		Time = 8
+	),
+	(
+		Fields ^ alloc_fields = no_alloc,
+		Alloc = 0
+	;
+		Fields ^ alloc_fields = alloc,
+		Alloc = 4
+	;
+		Fields ^ alloc_fields = alloc_and_percall,
+		Alloc = 6
+	),
+	(
+		Fields ^ memory_fields = no_memory,
+		Memory = 0
+	;
+		Fields ^ memory_fields = memory(_),
+		Memory = 4
+	;
+		Fields ^ memory_fields = memory_and_percall(_),
+		Memory = 6
+	),
+	(
+		TotalsDisp = totals_meaningful,
+		Width = Id + Port + Time + Alloc + Memory
+	;
+		TotalsDisp = totals_not_meaningful,
+		Width = Id + Port + Time // 2 + Alloc // 2 + Memory // 2
+	).
+
+%-----------------------------------------------------------------------------%
+
+add_context(Context, LineGroup0) = LineGroup :-
+	LineGroup0 = line_group(FileName, LineNumber, Name, Own, Desc,
+		HTML0, LaterLines),
+	HTML = string__format("<TD CLASS=id>%s</TD>%s",
+		[s(Context), s(HTML0)]),
+	LineGroup = line_group(FileName, LineNumber, Name, Own, Desc,
+		HTML, LaterLines).
+
+add_self_context(LineGroup0) = LineGroup :-
+	LineGroup0 = line_group(FileName, LineNumber, Name, Own, Desc,
+		HTML0, LaterLines),
+	HTML = string__format("<TD CLASS=id>%s:%d</TD>%s",
+		[s(FileName), i(LineNumber), s(HTML0)]),
+	LineGroup = line_group(FileName, LineNumber, Name, Own, Desc,
+		HTML, LaterLines).
+
+add_ranks(LineGroups0) = add_ranks_2(1, LineGroups0).
+
+:- func add_ranks_2(int, list(line_group(one_id, LL)))
+	= list(line_group(two_id, LL)).
+
+add_ranks_2(_Rank, []) = [].
+add_ranks_2(Rank, [LineGroup0 | LineGroups0]) = [LineGroup | LineGroups] :-
+	LineGroup0 = line_group(FileName, LineNumber, Name, Own, Desc,
+		HTML0, LaterLines),
+	HTML = string__format("<TD CLASS=id>%d</TD>%s",
+		[i(Rank), s(HTML0)]),
+	LineGroup = line_group(FileName, LineNumber, Name, Own, Desc,
+		HTML, LaterLines),
+	LineGroups = add_ranks_2(Rank + 1, LineGroups0).
+
+line_to_one_id_subline_group(LineGroup0) = LineGroup :-
+	LineGroup0 = line_group(FileName, LineNumber, Name, Own, Desc,
+		HTML, unit),
+	LineGroup = line_group(FileName, LineNumber, Name, Own, Desc,
+		HTML, sub_lines(one_id, [])).
+
+line_to_two_id_subline_group(LineGroup0) = LineGroup :-
+	LineGroup0 = line_group(FileName, LineNumber, Name, Own, Desc,
+		HTML, unit),
+	LineGroup = line_group(FileName, LineNumber, Name, Own, Desc,
+		HTML, sub_lines(two_id, [])).
+
+%-----------------------------------------------------------------------------%
+
+one_id_line_to_html(Pref, Deep, TotalsDisp, LineGroup) =
+	"<TR>\n" ++
+	LineGroup ^ group_first_line_id ++
+	own_and_desc_to_html(LineGroup ^ group_own, LineGroup ^ group_desc,
+		Pref, Deep, TotalsDisp) ++
+	"</TR>\n".
+
+one_id_line_group_to_html(Pref, Deep, TotalsDisp, LineGroup) =
+	"<TR>\n" ++
+	LineGroup ^ group_first_line_id ++
+	own_and_desc_to_html(LineGroup ^ group_own, LineGroup ^ group_desc,
+		Pref, Deep, TotalsDisp) ++
+	"</TR>\n" ++
+	string__append_list(
+		list__map(one_id_line_to_html(Pref, Deep, TotalsDisp),
+			LineGroup ^ group_later_lines ^ sub_line_list)).
+
+two_id_line_to_html(Pref, Deep, TotalsDisp, LineGroup) =
+	"<TR>\n" ++
+	LineGroup ^ group_first_line_id ++
+	own_and_desc_to_html(LineGroup ^ group_own, LineGroup ^ group_desc,
+		Pref, Deep, TotalsDisp) ++
+	"</TR>\n".
+
+two_id_line_group_to_html(Pref, Deep, TotalsDisp, LineGroup) =
+	"<TR>\n" ++
+	LineGroup ^ group_first_line_id ++
+	own_and_desc_to_html(LineGroup ^ group_own, LineGroup ^ group_desc,
+		Pref, Deep, TotalsDisp) ++
+	"</TR>\n" ++
+	string__append_list(
+		list__map(two_id_line_to_html(Pref, Deep, TotalsDisp),
+			LineGroup ^ group_later_lines ^ sub_line_list)).
+
+%-----------------------------------------------------------------------------%
+
+own_and_desc_to_html(Own, Desc, Pref, Deep, TotalsDisp) = HTML :-
+	add_own_to_inherit(Own, Desc) = OwnPlusDesc,
+	Root = root_total_info(Deep),
+	Calls = calls(Own),
+	Exits = exits(Own),
+	Fails = fails(Own),
+	Redos = redos(Own),
+
+	OwnQuanta = quanta(Own),
+	TotalQuanta = inherit_quanta(OwnPlusDesc),
+	RootQuanta = inherit_quanta(Root),
+	OwnQuantaProp = percentage(OwnQuanta, RootQuanta),
+	TotalQuantaProp = percentage(TotalQuanta, RootQuanta),
+
+	OwnAllocs = allocs(Own),
+	TotalAllocs = inherit_allocs(OwnPlusDesc),
+	RootAllocs = inherit_allocs(Root),
+	OwnAllocProp = percentage(OwnAllocs, RootAllocs),
+	TotalAllocProp = percentage(TotalAllocs, RootAllocs),
+
+	OwnWords = words(Own),
+	TotalWords = inherit_words(OwnPlusDesc),
+	RootWords = inherit_words(Root),
+	OwnMemoryProp = percentage(OwnWords, RootWords),
+	TotalMemoryProp = percentage(TotalWords, RootWords),
+
+	( show_memory(Fields) = yes(Unit) ->
+		(
+			Unit = words,
+			OwnMemory = OwnWords,
+			TotalMemory = TotalWords
+		;
+			Unit = bytes,
+			WordSize = Deep ^ profile_stats ^ word_size,
+			OwnMemory = OwnWords * WordSize,
+			TotalMemory = TotalWords * WordSize
+		)
+	;
+		% These values won't be used.
+		OwnMemory = 0,
+		TotalMemory = 0
+	),
+
+	Fields = Pref ^ pref_fields,
+	( show_port_counts(Fields) = yes ->
+		PortHTML =
+			string__format("<TD CLASS=port ALIGN=RIGHT>%s</TD>\n",
+				[s(commas(Calls))]) ++
+			string__format("<TD CLASS=port ALIGN=RIGHT>%s</TD>\n",
+				[s(commas(Exits))]) ++
+			string__format("<TD CLASS=port ALIGN=RIGHT>%s</TD>\n",
+				[s(commas(Fails))]) ++
+			string__format("<TD CLASS=port ALIGN=RIGHT>%s</TD>\n",
+				[s(commas(Redos))])
+	;
+		PortHTML = ""
+	),
+	( show_quanta(Fields) = yes ->
+		QuantaSelfHTML =
+			string__format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+				[s(commas(OwnQuanta))]),
+		QuantaTotalHTML =
+			string__format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+				[s(commas(TotalQuanta))])
+	;
+		QuantaSelfHTML = "",
+		QuantaTotalHTML = ""
+	),
+	( show_times(Fields) = yes ->
+		TimeSelfHTML =
+			string__format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+				[s(overall_time(Pref, Deep, OwnQuanta))]),
+		TimeTotalHTML =
+			string__format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+				[s(overall_time(Pref, Deep, TotalQuanta))])
+	;
+		TimeSelfHTML = "",
+		TimeTotalHTML = ""
+	),
+	( ( show_quanta(Fields) = yes ; show_times(Fields) = yes ) ->
+		QuantaPropSelfHTML =
+			string__format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+				[s(OwnQuantaProp)]),
+		QuantaPropTotalHTML =
+			string__format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+				[s(TotalQuantaProp)])
+	;
+		QuantaPropSelfHTML = "",
+		QuantaPropTotalHTML = ""
+	),
+	( show_per_times(Fields) = yes ->
+		TimePerCallSelfHTML =
+			string__format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+				[s(per_call_time(Pref, Deep,
+					OwnQuanta, Calls))]),
+		TimePerCallTotalHTML =
+			string__format("<TD CLASS=time ALIGN=RIGHT>%s</TD>\n",
+				[s(per_call_time(Pref, Deep,
+					TotalQuanta, Calls))])
+	;
+		TimePerCallSelfHTML = "",
+		TimePerCallTotalHTML = ""
+	),
+	( show_alloc(Fields) = yes ->
+		AllocSelfHTML =
+			string__format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
+				[s(commas(OwnAllocs))]) ++
+			string__format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
+				[s(OwnAllocProp)]),
+		AllocTotalHTML =
+			string__format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
+				[s(commas(TotalAllocs))]) ++
+			string__format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
+				[s(TotalAllocProp)])
+	;
+		AllocSelfHTML = "",
+		AllocTotalHTML = ""
+	),
+	( show_per_alloc(Fields) = yes ->
+		AllocPerCallSelfHTML =
+			string__format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
+				[s(count_per_call(OwnAllocs, Calls))]),
+		AllocPerCallTotalHTML =
+			string__format("<TD CLASS=alloc ALIGN=RIGHT>%s</TD>\n",
+				[s(count_per_call(TotalAllocs, Calls))])
+	;
+		AllocPerCallSelfHTML = "",
+		AllocPerCallTotalHTML = ""
+	),
+	( show_memory(Fields) = yes(_) ->
+		MemorySelfHTML =
+			string__format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
+				[s(commas(OwnMemory))]) ++
+			string__format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
+				[s(OwnMemoryProp)]),
+		MemoryTotalHTML =
+			string__format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
+				[s(commas(TotalMemory))]) ++
+			string__format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
+				[s(TotalMemoryProp)])
+	;
+		MemorySelfHTML = "",
+		MemoryTotalHTML = ""
+	),
+	( show_per_memory(Fields) = yes(_) ->
+		MemoryPerCallSelfHTML =
+			string__format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
+				[s(count_per_call(OwnMemory, Calls))]),
+		MemoryPerCallTotalHTML =
+			string__format("<TD CLASS=memory ALIGN=RIGHT>%s</TD>\n",
+				[s(count_per_call(TotalMemory, Calls))])
+	;
+		MemoryPerCallSelfHTML = "",
+		MemoryPerCallTotalHTML = ""
+	),
+	(
+		TotalsDisp = totals_meaningful,
+		HTML =
+			PortHTML ++
+
+			QuantaSelfHTML ++
+			TimeSelfHTML ++
+			QuantaPropSelfHTML ++
+			TimePerCallSelfHTML ++
+			QuantaTotalHTML ++
+			TimeTotalHTML ++
+			QuantaPropTotalHTML ++
+			TimePerCallTotalHTML ++
+
+			AllocSelfHTML ++
+			AllocPerCallSelfHTML ++
+			AllocTotalHTML ++
+			AllocPerCallTotalHTML ++
+
+			MemorySelfHTML ++
+			MemoryPerCallSelfHTML ++
+			MemoryTotalHTML ++
+			MemoryPerCallTotalHTML
+	;
+		TotalsDisp = totals_not_meaningful,
+		HTML =
+			PortHTML ++
+
+			QuantaSelfHTML ++
+			TimeSelfHTML ++
+			QuantaPropSelfHTML ++
+			TimePerCallSelfHTML ++
+
+			AllocSelfHTML ++
+			AllocPerCallSelfHTML ++
+
+			MemorySelfHTML ++
+			MemoryPerCallSelfHTML
+	).
+
+%-----------------------------------------------------------------------------%
+
+:- func overall_time(preferences, deep, int) = string.
+
+overall_time(Pref, Deep, Quanta) = TimeStr :-
+	lookup_ticks_per_sec(Deep ^ profile_stats, TicksPerSec, _Assumed),
+	% We display Time as seconds, with two digits after the decimal point.
+	% This is the most we can do, given clock granularity.
+	Time = float(Quanta) / float(TicksPerSec),
+	TimeStr = format_time(Pref, Time).
+
+:- func per_call_time(preferences, deep, int, int) = string.
+
+per_call_time(Pref, Deep, Quanta, Calls) = TimeStr :-
+	lookup_ticks_per_sec(Deep ^ profile_stats, TicksPerSec, _Assumed),
+	% We display Time as seconds, with two digits after the decimal point.
+	% This is the most we can do, given clock granularity.
+	Time = float(Quanta) / float(TicksPerSec),
+	TimePerCall = Time / float(Calls),
+	TimeStr = format_time(Pref, TimePerCall).
+
+:- func format_time(preferences, float) = string.
+
+format_time(Pref, Time) = TimeStr :-
+	(
+		Pref ^ pref_time = no_scale,
+		TimeStr0 = four_decimal_fraction(Time),
+		Unit = "s"
+	;
+		Pref ^ pref_time = scale_by_millions,
+		( Time >= 0.001 ->
+			ScaledTime = Time,
+			Unit = "s"
+		;
+			ScaledTime = 1000000.0 * Time,
+			Unit = "us"
+		),
+		TimeStr0 = four_decimal_fraction(ScaledTime)
+	;
+		Pref ^ pref_time = scale_by_thousands,
+		( Time >= 1.0 ->
+			ScaledTime = Time,
+			Unit = "s"
+		; Time >= 0.001 ->
+			ScaledTime = 1000.0 * Time,
+			Unit = "ms"
+		; Time >= 0.000001 ->
+			ScaledTime = 1000000.0 * Time,
+			Unit = "us"
+		;
+			ScaledTime = 1000000000.0 * Time,
+			Unit = "ns"
+		),
+		TimeStr0 = two_decimal_fraction(ScaledTime)
+	),
+	TimeStr = TimeStr0 ++ Unit.
+
+:- func two_decimal_fraction(float) = string.
+
+two_decimal_fraction(Measure) = Representation :-
+	string__format("%.2f", [f(Measure)], Str0),
+	string__to_char_list(Str0, Chars0),
+	list__reverse(Chars0, RevChars0),
+	(
+		RevChars0 = [Hundredth, Tenth, DecimalPoint | WholeRevChars0],
+		char__is_digit(Hundredth),
+		char__is_digit(Tenth)
+		% DecimalPoint = ('.')
+	->
+		WholeRevChars = add_commas(WholeRevChars0),
+		RevChars = [Hundredth, Tenth, DecimalPoint | WholeRevChars],
+		Chars = list__reverse(RevChars),
+		string__from_char_list(Chars, Representation)
+	;
+		error("two_decimal_fraction: malformed number")
+	).
+
+:- func four_decimal_fraction(float) = string.
+
+four_decimal_fraction(Measure) = Representation :-
+	string__format("%.4f", [f(Measure)], Str0),
+	string__to_char_list(Str0, Chars0),
+	list__reverse(Chars0, RevChars0),
+	(
+		RevChars0 = [TenThousandth, Thousandth, Hundredth, Tenth,
+			DecimalPoint | WholeRevChars0],
+		char__is_digit(TenThousandth),
+		char__is_digit(Thousandth),
+		char__is_digit(Hundredth),
+		char__is_digit(Tenth)
+		% DecimalPoint = ('.')
+	->
+		WholeRevChars = add_commas(WholeRevChars0),
+		RevChars = [TenThousandth, Thousandth, Hundredth, Tenth,
+			DecimalPoint | WholeRevChars],
+		Chars = list__reverse(RevChars),
+		string__from_char_list(Chars, Representation)
+	;
+		error("four_decimal_fraction: malformed number")
+	).
+
+:- func commas(int) = string.
+
+commas(Num) = Str :-
+	string__format("%d", [i(Num)], Str0),
+	string__to_char_list(Str0, Chars0),
+	reverse(Chars0, RevChars0),
+	string__from_char_list(reverse(add_commas(RevChars0)), Str).
+
+:- func add_commas(list(char)) = list(char).
+
+add_commas([]) = [].
+add_commas([C]) = [C].
+add_commas([C, D]) = [C, D].
+add_commas([C, D, E]) = [C, D, E].
+add_commas([C, D, E, F | R]) = [C, D, E, (',') | add_commas([F | R])].
+
+:- func percentage(int, int) = string.
+
+percentage(Fraction, Whole) = PercentageStr :-
+	( Whole = 0 ->
+		PercentageStr = "N/A"
+	;
+		Percentage = 100.0 * float(Fraction) / float(Whole),
+		PercentageStr = string__format("%5.2f", [f(Percentage)])
+	).
+
+lookup_ticks_per_sec(Stats, TicksPerSec, Assumed) :-
+	TicksPerSec0 = Stats ^ ticks_per_sec,
+	( TicksPerSec0 = 0 ->
+		TicksPerSec = default_ticks_per_sec,
+		Assumed = yes
+	;
+		TicksPerSec = TicksPerSec0,
+		Assumed = no
+	).
+
+	% The number of ticks per sec to assume if the profiling data file
+	% does not record the actual tick rate.
+:- func default_ticks_per_sec = int.
+
+default_ticks_per_sec = 100.
+
+%-----------------------------------------------------------------------------%
+
+:- func count_per_call(int, int) = string.
+
+count_per_call(Count, Calls) =
+	( Calls = 0 ->
+		two_decimal_fraction(0.0)
+	;
+		two_decimal_fraction(float(Count) / float(Calls))
+	).
+
+%-----------------------------------------------------------------------------%
+
+:- func show_port_counts(fields) = bool.
+
+show_port_counts(Fields) = ShowPorts :-
+	PortFields = Fields ^ port_fields,
+	( PortFields = no_port, ShowPorts = no
+	; PortFields = port, ShowPorts = yes
+	).
+
+:- func show_quanta(fields) = bool.
+
+show_quanta(Fields) = ShowQuanta :-
+	TimeFields = Fields ^ time_fields,
+	( TimeFields = no_time, ShowQuanta = no
+	; TimeFields = ticks, ShowQuanta = yes
+	; TimeFields = time, ShowQuanta = no
+	; TimeFields = ticks_and_time, ShowQuanta = yes
+	; TimeFields = time_and_percall, ShowQuanta = no
+	; TimeFields = ticks_and_time_and_percall, ShowQuanta = yes
+	).
+
+:- func show_times(fields) = bool.
+
+show_times(Fields) = ShowTimes :-
+	TimeFields = Fields ^ time_fields,
+	( TimeFields = no_time, ShowTimes = no
+	; TimeFields = ticks, ShowTimes = no
+	; TimeFields = time, ShowTimes = yes
+	; TimeFields = ticks_and_time, ShowTimes = yes
+	; TimeFields = time_and_percall, ShowTimes = yes
+	; TimeFields = ticks_and_time_and_percall, ShowTimes = yes
+	).
+
+:- func show_per_times(fields) = bool.
+
+show_per_times(Fields) = ShowPerTimes :-
+	TimeFields = Fields ^ time_fields,
+	( TimeFields = no_time, ShowPerTimes = no
+	; TimeFields = ticks, ShowPerTimes = no
+	; TimeFields = time, ShowPerTimes = no
+	; TimeFields = ticks_and_time, ShowPerTimes = no
+	; TimeFields = time_and_percall, ShowPerTimes = yes
+	; TimeFields = ticks_and_time_and_percall, ShowPerTimes = yes
+	).
+
+:- func show_alloc(fields) = bool.
+
+show_alloc(Fields) = ShowAlloc :-
+	AllocFields = Fields ^ alloc_fields,
+	( AllocFields = no_alloc, ShowAlloc = no
+	; AllocFields = alloc, ShowAlloc = yes
+	; AllocFields = alloc_and_percall, ShowAlloc = yes
+	).
+
+:- func show_per_alloc(fields) = bool.
+
+show_per_alloc(Fields) = ShowPerAlloc :-
+	AllocFields = Fields ^ alloc_fields,
+	( AllocFields = no_alloc, ShowPerAlloc = no
+	; AllocFields = alloc, ShowPerAlloc = no
+	; AllocFields = alloc_and_percall, ShowPerAlloc = yes
+	).
+
+:- func show_memory(fields) = maybe(memory_units).
+
+show_memory(Fields) = ShowMemory :-
+	MemoryFields = Fields ^ memory_fields,
+	( MemoryFields = no_memory, ShowMemory = no
+	; MemoryFields = memory(Unit), ShowMemory = yes(Unit)
+	; MemoryFields = memory_and_percall(Unit), ShowMemory = yes(Unit)
+	).
+
+:- func show_per_memory(fields) = maybe(memory_units).
+
+show_per_memory(Fields) = ShowPerMemory :-
+	MemoryFields = Fields ^ memory_fields,
+	( MemoryFields = no_memory, ShowPerMemory = no
+	; MemoryFields = memory(_Unit), ShowPerMemory = no
+	; MemoryFields = memory_and_percall(Unit), ShowPerMemory = yes(Unit)
+	).
+
+%-----------------------------------------------------------------------------%
+
+proc_dynamic_name(Deep, PDPtr) = Name :-
+	deep_lookup_proc_dynamics(Deep, PDPtr, PD),
+	PSPtr = PD ^ pd_proc_static,
+	deep_lookup_proc_statics(Deep, PSPtr, PS),
+	Name = PS ^ ps_refined_id.
+
+proc_static_name(Deep, PSPtr) = Name :-
+	deep_lookup_proc_statics(Deep, PSPtr, PS),
+	Name = PS ^ ps_refined_id.
+
+%-----------------------------------------------------------------------------%
+
+proc_dynamic_context(Deep, PDPtr, FileName, LineNumber) :-
+	deep_lookup_proc_dynamics(Deep, PDPtr, PD),
+	PSPtr = PD ^ pd_proc_static,
+	deep_lookup_proc_statics(Deep, PSPtr, PS),
+	FileName = PS ^ ps_file_name,
+	LineNumber = PS ^ ps_line_number.
+
+proc_static_context(Deep, PSPtr, FileName, LineNumber) :-
+	deep_lookup_proc_statics(Deep, PSPtr, PS),
+	FileName = PS ^ ps_file_name,
+	LineNumber = PS ^ ps_line_number.
+
+call_site_context(Deep, CSSPtr, FileName, LineNumber) :-
+	deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
+	CSS = call_site_static(PSPtr, _SlotNum, _Kind, LineNumber, _GoalPath),
+	deep_lookup_proc_statics(Deep, PSPtr, PS),
+	FileName = PS ^ ps_file_name.
+
+%-----------------------------------------------------------------------------%
+
+proc_static_to_line_group_info(Pref, Deep, PSPtr, FileName, LineNumber,
+		Name, HTML) :-
+	( valid_proc_static_ptr(Deep, PSPtr) ->
+		deep_lookup_proc_statics(Deep, PSPtr, PS),
+		FileName = PS ^ ps_file_name,
+		LineNumber = PS ^ ps_line_number,
+		Name = PS ^ ps_refined_id,
+		HTML = proc_static_to_html_ref(Pref, Deep, PSPtr)
+	;
+		FileName = "",
+		LineNumber = 0,
+		Name = "mercury_runtime",
+		HTML = Name
+	).
+
+proc_static_to_html_ref(Pref, Deep, PSPtr) = HTML :-
+	PSPtr = proc_static_ptr(PSI),
+	URL = deep_cmd_pref_to_url(Pref, Deep, proc(PSI)),
+	deep_lookup_proc_statics(Deep, PSPtr, PS),
+	ProcName = PS ^ ps_refined_id,
+	HTML = string__format("<A HREF=""%s"">%s</A>",
+		[s(URL), s(ProcName)]).
+
+module_name_to_html_ref(Pref, Deep, ModuleName) = HTML :-
+	URL = deep_cmd_pref_to_url(Pref, Deep, module(ModuleName)),
+	HTML = string__format("<A HREF=""%s"">%s</A>",
+		[s(URL), s(ModuleName)]).
+
+clique_ptr_to_html_ref(Pref, Deep, ProcName, CliquePtr) = HTML :-
+	CliquePtr = clique_ptr(CliqueNum),
+	URL = deep_cmd_pref_to_url(Pref, Deep, clique(CliqueNum)),
+	HTML = string__format("<A HREF=""%s"">%s</A>",
+		[s(URL), s(ProcName)]).
+
+deep_cmd_pref_to_url(Pref, Deep, Cmd) =
+	cmd_pref_to_url(Deep ^ server_name, Deep ^ data_file_name, Cmd, Pref).
+
+%-----------------------------------------------------------------------------%
+
+plural(N) = Plural :-
+	( N = 1 ->
+		Plural = ""
+	;
+		Plural = "s"
+	).
+
+%-----------------------------------------------------------------------------%
Index: deep_profiler/interface.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/interface.m,v
retrieving revision 1.2
diff -u -b -r1.2 interface.m
--- deep_profiler/interface.m	2001/06/05 04:47:31	1.2
+++ deep_profiler/interface.m	2001/07/02 13:08:34
@@ -16,26 +16,48 @@
 
 :- import_module std_util, io.
 
+:- func to_server_pipe_name(string) = string.
+:- func from_server_pipe_name(string) = string.
+:- func server_startup_name(string) = string.
+:- func contour_file_name(string) = string.
+
+:- pred to(string::in, cmd_pref::in, io__state::di, io__state::uo) is det.
+:- pred from(string::in, resp::out, io__state::di, io__state::uo) is det.
+
+:- type resp
+	--->	html(string).
+
+:- type cmd_pref
+	--->	cmd_pref(cmd, preferences).
+
 :- type cmd
 	--->	quit
+	;	restart
 	;	timeout(int)
 	;	menu
-	;	root(fields)
-	;	clique(int, fields, maybe(int))
-	;	proc(int, fields)
-	;	top_procs(sort_measurement, include_descendants,
-			display_limit, fields)
+	;	root(maybe(int))
+	;	clique(int)
+	;	proc(int)
+	;	proc_callers(int, caller_groups, int)
+	;	modules
+	;	module(string)
+	;	top_procs(display_limit,
+			cost_kind, include_descendants, measurement_scope)
+
+		% The commands below are for debugging.
 	;	proc_static(int)
 	;	proc_dynamic(int)
 	;	call_site_static(int)
 	;	call_site_dynamic(int)
-	;	raw_clique(int)
-	;	num_proc_statics
-	;	num_call_site_statics
-	;	num_proc_dynamics
-	;	num_call_site_dynamics.
+	;	raw_clique(int).
+
+:- type caller_groups
+	--->	group_by_call_site
+	;	group_by_proc
+	;	group_by_module
+	;	group_by_clique.
 
-:- type sort_measurement
+:- type cost_kind
 	--->	calls
 	;	time
 	;	allocs
@@ -49,32 +71,115 @@
 	--->	rank_range(int, int)
 	;	threshold(float).
 
-:- type resp
-	--->	html(string).
-
-:- type fields	==	string.		% some subset of "apqtw", meaning
-					% a: memory allocations
-					% p: port counts
-					% q: quanta
-					% t: times
-					% w: memory words
-					% The characters must be sorted.
-
-:- func default_ancestor_limit = maybe(int).
-
-:- func default_fields = string.
-:- func all_fields = string.
+:- type preferences
+	--->	preferences(
+			pref_fields	:: fields,
+					% set of fields to display
+			pref_box	:: box,
+					% whether displays should be boxed
+			pref_colour	:: colour_scheme,
+					% what principle governs colours
+			pref_anc	:: maybe(int),
+					% max number of ancestors to display
+			pref_summarize	:: summarize,
+					% whether pages should summarize
+					% at higher order call sites
+			pref_criteria	:: order_criteria,
+					% the criteria for ordering lines in
+					% pages, if the command doesn't specify
+					% otherwise
+			pref_contour	:: contour,
+					% whether contour exclusion should be
+					% applied
+			pref_time	:: time_format
+		).
 
-:- func to_server_pipe_name(string) = string.
-:- func from_server_pipe_name(string) = string.
-:- func server_startup_name(string) = string.
+:- type port_fields
+	--->	no_port
+	;	port.
+
+:- type time_fields
+	--->	no_time
+	;	ticks
+	;	time
+	;	ticks_and_time
+	;	time_and_percall
+	;	ticks_and_time_and_percall.
+
+:- type alloc_fields
+	--->	no_alloc
+	;	alloc
+	;	alloc_and_percall.
+
+:- type memory_fields
+	--->	no_memory
+	;	memory(memory_units)
+	;	memory_and_percall(memory_units).
+
+:- type memory_units
+	--->	words
+	;	bytes.
+
+:- type fields
+	--->	fields(
+			port_fields	:: port_fields,
+			time_fields	:: time_fields,
+			alloc_fields	:: alloc_fields,
+			memory_fields	:: memory_fields
+		).
 
-:- pred to(string::in, cmd::in, io__state::di, io__state::uo) is det.
-:- pred from(string::in, resp::out, io__state::di, io__state::uo) is det.
+:- type box
+	--->	box
+	;	nobox.
+
+:- type colour_scheme
+	--->	column_groups
+	;	none.
+
+:- type summarize
+	--->	summarize
+	;	dont_summarize.
+
+:- type order_criteria
+	--->	by_context
+	;	by_name
+	;	by_cost(
+			cost_kind,
+			include_descendants,
+			measurement_scope
+		).
 
-:- pred cmd_to_url(string::in, string::in, cmd::in, string::out) is det.
-:- pred cmd_to_query(cmd::in, string::out) is det.
-:- pred query_to_cmd(string::in, maybe(cmd)::out) is det.
+:- type measurement_scope
+	--->	per_call
+	;	overall.
+
+:- type contour
+	--->	apply_contour
+	;	no_contour.
+
+:- type time_format
+	--->	no_scale
+	;	scale_by_millions
+	;	scale_by_thousands.
+
+:- func default_preferences = preferences.
+
+:- func default_fields = fields.
+:- func all_fields = fields.
+:- func default_box = box.
+:- func default_colour_scheme = colour_scheme.
+:- func default_ancestor_limit = maybe(int).
+:- func default_summarize = summarize.
+:- func default_order_criteria = order_criteria.
+:- func default_cost_kind = cost_kind.
+:- func default_incl_desc = include_descendants.
+:- func default_scope = measurement_scope.
+:- func default_contour = contour.
+:- func default_time_format = time_format.
+
+:- func cmd_pref_to_url(string, string, cmd, preferences) = string.
+:- func url_component_to_cmd(string) = maybe(cmd).
+:- func url_component_to_preferences(string) = maybe(preferences).
 
 %-----------------------------------------------------------------------------%
 
@@ -83,10 +188,32 @@
 :- import_module util.
 :- import_module char, string, list, set, require.
 
+default_preferences =
+	preferences(
+		default_fields,
+		default_box,
+		default_colour_scheme,
+		default_ancestor_limit,
+		default_summarize,
+		default_order_criteria,
+		default_contour,
+		default_time_format
+	).
+
+default_fields = fields(port, ticks, no_alloc, memory(words)).
+all_fields = fields(port, ticks_and_time_and_percall, alloc, memory(words)).
+default_box = box.
+default_colour_scheme = column_groups.
 default_ancestor_limit = yes(5).
+default_summarize = dont_summarize.
+default_order_criteria = by_context.
+default_cost_kind = time.
+default_incl_desc = self_and_desc.
+default_scope = overall.
+default_contour = no_contour.
+default_time_format = scale_by_thousands.
 
-default_fields = "pqw".
-all_fields = "apqtw".
+%-----------------------------------------------------------------------------%
 
 to_server_pipe_name(DataFileName) =
 	server_dir ++ "/" ++
@@ -103,6 +230,9 @@
 	"mdprof_startup_err" ++
 	filename_mangle(DataFileName).
 
+contour_file_name(DataFileName) =
+	DataFileName ++ ".contour".
+
 :- func server_dir = string.
 
 server_dir = "/var/tmp".
@@ -132,194 +262,216 @@
 	).
 
 %-----------------------------------------------------------------------------%
+
+to(Where, CmdPref) -->
+	io__tell(Where, Res),
+	( { Res = ok } ->
+		io__write(CmdPref),
+		io__write_string(".\n"),
+		io__told
+	;
+		{ error("mdprof to: couldn't open pipe") }
+	).
+
+from(Where, Resp) -->
+	io__see(Where, Res0),
+	( { Res0 = ok } ->
+		io__read(Res1),
+		( { Res1 = ok(Resp0) } ->
+			{ Resp = Resp0 }
+		;
+			{ error("mdprof from: read failed") }
+		),
+		io__seen
+	;
+		{ error("mdprof from: couldn't open pipe") }
+	).
+
+%-----------------------------------------------------------------------------%
 
-cmd_to_url(Machine, DataFileName, Cmd, URL) :-
-	cmd_to_query(Cmd, Query),
-	URL =
+cmd_pref_to_url(Machine, DataFileName, Cmd, Preferences) =
 		"http://" ++
 		Machine ++
 		"/cgi-bin/mdprof?" ++
-		Query ++
+	cmd_to_string(Cmd) ++
+	"$" ++
+	preferences_to_string(Preferences) ++
 		"$" ++
 		DataFileName.
 
-cmd_to_query(Cmd, Query) :-
+:- func cmd_to_string(cmd) = string.
+
+cmd_to_string(Cmd) = CmdStr :-
 	(
 		Cmd = quit,
-		Query = "quit"
+		CmdStr = "quit"
+	;
+		Cmd = restart,
+		CmdStr = "restart"
 	;
 		Cmd = timeout(Minutes),
-		Query = format("timeout+%d", [i(Minutes)])
+		CmdStr = string__format("timeout+%d", [i(Minutes)])
 	;
 		Cmd = menu,
-		Query = "menu"
-	;
-		Cmd = root(Fields),
-		Query = format("root+%s", [s(Fields)])
+		CmdStr = "menu"
 	;
-		Cmd = clique(CliqueNum, Fields, MaybeAncestorLimit),
+		Cmd = root(MaybePercent),
 		(
-			MaybeAncestorLimit = yes(AncestorLimit),
-			Query = format("clique+%s+%d+%d",
-				[s(Fields), i(CliqueNum), i(AncestorLimit)])
+			MaybePercent = yes(Percent),
+			CmdStr = string__format("root+%d", [i(Percent)])
 		;
-			MaybeAncestorLimit = no,
-			Query = format("clique+%s+%d+no",
-				[s(Fields), i(CliqueNum)])
+			MaybePercent = no,
+			CmdStr = "root+no"
 		)
+	;
+		Cmd = clique(CliqueNum),
+		CmdStr = string__format("clique+%d", [i(CliqueNum)])
 	;
-		Cmd = proc(ProcNum, Fields),
-		Query = format("proc+%s+%d", [s(Fields), i(ProcNum)])
+		Cmd = proc(ProcNum),
+		CmdStr = string__format("proc+%d", [i(ProcNum)])
 	;
-		Cmd = top_procs(Sort, InclDesc, Limit, Fields),
-		sort_to_str(Sort, SortStr),
-		incl_desc_to_str(InclDesc, InclDescStr),
-		limit_to_str(Limit, LimitStr),
-		Query = format("procs+%s+%s+%s+%s",
-			[s(SortStr), s(InclDescStr), s(LimitStr), s(Fields)])
+		Cmd = proc_callers(ProcNum, GroupCallers, BunchNum),
+		GroupCallersStr = caller_groups_to_string(GroupCallers),
+		CmdStr = string__format("proc_callers+%d+%s+%d",
+			[i(ProcNum), s(GroupCallersStr), i(BunchNum)])
+	;
+		Cmd = modules,
+		CmdStr = "modules"
+	;
+		Cmd = module(ModuleName),
+		CmdStr = "module+" ++ ModuleName
+	;
+		Cmd = top_procs(Limit, CostKind, InclDesc, Scope),
+		LimitStr = limit_to_string(Limit),
+		CostKindStr = cost_kind_to_string(CostKind),
+		InclDescStr = incl_desc_to_string(InclDesc),
+		ScopeStr = scope_to_string(Scope),
+		CmdStr = string__format("top_procs+%s+%s+%s+%s",
+			[s(LimitStr), s(CostKindStr),
+			s(InclDescStr), s(ScopeStr)])
 	;
 		Cmd = proc_static(PSI),
-		Query = format("proc_static+%d", [i(PSI)])
+		CmdStr = string__format("proc_static+%d", [i(PSI)])
 	;
 		Cmd = proc_dynamic(PDI),
-		Query = format("proc_dynamic+%d", [i(PDI)])
+		CmdStr = string__format("proc_dynamic+%d", [i(PDI)])
 	;
 		Cmd = call_site_static(CSSI),
-		Query = format("call_site_static+%d", [i(CSSI)])
+		CmdStr = string__format("call_site_static+%d", [i(CSSI)])
 	;
 		Cmd = call_site_dynamic(CSDI),
-		Query = format("call_site_dynamic+%d", [i(CSDI)])
+		CmdStr = string__format("call_site_dynamic+%d", [i(CSDI)])
 	;
 		Cmd = raw_clique(CI),
-		Query = format("raw_clique+%d", [i(CI)])
-	;
-		Cmd = num_proc_statics,
-		Query = "num_proc_statics"
-	;
-		Cmd = num_proc_dynamics,
-		Query = "num_proc_dynamics"
-	;
-		Cmd = num_call_site_statics,
-		Query = "num_call_site_statics"
-	;
-		Cmd = num_call_site_dynamics,
-		Query = "num_call_site_dynamics"
+		CmdStr = string__format("raw_clique+%d", [i(CI)])
 	).
 
-query_to_cmd(QueryString, MaybeCmd) :-
-	split(QueryString, ('+'), Pieces),
-	(
+:- func preferences_to_string(preferences) = string.
+
+preferences_to_string(Pref) = PrefStr :-
+	Pref = preferences(Fields, Box, Colour, MaybeAncestorLimit,
+		Summarize, Order, Contour, Time),
 		(
-			Pieces = ["clique", NStr, AncestorLimitStr],
-			string__to_int(NStr, N),
-			( string__to_int(AncestorLimitStr, AncestorLimit) ->
-				MaybeAncestorLimit = yes(AncestorLimit)
-			; AncestorLimitStr = "no" ->
-				MaybeAncestorLimit = no
+		MaybeAncestorLimit = yes(AncestorLimit),
+		MaybeAncestorLimitStr =
+			string__format("%d", [i(AncestorLimit)])
 			;
-				fail
+		MaybeAncestorLimit = no,
+		MaybeAncestorLimitStr = "no"
 			),
-			Fields = default_fields
-		;
-			Pieces = ["clique", Fields, NStr, AncestorLimitStr],
-			string__to_int(NStr, N),
-			( string__to_int(AncestorLimitStr, AncestorLimit) ->
-				MaybeAncestorLimit = yes(AncestorLimit)
-			; AncestorLimitStr = "no" ->
-				MaybeAncestorLimit = no
+	PrefStr = string__format("%s+%s+%s+%s+%s+%s+%s+%s",
+		[s(fields_to_string(Fields)), s(box_to_string(Box)),
+		s(colour_scheme_to_string(Colour)), s(MaybeAncestorLimitStr),
+		s(summarize_to_string(Summarize)),
+		s(order_criteria_to_string(Order)),
+		s(contour_to_string(Contour)),
+		s(time_format_to_string(Time))]).
+
+url_component_to_cmd(QueryString) = MaybeCmd :-
+	split(QueryString, ('+'), Pieces),
+	(
+		Pieces = ["root", MaybePercentStr],
+		( MaybePercentStr = "no" ->
+			MaybePercent = no
+		; string__to_int(MaybePercentStr, Percent) ->	
+			MaybePercent = yes(Percent)
 			;
 				fail
-			),
-			validate_fields(Fields)
 		)
 	->
-		MaybeCmd = yes(clique(N, Fields, MaybeAncestorLimit))
+		MaybeCmd = yes(root(MaybePercent))
 	;
-		(
-			Pieces = ["proc", NStr],
-			string__to_int(NStr, N),
-			Fields = default_fields
-		;
-			Pieces = ["proc", Fields, NStr],
-			string__to_int(NStr, N),
-			validate_fields(Fields)
-		)
+		Pieces = ["clique", CliqueNumStr],
+		string__to_int(CliqueNumStr, CliqueNum)
 	->
-		MaybeCmd = yes(proc(N, Fields))
+		MaybeCmd = yes(clique(CliqueNum))
 	;
-		(
-			Pieces = ["procs", SortStr, InclDescStr,
-				LimitStr],
-			Fields = default_fields
-		;
-			Pieces = ["procs", SortStr, InclDescStr,
-				LimitStr, Fields],
-			validate_fields(Fields)
-		),
-		translate_criteria(SortStr, Sort,
-			InclDescStr, InclDesc, LimitStr, Limit)
+		Pieces = ["proc", PSIStr],
+		string__to_int(PSIStr, PSI)
 	->
-		MaybeCmd = yes(top_procs(Sort, InclDesc, Limit, Fields))
-	;
-		(
-			Pieces = ["root"],
-			Fields = default_fields
+		MaybeCmd = yes(proc(PSI))
 		;
-			Pieces = ["root", Fields],
-			validate_fields(Fields)
-		)
+		Pieces = ["proc_callers", PSIStr, GroupCallersStr, BunchNumStr],
+		string__to_int(PSIStr, PSI),
+		string__to_int(BunchNumStr, BunchNum),
+		string_to_caller_groups(GroupCallersStr, GroupCallers)
 	->
-		MaybeCmd = yes(root(Fields))
+		MaybeCmd = yes(proc_callers(PSI, GroupCallers, BunchNum))
 	;
-		Pieces = ["menu"]
+		Pieces = ["modules"]
 	->
-		MaybeCmd = yes(menu)
+		MaybeCmd = yes(modules)
 	;
-		Pieces = ["proc_static", NStr],
-		string__to_int(NStr, N)
+		Pieces = ["module", ModuleName]
 	->
-		MaybeCmd = yes(proc_static(N))
+		MaybeCmd = yes(module(ModuleName))
 	;
-		Pieces = ["proc_dynamic", NStr],
-		string__to_int(NStr, N)
+		Pieces = ["top_procs", LimitStr,
+			CostKindStr, InclDescStr, ScopeStr],
+		string_to_limit(LimitStr, Limit),
+		string_to_cost_kind(CostKindStr, CostKind),
+		string_to_incl_desc(InclDescStr, InclDesc),
+		string_to_scope(ScopeStr, Scope)
 	->
-		MaybeCmd = yes(proc_dynamic(N))
+		MaybeCmd = yes(top_procs(Limit, CostKind, InclDesc, Scope))
 	;
-		Pieces = ["call_site_static", NStr],
-		string__to_int(NStr, N)
+		Pieces = ["menu"]
 	->
-		MaybeCmd = yes(call_site_static(N))
+		MaybeCmd = yes(menu)
 	;
-		Pieces = ["call_site_dynamic", NStr],
-		string__to_int(NStr, N)
+		Pieces = ["proc_static", PSIStr],
+		string__to_int(PSIStr, PSI)
 	->
-		MaybeCmd = yes(call_site_dynamic(N))
+		MaybeCmd = yes(proc_static(PSI))
 	;
-		Pieces = ["raw_clique", NStr],
-		string__to_int(NStr, N)
+		Pieces = ["proc_dynamic", PDIStr],
+		string__to_int(PDIStr, PDI)
 	->
-		MaybeCmd = yes(raw_clique(N))
+		MaybeCmd = yes(proc_dynamic(PDI))
 	;
-		Pieces = ["num_proc_statics"]
+		Pieces = ["call_site_static", CSSIStr],
+		string__to_int(CSSIStr, CSSI)
 	->
-		MaybeCmd = yes(num_proc_statics)
+		MaybeCmd = yes(call_site_static(CSSI))
 	;
-		Pieces = ["num_call_site_statics"]
+		Pieces = ["call_site_dynamic", CSDIStr],
+		string__to_int(CSDIStr, CSDI)
 	->
-		MaybeCmd = yes(num_call_site_statics)
+		MaybeCmd = yes(call_site_dynamic(CSDI))
 	;
-		Pieces = ["num_proc_dynamics"]
+		Pieces = ["raw_clique", CliqueNumStr],
+		string__to_int(CliqueNumStr, CliqueNum)
 	->
-		MaybeCmd = yes(num_proc_dynamics)
+		MaybeCmd = yes(raw_clique(CliqueNum))
 	;
-		Pieces = ["num_call_site_dynamics"]
+		Pieces = ["timeout", TimeOutStr],
+		string__to_int(TimeOutStr, TimeOut)
 	->
-		MaybeCmd = yes(num_call_site_dynamics)
+		MaybeCmd = yes(timeout(TimeOut))
 	;
-		Pieces = ["timeout", TStr],
-		string__to_int(TStr, TimeOut)
+		Pieces = ["restart"]
 	->
-		MaybeCmd = yes(timeout(TimeOut))
+		MaybeCmd = yes(restart)
 	;
 		Pieces = ["quit"]
 	->
@@ -328,50 +480,161 @@
 		MaybeCmd = no
 	).
 
+url_component_to_preferences(QueryString) = MaybePreferences :-
+	split(QueryString, ('+'), Pieces),
+	(
+		Pieces = [FieldsStr, BoxStr, ColourStr, MaybeAncestorLimitStr,
+			SummarizeStr, OrderStr, ContourStr, TimeStr],
+		string_to_fields(FieldsStr, Fields),
+		string_to_box(BoxStr, Box),
+		string_to_colour_scheme(ColourStr, Colour),
+		( string__to_int(MaybeAncestorLimitStr, AncestorLimit) ->
+			MaybeAncestorLimit = yes(AncestorLimit)
+		; MaybeAncestorLimitStr = "no" ->
+			MaybeAncestorLimit = no
+		;
+			fail
+		),
+		string_to_summarize(SummarizeStr, Summarize),
+		string_to_order_criteria(OrderStr, Order),
+		string_to_contour(ContourStr, Contour),
+		string_to_time_format(TimeStr, Time)
+	->
+		Preferences = preferences(Fields, Box, Colour,
+			MaybeAncestorLimit, Summarize, Order, Contour, Time),
+		MaybePreferences = yes(Preferences)
+	;
+		MaybePreferences = no
+	).
+
 %-----------------------------------------------------------------------------%
 
-:- pred sort_to_str(sort_measurement::in, string::out) is det.
+:- func port_fields_to_string(port_fields) = string.
 
-sort_to_str(calls,  "calls").
-sort_to_str(time,   "time").
-sort_to_str(allocs, "allocs").
-sort_to_str(words,  "words").
+port_fields_to_string(no_port) = "_".
+port_fields_to_string(port)    = "p".
 
-:- pred incl_desc_to_str(include_descendants::in, string::out) is det.
+:- pred string_to_port_fields(string::in, port_fields::out) is semidet.
 
-incl_desc_to_str(self,          "self").
-incl_desc_to_str(self_and_desc, "both").
+string_to_port_fields("_", no_port).
+string_to_port_fields("p", port).
 
-:- pred limit_to_str(display_limit::in, string::out) is det.
+:- func time_fields_to_string(time_fields) = string.
 
-limit_to_str(rank_range(Lo, Hi),   format("%d-%d", [i(Lo), i(Hi)])).
-limit_to_str(threshold(Threshold), format("%f", [f(Threshold)])).
+time_fields_to_string(no_time)                    = "_".
+time_fields_to_string(ticks)                      = "q".
+time_fields_to_string(time)                       = "t".
+time_fields_to_string(ticks_and_time)             = "qt".
+time_fields_to_string(time_and_percall)           = "tp".
+time_fields_to_string(ticks_and_time_and_percall) = "qtp".
 
-:- pred translate_criteria(string::in, sort_measurement::out,
-	string::in, include_descendants::out, string::in, display_limit::out)
-	is semidet.
+:- pred string_to_time_fields(string::in, time_fields::out) is semidet.
 
-translate_criteria(SortStr, Sort, InclDescStr, InclDesc, LimitStr, Limit) :-
-	(
-		SortStr = "calls",
-		Sort = calls
-	;
-		SortStr = "time",
-		Sort = time
-	;
-		SortStr = "allocs",
-		Sort = allocs
-	;
-		SortStr = "words",
-		Sort = words
-	),
+string_to_time_fields("_",   no_time).
+string_to_time_fields("q",   ticks).
+string_to_time_fields("t",   time).
+string_to_time_fields("qt",  ticks_and_time).
+string_to_time_fields("tp",  time_and_percall).
+string_to_time_fields("qtp", ticks_and_time_and_percall).
+
+:- func alloc_fields_to_string(alloc_fields) = string.
+
+alloc_fields_to_string(no_alloc)          = "_".
+alloc_fields_to_string(alloc)             = "a".
+alloc_fields_to_string(alloc_and_percall) = "ap".
+
+:- pred string_to_alloc_fields(string::in, alloc_fields::out) is semidet.
+
+string_to_alloc_fields("_",  no_alloc).
+string_to_alloc_fields("a",  alloc).
+string_to_alloc_fields("ap", alloc_and_percall).
+
+:- func memory_fields_to_string(memory_fields) = string.
+
+memory_fields_to_string(no_memory)                 = "_".
+memory_fields_to_string(memory(bytes))             = "b".
+memory_fields_to_string(memory(words))             = "w".
+memory_fields_to_string(memory_and_percall(bytes)) = "bp".
+memory_fields_to_string(memory_and_percall(words)) = "wp".
+
+:- pred string_to_memory_fields(string::in, memory_fields::out) is semidet.
+
+string_to_memory_fields("_",  no_memory).
+string_to_memory_fields("b",  memory(bytes)).
+string_to_memory_fields("w",  memory(words)).
+string_to_memory_fields("bp", memory_and_percall(bytes)).
+string_to_memory_fields("wp", memory_and_percall(words)).
+
+:- func fields_to_string(fields) = string.
+
+fields_to_string(fields(Port, Time, Allocs, Memory)) =
+	port_fields_to_string(Port) ++ "-" ++
+	time_fields_to_string(Time) ++ "-" ++
+	alloc_fields_to_string(Allocs) ++ "-" ++
+	memory_fields_to_string(Memory).
+
+:- pred string_to_fields(string::in, fields::out) is semidet.
+
+string_to_fields(FieldsStr, Fields) :-
 	(
-		InclDescStr = "self",
-		InclDesc = self
+		split(FieldsStr, '-', Pieces),
+		Pieces = [PortStr, TimeStr, AllocStr, MemoryStr],
+		string_to_port_fields(PortStr, Port),
+		string_to_time_fields(TimeStr, Time),
+		string_to_alloc_fields(AllocStr, Alloc),
+		string_to_memory_fields(MemoryStr, Memory)
+	->
+		Fields = fields(Port, Time, Alloc, Memory)
 	;
-		InclDescStr = "both",
-		InclDesc = self_and_desc
-	),
+		fail
+	).
+
+:- func caller_groups_to_string(caller_groups) = string.
+
+caller_groups_to_string(group_by_call_site) = "cs".
+caller_groups_to_string(group_by_proc)      = "pr".
+caller_groups_to_string(group_by_module)    = "mo".
+caller_groups_to_string(group_by_clique)    = "cl".
+
+:- pred string_to_caller_groups(string::in, caller_groups::out) is semidet.
+
+string_to_caller_groups("cs", group_by_call_site).
+string_to_caller_groups("pr", group_by_proc).
+string_to_caller_groups("mo", group_by_module).
+string_to_caller_groups("cl", group_by_clique).
+
+:- func cost_kind_to_string(cost_kind) = string.
+
+cost_kind_to_string(calls) =  "calls".
+cost_kind_to_string(time) =   "time".
+cost_kind_to_string(allocs) = "allocs".
+cost_kind_to_string(words) =  "words".
+
+:- pred string_to_cost_kind(string::in, cost_kind::out) is semidet.
+
+string_to_cost_kind("calls",  calls).
+string_to_cost_kind("time",   time).
+string_to_cost_kind("allocs", allocs).
+string_to_cost_kind("words",  words).
+
+:- func incl_desc_to_string(include_descendants) = string.
+
+incl_desc_to_string(self) =          "self".
+incl_desc_to_string(self_and_desc) = "both".
+
+:- pred string_to_incl_desc(string::in, include_descendants::out) is semidet.
+
+string_to_incl_desc("self", self).
+string_to_incl_desc("both", self_and_desc).
+
+:- func limit_to_string(display_limit) = string.
+
+limit_to_string(rank_range(Lo, Hi)) =   string__format("%d-%d", [i(Lo), i(Hi)]).
+limit_to_string(threshold(Threshold)) = string__format("%f", [f(Threshold)]).
+
+:- pred string_to_limit(string::in, display_limit::out) is semidet.
+
+string_to_limit(LimitStr, Limit) :-
 	(
 		split(LimitStr, '-', Pieces),
 		Pieces = [FirstStr, LastStr],
@@ -387,45 +650,99 @@
 		fail
 	).
 
-:- pred validate_fields(string::in) is semidet.
+:- func summarize_to_string(summarize) = string.
 
-validate_fields(String) :-
-	Chars = string__to_char_list(String),
-	list__sort_and_remove_dups(Chars, Chars),
-	validate_field_chars(Chars,
-		set__list_to_set(string__to_char_list(all_fields))).
-
-:- pred validate_field_chars(list(char)::in, set(char)::in) is semidet.
-
-validate_field_chars([], _).
-validate_field_chars([Char | Chars], AvailFields0) :-
-	set__delete(AvailFields0, Char, AvailFields1),
-	validate_field_chars(Chars, AvailFields1).
+summarize_to_string(summarize)      = "sum".
+summarize_to_string(dont_summarize) = "nosum".
 
-%-----------------------------------------------------------------------------%
+:- pred string_to_summarize(string::in, summarize::out) is semidet.
 
-to(Where, Cmd) -->
-	io__tell(Where, Res),
-	( { Res = ok } ->
-		io__write(Cmd),
-		io__write_string(".\n"),
-		io__told
-	;
-		{ error("mdprof to: couldn't open pipe") }
-	).
+string_to_summarize("sum",   summarize).
+string_to_summarize("nosum", dont_summarize).
 
-from(Where, Resp) -->
-	io__see(Where, Res0),
-	( { Res0 = ok } ->
-		io__read(Res1),
-		( { Res1 = ok(Resp0) } ->
-			{ Resp = Resp0 }
+:- func order_criteria_to_string(order_criteria) = string.
+
+order_criteria_to_string(by_context) = "context".
+order_criteria_to_string(by_name) = "name".
+order_criteria_to_string(by_cost(CostKind, InclDesc, Scope)) =
+	"cost" ++ "-" ++
+	cost_kind_to_string(CostKind) ++ "-" ++
+	incl_desc_to_string(InclDesc) ++ "-" ++
+	scope_to_string(Scope).
+
+:- pred string_to_order_criteria(string::in, order_criteria::out) is semidet.
+
+string_to_order_criteria(CriteriaStr, Criteria) :-
+	(
+		CriteriaStr = "context"
+	->
+		Criteria = by_context
 		;
-			{ error("mdprof from: read failed") }
-		),
-		io__seen
+		CriteriaStr = "name"
+	->
+		Criteria = by_name
 	;
-		{ error("mdprof from: couldn't open pipe") }
+		split(CriteriaStr, '-', Pieces),
+		Pieces = ["cost", CostKindStr, InclDescStr, ScopeStr],
+		string_to_cost_kind(CostKindStr, CostKind),
+		string_to_incl_desc(InclDescStr, InclDesc),
+		string_to_scope(ScopeStr, Scope)
+	->
+		Criteria = by_cost(CostKind, InclDesc, Scope)
+	;
+		fail
 	).
+
+:- func scope_to_string(measurement_scope) = string.
+
+scope_to_string(per_call) = "pc".
+scope_to_string(overall)  = "oa".
+
+:- pred string_to_scope(string::in, measurement_scope::out) is semidet.
+
+string_to_scope("pc", per_call).
+string_to_scope("oa",  overall).
+
+:- func contour_to_string(contour) = string.
+
+contour_to_string(apply_contour) = "ac".
+contour_to_string(no_contour)    = "nc".
+
+:- pred string_to_contour(string::in, contour::out) is semidet.
+
+string_to_contour("ac", apply_contour).
+string_to_contour("nc", no_contour).
+
+:- func time_format_to_string(time_format) = string.
+
+time_format_to_string(no_scale)           = "no".
+time_format_to_string(scale_by_millions)  = "mi".
+time_format_to_string(scale_by_thousands) = "th".
+
+:- pred string_to_time_format(string::in, time_format::out) is semidet.
+
+string_to_time_format("no", no_scale).
+string_to_time_format("mi", scale_by_millions).
+string_to_time_format("th", scale_by_thousands).
+
+:- pred string_to_colour_scheme(string::in, colour_scheme::out) is semidet.
+
+string_to_colour_scheme("cols", column_groups).
+string_to_colour_scheme("none", none).
+
+:- func colour_scheme_to_string(colour_scheme) = string.
+
+colour_scheme_to_string(column_groups) = "cols".
+colour_scheme_to_string(none)          = "none".
+
+:- pred string_to_box(string::in, box::out) is semidet.
+
+string_to_box("box",   box).
+string_to_box("nobox", nobox).
+
+:- func box_to_string(box) = string.
+
+box_to_string(box)   = "box".
+box_to_string(nobox) = "nobox".
 
 %-----------------------------------------------------------------------------%
Index: deep_profiler/io_combinator.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/io_combinator.m,v
retrieving revision 1.1
diff -u -b -r1.1 io_combinator.m
--- deep_profiler/io_combinator.m	2001/06/05 04:47:31	1.1
+++ deep_profiler/io_combinator.m	2001/07/02 07:19:00
@@ -183,6 +183,33 @@
     pred(in, in, in, in, in, in, in, in, in, out) is det,
     out, di, uo) is det.
 
+:- pred io_combinator__sequence_10(
+    pred(io__result(T1), io__state, io__state),
+    pred(io__result(T2), io__state, io__state),
+    pred(io__result(T3), io__state, io__state),
+    pred(io__result(T4), io__state, io__state),
+    pred(io__result(T5), io__state, io__state),
+    pred(io__result(T6), io__state, io__state),
+    pred(io__result(T7), io__state, io__state),
+    pred(io__result(T8), io__state, io__state),
+    pred(io__result(T9), io__state, io__state),
+    pred(io__result(T10), io__state, io__state),
+    pred(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, io__result(T)),
+    io__result(T), io__state, io__state).
+:- mode io_combinator__sequence_10(
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(in, in, in, in, in, in, in, in, in, in, out) is det,
+    out, di, uo) is det.
+
 %-----------------------------------------------------------------------------%
 
 :- pred io_combinator__res_sequence_2(
@@ -329,6 +356,33 @@
     pred(in, in, in, in, in, in, in, in, in, out) is det,
     out, di, uo) is det.
 
+:- pred io_combinator__res_sequence_10(
+    pred(io__res(T1), io__state, io__state),
+    pred(io__res(T2), io__state, io__state),
+    pred(io__res(T3), io__state, io__state),
+    pred(io__res(T4), io__state, io__state),
+    pred(io__res(T5), io__state, io__state),
+    pred(io__res(T6), io__state, io__state),
+    pred(io__res(T7), io__state, io__state),
+    pred(io__res(T8), io__state, io__state),
+    pred(io__res(T9), io__state, io__state),
+    pred(io__res(T10), io__state, io__state),
+    pred(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, io__res(T)),
+    io__res(T), io__state, io__state).
+:- mode io_combinator__res_sequence_10(
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(in, in, in, in, in, in, in, in, in, in, out) is det,
+    out, di, uo) is det.
+
 %-----------------------------------------------------------------------------%
 
 :- pred io_combinator__maybe_error_sequence_2(
@@ -475,6 +529,33 @@
     pred(in, in, in, in, in, in, in, in, in, out) is det,
     out, di, uo) is det.
 
+:- pred io_combinator__maybe_error_sequence_10(
+    pred(maybe_error(T1), io__state, io__state),
+    pred(maybe_error(T2), io__state, io__state),
+    pred(maybe_error(T3), io__state, io__state),
+    pred(maybe_error(T4), io__state, io__state),
+    pred(maybe_error(T5), io__state, io__state),
+    pred(maybe_error(T6), io__state, io__state),
+    pred(maybe_error(T7), io__state, io__state),
+    pred(maybe_error(T8), io__state, io__state),
+    pred(maybe_error(T9), io__state, io__state),
+    pred(maybe_error(T10), io__state, io__state),
+    pred(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, maybe_error(T)),
+    maybe_error(T), io__state, io__state).
+:- mode io_combinator__maybe_error_sequence_10(
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(out, di, uo) is det,
+    pred(in, in, in, in, in, in, in, in, in, in, out) is det,
+    out, di, uo) is det.
+
 %-----------------------------------------------------------------------------%
 
 :- implementation.
@@ -949,6 +1030,111 @@
         { Res = error(Err) }
     ).
 
+io_combinator__sequence_10(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10,
+		Combine, Res) -->
+    call(P1, Res1),
+    (
+        { Res1 = ok(T1) },
+        call(P2, Res2),
+        (
+            { Res2 = ok(T2) },
+            call(P3, Res3),
+            (
+                { Res3 = ok(T3) },
+                call(P4, Res4),
+                (
+                    { Res4 = ok(T4) },
+                    call(P5, Res5),
+                    (
+                        { Res5 = ok(T5) },
+                        call(P6, Res6),
+                        (
+                            { Res6 = ok(T6) },
+                            call(P7, Res7),
+                            (
+                                { Res7 = ok(T7) },
+                                call(P8, Res8),
+                                (
+                                    { Res8 = ok(T8) },
+                                    call(P9, Res9),
+                                    (
+                                        { Res9 = ok(T9) },
+                                        call(P10, Res10),
+                                        (
+                                            { Res10 = ok(T10) },
+                                            { call(Combine, T1, T2, T3, T4, T5,
+                                                T6, T7, T8, T9, T10, Res) }
+                                        ;
+                                            { Res10 = eof },
+                                            { Res = eof }
+                                        ;
+                                            { Res10 = error(Err) },
+                                            { Res = error(Err) }
+                                        )
+                                    ;
+                                        { Res9 = eof },
+                                        { Res = eof }
+                                    ;
+                                        { Res9 = error(Err) },
+                                        { Res = error(Err) }
+                                    )
+                                ;
+                                    { Res8 = eof },
+                                    { Res = eof }
+                                ;
+                                    { Res8 = error(Err) },
+                                    { Res = error(Err) }
+                                )
+                            ;
+                                { Res7 = eof },
+                                { Res = eof }
+                            ;
+                                { Res7 = error(Err) },
+                                { Res = error(Err) }
+                            )
+                        ;
+                            { Res6 = eof },
+                            { Res = eof }
+                        ;
+                            { Res6 = error(Err) },
+                            { Res = error(Err) }
+                        )
+                    ;
+                        { Res5 = eof },
+                        { Res = eof }
+                    ;
+                        { Res5 = error(Err) },
+                        { Res = error(Err) }
+                    )
+                ;
+                    { Res4 = eof },
+                    { Res = eof }
+                ;
+                    { Res4 = error(Err) },
+                    { Res = error(Err) }
+                )
+            ;
+                { Res3 = eof },
+                { Res = eof }
+            ;
+                { Res3 = error(Err) },
+                { Res = error(Err) }
+            )
+        ;
+            { Res2 = eof },
+            { Res = eof }
+        ;
+            { Res2 = error(Err) },
+            { Res = error(Err) }
+        )
+    ;
+        { Res1 = eof },
+        { Res = eof }
+    ;
+        { Res1 = error(Err) },
+        { Res = error(Err) }
+    ).
+
 %-----------------------------------------------------------------------------%
 
 io_combinator__res_sequence_2(P1, P2, Combine, Res) -->
@@ -1290,6 +1476,81 @@
         { Res = error(Err) }
     ).
 
+io_combinator__res_sequence_10(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10,
+        Combine, Res) -->
+    call(P1, Res1),
+    (
+        { Res1 = ok(T1) },
+        call(P2, Res2),
+        (
+            { Res2 = ok(T2) },
+            call(P3, Res3),
+            (
+                { Res3 = ok(T3) },
+                call(P4, Res4),
+                (
+                    { Res4 = ok(T4) },
+                    call(P5, Res5),
+                    (
+                        { Res5 = ok(T5) },
+                        call(P6, Res6),
+                        (
+                            { Res6 = ok(T6) },
+                            call(P7, Res7),
+                            (
+                                { Res7 = ok(T7) },
+                                call(P8, Res8),
+                                (
+                                    { Res8 = ok(T8) },
+                                    call(P9, Res9),
+                                    (
+                                        { Res9 = ok(T9) },
+                                        call(P10, Res10),
+                                        (
+                                            { Res10 = ok(T10) },
+                                            { call(Combine, T1, T2, T3, T4, T5,
+                                                T6, T7, T8, T9, T10, Res) }
+                                        ;
+                                            { Res10 = error(Err) },
+                                            { Res = error(Err) }
+                                        )
+                                    ;
+                                        { Res9 = error(Err) },
+                                        { Res = error(Err) }
+                                    )
+                                ;
+                                    { Res8 = error(Err) },
+                                    { Res = error(Err) }
+                                )
+                            ;
+                                { Res7 = error(Err) },
+                                { Res = error(Err) }
+                            )
+                        ;
+                            { Res6 = error(Err) },
+                            { Res = error(Err) }
+                        )
+                    ;
+                        { Res5 = error(Err) },
+                        { Res = error(Err) }
+                    )
+                ;
+                    { Res4 = error(Err) },
+                    { Res = error(Err) }
+                )
+            ;
+                { Res3 = error(Err) },
+                { Res = error(Err) }
+            )
+        ;
+            { Res2 = error(Err) },
+            { Res = error(Err) }
+        )
+    ;
+        { Res1 = error(Err) },
+        { Res = error(Err) }
+    ).
+
 %-----------------------------------------------------------------------------%
 
 io_combinator__maybe_error_sequence_2(P1, P2, Combine, Res) -->
@@ -1596,6 +1857,81 @@
                                         { Res9 = ok(T9) },
                                         { call(Combine, T1, T2, T3, T4, T5,
                                             T6, T7, T8, T9, Res) }
+                                    ;
+                                        { Res9 = error(Err) },
+                                        { Res = error(Err) }
+                                    )
+                                ;
+                                    { Res8 = error(Err) },
+                                    { Res = error(Err) }
+                                )
+                            ;
+                                { Res7 = error(Err) },
+                                { Res = error(Err) }
+                            )
+                        ;
+                            { Res6 = error(Err) },
+                            { Res = error(Err) }
+                        )
+                    ;
+                        { Res5 = error(Err) },
+                        { Res = error(Err) }
+                    )
+                ;
+                    { Res4 = error(Err) },
+                    { Res = error(Err) }
+                )
+            ;
+                { Res3 = error(Err) },
+                { Res = error(Err) }
+            )
+        ;
+            { Res2 = error(Err) },
+            { Res = error(Err) }
+        )
+    ;
+        { Res1 = error(Err) },
+        { Res = error(Err) }
+    ).
+
+io_combinator__maybe_error_sequence_10(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10,
+        Combine, Res) -->
+    call(P1, Res1),
+    (
+        { Res1 = ok(T1) },
+        call(P2, Res2),
+        (
+            { Res2 = ok(T2) },
+            call(P3, Res3),
+            (
+                { Res3 = ok(T3) },
+                call(P4, Res4),
+                (
+                    { Res4 = ok(T4) },
+                    call(P5, Res5),
+                    (
+                        { Res5 = ok(T5) },
+                        call(P6, Res6),
+                        (
+                            { Res6 = ok(T6) },
+                            call(P7, Res7),
+                            (
+                                { Res7 = ok(T7) },
+                                call(P8, Res8),
+                                (
+                                    { Res8 = ok(T8) },
+                                    call(P9, Res9),
+                                    (
+                                        { Res9 = ok(T9) },
+                                        call(P10, Res10),
+                                        (
+                                            { Res10 = ok(T10) },
+                                            { call(Combine, T1, T2, T3, T4, T5,
+                                                T6, T7, T8, T9, T10, Res) }
+                                        ;
+                                            { Res10 = error(Err) },
+                                            { Res = error(Err) }
+                                        )
                                     ;
                                         { Res9 = error(Err) },
                                         { Res = error(Err) }
Index: deep_profiler/mdprof_cgi.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/mdprof_cgi.m,v
retrieving revision 1.1
diff -u -b -r1.1 mdprof_cgi.m
--- deep_profiler/mdprof_cgi.m	2001/05/31 05:59:57	1.1
+++ deep_profiler/mdprof_cgi.m	2001/06/16 18:12:12
@@ -34,10 +34,12 @@
 		(
 			{ MaybeQueryString = yes(QueryString0) },
 			{ split(QueryString0, ('$'), Pieces) },
-			( { Pieces = [ActualQuery, FileName] } ->
-				process_query(ActualQuery, FileName)
+			( { Pieces = [CmdStr, PrefStr, FileName] } ->
+				process_query(CmdStr, yes(PrefStr), FileName)
+			; { Pieces = [CmdStr, FileName] } ->
+				process_query(CmdStr, no, FileName)
 			; { Pieces = [FileName] } ->
-				process_query("menu", FileName)
+				process_query("menu", no, FileName)
 			;
 				io__write_string(
 					"Bad URL; expected query$/full/path/name\n")
@@ -49,10 +51,10 @@
 		io__write_string("Usage: mdprof_cgi\n")
 	).
 
-:- pred process_query(string::in, string::in,
+:- pred process_query(string::in, maybe(string)::in, string::in,
 	io__state::di, io__state::uo) is det.
 
-process_query(ActualQuery, DataFileName) -->
+process_query(CmdStr, MaybePrefStr, DataFileName) -->
 	{ ToServer = to_server_pipe_name(DataFileName) },
 	{ FromServer = from_server_pipe_name(DataFileName) },
 	{ TestCmd = string__format("test -p %s -a -p %s",
@@ -68,7 +70,8 @@
 		),
 		(
 			{ MaybeError = no },
-			handle_query(ActualQuery, ToServer, FromServer)
+			handle_query(CmdStr, MaybePrefStr,
+				ToServer, FromServer)
 		;
 			{ MaybeError = yes(Error) },
 			io__write_string(Error)
@@ -119,16 +122,22 @@
 
 server_path_name = "mdprof_server".
 
-:- pred handle_query(string::in, string::in, string::in,
+:- pred handle_query(string::in, maybe(string)::in, string::in, string::in,
 	io__state::di, io__state::uo) is det.
 
-handle_query(QueryString, ToServer, FromServer) -->
-	{ query_to_cmd(QueryString, MaybeCmd) },
-	(
-		{ MaybeCmd = yes(Cmd) },
-		to(ToServer, Cmd),
-		from(FromServer, html(Str)),
-		io__write_string(Str)
+handle_query(CmdStr, MaybePrefStr, ToServer, FromServer) -->
+	{ MaybeCmd = url_component_to_cmd(CmdStr) },
+	{
+		MaybePrefStr = yes(PrefStr),
+		MaybePref = url_component_to_preferences(PrefStr)
+	;
+		MaybePrefStr = no,
+		MaybePref = yes(default_preferences)
+	},
+	( { MaybeCmd = yes(Cmd), MaybePref = yes(Pref) } ->
+		to(ToServer, cmd_pref(Cmd, Pref)),
+		from(FromServer, html(Page)),
+		io__write_string(Page)
 	;
-		{ MaybeCmd = no }
+		io__write_string("mdprof: unknown URL format")
 	).
Index: deep_profiler/mdprof_server.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/mdprof_server.m,v
retrieving revision 1.1
diff -u -b -r1.1 mdprof_server.m
--- deep_profiler/mdprof_server.m	2001/05/31 05:59:58	1.1
+++ deep_profiler/mdprof_server.m	2001/07/02 13:09:45
@@ -19,7 +19,8 @@
 
 :- implementation.
 
-:- import_module conf, interface, profile, read_profile, startup, server.
+:- import_module conf, interface.
+:- import_module profile, read_profile, startup, timeout, server.
 :- import_module array, bool, char, getopt, int, list, assoc_list.
 :- import_module map, require, set, std_util, string, require.
 
@@ -28,7 +29,6 @@
 	;	debug
 	;	test
 	;	test_dir
-	;	test_fields
 	;	timeout.
 
 :- type options ---> options.
@@ -70,14 +70,12 @@
 		(
 			{ Res = ok(Deep) },
 			{ lookup_string_option(Options, test_dir, TestDir) },
-			{ lookup_string_option(Options, test_fields,
-				TestFields) },
-			test_server(TestDir, Deep, TestFields)
+			{ Pref = default_preferences },
+			test_server(TestDir, Pref, Deep)
 		;
 			{ Res = error(Error) },
 			io__set_exit_status(1),
-			io__format(StdErr,
-				"error reading data file: %s\n",
+			io__format(StdErr, "error reading data file: %s\n",
 				[s(Error)])
 		)
 	;
@@ -92,7 +90,7 @@
 				{ lookup_int_option(Options, timeout,
 					TimeOut) },
 				{ lookup_bool_option(Options, debug, Debug) },
-				server(TimeOut, Debug, Deep)
+				server(TimeOut, Debug, CanonicalClique, Deep)
 			;
 				{ Res = error(Error) },
 				io__set_exit_status(1),
@@ -119,14 +117,16 @@
 		{ MakeOutputPipeCmd = make_pipe_cmd(OutputPipe) },
 		io__call_system(MakeInputPipeCmd, InputRes),
 		io__call_system(MakeOutputPipeCmd, OutputRes),
-		{
-			InputRes = ok(0),
-			OutputRes = ok(0)
+		(
+			{ InputRes = ok(0) },
+			{ OutputRes = ok(0) }
 		->
-			OK = yes
+			{ OK = yes },
+			{ StartupFile = server_startup_name(FileName) },
+			setup_exit(InputPipe, OutputPipe, StartupFile)
 		;
-			OK = no
-		}
+			{ OK = no }
+		)
 	;
 		{ error("make_pipes: multiple filenames not yet implemented") }
 	).
@@ -137,7 +137,6 @@
 
 short('c',	canonical_clique).
 short('D',	test_dir).
-short('F',	test_fields).
 short('t',	timeout).
 short('T',	test).
 
@@ -147,7 +146,6 @@
 long("debug",		debug).
 long("test",		test).
 long("test-dir",	test_dir).
-long("test-fields",	test_fields).
 long("timeout",		timeout).
 
 :- pred defaults(option::out, option_data::out) is nondet.
@@ -159,8 +157,7 @@
 :- pred defaults0(option::out, option_data::out) is multi.
 
 defaults0(canonical_clique,	bool(no)).
-defaults0(debug,		bool(no)).
+defaults0(debug,		bool(yes)).
 defaults0(test,			bool(no)).
 defaults0(test_dir,		string("deep_test")).
-defaults0(test_fields,		string("pqw")).
 defaults0(timeout,		int(30)).
Index: deep_profiler/measurements.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/measurements.m,v
retrieving revision 1.2
diff -u -b -r1.2 measurements.m
--- deep_profiler/measurements.m	2001/06/05 04:47:32	1.2
+++ deep_profiler/measurements.m	2001/06/19 16:14:12
@@ -40,6 +40,8 @@
 	= inherit_prof_info.
 :- func subtract_own_from_inherit(own_prof_info, inherit_prof_info)
 	= inherit_prof_info.
+:- func subtract_inherit_from_inherit(inherit_prof_info, inherit_prof_info)
+	= inherit_prof_info.
 :- func add_inherit_to_own(inherit_prof_info, own_prof_info) = own_prof_info.
 :- func add_own_to_own(own_prof_info, own_prof_info) = own_prof_info.
 
@@ -141,6 +143,12 @@
 	Quanta = inherit_quanta(PI2) - quanta(PI1),
 	Allocs = inherit_allocs(PI2) - allocs(PI1),
 	Words = inherit_words(PI2) - words(PI1),
+	SumPI = inherit_prof_info(Quanta, Allocs, Words).
+
+subtract_inherit_from_inherit(PI1, PI2) = SumPI :-
+	Quanta = inherit_quanta(PI2) - inherit_quanta(PI1),
+	Allocs = inherit_allocs(PI2) - inherit_allocs(PI1),
+	Words = inherit_words(PI2) - inherit_words(PI1),
 	SumPI = inherit_prof_info(Quanta, Allocs, Words).
 
 add_inherit_to_own(PI1, PI2) = SumPI :-
Index: deep_profiler/profile.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/profile.m,v
retrieving revision 1.2
diff -u -b -r1.2 profile.m
--- deep_profiler/profile.m	2001/06/05 04:47:32	1.2
+++ deep_profiler/profile.m	2001/07/02 07:09:10
@@ -21,7 +21,7 @@
 :- interface.
 
 :- import_module measurements.
-:- import_module std_util, array, list, map.
+:- import_module bool, std_util, array, list, map.
 
 :- type profile_stats --->
 	profile_stats(
@@ -31,7 +31,9 @@
 		max_ps			:: int,
 		ticks_per_sec		:: int,
 		instrument_quanta	:: int,
-		user_quanta		:: int
+		user_quanta		:: int,
+		word_size		:: int,
+		canonical		:: bool
 	).
 
 :- type initial_deep --->
@@ -81,7 +83,25 @@
 		ps_own			:: array(own_prof_info),
 		ps_desc			:: array(inherit_prof_info),
 		css_own			:: array(own_prof_info),
-		css_desc		:: array(inherit_prof_info)
+		css_desc		:: array(inherit_prof_info),
+			% Additional propagated timing info to solve the
+			% problem of undetected recursion through higher order
+			% calls, which is caused by our use of zeroing.
+		pd_zero_total_map	:: array(zero_total_map),
+		csd_zero_total_map	:: array(zero_total_map),
+			% Information about modules.
+		module_data		:: map(string, module_data)
+	).
+
+:- type zero_total_map == map(proc_static_ptr, inherit_prof_info).
+
+:- type module_data
+	--->	module_data(
+					% The total cost of the module.
+			module_own	:: own_prof_info,
+			module_desc	:: inherit_prof_info,
+					% The procedures defined in the module.
+			module_procs	:: list(proc_static_ptr)
 	).
 
 %-----------------------------------------------------------------------------%
@@ -118,10 +138,14 @@
 :- type proc_static
 	--->	proc_static(
 			ps_id		:: proc_id,	% procedure ID
+			ps_decl_module	:: string,	% declaring module
 			ps_refined_id	:: string, 	% refined procedure id
 			ps_raw_id	:: string, 	% raw procedure id
-			ps_filename	:: string, 	% file name
-			ps_sites	:: array(call_site_static_ptr)
+			ps_file_name	:: string, 	% file name of proc
+			ps_line_number	:: int, 	% line number of proc
+			ps_in_interface	:: bool,	% is in interface?
+			ps_sites	:: array(call_site_static_ptr),
+			ps_is_zeroed	:: is_zeroed
 		).
 
 :- type call_site_dynamic
@@ -169,8 +193,12 @@
 
 :- type call_site_array_slot
 	--->	normal(call_site_dynamic_ptr)
-	;	multi(array(call_site_dynamic_ptr)).
+	;	multi(is_zeroed, array(call_site_dynamic_ptr)).
 
+:- type is_zeroed
+	--->	zeroed
+	;	not_zeroed.
+
 :- type call_site_kind
 	--->	normal_call
 	;	special_call
@@ -197,9 +225,17 @@
 
 %-----------------------------------------------------------------------------%
 
+:- func decl_module(proc_id) = string.
+
 :- func dummy_proc_id = proc_id.
 :- func main_parent_proc_id = proc_id.
 
+:- func dummy_proc_dynamic_ptr = proc_dynamic_ptr.
+:- func dummy_proc_static_ptr = proc_static_ptr.
+:- func dummy_call_site_dynamic_ptr = call_site_dynamic_ptr.
+:- func dummy_call_site_static_ptr = call_site_static_ptr.
+:- func dummy_clique_ptr = clique_ptr.
+
 :- pred valid_clique_ptr(deep::in, clique_ptr::in) is semidet.
 :- pred valid_proc_dynamic_ptr(deep::in, proc_dynamic_ptr::in) is semidet.
 :- pred valid_proc_static_ptr(deep::in, proc_static_ptr::in) is semidet.
@@ -233,11 +269,33 @@
 	clique_ptr::in, call_site_dynamic_ptr::out) is det.
 :- pred lookup_clique_maybe_child(array(maybe(clique_ptr))::in,
 	call_site_dynamic_ptr::in, maybe(clique_ptr)::out) is det.
+:- pred lookup_proc_callers(array(list(call_site_dynamic_ptr))::in,
+	proc_static_ptr::in, list(call_site_dynamic_ptr)::out) is det.
 :- pred lookup_call_site_static_map(call_site_static_map::in,
 	call_site_dynamic_ptr::in, call_site_static_ptr::out) is det.
 :- pred lookup_call_site_calls(array(map(proc_static_ptr,
 	list(call_site_dynamic_ptr)))::in, call_site_static_ptr::in,
 	map(proc_static_ptr, list(call_site_dynamic_ptr))::out) is det.
+:- pred lookup_pd_own(array(own_prof_info)::in,
+	proc_dynamic_ptr::in, own_prof_info::out) is det.
+:- pred lookup_pd_desc(array(inherit_prof_info)::in,
+	proc_dynamic_ptr::in, inherit_prof_info::out) is det.
+:- pred lookup_csd_own(array(own_prof_info)::in,
+	call_site_dynamic_ptr::in, own_prof_info::out) is det.
+:- pred lookup_csd_desc(array(inherit_prof_info)::in,
+	call_site_dynamic_ptr::in, inherit_prof_info::out) is det.
+:- pred lookup_ps_own(array(own_prof_info)::in,
+	proc_static_ptr::in, own_prof_info::out) is det.
+:- pred lookup_ps_desc(array(inherit_prof_info)::in,
+	proc_static_ptr::in, inherit_prof_info::out) is det.
+:- pred lookup_css_own(array(own_prof_info)::in,
+	call_site_static_ptr::in, own_prof_info::out) is det.
+:- pred lookup_css_desc(array(inherit_prof_info)::in,
+	call_site_static_ptr::in, inherit_prof_info::out) is det.
+:- pred lookup_pd_zero_map(array(zero_total_map)::in,
+	proc_dynamic_ptr::in, zero_total_map::out) is det.
+:- pred lookup_csd_zero_map(array(zero_total_map)::in,
+	call_site_dynamic_ptr::in, zero_total_map::out) is det.
 
 :- pred deep_lookup_call_site_dynamics(deep::in, call_site_dynamic_ptr::in,
 	call_site_dynamic::out) is det.
@@ -255,12 +313,18 @@
 	call_site_dynamic_ptr::out) is det.
 :- pred deep_lookup_clique_maybe_child(deep::in, call_site_dynamic_ptr::in,
 	maybe(clique_ptr)::out) is det.
+:- pred deep_lookup_proc_callers(deep::in, proc_static_ptr::in,
+	list(call_site_dynamic_ptr)::out) is det.
 :- pred deep_lookup_call_site_static_map(deep::in, call_site_dynamic_ptr::in,
 	call_site_static_ptr::out) is det.
 :- pred deep_lookup_call_site_calls(deep::in, call_site_static_ptr::in,
 	map(proc_static_ptr, list(call_site_dynamic_ptr))::out) is det.
 :- pred deep_lookup_proc_dynamic_sites(deep::in, proc_dynamic_ptr::in,
 	array(call_site_array_slot)::out) is det.
+:- pred deep_lookup_pd_zero_map(deep::in, proc_dynamic_ptr::in,
+	zero_total_map::out) is det.
+:- pred deep_lookup_csd_zero_map(deep::in, call_site_dynamic_ptr::in,
+	zero_total_map::out) is det.
 
 :- pred deep_lookup_pd_own(deep::in, proc_dynamic_ptr::in,
 	own_prof_info::out) is det.
@@ -290,9 +354,24 @@
 	proc_dynamics::array_uo) is det.
 :- pred update_proc_statics(proc_statics::array_di,
 	proc_static_ptr::in, proc_static::in, proc_statics::array_uo) is det.
+:- pred update_proc_callers(array(list(call_site_dynamic_ptr))::array_di,
+	proc_static_ptr::in, list(call_site_dynamic_ptr)::in,
+	array(list(call_site_dynamic_ptr))::array_uo) is det.
 :- pred update_call_site_static_map(call_site_static_map::array_di,
 	call_site_dynamic_ptr::in, call_site_static_ptr::in,
 	call_site_static_map::array_uo) is det.
+:- pred update_ps_own(array(own_prof_info)::array_di,
+	proc_static_ptr::in, own_prof_info::in,
+	array(own_prof_info)::array_uo) is det.
+:- pred update_ps_desc(array(inherit_prof_info)::array_di,
+	proc_static_ptr::in, inherit_prof_info::in,
+	array(inherit_prof_info)::array_uo) is det.
+:- pred update_css_own(array(own_prof_info)::array_di,
+	call_site_static_ptr::in, own_prof_info::in,
+	array(own_prof_info)::array_uo) is det.
+:- pred update_css_desc(array(inherit_prof_info)::array_di,
+	call_site_static_ptr::in, inherit_prof_info::in,
+	array(inherit_prof_info)::array_uo) is det.
 
 :- pred deep_update_csd_desc(deep::in, call_site_dynamic_ptr::in,
 	inherit_prof_info::in, deep::out) is det.
@@ -300,6 +379,49 @@
 	inherit_prof_info::in, deep::out) is det.
 :- pred deep_update_pd_own(deep::in, proc_dynamic_ptr::in,
 	own_prof_info::in, deep::out) is det.
+:- pred deep_update_pd_zero_map(deep::in, proc_dynamic_ptr::in,
+	zero_total_map::in, deep::out) is det.
+:- pred deep_update_csd_zero_map(deep::in, call_site_dynamic_ptr::in,
+	zero_total_map::in, deep::out) is det.
+
+:- pred extract_pd_sites(proc_dynamic::in, array(call_site_array_slot)::out)
+	is det.
+:- pred extract_csd_caller(call_site_dynamic::in, proc_dynamic_ptr::out)
+	is det.
+:- pred extract_csd_callee(call_site_dynamic::in, proc_dynamic_ptr::out)
+	is det.
+:- pred extract_csdptr_caller(initial_deep::in, call_site_dynamic_ptr::in,
+	proc_dynamic_ptr::out) is det.
+:- pred extract_csdptr_callee(initial_deep::in, call_site_dynamic_ptr::in,
+	proc_dynamic_ptr::out) is det.
+:- pred extract_ticks_per_sec(initial_deep::in, int::out) is det.
+:- pred extract_instrument_quanta(initial_deep::in, int::out) is det.
+:- pred extract_user_quanta(initial_deep::in, int::out) is det.
+:- pred extract_max_css(initial_deep::in, int::out) is det.
+:- pred extract_max_ps(initial_deep::in, int::out) is det.
+:- pred extract_max_csd(initial_deep::in, int::out) is det.
+:- pred extract_max_pd(initial_deep::in, int::out) is det.
+:- pred extract_init_call_site_dynamics(initial_deep::in,
+	call_site_dynamics::out) is det.
+:- pred extract_init_call_site_statics(initial_deep::in,
+	call_site_statics::out) is det.
+:- pred extract_init_proc_dynamics(initial_deep::in,
+	proc_dynamics::out) is det.
+:- pred extract_init_proc_statics(initial_deep::in,
+	proc_statics::out) is det.
+:- pred extract_init_root(initial_deep::in,
+	proc_dynamic_ptr::out) is det.
+
+:- pred deep_extract_csdptr_caller(deep::in, call_site_dynamic_ptr::in,
+	proc_dynamic_ptr::out) is det.
+:- pred deep_extract_csdptr_callee(deep::in, call_site_dynamic_ptr::in,
+	proc_dynamic_ptr::out) is det.
+
+:- func wrap_proc_static_ptr(int) = proc_static_ptr.
+
+:- func root_total_info(deep) = inherit_prof_info.
+:- func root_desc_info(deep) = inherit_prof_info.
+:- func root_own_info(deep) = own_prof_info.
 
 %-----------------------------------------------------------------------------%
 
@@ -308,6 +430,13 @@
 :- import_module array_util.
 :- import_module int, require.
 
+decl_module(ProcId) = DeclModule :-
+	(
+		ProcId = user_defined(_, DeclModule, _, _, _, _)
+	;
+		ProcId = compiler_generated(_, DeclModule, _, _, _, _)
+	).
+
 dummy_proc_id = user_defined(predicate, "unknown", "unknown", "unknown",
 	-1, -1).
 
@@ -316,6 +445,14 @@
 
 %-----------------------------------------------------------------------------%
 
+dummy_proc_static_ptr = proc_static_ptr(-1).
+dummy_proc_dynamic_ptr = proc_dynamic_ptr(-1).
+dummy_call_site_static_ptr = call_site_static_ptr(-1).
+dummy_call_site_dynamic_ptr = call_site_dynamic_ptr(-1).
+dummy_clique_ptr = clique_ptr(-1).
+
+%-----------------------------------------------------------------------------%
+
 valid_clique_ptr(Deep, clique_ptr(CliqueNum)) :-
 	CliqueNum > 0,
 	array__in_bounds(Deep ^ clique_members, CliqueNum).
@@ -421,6 +558,14 @@
 		error("lookup_clique_maybe_child: bounds error")
 	).
 
+lookup_proc_callers(ProcCallers, PSPtr, Callers) :-
+	PSPtr = proc_static_ptr(PSI),
+	( PSI > 0, array__in_bounds(ProcCallers, PSI) ->
+		array__lookup(ProcCallers, PSI, Callers)
+	;
+		error("lookup_proc_callers: bounds error")
+	).
+
 lookup_call_site_static_map(CallSiteStaticMap, CSDPtr, CSSPtr) :-
 	CSDPtr = call_site_dynamic_ptr(CSDI),
 	( CSDI > 0, array__in_bounds(CallSiteStaticMap, CSDI) ->
@@ -437,6 +582,86 @@
 		error("lookup_call_site_static_map: bounds error")
 	).
 
+lookup_pd_own(PDOwns, PDPtr, PDOwn) :-
+	PDPtr = proc_dynamic_ptr(PDI),
+	( PDI > 0, array__in_bounds(PDOwns, PDI) ->
+		array__lookup(PDOwns, PDI, PDOwn)
+	;
+		error("lookup_pd_own: bounds error")
+	).
+
+lookup_pd_desc(PDDescs, PDPtr, PDDesc) :-
+	PDPtr = proc_dynamic_ptr(PDI),
+	( PDI > 0, array__in_bounds(PDDescs, PDI) ->
+		array__lookup(PDDescs, PDI, PDDesc)
+	;
+		error("lookup_pd_desc: bounds error")
+	).
+
+lookup_csd_own(CSDOwns, CSDPtr, CSDOwn) :-
+	CSDPtr = call_site_dynamic_ptr(CSDI),
+	( CSDI > 0, array__in_bounds(CSDOwns, CSDI) ->
+		array__lookup(CSDOwns, CSDI, CSDOwn)
+	;
+		error("lookup_csd_own: bounds error")
+	).
+
+lookup_csd_desc(CSDDescs, CSDPtr, CSDDesc) :-
+	CSDPtr = call_site_dynamic_ptr(CSDI),
+	( CSDI > 0, array__in_bounds(CSDDescs, CSDI) ->
+		array__lookup(CSDDescs, CSDI, CSDDesc)
+	;
+		error("lookup_csd_desc: bounds error")
+	).
+
+lookup_ps_own(PSOwns, PSPtr, PSOwn) :-
+	PSPtr = proc_static_ptr(PSI),
+	( PSI > 0, array__in_bounds(PSOwns, PSI) ->
+		array__lookup(PSOwns, PSI, PSOwn)
+	;
+		error("lookup_ps_own: bounds error")
+	).
+
+lookup_ps_desc(PSDescs, PSPtr, PSDesc) :-
+	PSPtr = proc_static_ptr(PSI),
+	( PSI > 0, array__in_bounds(PSDescs, PSI) ->
+		array__lookup(PSDescs, PSI, PSDesc)
+	;
+		error("lookup_ps_desc: bounds error")
+	).
+
+lookup_css_own(CSSOwns, CSSPtr, CSSOwn) :-
+	CSSPtr = call_site_static_ptr(CSSI),
+	( CSSI > 0, array__in_bounds(CSSOwns, CSSI) ->
+		array__lookup(CSSOwns, CSSI, CSSOwn)
+	;
+		error("lookup_css_own: bounds error")
+	).
+
+lookup_css_desc(CSSDescs, CSSPtr, CSSDesc) :-
+	CSSPtr = call_site_static_ptr(CSSI),
+	( CSSI > 0, array__in_bounds(CSSDescs, CSSI) ->
+		array__lookup(CSSDescs, CSSI, CSSDesc)
+	;
+		error("lookup_css_desc: bounds error")
+	).
+
+lookup_pd_zero_map(PDZeroMaps, PDPtr, ZeroMap) :-
+	PDPtr = proc_dynamic_ptr(PDI),
+	( PDI > 0, array__in_bounds(PDZeroMaps, PDI) ->
+		array__lookup(PDZeroMaps, PDI, ZeroMap)
+	;
+		error("lookup_pd_zero_map: bounds error")
+	).
+
+lookup_csd_zero_map(CSDZeroMaps, CSDPtr, ZeroMap) :-
+	CSDPtr = call_site_dynamic_ptr(CSDI),
+	( CSDI > 0, array__in_bounds(CSDZeroMaps, CSDI) ->
+		array__lookup(CSDZeroMaps, CSDI, ZeroMap)
+	;
+		error("lookup_csd_zero_map: bounds error")
+	).
+
 %-----------------------------------------------------------------------------%
 
 deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD) :-
@@ -464,6 +689,9 @@
 	lookup_clique_maybe_child(Deep ^ clique_maybe_child, CSDPtr,
 		MaybeCliquePtr).
 
+deep_lookup_proc_callers(Deep, PSPtr, CallerCSDPtrs) :-
+	lookup_proc_callers(Deep ^ proc_callers, PSPtr, CallerCSDPtrs).
+
 deep_lookup_call_site_static_map(Deep, CSDPtr, CSSPtr) :-
 	lookup_call_site_static_map(Deep ^ call_site_static_map, CSDPtr,
 		CSSPtr).
@@ -475,6 +703,12 @@
 	deep_lookup_proc_dynamics(Deep, PDPtr, PD),
 	PDSites = PD ^ pd_sites.
 
+deep_lookup_pd_zero_map(Deep, PDPtr, ZeroMap) :-
+	lookup_pd_zero_map(Deep ^ pd_zero_total_map, PDPtr, ZeroMap).
+
+deep_lookup_csd_zero_map(Deep, CSDPtr, ZeroMap) :-
+	lookup_csd_zero_map(Deep ^ csd_zero_total_map, CSDPtr, ZeroMap).
+
 %-----------------------------------------------------------------------------%
 
 deep_lookup_pd_own(Deep, PDPtr, Own) :-
@@ -533,6 +767,26 @@
 	CSDPtr = call_site_dynamic_ptr(CSDI),
 	array__set(CallSiteStaticMap0, CSDI, CSSPtr, CallSiteStaticMap).
 
+update_proc_callers(ProcCallers0, PSPtr, CSDPtrs, ProcCallers) :-
+	PSPtr = proc_static_ptr(PSI),
+	array__set(ProcCallers0, PSI, CSDPtrs, ProcCallers).
+
+update_ps_own(PSOwns0, PSPtr, Own, PSOwns) :-
+	PSPtr = proc_static_ptr(PSI),
+	array__set(PSOwns0, PSI, Own, PSOwns).
+
+update_ps_desc(PSDescs0, PSPtr, Desc, PSDescs) :-
+	PSPtr = proc_static_ptr(PSI),
+	array__set(PSDescs0, PSI, Desc, PSDescs).
+
+update_css_own(CSSOwns0, CSSPtr, Own, CSSOwns) :-
+	CSSPtr = call_site_static_ptr(CSSI),
+	array__set(CSSOwns0, CSSI, Own, CSSOwns).
+
+update_css_desc(CSSDescs0, CSSPtr, Desc, CSSDescs) :-
+	CSSPtr = call_site_static_ptr(CSSI),
+	array__set(CSSDescs0, CSSI, Desc, CSSDescs).
+
 %-----------------------------------------------------------------------------%
 
 deep_update_csd_desc(Deep0, CSDPtr, CSDDesc, Deep) :-
@@ -549,5 +803,91 @@
 	PDPtr = proc_dynamic_ptr(PDI),
 	array__set(u(Deep0 ^ pd_own), PDI, PDOwn, PDOwns),
 	Deep = Deep0 ^ pd_own := PDOwns.
+
+deep_update_pd_zero_map(Deep0, PDPtr, ZeroMap, Deep) :-
+	PDPtr = proc_dynamic_ptr(PDI),
+	array__set(u(Deep0 ^ pd_zero_total_map), PDI, ZeroMap, PDZeroMaps),
+	Deep = Deep0 ^ pd_zero_total_map := PDZeroMaps.
+
+deep_update_csd_zero_map(Deep0, CSDPtr, ZeroMap, Deep) :-
+	CSDPtr = call_site_dynamic_ptr(CSDI),
+	array__set(u(Deep0 ^ csd_zero_total_map), CSDI, ZeroMap, CSDZeroMaps),
+	Deep = Deep0 ^ csd_zero_total_map := CSDZeroMaps.
+
+%-----------------------------------------------------------------------------%
+
+extract_pd_sites(PD, PD ^ pd_sites).
+
+extract_csd_caller(CSD, CSD ^ csd_caller).
+
+extract_csd_callee(CSD, CSD ^ csd_callee).
+
+extract_csdptr_caller(InitDeep, CSDPtr, CallerPDPtr) :-
+	lookup_call_site_dynamics(InitDeep ^ init_call_site_dynamics,
+		CSDPtr, CSD),
+	CallerPDPtr = CSD ^ csd_caller.
+
+extract_csdptr_callee(InitDeep, CSDPtr, CalleePDPtr) :-
+	lookup_call_site_dynamics(InitDeep ^ init_call_site_dynamics,
+		CSDPtr, CSD),
+	CalleePDPtr = CSD ^ csd_callee.
+
+extract_ticks_per_sec(InitDeep,
+	InitDeep ^ init_profile_stats ^ ticks_per_sec).
+
+extract_instrument_quanta(InitDeep,
+	InitDeep ^ init_profile_stats ^ instrument_quanta).
+
+extract_user_quanta(InitDeep,
+	InitDeep ^ init_profile_stats ^ user_quanta).
+
+extract_max_css(InitDeep, MaxCSS) :-
+	array__max(InitDeep ^ init_call_site_statics, MaxCSS).
+
+extract_max_ps(InitDeep, MaxPS) :-
+	array__max(InitDeep ^ init_proc_statics, MaxPS).
+
+extract_max_csd(InitDeep, MaxCSD) :-
+	array__max(InitDeep ^ init_call_site_dynamics, MaxCSD).
+
+extract_max_pd(InitDeep, MaxPD) :-
+	array__max(InitDeep ^ init_proc_dynamics, MaxPD).
+
+extract_init_call_site_dynamics(InitDeep, InitDeep ^ init_call_site_dynamics).
+
+extract_init_call_site_statics(InitDeep, InitDeep ^ init_call_site_statics).
+
+extract_init_proc_dynamics(InitDeep, InitDeep ^ init_proc_dynamics).
+
+extract_init_proc_statics(InitDeep, InitDeep ^ init_proc_statics).
+
+extract_init_root(InitDeep, InitDeep ^ init_root).
+
+%-----------------------------------------------------------------------------%
+
+deep_extract_csdptr_caller(Deep, CSDPtr, CallerPDPtr) :-
+	lookup_call_site_dynamics(Deep ^ call_site_dynamics, CSDPtr, CSD),
+	CallerPDPtr = CSD ^ csd_caller.
+
+deep_extract_csdptr_callee(Deep, CSDPtr, CalleePDPtr) :-
+	lookup_call_site_dynamics(Deep ^ call_site_dynamics, CSDPtr, CSD),
+	CalleePDPtr = CSD ^ csd_callee.
+
+%-----------------------------------------------------------------------------%
+
+wrap_proc_static_ptr(PSI) = proc_static_ptr(PSI).
+
+%-----------------------------------------------------------------------------%
+
+root_total_info(Deep) = RootTotal :-
+	deep_lookup_pd_own(Deep, Deep ^ root, RootOwn),
+	deep_lookup_pd_desc(Deep, Deep ^ root, RootDesc),
+	add_own_to_inherit(RootOwn, RootDesc) = RootTotal.
+
+root_desc_info(Deep) = RootDesc :-
+	deep_lookup_pd_desc(Deep, Deep ^ root, RootDesc).
+
+root_own_info(Deep) = RootOwn :-
+	deep_lookup_pd_own(Deep, Deep ^ root, RootOwn).
 
 %-----------------------------------------------------------------------------%
Index: deep_profiler/query.m
===================================================================
RCS file: query.m
diff -N query.m
--- /dev/null	Fri Dec  1 02:25:58 2000
+++ query.m	Mon Jul  2 23:26:54 2001
@@ -0,0 +1,1923 @@
+%-----------------------------------------------------------------------------%
+% Copyright (C) 2001 The University of Melbourne.
+% This file may only be copied under the terms of the GNU General
+% Public License - see the file COPYING in the Mercury distribution.
+%-----------------------------------------------------------------------------%
+%
+% Authors: conway, zs.
+%
+% This module contains the top level predicates for servicing individual
+% queries.
+
+:- module query.
+
+:- interface.
+
+:- import_module profile, interface.
+:- import_module io.
+
+:- pred exec(cmd::in, preferences::in, deep::in, string::out,
+	io__state::di, io__state::uo) is det.
+
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module measurements, top_procs, html_format, exclude.
+:- import_module std_util, bool, int, float, char, string.
+:- import_module array, list, assoc_list, set, map, require.
+
+%-----------------------------------------------------------------------------%
+
+exec(restart, _Pref, _Deep, _HTML, IO, IO) :-
+	% Our caller is supposed to filter out restart commands.
+	error("exec: found restart command").
+exec(quit, _Pref, Deep, HTML, IO, IO) :-
+	HTML = string__format(
+		"<H3>Shutting down deep profile server for %s.</H3>\n",
+		[s(Deep ^ data_file_name)]).
+exec(timeout(TimeOut), _Pref, _Deep, HTML, IO, IO) :-
+	HTML = string__format("<H3>Timeout set to %d minutes</H3>\n",
+		[i(TimeOut)]).
+exec(Cmd, Pref, Deep, HTML, IO, IO) :-
+	Cmd = menu,
+	HTML = generate_menu_page(Cmd, Pref, Deep).
+exec(Cmd, Pref, Deep, HTML, IO, IO) :-
+	Cmd = root(MaybePercent),
+	deep_lookup_clique_index(Deep, Deep ^ root, RootCliquePtr),
+	RootCliquePtr = clique_ptr(RootCliqueNum),
+	(
+		MaybePercent = yes(Percent),
+		HTML = chase_the_action(Cmd, RootCliqueNum,
+			Pref, Deep, Percent)
+	;
+		MaybePercent = no,
+		generate_clique_page(Cmd, RootCliqueNum, Pref, Deep, HTML,
+			100, _)
+	).
+exec(Cmd, Pref, Deep, HTML, IO, IO) :-
+	Cmd = clique(CliqueNum),
+	CliquePtr = clique_ptr(CliqueNum),
+	( valid_clique_ptr(Deep, CliquePtr) ->
+		generate_clique_page(Cmd, CliqueNum, Pref, Deep, HTML, 100, _)
+	;
+		HTML =
+			page_banner(Cmd, Pref) ++
+			"There is no clique with that number.\n" ++
+			page_footer(Cmd, Pref, Deep)
+	).
+exec(Cmd, Pref, Deep, HTML, IO, IO) :-
+	Cmd = proc(PSI),
+	PSPtr = proc_static_ptr(PSI),
+	( valid_proc_static_ptr(Deep, PSPtr) ->
+		HTML = generate_proc_page(Cmd, PSPtr, Pref, Deep)
+	;
+		HTML =
+			page_banner(Cmd, Pref) ++
+			"There is no procedure with that number.\n" ++
+			page_footer(Cmd, Pref, Deep)
+	).
+exec(Cmd, Pref, Deep, HTML, IO0, IO) :-
+	Cmd = proc_callers(PSI, CallerGroups, BunchNum),
+	PSPtr = proc_static_ptr(PSI),
+	( valid_proc_static_ptr(Deep, PSPtr) ->
+		generate_proc_callers_page(Cmd, PSPtr, CallerGroups, BunchNum,
+			Pref, Deep, HTML, IO0, IO)
+	;
+		HTML =
+			page_banner(Cmd, Pref) ++
+			"There is no procedure with that number.\n" ++
+			page_footer(Cmd, Pref, Deep),
+		IO = IO0
+	).
+exec(Cmd, Pref, Deep, HTML, IO, IO) :-
+	Cmd = modules,
+	HTML = generate_modules_page(Cmd, Pref, Deep).
+exec(Cmd, Pref, Deep, HTML, IO, IO) :-
+	Cmd = module(ModuleName),
+	( map__search(Deep ^ module_data, ModuleName, ModuleData) ->
+		HTML = generate_module_page(Cmd, ModuleName, ModuleData,
+			Pref, Deep)
+	;
+		HTML =
+			page_banner(Cmd, Pref) ++
+			"There is no procedure with that number.\n" ++
+			page_footer(Cmd, Pref, Deep)
+	).
+exec(Cmd, Pref, Deep, HTML, IO, IO) :-
+	Cmd = top_procs(Limit, CostKind, InclDesc, Scope),
+	HTML = generate_top_procs_page(Cmd, Limit, CostKind, InclDesc, Scope,
+		Pref, Deep).
+exec(proc_static(PSI), _Pref, Deep, HTML, IO, IO) :-
+	HTML = generate_proc_static_debug_page(PSI, Deep).
+exec(proc_dynamic(PDI), _Pref, Deep, HTML, IO, IO) :-
+	HTML = generate_proc_dynamic_debug_page(PDI, Deep).
+exec(call_site_static(CSSI), _Pref, Deep, HTML, IO, IO) :-
+	HTML = generate_call_site_static_debug_page(CSSI, Deep).
+exec(call_site_dynamic(CSDI), _Pref, Deep, HTML, IO, IO) :-
+	HTML = generate_call_site_dynamic_debug_page(CSDI, Deep).
+exec(raw_clique(CI), _Pref, Deep, HTML, IO, IO) :-
+	HTML = generate_clique_debug_page(CI, Deep).
+
+%-----------------------------------------------------------------------------%
+
+:- func generate_proc_static_debug_page(int, deep) = string.
+
+generate_proc_static_debug_page(PSI, Deep) = HTML :-
+	PSPtr = proc_static_ptr(PSI),
+	( valid_proc_static_ptr(Deep, PSPtr) ->
+		deep_lookup_proc_statics(Deep, PSPtr, PS),
+		Refined = PS ^ ps_refined_id,
+		Raw = PS ^ ps_raw_id,
+		FileName = PS ^ ps_file_name,
+		HTML =
+			"<HTML>\n" ++
+			Refined ++ " " ++ Raw ++ " " ++ FileName ++ " " ++
+			string__int_to_string(array__max(PS ^ ps_sites)) ++
+			"</HTML>\n"
+	;
+		HTML =
+			"<HTML>\n" ++
+			"Invalid proc_static_ptr" ++
+			"</HTML>\n"
+	).
+
+:- func generate_proc_dynamic_debug_page(int, deep) = string.
+
+generate_proc_dynamic_debug_page(PDI, Deep) = HTML :-
+	PDPtr = proc_dynamic_ptr(PDI),
+	( valid_proc_dynamic_ptr(Deep, PDPtr) ->
+		deep_lookup_proc_dynamics(Deep, PDPtr, PD),
+		PSPtr = PD ^ pd_proc_static,
+		PSPtr = proc_static_ptr(PSI),
+		HTML =
+			"<HTML>\n" ++
+			string__format("proc_static %d, ", [i(PSI)]) ++
+			array_slots_to_html(PD ^ pd_sites) ++
+			"</HTML>\n"
+	;
+		HTML =
+			"<HTML>\n" ++
+			"Invalid proc_dynamic_ptr" ++
+			"</HTML>\n"
+	).
+
+:- func generate_call_site_static_debug_page(int, deep) = string.
+
+generate_call_site_static_debug_page(CSSI, Deep) = HTML :-
+	CSSPtr = call_site_static_ptr(CSSI),
+	( valid_call_site_static_ptr(Deep, CSSPtr) ->
+		deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
+		ContainerPtr = CSS ^ css_container,
+		ContainerPtr = proc_static_ptr(Container),
+		HTML =
+			"<HTML>\n" ++
+			string__int_to_string(Container) ++ " " ++
+			string__int_to_string(CSS ^ css_slot_num) ++ " " ++
+			string__int_to_string(CSS ^ css_line_num) ++ " " ++
+			kind_and_callee_to_string(CSS ^ css_kind) ++ " " ++
+			CSS ^ css_goal_path ++
+			"</HTML>\n"
+	;
+		HTML =
+			"<HTML>\n" ++
+			"Invalid call_site_static_ptr" ++
+			"</HTML>\n"
+	).
+
+:- func generate_call_site_dynamic_debug_page(int, deep) = string.
+
+generate_call_site_dynamic_debug_page(CSDI, Deep) = HTML :-
+	CSDPtr = call_site_dynamic_ptr(CSDI),
+	( valid_call_site_dynamic_ptr(Deep, CSDPtr) ->
+		deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
+		CSD ^ csd_caller = proc_dynamic_ptr(CallerPDI),
+		CSD ^ csd_callee = proc_dynamic_ptr(CalleePDI),
+		HTML =
+			"<HTML>\n" ++
+			string__int_to_string(CallerPDI) ++ " -> " ++
+			string__int_to_string(CalleePDI) ++ ": " ++
+			own_to_string(CSD ^ csd_own_prof) ++
+			"</HTML>\n"
+	;
+		HTML =
+			"<HTML>\n" ++
+			"Invalid call_site_dynamic_ptr" ++
+			"</HTML>\n"
+	).
+
+:- func generate_clique_debug_page(int, deep) = string.
+
+generate_clique_debug_page(CI, Deep) = HTML :-
+	CliquePtr = clique_ptr(CI),
+	( valid_clique_ptr(Deep, CliquePtr) ->
+		deep_lookup_clique_parents(Deep, CliquePtr, Parent),
+		Parent = call_site_dynamic_ptr(ParentPDI),
+		ParentStr = string__format("%d ->", [i(ParentPDI)]),
+		deep_lookup_clique_members(Deep, CliquePtr, Members),
+		HTML =
+			"<HTML>\n" ++
+			ParentStr ++
+			list__foldl(append_pdi_to_string, Members, "") ++
+			"</HTML>\n"
+	;
+		HTML =
+			"<HTML>\n" ++
+			"Invalid call_site_dynamic_ptr" ++
+			"</HTML>\n"
+	).
+
+%-----------------------------------------------------------------------------%
+
+:- func array_slots_to_html(array(call_site_array_slot)) = string.
+
+array_slots_to_html(SlotArray) = HTML :-
+	array__to_list(SlotArray, SlotList),
+	list__foldl(append_slot_to_string, SlotList, "multi", HTML).
+
+:- pred append_slot_to_string(call_site_array_slot::in,
+	string::in, string::out) is det.
+
+append_slot_to_string(Slot, Str0, Str) :-
+	Str = Str0 ++ " " ++ array_slot_to_html(Slot).
+
+:- func array_slot_to_html(call_site_array_slot) = string.
+
+array_slot_to_html(normal(CSDPtr)) = HTML :-
+	CSDPtr = call_site_dynamic_ptr(CSDI),
+	HTML = "normal " ++ string__int_to_string(CSDI).
+array_slot_to_html(multi(_, CSDPtrArray)) = HTML :-
+	array__to_list(CSDPtrArray, CSDPtrs),
+	list__foldl(append_csdi_to_string, CSDPtrs, "", CSDI_HTML),
+	list__length(CSDPtrs, CSDPtrCount),
+	HTML = string__format("multi(%d): [", [i(CSDPtrCount)])
+		++ CSDI_HTML ++ "]".
+
+:- pred append_csdi_to_string(call_site_dynamic_ptr::in,
+	string::in, string::out) is det.
+
+append_csdi_to_string(call_site_dynamic_ptr(CSDI), Str0, Str) :-
+	Str = Str0 ++ " " ++ string__int_to_string(CSDI).
+
+:- func append_pdi_to_string(proc_dynamic_ptr, string) = string.
+
+append_pdi_to_string(proc_dynamic_ptr(PDI), Str0) =
+	Str0 ++ " " ++ string__int_to_string(PDI).
+
+:- func kind_and_callee_to_string(call_site_kind_and_callee) = string.
+
+kind_and_callee_to_string(normal_call(proc_static_ptr(PSI), TypeSpec)) =
+	"normal " ++ string__int_to_string(PSI) ++ " " ++ TypeSpec.
+kind_and_callee_to_string(special_call) = "special_call".
+kind_and_callee_to_string(higher_order_call) = "higher_order_call".
+kind_and_callee_to_string(method_call) = "method_call".
+kind_and_callee_to_string(callback) = "callback".
+
+%-----------------------------------------------------------------------------%
+
+:- func call_site_kind_and_callee_to_html(call_site_kind_and_callee) = string.
+
+call_site_kind_and_callee_to_html(normal_call(_, _)) = "normal_call".
+call_site_kind_and_callee_to_html(special_call) =      "special_call".
+call_site_kind_and_callee_to_html(higher_order_call) = "higher_order_call".
+call_site_kind_and_callee_to_html(method_call) =       "method_call".
+call_site_kind_and_callee_to_html(callback) =          "callback".
+
+%-----------------------------------------------------------------------------%
+
+:- func generate_menu_page(cmd, preferences, deep) = string.
+
+generate_menu_page(Cmd, Pref, Deep) = HTML :-
+	HTML =
+		page_banner(Cmd, Pref) ++
+		"<p>\n" ++
+		menu_text ++
+		"<ul>\n" ++
+		"<li>\n" ++
+		menu_item(Deep, Pref, root(no),
+			"Exploring the call graph, starting at the root.") ++
+		"<li>\n" ++
+		menu_item(Deep, Pref, root(yes(90)),
+			"Exploring the call graph, starting at the action.") ++
+		"<li>\n" ++
+		menu_item(Deep, Pref, modules,
+			"Exploring the program module by module.") ++
+		"<li>\n" ++
+		menu_item(Deep, Pref,
+			top_procs(rank_range(1, 100), time,
+				self, overall),
+			"Top 100 most expensive procedures: time, self.") ++
+		"<li>\n" ++
+		menu_item(Deep, Pref,
+			top_procs(rank_range(1, 100), time,
+				self_and_desc, overall),
+			"Top 100 most expensive procedures: time, self+desc.")
+			++
+		"<li>\n" ++
+		menu_item(Deep, Pref,
+			top_procs(rank_range(1, 100), words,
+				self, overall),
+			"Top 100 most expensive procedures: words, self.") ++
+		"<li>\n" ++
+		menu_item(Deep, Pref,
+			top_procs(rank_range(1, 100), words,
+				self_and_desc, overall),
+			"Top 100 most expensive procedures: words, self+desc.")
+			++
+		"<li>\n" ++
+		menu_item(Deep, Pref,
+			top_procs(threshold(0.1), time,
+				self, overall),
+			"Procedures above 0.1% threshold: time, self.") ++
+		"<li>\n" ++
+		menu_item(Deep, Pref,
+			top_procs(threshold(0.1), time,
+				self_and_desc, overall),
+			"Procedures above 1% threshold: time, self+desc.")
+			++
+		"<li>\n" ++
+		menu_item(Deep, Pref,
+			top_procs(threshold(0.1), words,
+				self, overall),
+			"Procedures above 0.1% threshold: words, self.") ++
+		"<li>\n" ++
+		menu_item(Deep, Pref,
+			top_procs(threshold(0.1), words,
+				self_and_desc, overall),
+			"Procedures above 1% threshold: words, self+desc.")
+			++
+		"</ul>\n" ++
+		"<p>\n" ++
+		present_stats(Deep) ++
+		page_footer(Cmd, Pref, Deep).
+
+:- func menu_text = string.
+
+menu_text =
+	"You can start exploring the deep profile at the following points.\n".
+
+:- func menu_item(deep, preferences, cmd, string) = string.
+
+menu_item(Deep, Pref, Cmd, Text) =
+	string__format("<A HREF=""%s"">%s</A>\n",
+		[s(deep_cmd_pref_to_url(Pref, Deep, Cmd)), s(Text)]).
+
+:- func present_stats(deep) = string.
+
+present_stats(Deep) = HTML :-
+	Stats = Deep ^ profile_stats,
+	lookup_ticks_per_sec(Stats, TicksPerSec, Assumed),
+	(
+		Assumed = yes,
+		AssumedStr = " (assumed)"
+	;
+		Assumed = no,
+		AssumedStr = ""
+	),
+	HTML =
+		"<TABLE>\n" ++
+		"<TR><TD ALIGN=left>Quanta per second:</TD>\n" ++
+		string__format("<TD ALIGN=right>%d%s</TD></TR>\n",
+			[i(TicksPerSec), s(AssumedStr)]) ++
+		"<TR><TD ALIGN=left>Quanta in user code:</TD>\n" ++
+		string__format("<TD ALIGN=right>%d</TD></TR>\n",
+			[i(Stats ^ user_quanta)]) ++
+		"<TR><TD ALIGN=left>Quanta in instrumentation:</TD>\n" ++
+		string__format("<TD ALIGN=right>%d</TD></TR>\n",
+			[i(Stats ^ instrument_quanta)]) ++
+		"<TR><TD ALIGN=left>CallSiteDynamic structures:</TD>\n" ++
+		string__format("<TD ALIGN=right>%d</TD></TR>\n",
+			[i(Stats ^ max_csd)]) ++
+		"<TR><TD ALIGN=left>ProcDynamic structures:</TD>\n" ++
+		string__format("<TD ALIGN=right>%d</TD></TR>\n",
+			[i(Stats ^ max_pd)]) ++
+		"<TR><TD ALIGN=left>CallSiteStatic structures:</TD>\n" ++
+		string__format("<TD ALIGN=right>%d</TD></TR>\n",
+			[i(Stats ^ max_css)]) ++
+		"<TR><TD ALIGN=left>ProcStatic structures:</TD>\n" ++
+		string__format("<TD ALIGN=right>%d</TD></TR>\n",
+			[i(Stats ^ max_ps)]) ++
+		"<TR><TD ALIGN=left>Cliques:</TD>\n" ++
+		string__format("<TD ALIGN=right>%d</TD></TR>\n",
+			[i(array__max(Deep ^ clique_members))]) ++
+		"</TABLE>\n".
+
+%-----------------------------------------------------------------------------%
+
+:- func chase_the_action(cmd, int, preferences, deep, int) = string.
+
+chase_the_action(Cmd, CliqueNum, Pref, Deep, Percent) = HTML :-
+	generate_clique_page(Cmd, CliqueNum, Pref, Deep, HTML0,
+		Percent, ActionPtrs),
+	( ActionPtrs = [clique_ptr(ActionCliqueNum)] ->
+		HTML = chase_the_action(Cmd, ActionCliqueNum,
+			Pref, Deep, Percent)
+	;
+		HTML = HTML0
+	).
+
+%-----------------------------------------------------------------------------%
+
+:- pred generate_clique_page(cmd::in, int::in, preferences::in, deep::in,
+	string::out, int::in, list(clique_ptr)::out) is det.
+
+generate_clique_page(Cmd, CliqueNum, Pref, Deep, HTML,
+		Percent, ActionPtrs) :-
+	clique_to_html(Pref, Deep, clique_ptr(CliqueNum),
+		CliqueHTML, Percent, ActionPtrs),
+	HTML =
+		page_banner(Cmd, Pref) ++
+		string__format("<H3>Clique %d:</H3>\n",
+			[i(CliqueNum)]) ++
+		table_start(Pref) ++
+		fields_header(Pref, source_proc, totals_meaningful,
+			wrap_clique_links(clique_ptr(CliqueNum),
+				Pref, Deep)) ++
+		CliqueHTML ++
+		table_end(Pref) ++
+		page_footer(Cmd, Pref, Deep).
+
+:- func generate_proc_page(cmd, proc_static_ptr, preferences, deep)
+	= string.
+
+generate_proc_page(Cmd, PSPtr, Pref, Deep) =
+	page_banner(Cmd, Pref) ++
+	string__format("<H3>Summary of procedure %s:</H3>\n",
+		[s(proc_static_name(Deep, PSPtr))]) ++
+	table_start(Pref) ++
+	fields_header(Pref, source_proc, totals_meaningful,
+		wrap_proc_links(PSPtr, Pref, Deep)) ++
+	proc_summary_to_html(Pref, Deep, PSPtr) ++
+	table_end(Pref) ++
+	"<p>\n" ++
+	proc_summary_toggles_to_html(Pref, Deep, PSPtr) ++
+	page_footer(Cmd, Pref, Deep).
+
+:- pred generate_proc_callers_page(cmd::in, proc_static_ptr::in,
+	caller_groups::in, int::in, preferences::in, deep::in, string::out,
+	io__state::di, io__state::uo) is det.
+
+generate_proc_callers_page(Cmd, PSPtr, CallerGroups, BunchNum, Pref, Deep,
+		HTML, IO0, IO) :-
+	proc_callers_to_html(Pref, Deep, PSPtr, CallerGroups, BunchNum,
+		MaybePage, IO0, IO),
+	(
+		MaybePage = ok({IdFields, Heading, CallersHTML, Toggles}),
+		HTML =
+			page_banner(Cmd, Pref) ++
+			Heading ++
+			( CallersHTML = "" ->
+				""
+			;
+				table_start(Pref) ++
+				fields_header(Pref, IdFields,
+					totals_meaningful,
+					wrap_proc_callers_links(PSPtr,
+						CallerGroups, 1,
+						Pref, Deep)) ++
+				CallersHTML ++
+				table_end(Pref) ++
+				"<p>\n"
+			) ++
+			Toggles ++
+			page_footer(Cmd, Pref, Deep)
+	;
+		MaybePage = error(Msg),
+		HTML =
+			string__format("<H3>%s</H3>\n", [s(Msg)])
+	).
+
+:- func generate_modules_page(cmd, preferences, deep) = string.
+
+generate_modules_page(Cmd, Pref, Deep) =
+	page_banner(Cmd, Pref) ++
+	"<H3>The modules of the program:</H3>\n" ++
+	table_start(Pref) ++
+	fields_header(Pref, rank_module, totals_not_meaningful,
+		wrap_modules_links(Pref, Deep)) ++
+	modules_to_html(Pref, Deep) ++
+	table_end(Pref) ++
+	page_footer(Cmd, Pref, Deep).
+
+:- func generate_module_page(cmd, string, module_data, preferences, deep)
+	= string.
+
+generate_module_page(Cmd, ModuleName, ModuleData, Pref, Deep) = HTML :-
+	module_to_html(Pref, Deep, ModuleName, ModuleData,
+		IdFields, ModulesHTML),
+	HTML =
+		page_banner(Cmd, Pref) ++
+		string__format("<H3>The procedures of module %s:</H3>\n",
+			[s(ModuleName)]) ++
+		table_start(Pref) ++
+		fields_header(Pref, IdFields, totals_meaningful,
+			wrap_module_links(ModuleName, Pref, Deep)) ++
+		ModulesHTML ++
+		table_end(Pref) ++
+		page_footer(Cmd, Pref, Deep).
+
+:- func generate_top_procs_page(cmd, display_limit,
+	cost_kind, include_descendants, measurement_scope,
+	preferences, deep) = string.
+
+generate_top_procs_page(Cmd, Limit, CostKind, InclDesc0, Scope0, Pref, Deep)
+		= HTML :-
+	( CostKind = calls ->
+		% counting calls is incompatible both with self_and_desc
+		% and per_call.
+		InclDesc = self,
+		Scope = overall
+	;
+		InclDesc = InclDesc0,
+		Scope = Scope0
+	),
+	MaybeTopPSIs = find_top_procs(CostKind, InclDesc, Scope, Limit, Deep),
+	(
+		MaybeTopPSIs = error(ErrorMessage),
+		HTML =
+			page_banner(Cmd, Pref) ++
+			ErrorMessage ++ "\n" ++
+			page_footer(Cmd, Pref, Deep)
+	;
+		MaybeTopPSIs = ok(TopPSIs),
+		ToggleLimitHTML = "",
+		ToggleCostHTML = toggle_cost_criteria_in_top_procs_cmd(
+			Pref, Deep, Limit, CostKind, InclDesc, Scope),
+		Desc = cost_criteria_to_description(CostKind, InclDesc, Scope),
+		Heading = string__format("<H3>Top procedures %s</H3>\n",
+			[s(Desc)]),
+		( TopPSIs = [] ->
+			HTML =
+				page_banner(Cmd, Pref) ++
+				Heading ++ "<p>\n" ++
+				"No procedures match the specification.\n" ++
+				"<p>\n" ++
+				ToggleLimitHTML ++
+				ToggleCostHTML ++
+				page_footer(Cmd, Pref, Deep)
+		;
+			TopProcs = list__map(
+				lookup_proc_total_to_html(Pref, Deep, no, ""),
+				list__map(wrap_proc_static_ptr, TopPSIs)),
+			RankedTopProcs = add_ranks(TopProcs),
+			SummaryHTMLs = list__map(
+				two_id_line_to_html(Pref, Deep,
+					totals_meaningful),
+				RankedTopProcs),
+			HTML =
+				page_banner(Cmd, Pref) ++
+				Heading ++ "<p>\n" ++
+				table_start(Pref) ++
+				fields_header(Pref, rank_proc,
+					totals_meaningful,
+					wrap_top_procs_links(Limit, Pref,
+						Deep)) ++
+				string__append_list(SummaryHTMLs) ++
+				table_end(Pref) ++
+				"<p>\n" ++
+				ToggleLimitHTML ++
+				ToggleCostHTML ++
+				page_footer(Cmd, Pref, Deep)
+		)
+	).
+
+%-----------------------------------------------------------------------------%
+
+:- func modules_to_html(preferences, deep) = string.
+
+modules_to_html(Pref, Deep) = HTML :-
+	map__to_assoc_list(Deep ^ module_data, ModulePairs0),
+	list__filter(not_mercury_runtime, ModulePairs0, ModulePairs),
+	ModuleLines = list__map(module_summary_to_html(Pref, Deep),
+		ModulePairs),
+	SortedModuleLines = sort_line_groups(Pref ^ pref_criteria,
+		ModuleLines),
+	RankedModuleLines = add_ranks(SortedModuleLines),
+	ModuleHTMLs = list__map(
+		two_id_line_to_html(Pref, Deep, totals_not_meaningful),
+		RankedModuleLines),
+	HTML =
+		separator_row(Pref, rank_module, totals_not_meaningful) ++
+		string__append_list(ModuleHTMLs).
+
+:- pred not_mercury_runtime(pair(string, module_data)::in) is semidet.
+
+not_mercury_runtime(ModuleName - _) :-
+	ModuleName \= "Mercury runtime".
+
+:- func module_summary_to_html(preferences, deep, pair(string, module_data))
+	= one_id_line.
+
+module_summary_to_html(Pref, Deep, ModuleName - ModuleData) = LineGroup :-
+	Own = ModuleData ^ module_own,
+	Desc = ModuleData ^ module_desc,
+	HTML =
+		string__format("<TD><A HREF=""%s"">%s</A></TD>\n",
+			[s(deep_cmd_pref_to_url(Pref, Deep,
+				module(ModuleName))),
+			s(ModuleName)]),
+	LineGroup = line_group(ModuleName, 0, ModuleName, Own, Desc, HTML,
+		unit).
+
+%-----------------------------------------------------------------------------%
+
+:- pred module_to_html(preferences::in, deep::in, string::in, module_data::in,
+	id_fields::out, string::out) is det.
+
+module_to_html(Pref, Deep, _ModuleName, ModuleData, IdHeaders, HTML) :-
+	ModuleData = module_data(_Own, _Desc, PSPtrs),
+	ProcLines = list__map(lookup_proc_total_to_html(Pref, Deep, yes, ""),
+		PSPtrs),
+	Criteria = Pref ^ pref_criteria,
+	SortedProcLines = sort_line_groups(Criteria, ProcLines),
+	( Criteria = by_cost(_, _, _) ->
+		IdHeaders = rank_proc,
+		RankedProcLines = add_ranks(SortedProcLines)
+	;
+		IdHeaders = source_proc,
+		RankedProcLines = list__map(add_self_context, SortedProcLines)
+	),
+	ProcHTMLs = list__map(
+		two_id_line_to_html(Pref, Deep, totals_meaningful),
+		RankedProcLines),
+	HTML =
+		separator_row(Pref, IdHeaders, totals_meaningful) ++
+		string__append_list(ProcHTMLs).
+
+%-----------------------------------------------------------------------------%
+
+:- pred clique_to_html(preferences::in, deep::in, clique_ptr::in,
+	string::out, int::in, list(clique_ptr)::out) is det.
+
+clique_to_html(Pref, Deep, CliquePtr, HTML, PerCent, ActionPtrs) :-
+	(
+		Pref ^ pref_anc = yes(AncestorLimit),
+		RespectLimit = yes
+	;
+		Pref ^ pref_anc = no,
+		AncestorLimit = 0, % the value doesn't matter
+		RespectLimit = no
+	),
+	clique_ancestors_to_html(Pref, Deep,
+		AncestorLimit, RespectLimit, CliquePtr, Ancestors, Cutoff),
+	deep_lookup_clique_members(Deep, CliquePtr, PDPtrs),
+	list__foldl(group_proc_dynamics_by_proc_static(Deep), PDPtrs,
+		map__init, PStoPDsMap),
+	map__to_assoc_list(PStoPDsMap, PStoPDsList0),
+
+	deep_lookup_clique_parents(Deep, CliquePtr, EntryCSDPtr),
+	( valid_call_site_dynamic_ptr(Deep, EntryCSDPtr) ->
+		deep_lookup_call_site_dynamics(Deep, EntryCSDPtr, EntryCSD),
+		EntryPDPtr = EntryCSD ^ csd_callee,
+		list__filter(proc_group_contains(EntryPDPtr), PStoPDsList0,
+			EntryGroup, RestGroup),
+		list__append(EntryGroup, RestGroup, PStoPDsList)
+	;
+		PStoPDsList = PStoPDsList0
+	),
+
+	list__map2(procs_in_clique_to_html(Pref, Deep, CliquePtr, PerCent),
+		PStoPDsList, PDsStrs, ActionPtrLists),
+	list__condense(ActionPtrLists, ActionPtrs),
+	string__append_list(PDsStrs, ProcGroups),
+	(
+		Cutoff = yes,
+		Heading = string__format("The %d closest ancestors:",
+			[i(AncestorLimit)])
+	;
+		Cutoff = no,
+		Heading = "Ancestors:"
+	),
+	HTML =
+		header_row(Heading, Pref, source_proc, totals_meaningful) ++
+		separator_row(Pref, source_proc, totals_meaningful) ++
+		Ancestors ++
+		separator_row(Pref, source_proc, totals_meaningful) ++
+		header_row("Procedures of the clique:",
+			Pref, source_proc, totals_meaningful) ++
+		separator_row(Pref, source_proc, totals_meaningful) ++
+		ProcGroups.
+
+:- pred proc_group_contains(proc_dynamic_ptr::in,
+	pair(proc_static_ptr, list(proc_dynamic_ptr))::in) is semidet.
+
+proc_group_contains(EntryPDPtr, _ - PDPtrs) :-
+	list__member(EntryPDPtr, PDPtrs).
+
+:- pred clique_ancestors_to_html(preferences::in, deep::in, int::in, bool::in,
+	clique_ptr::in, string::out, bool::out) is det.
+
+clique_ancestors_to_html(Pref, Deep, AncestorLimit, RespectLimit, CliquePtr,
+		HTML, Cutoff) :-
+	deep_lookup_clique_parents(Deep, CliquePtr, EntryCSDPtr),
+	( valid_call_site_dynamic_ptr(Deep, EntryCSDPtr) ->
+		deep_lookup_call_site_dynamics(Deep, EntryCSDPtr, EntryCSD),
+		EntryPDPtr = EntryCSD ^ csd_caller,
+		( EntryPDPtr = Deep ^ root ->
+			% we have reached the root
+			HTML = "",
+			Cutoff = no
+		; RespectLimit = yes, AncestorLimit =< 0 ->
+			HTML = "",
+			Cutoff = yes
+		;
+			deep_lookup_clique_index(Deep, EntryPDPtr,
+				EntryCliquePtr),
+			ThisLine = call_site_dynamic_to_html(Pref, Deep,
+				ancestor_display, yes(EntryCliquePtr),
+				EntryCSDPtr),
+			ThisHTML = two_id_line_to_html(Pref, Deep,
+				totals_meaningful, ThisLine),
+			clique_ancestors_to_html(Pref, Deep, AncestorLimit - 1,
+				RespectLimit, EntryCliquePtr,
+				AncestorHTML, Cutoff),
+			HTML =
+				AncestorHTML ++
+				ThisHTML
+		)
+	;
+		% we have reached the parent of root
+		HTML = "",
+		Cutoff = no
+	).
+
+:- pred group_proc_dynamics_by_proc_static(deep::in, proc_dynamic_ptr::in,
+	map(proc_static_ptr, list(proc_dynamic_ptr))::in,
+	map(proc_static_ptr, list(proc_dynamic_ptr))::out) is det.
+
+group_proc_dynamics_by_proc_static(Deep, PDPtr, PStoPDsMap0, PStoPDsMap) :-
+	require(valid_proc_dynamic_ptr(Deep, PDPtr),
+		"group_proc_dynamics_by_proc_static: invalid PDPtr"),
+	deep_lookup_proc_dynamics(Deep, PDPtr, PD),
+	PSPtr = PD ^ pd_proc_static,
+	( map__search(PStoPDsMap0, PSPtr, PSPDs0) ->
+		PSPDs = [PDPtr | PSPDs0],
+		map__det_update(PStoPDsMap0, PSPtr, PSPDs, PStoPDsMap)
+	;
+		map__det_insert(PStoPDsMap0, PSPtr, [PDPtr], PStoPDsMap)
+	).
+
+:- pred procs_in_clique_to_html(preferences::in, deep::in, clique_ptr::in,
+	int::in, pair(proc_static_ptr, list(proc_dynamic_ptr))::in,
+	string::out, list(clique_ptr)::out) is det.
+
+procs_in_clique_to_html(Pref, Deep, CliquePtr, Percent, PSPtr - PDPtrs,
+		HTML, ActionPtrs) :-
+	( PDPtrs = [] ->
+		HTML = "",
+		ActionPtrs = []
+	; PDPtrs = [PDPtr] ->
+		proc_in_clique_to_html(Pref, Deep, CliquePtr, Percent, PDPtr,
+			HTML, ActionPtrs)
+	;
+		list__map(deep_lookup_pd_own(Deep), PDPtrs, ProcOwns),
+		list__map(deep_lookup_pd_desc(Deep), PDPtrs, ProcDescs),
+		ProcOwn = sum_own_infos(ProcOwns),
+		ProcDesc = sum_inherit_infos(ProcDescs),
+		ProcTotal = proc_total_to_two_id_line(Pref, Deep,
+			yes, "summary ", PSPtr, ProcOwn, ProcDesc),
+		list__map2(
+			proc_in_clique_to_html(Pref, Deep, CliquePtr, Percent),
+			PDPtrs, ComponentHTMLs, ActionPtrLists),
+		list__condense(ActionPtrLists, ActionPtrs),
+		string__append_list(ComponentHTMLs, ComponentHTML),
+		HTML =
+			separator_row(Pref, source_proc, totals_meaningful) ++
+			two_id_line_to_html(Pref, Deep, totals_meaningful,
+				ProcTotal) ++
+			separator_row(Pref, source_proc, totals_meaningful) ++
+			ComponentHTML
+	).
+
+:- pred proc_in_clique_to_html(preferences::in, deep::in, clique_ptr::in,
+	int::in, proc_dynamic_ptr::in, string::out, list(clique_ptr)::out)
+	is det.
+
+proc_in_clique_to_html(Pref, Deep, CliquePtr, Percent, PDPtr,
+		HTML, ActionPtrs) :-
+	( valid_proc_dynamic_ptr(Deep, PDPtr) ->
+		deep_lookup_pd_own(Deep, PDPtr, ProcOwn),
+		deep_lookup_pd_desc(Deep, PDPtr, ProcDesc),
+		deep_lookup_proc_dynamics(Deep, PDPtr, PD),
+		PSPtr = PD ^ pd_proc_static,
+		ProcTotal = proc_total_to_two_id_line(Pref, Deep,
+			yes, "", PSPtr, ProcOwn, ProcDesc),
+		child_call_sites(Deep ^ proc_dynamics, Deep ^ proc_statics,
+			PDPtr, GroupPairs),
+		( GroupPairs = [] ->
+			HTML =
+				separator_row(Pref, source_proc,
+					totals_meaningful) ++
+				two_id_line_to_html(Pref, Deep,
+					totals_meaningful,ProcTotal),
+			ActionPtrs = []
+		;
+			list__map2(call_site_clique_to_html(Pref, Deep,
+				CliquePtr, Percent),
+				GroupPairs, CallSiteLists, ActionPtrLists),
+			list__condense(CallSiteLists, CallSites),
+			list__condense(ActionPtrLists, ActionPtrs),
+			SortedCallSites = sort_line_groups(
+				Pref ^ pref_criteria, CallSites),
+			BodyHTMLs = list__map(
+				two_id_line_group_to_html(Pref, Deep,
+					totals_meaningful),
+				SortedCallSites),
+			HTML =
+				separator_row(Pref, source_proc,
+					totals_meaningful) ++
+				two_id_line_to_html(Pref, Deep,
+					totals_meaningful,ProcTotal) ++
+				separator_row(Pref, source_proc,
+					totals_meaningful) ++
+				string__append_list(BodyHTMLs)
+		)
+	;
+		HTML = "",
+		ActionPtrs = []
+	).
+
+:- pred child_call_sites(proc_dynamics::in, proc_statics::in,
+	proc_dynamic_ptr::in,
+	assoc_list(call_site_static_ptr, call_site_array_slot)::out) is det.
+
+child_call_sites(ProcDynamics, ProcStatics, PDPtr, PairedSlots) :-
+	lookup_proc_dynamics(ProcDynamics, PDPtr, PD),
+	PSPtr = PD ^ pd_proc_static,
+	CSDArray = PD ^ pd_sites,
+	lookup_proc_statics(ProcStatics, PSPtr, PS),
+	CSSArray = PS ^ ps_sites,
+	array__to_list(CSDArray, CSDSlots),
+	array__to_list(CSSArray, CSSSlots),
+	assoc_list__from_corresponding_lists(CSSSlots, CSDSlots, PairedSlots).
+
+%-----------------------------------------------------------------------------%
+
+:- func lookup_proc_total_to_html(preferences, deep, bool, string,
+	proc_static_ptr) = one_id_line.
+
+lookup_proc_total_to_html(Pref, Deep, Bold, Prefix, PSPtr) = LineGroup :-
+	deep_lookup_ps_own(Deep, PSPtr, Own),
+	deep_lookup_ps_desc(Deep, PSPtr, Desc),
+	LineGroup = proc_total_to_html(Pref, Deep, Bold, Prefix,
+		PSPtr, Own, Desc).
+
+:- func lookup_proc_total_to_two_id_line(preferences, deep, bool, string,
+	proc_static_ptr) = two_id_line.
+
+lookup_proc_total_to_two_id_line(Pref, Deep, Bold, Prefix, PSPtr) = LineGroup :-
+	deep_lookup_ps_own(Deep, PSPtr, Own),
+	deep_lookup_ps_desc(Deep, PSPtr, Desc),
+	LineGroup = proc_total_to_two_id_line(Pref, Deep, Bold, Prefix,
+		PSPtr, Own, Desc).
+
+:- func proc_total_to_html(preferences, deep, bool, string,
+	proc_static_ptr, own_prof_info, inherit_prof_info) = one_id_line.
+
+proc_total_to_html(Pref, Deep, Bold, Prefix, PSPtr, Own, Desc)
+		= LineGroup :-
+	proc_total_to_html_base(Pref, Deep, 1, Bold, Prefix, PSPtr,
+		FileName, LineNumber, ProcName, HTML),
+	LineGroup = line_group(FileName, LineNumber, ProcName, Own, Desc, HTML,
+		unit).
+
+:- func proc_total_to_two_id_line(preferences, deep, bool, string,
+	proc_static_ptr, own_prof_info, inherit_prof_info) = two_id_line.
+
+proc_total_to_two_id_line(Pref, Deep, Bold, Prefix, PSPtr, Own, Desc)
+		= LineGroup :-
+	proc_total_to_html_base(Pref, Deep, 2, Bold, Prefix, PSPtr,
+		FileName, LineNumber, ProcName, HTML),
+	LineGroup = line_group(FileName, LineNumber, ProcName, Own, Desc, HTML,
+		unit).
+
+:- pred proc_total_to_html_base(preferences::in, deep::in,
+	int::in, bool::in, string::in, proc_static_ptr::in,
+	string::out, int::out, string::out, string::out) is det.
+
+proc_total_to_html_base(Pref, Deep, Span, Bold, Prefix, PSPtr,
+		FileName, LineNumber, ProcName, HTML) :-
+	proc_static_to_line_group_info(Pref, Deep, PSPtr, FileName, LineNumber,
+		ProcName, WrappedProcName),
+	(
+		Bold = no,
+		BoldStart = "",
+		BoldEnd = ""
+	;
+		Bold = yes,
+		BoldStart = "<B>",
+		BoldEnd = "</B>"
+	),
+	HTML = string__format("<TD CLASS=id COLSPAN=%d>%s%s%s%s</TD>\n",
+		[i(Span), s(BoldStart), s(Prefix),
+		s(WrappedProcName), s(BoldEnd)]).
+
+%-----------------------------------------------------------------------------%
+
+:- pred call_site_clique_to_html(preferences::in, deep::in,
+	clique_ptr::in, int::in,
+	pair(call_site_static_ptr, call_site_array_slot)::in,
+	list(two_id_line_group)::out, list(clique_ptr)::out) is det.
+
+call_site_clique_to_html(Pref, Deep, CallerCliquePtr, Percent, Pair,
+		LineGroups, ActionPtrs) :-
+	Pair = CSSPtr - CallSiteArraySlot,
+	deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
+	Kind = CSS ^ css_kind,
+	( Kind = normal_call(_CalleePSPtr, _) ->
+		( CallSiteArraySlot = normal(CSDPtr0) ->
+			CSDPtr = CSDPtr0
+		;
+			error("call_site_clique_to_html: normal_call error")
+		),
+		normal_call_site_clique_to_html(Pref, Deep, CallerCliquePtr,
+			CSDPtr, LineGroups, Percent, ActionPtrs)
+	;
+		( CallSiteArraySlot = multi(_, CSDPtrs0) ->
+			array__to_list(CSDPtrs0, CSDPtrs)
+		;
+			error("call_site_clique_to_html: non-normal_call error")
+		),
+		call_site_context(Deep, CSSPtr, FileName, LineNumber),
+		multi_call_site_clique_to_html(Pref, Deep,
+			FileName, LineNumber, Kind, CallerCliquePtr, CSDPtrs,
+			LineGroups, Percent, ActionPtrs)
+	).
+
+:- func maybe_extract_action_clique(deep, clique_ptr, int,
+	call_site_dynamic_ptr) = list(clique_ptr).
+
+maybe_extract_action_clique(Deep, CallerCliquePtr, Percent, CSDPtr)
+		= ActionPtrs :-
+	( Percent > 100 ->
+		ActionPtrs = []
+	;
+		deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
+		deep_lookup_clique_index(Deep, CSD ^ csd_callee,
+			CalleeCliquePtr),
+		( CalleeCliquePtr = CallerCliquePtr ->
+			ActionPtrs = []
+		;
+			deep_lookup_csd_desc(Deep, CSDPtr, CSDDesc),
+			CSDOwn = CSD ^ csd_own_prof,
+			CSDTotal = add_own_to_inherit(CSDOwn, CSDDesc),
+			RootTotal = root_total_info(Deep),
+			CSDQuanta = inherit_quanta(CSDTotal),
+			RootQuanta = inherit_quanta(RootTotal),
+			( CSDQuanta * 100 > RootQuanta * Percent ->
+				ActionPtrs = [CalleeCliquePtr]
+			;
+				ActionPtrs = []
+			)
+		)
+	).
+
+:- pred normal_call_site_clique_to_html(preferences::in, deep::in,
+	clique_ptr::in, call_site_dynamic_ptr::in,
+	list(two_id_line_group)::out, int::in, list(clique_ptr)::out) is det.
+
+normal_call_site_clique_to_html(Pref, Deep, CallerCliquePtr, CSDPtr,
+		LineGroups, Percent, ActionPtrs) :-
+	( valid_call_site_dynamic_ptr(Deep, CSDPtr) ->
+		LineGroup = call_site_dynamic_to_html(Pref, Deep,
+			downward_display, yes(CallerCliquePtr), CSDPtr),
+		LineGroups = [line_to_two_id_subline_group(LineGroup)],
+		ActionPtrs = maybe_extract_action_clique(Deep, CallerCliquePtr,
+			Percent, CSDPtr)
+	;
+		LineGroups = [],
+		ActionPtrs = []
+	).
+
+:- pred multi_call_site_clique_to_html(preferences::in, deep::in,
+	string::in, int::in, call_site_kind_and_callee::in, clique_ptr::in,
+	list(call_site_dynamic_ptr)::in, list(two_id_line_group)::out,
+	int::in, list(clique_ptr)::out) is det.
+
+multi_call_site_clique_to_html(Pref, Deep, FileName, LineNumber, Kind,
+		CallerCliquePtr, CSDPtrs, LineGroups, Percent, ActionPtrs) :-
+	ValidCSDPtrs = CSDPtrs,
+	RawCallSiteName = call_site_kind_and_callee_to_html(Kind),
+	CallSiteName = multi_call_site_add_suffix(Pref, RawCallSiteName,
+		ValidCSDPtrs),
+	SubLines = list__map(call_site_dynamic_to_html(Pref, Deep,
+		downward_summary_display, yes(CallerCliquePtr)),
+		ValidCSDPtrs),
+	ActionPtrLists = list__map(
+		maybe_extract_action_clique(Deep, CallerCliquePtr, Percent),
+		ValidCSDPtrs),
+	list__condense(ActionPtrLists, ActionPtrs),
+	sum_line_group_measurements(SubLines, Own, Desc),
+	SummaryHTML =
+		string__format("<TD CLASS=id>%s:%d</TD>\n",
+			[s(FileName), i(LineNumber)]) ++
+		string__format("<TD CLASS=id>%s</TD>\n",
+			[s(CallSiteName)]),
+	(
+		Pref ^ pref_summarize = summarize,
+		LineGroup = line_group(FileName, LineNumber,
+			RawCallSiteName, Own, Desc, SummaryHTML,
+				sub_lines(two_id, []))
+	;
+		Pref ^ pref_summarize = dont_summarize,
+		LineGroup = line_group(FileName, LineNumber,
+			RawCallSiteName, Own, Desc, SummaryHTML,
+				sub_lines(two_id, SubLines))
+	),
+	LineGroups = [LineGroup].
+
+%-----------------------------------------------------------------------------%
+
+:- func call_site_summary_to_html(preferences, deep,
+	call_site_static_ptr) = two_id_line_group.
+
+call_site_summary_to_html(Pref, Deep, CSSPtr) = LineGroup :-
+	deep_lookup_call_site_calls(Deep, CSSPtr, CallSiteCallMap),
+	map__to_assoc_list(CallSiteCallMap, CallSiteCallList),
+	deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
+	Kind = CSS ^ css_kind,
+	CallerPSPtr = CSS ^ css_container,
+	call_site_context(Deep, CSSPtr, FileName, LineNumber),
+	( Kind = normal_call(CalleePSPtr, _) ->
+		LineGroup0 = normal_call_site_summary_to_html(Pref, Deep,
+			FileName, LineNumber, CallerPSPtr, CalleePSPtr,
+			CallSiteCallList)
+	;
+		LineGroup0 = multi_call_site_summary_to_html(Pref, Deep,
+			FileName, LineNumber, Kind,
+			CallerPSPtr, CallSiteCallList)
+	),
+	CSSContext = string__format("%s:%d", [s(FileName), i(LineNumber)]),
+	LineGroup = add_context(CSSContext, LineGroup0).
+
+:- func normal_call_site_summary_to_html(preferences, deep, string, int,
+	proc_static_ptr, proc_static_ptr,
+	assoc_list(proc_static_ptr, list(call_site_dynamic_ptr))) =
+	one_two_id_line_group.
+
+normal_call_site_summary_to_html(Pref, Deep, FileName, LineNumber,
+		CallerPSPtr, CalleePSPtr, CallSiteCallList) = LineGroup :-
+	deep_lookup_proc_statics(Deep, CalleePSPtr, CalleePS),
+	ProcName = CalleePS ^ ps_refined_id,
+	( CallSiteCallList = [] ->
+		Own = zero_own_prof_info,
+		Desc = zero_inherit_prof_info,
+		SummaryHTML =
+			string__format("<TD CLASS=id>%s</TD>\n",
+				[s(proc_static_to_html_ref(Pref,
+					Deep, CalleePSPtr))]),
+		LineGroup = line_group(FileName, LineNumber,
+			ProcName, Own, Desc, SummaryHTML,
+			sub_lines(two_id, []))
+	; CallSiteCallList = [CallSiteCall] ->
+		CallSiteCall = CalleePSPtrFromCall - _,
+		require(unify(CalleePSPtr, CalleePSPtrFromCall),
+			"call_site_summary_to_html: callee mismatch"),
+		LineGroup0 = call_site_summary_group_to_html(Pref, Deep,
+			FileName, LineNumber, ProcName,
+			CallerPSPtr, CallSiteCall),
+		LineGroup = line_to_two_id_subline_group(LineGroup0)
+	;
+		error("normal_call_site_summary_to_html: too many procedures")
+	).
+
+:- func multi_call_site_summary_to_html(preferences, deep, string, int,
+	call_site_kind_and_callee, proc_static_ptr,
+	assoc_list(proc_static_ptr, list(call_site_dynamic_ptr))) =
+	one_two_id_line_group.
+
+multi_call_site_summary_to_html(Pref, Deep, FileName, LineNumber, Kind,
+		CallerPSPtr, CallSiteCallList) = LineGroup :-
+	RawCallSiteName = call_site_kind_and_callee_to_html(Kind),
+	CallSiteName = multi_call_site_add_suffix(Pref, RawCallSiteName,
+		CallSiteCallList),
+	SubLines = list__map(call_site_summary_group_to_html(Pref, Deep,
+		FileName, LineNumber, RawCallSiteName, CallerPSPtr),
+		CallSiteCallList),
+	sum_line_group_measurements(SubLines, Own, Desc),
+	SummaryHTML =
+		string__format("<TD CLASS=id>%s</TD>\n",
+			[s(CallSiteName)]),
+	(
+		Pref ^ pref_summarize = summarize,
+		LineGroup = line_group(FileName, LineNumber,
+			RawCallSiteName, Own, Desc, SummaryHTML,
+			sub_lines(two_id, []))
+	;
+		Pref ^ pref_summarize = dont_summarize,
+		ContextSubLines = list__map(add_context(""), SubLines),
+		LineGroup = line_group(FileName, LineNumber,
+			RawCallSiteName, Own, Desc, SummaryHTML,
+			sub_lines(two_id, ContextSubLines))
+	).
+
+:- func call_site_summary_group_to_html(preferences, deep,
+	string, int, string, proc_static_ptr,
+	pair(proc_static_ptr, list(call_site_dynamic_ptr))) = one_id_line.
+
+call_site_summary_group_to_html(Pref, Deep, FileName, LineNumber, ProcName,
+		CallerPSPtr, PSPtr - CSDPtrs) = LineGroup :-
+	list__foldl2(accumulate_csd_prof_info(Deep, CallerPSPtr), CSDPtrs,
+		zero_own_prof_info, Own, zero_inherit_prof_info, Desc),
+	HTML =
+		string__format("<TD CLASS=id>%s</TD>\n",
+			[s(proc_static_to_html_ref(Pref, Deep, PSPtr))]),
+	LineGroup = line_group(FileName, LineNumber, ProcName,
+		Own, Desc, HTML, unit).
+
+%-----------------------------------------------------------------------------%
+
+:- func multi_call_site_add_suffix(preferences, string, list(T)) = string.
+
+multi_call_site_add_suffix(Pref, RawCallSiteName, CallList) = CallSiteName :-
+	( CallList = [] ->
+		CallSiteName = RawCallSiteName ++ " (no&nbps;calls&nbps;made)"
+	; Pref ^ pref_summarize = summarize ->
+		CallSiteName = RawCallSiteName ++ " (summary)"
+	;
+		CallSiteName = RawCallSiteName
+	).
+
+%-----------------------------------------------------------------------------%
+
+:- pred process_call_site_dynamics_group(list(call_site_dynamic_ptr)::in,
+	deep::in, proc_static_ptr::in,
+	maybe(clique_ptr)::in, maybe(clique_ptr)::out,
+	own_prof_info::in, own_prof_info::out,
+	inherit_prof_info::in, inherit_prof_info::out) is det.
+
+process_call_site_dynamics_group([], _, _,
+		MaybeCalleeCliquePtr, MaybeCalleeCliquePtr,
+		Own, Own, Desc, Desc).
+process_call_site_dynamics_group([CSDPtr | CSDPtrs], Deep, CalleePSPtr,
+		MaybeCalleeCliquePtr0, MaybeCalleeCliquePtr,
+		Own0, Own, Desc0, Desc) :-
+	deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
+	PDPtr = CSD ^ csd_callee,
+	deep_lookup_proc_dynamics(Deep, PDPtr, PD),
+	PSPtr = PD ^ pd_proc_static,
+	require(unify(CalleePSPtr, PSPtr),
+		"process_call_site_dynamics_group: callee mismatch"),
+	deep_lookup_clique_index(Deep, PDPtr, CalleeCliquePtr),
+	(
+		MaybeCalleeCliquePtr0 = no,
+		MaybeCalleeCliquePtr1 = yes(CalleeCliquePtr)
+	;
+		MaybeCalleeCliquePtr0 = yes(PrevCalleeCliquePtr),
+		MaybeCalleeCliquePtr1 = MaybeCalleeCliquePtr0,
+		require(unify(PrevCalleeCliquePtr, CalleeCliquePtr),
+			"process_call_site_dynamics_group: clique mismatch")
+	),
+	deep_lookup_csd_own(Deep, CSDPtr, CSDOwn),
+	deep_lookup_csd_desc(Deep, CSDPtr, CSDDesc),
+	Own1 = add_own_to_own(Own0, CSDOwn),
+	Desc1 = add_inherit_to_inherit(Desc0, CSDDesc),
+	process_call_site_dynamics_group(CSDPtrs, Deep, CalleePSPtr,
+		MaybeCalleeCliquePtr1, MaybeCalleeCliquePtr,
+		Own1, Own, Desc1, Desc).
+
+:- pred accumulate_csd_prof_info(deep::in, proc_static_ptr::in,
+	call_site_dynamic_ptr::in,
+	own_prof_info::in, own_prof_info::out,
+	inherit_prof_info::in, inherit_prof_info::out) is det.
+
+accumulate_csd_prof_info(Deep, CallerPSPtr, CSDPtr, Own0, Own, Desc0, Desc) :-
+	deep_lookup_csd_own(Deep, CSDPtr, CSDOwn),
+	deep_lookup_csd_desc(Deep, CSDPtr, CSDDesc),
+	add_own_to_own(Own0, CSDOwn) = Own,
+	add_inherit_to_inherit(Desc0, CSDDesc) = Desc1,
+	deep_lookup_csd_zero_map(Deep, CSDPtr, ZeroMapArray),
+	( map__search(ZeroMapArray, CallerPSPtr, InnerTotal) ->
+		Desc = subtract_inherit_from_inherit(InnerTotal, Desc1)
+	;
+		Desc = Desc1
+	).
+
+:- func call_site_dynamic_to_html_with_caller(preferences, deep,
+	call_site_display, call_site_dynamic_ptr) = two_id_line.
+
+call_site_dynamic_to_html_with_caller(Pref, Deep, Display, CSDPtr)
+		= LineGroup :-
+	deep_extract_csdptr_caller(Deep, CSDPtr, CallerPDPtr),
+	deep_lookup_clique_index(Deep, CallerPDPtr, CallerClique),
+	LineGroup = call_site_dynamic_to_html(Pref, Deep, Display,
+		yes(CallerClique), CSDPtr).
+
+:- func call_site_dynamic_to_html(preferences, deep, call_site_display,
+	maybe(clique_ptr), call_site_dynamic_ptr) = two_id_line.
+
+call_site_dynamic_to_html(Pref, Deep, CallSiteDisplay, MaybeCallerCliquePtr,
+		CSDPtr) = LineGroup :-
+	require(valid_call_site_dynamic_ptr(Deep, CSDPtr),
+		"call_site_dynamic_to_html: invalid call_site_dynamic_ptr"),
+	deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
+	CallerPDPtr = CSD ^ csd_caller,
+	CalleePDPtr = CSD ^ csd_callee,
+	CallSiteOwn = CSD ^ csd_own_prof,
+	deep_lookup_csd_desc(Deep, CSDPtr, CallSiteDesc),
+	deep_lookup_clique_index(Deep, CalleePDPtr, CalleeCliquePtr),
+	call_site_dynamic_context(Deep, CSDPtr, FileName, LineNumber),
+	Context = string__format("%s:%d", [s(FileName), i(LineNumber)]),
+	HTML = call_to_html(Pref, Deep, CallSiteDisplay, Context,
+		CallerPDPtr, CalleePDPtr,
+		MaybeCallerCliquePtr, CalleeCliquePtr),
+	ProcName = proc_dynamic_name(Deep, CalleePDPtr),
+	LineGroup = line_group(FileName, LineNumber, ProcName,
+		CallSiteOwn, CallSiteDesc, HTML, unit).
+
+%-----------------------------------------------------------------------------%
+
+:- type call_site_display
+	--->	call_site_display(
+			display_context		:: call_site_context,
+			display_proc_name	:: call_site_proc_name,
+			display_url		:: url_with_proc_name,
+			display_wrap		:: wrap_with_url
+		).
+
+:- type call_site_context
+	--->	call_context
+	;	empty_context.
+
+:- type call_site_proc_name
+	--->	caller_proc_name
+	;	callee_proc_name.
+
+:- type url_with_proc_name
+	--->	caller_clique
+	;	callee_clique.
+
+:- type wrap_with_url
+	--->	always
+	;	if_cross_clique(assume_cross_clique)
+	;	never.
+
+:- type assume_cross_clique
+	--->	assume_cross_clique
+	;	assume_within_clique.
+
+:- func ancestor_display = call_site_display.
+:- func upward_display = call_site_display.
+:- func downward_display = call_site_display.
+:- func downward_summary_display = call_site_display.
+
+ancestor_display =
+	call_site_display(call_context, caller_proc_name,
+		caller_clique, always).
+
+upward_display =
+	call_site_display(call_context, caller_proc_name,
+		callee_clique, always).
+
+downward_display =
+	call_site_display(call_context, callee_proc_name,
+		callee_clique, if_cross_clique(assume_within_clique)).
+
+downward_summary_display =
+	call_site_display(empty_context, callee_proc_name,
+		callee_clique, if_cross_clique(assume_within_clique)).
+
+%-----------------------------------------------------------------------------%
+
+:- func call_to_html(preferences, deep, call_site_display, string,
+	proc_dynamic_ptr, proc_dynamic_ptr, maybe(clique_ptr), clique_ptr)
+	= string.
+
+call_to_html(Pref, Deep, CallSiteDisplay, CallContext,
+		CallerPDPtr, CalleePDPtr,
+		MaybeCallerCliquePtr, CalleeCliquePtr) = HTML :-
+	( MaybeCallerCliquePtr = yes(CallerCliquePtr0) ->
+		CallerCliquePtr = CallerCliquePtr0
+	;
+		CallerCliquePtr = dummy_clique_ptr
+	),
+	(
+		CallSiteDisplay ^ display_context = call_context,
+		Context = CallContext
+	;
+		CallSiteDisplay ^ display_context = empty_context,
+		Context = ""
+	),
+	(
+		CallSiteDisplay ^ display_proc_name = caller_proc_name,
+		ProcName = proc_dynamic_name(Deep, CallerPDPtr)
+	;
+		CallSiteDisplay ^ display_proc_name = callee_proc_name,
+		ProcName = proc_dynamic_name(Deep, CalleePDPtr)
+	),
+	(
+		CallSiteDisplay ^ display_url = caller_clique,
+		ChosenCliquePtr = CallerCliquePtr
+	;
+		CallSiteDisplay ^ display_url = callee_clique,
+		ChosenCliquePtr = CalleeCliquePtr
+	),
+	ChosenCliquePtr = clique_ptr(ChosenCliqueNum),
+	WrappedProcName = string__format("<A HREF=""%s"">%s</A>",
+		[s(deep_cmd_pref_to_url(Pref, Deep, clique(ChosenCliqueNum))),
+		s(ProcName)]),
+	(
+		CallSiteDisplay ^ display_wrap = always,
+		UsedProcName0 = WrappedProcName
+	;
+		CallSiteDisplay ^ display_wrap = if_cross_clique(Assume),
+		(
+			MaybeCallerCliquePtr = yes(_),
+			( CallerCliquePtr \= CalleeCliquePtr ->
+				UsedProcName0 = WrappedProcName
+			;
+				UsedProcName0 = ProcName
+			)
+		;
+			MaybeCallerCliquePtr = no,
+			(
+				Assume = assume_cross_clique,
+				UsedProcName0 = WrappedProcName
+			;
+				Assume = assume_within_clique,
+				UsedProcName0 = ProcName
+			)
+		)
+	;
+		CallSiteDisplay ^ display_wrap = never,
+		UsedProcName0 = ProcName
+	),
+	(
+		UsedProcName0 = WrappedProcName,
+		valid_clique_ptr(Deep, ChosenCliquePtr)
+	->
+		UsedProcName = UsedProcName0
+	;
+		UsedProcName = ProcName
+	),
+	HTML =
+		string__format("<TD CLASS=id>%s</TD>\n", [s(Context)]) ++
+		string__format("<TD CLASS=id>%s</TD>\n", [s(UsedProcName)]).
+
+%-----------------------------------------------------------------------------%
+
+:- pred call_site_dynamic_context(deep::in, call_site_dynamic_ptr::in,
+	string::out, int::out) is det.
+
+call_site_dynamic_context(Deep, CSDPtr, FileName, LineNumber) :-
+	deep_lookup_call_site_static_map(Deep, CSDPtr, CSSPtr),
+	deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
+	PSPtr = CSS ^ css_container,
+	LineNumber = CSS ^ css_line_num,
+	deep_lookup_proc_statics(Deep, PSPtr, PS),
+	FileName = PS ^ ps_file_name.
+
+%-----------------------------------------------------------------------------%
+
+:- pred proc_callers_to_html(preferences::in, deep::in, proc_static_ptr::in,
+	caller_groups::in, int::in,
+	maybe_error({id_fields, string, string, string})::out,
+	io__state::di, io__state::uo) is det.
+
+proc_callers_to_html(Pref, Deep, PSPtr, CallerGroups, BunchNum0, MaybePage,
+		IO0, IO) :-
+	deep_lookup_proc_callers(Deep, PSPtr, CallerCSDPtrs),
+	(
+		Pref ^ pref_contour = no_contour,
+		CallerCSDPtrPairs = list__map(pair_self, CallerCSDPtrs),
+		IO = IO0,
+		MaybeErrorMsg = no
+	;
+		Pref ^ pref_contour = apply_contour,
+		read_exclude_file(contour_file_name(Deep ^ data_file_name),
+			Deep, Result, IO0, IO),
+		(
+			Result = ok(ExcludeSpec),
+			CallerCSDPtrPairs = list__map(
+				pair_contour(Deep, ExcludeSpec),
+				CallerCSDPtrs),
+			MaybeErrorMsg = no
+		;
+			Result = error(ErrorMsg),
+			MaybeErrorMsg = yes(ErrorMsg ++ "\n<br>"),
+			CallerCSDPtrPairs = list__map(pair_self, CallerCSDPtrs)
+		)
+	),
+	ProcName = proc_static_name(Deep, PSPtr),
+	PSPtr = proc_static_ptr(PSI),
+	CmdSite    = proc_callers(PSI, group_by_call_site, 1),
+	CmdProc    = proc_callers(PSI, group_by_proc, 1),
+	CmdModule  = proc_callers(PSI, group_by_module, 1),
+	CmdClique  = proc_callers(PSI, group_by_clique, 1),
+	LinkSite   = "Group callers by call site",
+	LinkProc   = "Group callers by procedure",
+	LinkModule = "Group callers by module",
+	LinkClique = "Group callers by clique",
+	BunchSize = 100,	% don't display more lines than this,
+				% to avoid quadratic behaviour in Netscape.
+	(
+		CallerGroups = group_by_call_site,
+		GroupMap = list__foldl(accumulate_csds_by_call_site(Deep),
+			CallerCSDPtrPairs, map__init),
+		map__to_assoc_list(GroupMap, GroupList),
+		Lines = list__map(
+			proc_callers_call_site_to_html(Pref, Deep, PSPtr),
+			GroupList),
+		SortedLines = sort_line_groups(Pref ^ pref_criteria, Lines),
+		IdFields = source_proc,
+		Entity = "call site",
+		GroupToggles =
+			string__format("<A HREF=""%s"">%s</A>\n",
+				[s(deep_cmd_pref_to_url(Pref, Deep, CmdProc)),
+				s(LinkProc)]) ++
+			string__format("<A HREF=""%s"">%s</A>\n",
+				[s(deep_cmd_pref_to_url(Pref, Deep, CmdModule)),
+				s(LinkModule)]) ++
+			string__format("<A HREF=""%s"">%s</A>\n",
+				[s(deep_cmd_pref_to_url(Pref, Deep, CmdClique)),
+				s(LinkClique)])
+	;
+		CallerGroups = group_by_proc,
+		GroupMap = list__foldl(accumulate_csds_by_procedure(Deep),
+			CallerCSDPtrPairs, map__init),
+		map__to_assoc_list(GroupMap, GroupList),
+		Lines = list__map(
+			proc_callers_proc_to_html(Pref, Deep, PSPtr),
+			GroupList),
+		SortedLines = sort_line_groups(Pref ^ pref_criteria, Lines),
+		IdFields = source_proc,
+		Entity = "procedure",
+		GroupToggles =
+			string__format("<A HREF=""%s"">%s</A>\n",
+				[s(deep_cmd_pref_to_url(Pref, Deep, CmdSite)),
+				s(LinkSite)]) ++
+			string__format("<A HREF=""%s"">%s</A>\n",
+				[s(deep_cmd_pref_to_url(Pref, Deep, CmdModule)),
+				s(LinkModule)]) ++
+			string__format("<A HREF=""%s"">%s</A>\n",
+				[s(deep_cmd_pref_to_url(Pref, Deep, CmdClique)),
+				s(LinkClique)])
+	;
+		CallerGroups = group_by_module,
+		GroupMap = list__foldl(accumulate_csds_by_module(Deep),
+			CallerCSDPtrPairs, map__init),
+		map__to_assoc_list(GroupMap, GroupList),
+		RawLines = list__map(
+			proc_callers_module_to_html(Pref, Deep, PSPtr),
+			GroupList),
+		SortedRawLines = sort_line_groups(Pref ^ pref_criteria,
+			RawLines),
+		SortedLines = add_ranks(SortedRawLines),
+		IdFields = rank_module,
+		Entity = "module",
+		GroupToggles =
+			string__format("<A HREF=""%s"">%s</A>\n",
+				[s(deep_cmd_pref_to_url(Pref, Deep, CmdSite)),
+				s(LinkSite)]) ++
+			string__format("<A HREF=""%s"">%s</A>\n",
+				[s(deep_cmd_pref_to_url(Pref, Deep, CmdProc)),
+				s(LinkProc)]) ++
+			string__format("<A HREF=""%s"">%s</A>\n",
+				[s(deep_cmd_pref_to_url(Pref, Deep, CmdClique)),
+				s(LinkClique)])
+	;
+		CallerGroups = group_by_clique,
+		GroupMap = list__foldl(accumulate_csds_by_clique(Deep),
+			CallerCSDPtrPairs, map__init),
+		map__to_assoc_list(GroupMap, GroupList),
+		RawLines = list__map(
+			proc_callers_clique_to_html(Pref, Deep, PSPtr),
+			GroupList),
+		SortedRawLines = sort_line_groups(Pref ^ pref_criteria,
+			RawLines),
+		SortedLines = add_ranks(SortedRawLines),
+		IdFields = source_proc,
+		Entity = "clique",
+		GroupToggles =
+			string__format("<A HREF=""%s"">%s</A>\n",
+				[s(deep_cmd_pref_to_url(Pref, Deep, CmdSite)),
+				s(LinkSite)]) ++
+			string__format("<A HREF=""%s"">%s</A>\n",
+				[s(deep_cmd_pref_to_url(Pref, Deep, CmdProc)),
+				s(LinkProc)]) ++
+			string__format("<A HREF=""%s"">%s</A>\n",
+				[s(deep_cmd_pref_to_url(Pref, Deep, CmdModule)),
+				s(LinkModule)])
+	),
+	% SortedLines may contain many thousand elements, and Netscape
+	% chokes on the output unless we filter them or break them into chunks.
+	% This simple limit device is temporary until we decide what filtering
+	% and/or chunking mechanism we want to use.
+	list__length(SortedLines, NumLines),
+	select_line_bunch(NumLines, BunchNum0, BunchNum, BunchSize,
+		SortedLines, DisplayedLines),
+	Banner = proc_callers_banner(PSI, ProcName, Pref, Deep,
+		NumLines, BunchSize, BunchNum, Entity),
+	DisplayedHTMLs = list__map(
+		two_id_line_to_html(Pref, Deep, totals_meaningful),
+		DisplayedLines),
+	HTML = string__append_list(DisplayedHTMLs),
+	( BunchNum > 1 ->
+		FirstCmd = proc_callers(PSI, CallerGroups, 1),
+		FirstLink = "First group",
+		FirstToggle =
+			string__format("<A HREF=""%s"">%s</A>\n",
+				[s(deep_cmd_pref_to_url(Pref, Deep, FirstCmd)),
+				s(FirstLink)])
+	;
+		FirstToggle = ""
+	),
+	( BunchNum > 2 ->
+		PrevCmd = proc_callers(PSI, CallerGroups, BunchNum - 1),
+		PrevLink = "Previous group",
+		PrevToggle =
+			string__format("<A HREF=""%s"">%s</A>\n",
+				[s(deep_cmd_pref_to_url(Pref, Deep, PrevCmd)),
+				s(PrevLink)])
+	;
+		PrevToggle = ""
+	),
+	( NumLines > BunchNum * BunchSize ->
+		NextCmd = proc_callers(PSI, CallerGroups, BunchNum + 1),
+		NextLink = "Next group",
+		NextToggle =
+			string__format("<A HREF=""%s"">%s</A>\n",
+				[s(deep_cmd_pref_to_url(Pref, Deep, NextCmd)),
+				s(NextLink)])
+	;
+		NextToggle = ""
+	),
+	Toggles = GroupToggles ++ FirstToggle ++ PrevToggle ++ NextToggle,
+	(
+		MaybeErrorMsg = no,
+		MaybePage = ok({IdFields, Banner, HTML, Toggles})
+	;
+		MaybeErrorMsg = yes(Msg),
+		MaybePage = error(Msg)
+	).
+
+:- pred select_line_bunch(int::in, int::in, int::out, int::in, list(T)::in,
+	list(T)::out) is det.
+
+select_line_bunch(NumLines, BunchNum0, BunchNum, BunchSize,
+		Lines, DisplayedLines) :-
+	ToDelete = (BunchNum0 - 1) * BunchSize,
+	(
+		list__drop(ToDelete, Lines, RemainingLines0),
+		RemainingLines0 = [_ | _]
+	->
+		BunchNum = BunchNum0,
+		RemainingLines = RemainingLines0,
+		RemainingNumLines = NumLines - ToDelete
+	;
+		BunchNum = 1,
+		RemainingLines = Lines,
+		RemainingNumLines = NumLines
+	),
+	( RemainingNumLines > BunchSize ->
+		list__take_upto(BunchSize, RemainingLines, DisplayedLines)
+	;
+		DisplayedLines = RemainingLines
+	).
+
+:- func proc_callers_banner(int, string, preferences, deep, int, int, int,
+	string) = string.
+
+proc_callers_banner(PSI, ProcName, Pref, Deep, NumLines, BunchSize, BunchNum,
+		Parent) = HTML :-
+	Cmd = proc(PSI),
+	WrappedProcName = string__format("<A HREF=""%s"">%s</A>",
+		[s(deep_cmd_pref_to_url(Pref, Deep, Cmd)), s(ProcName)]),
+	( NumLines = 0 ->
+		HTML = string__format("<H3>There are no %ss calling %s</H3>",
+			[s(Parent), s(WrappedProcName)])
+	; NumLines = 1 ->
+		HTML = string__format("<H3>There is one %s calling %s:</H3>\n",
+			[s(Parent), s(WrappedProcName)])
+	; NumLines =< BunchSize ->
+		HTML = string__format("<H3>The %d %ss calling %s:</H3>",
+			[i(NumLines), s(Parent), s(WrappedProcName)])
+	; BunchNum = 1 ->
+		HTML = string__format("<H3>There are %d %ss calling %s, showing first %d:</H3>",
+			[i(NumLines), s(Parent), s(WrappedProcName),
+			i(BunchSize)])
+	;
+		First = BunchNum * BunchSize + 1,
+		Last0 = (BunchNum + 1) * BunchSize,
+		( Last0 > NumLines ->
+			Last = NumLines
+		;
+			Last = Last0
+		),
+		HTML = string__format("<H3>There are %d %ss calling %s, showing %d to %d:</H3>",
+			[i(NumLines), s(Parent), s(WrappedProcName),
+			i(First), i(Last)])
+	).
+
+:- func proc_callers_call_site_to_html(preferences, deep, proc_static_ptr,
+	pair(call_site_static_ptr, list(call_site_dynamic_ptr))) = two_id_line.
+
+proc_callers_call_site_to_html(Pref, Deep, CalleePSPtr, CSSPtr - CSDPtrs)
+		= LineGroup :-
+	call_site_context(Deep, CSSPtr, FileName, LineNumber),
+	deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
+	CallerPSPtr = CSS ^ css_container,
+	deep_lookup_proc_statics(Deep, CallerPSPtr, CallerPS),
+	CallerProcName = CallerPS ^ ps_refined_id,
+	list__foldl2(accumulate_parent_csd_prof_info(Deep, CalleePSPtr),
+		CSDPtrs,
+		zero_own_prof_info, Own, zero_inherit_prof_info, Desc),
+	HTML =
+		string__format("<TD CLASS=id>%s:%d</TD>\n",
+			[s(FileName), i(LineNumber)]) ++
+		string__format("<TD CLASS=id>%s</TD>\n",
+			[s(proc_static_to_html_ref(Pref, Deep,
+				CallerPSPtr))]),
+	LineGroup = line_group(FileName, LineNumber, CallerProcName,
+		Own, Desc, HTML, unit).
+
+:- func proc_callers_proc_to_html(preferences, deep, proc_static_ptr,
+	pair(proc_static_ptr, list(call_site_dynamic_ptr))) = two_id_line.
+
+proc_callers_proc_to_html(Pref, Deep, CalleePSPtr, CallerPSPtr - CSDPtrs)
+		= LineGroup :-
+	proc_static_context(Deep, CallerPSPtr, FileName, LineNumber),
+	deep_lookup_proc_statics(Deep, CallerPSPtr, CallerPS),
+	CallerProcName = CallerPS ^ ps_refined_id,
+	list__foldl2(accumulate_parent_csd_prof_info(Deep, CalleePSPtr),
+		CSDPtrs,
+		zero_own_prof_info, Own, zero_inherit_prof_info, Desc),
+	HTML =
+		string__format("<TD CLASS=id>%s:%d</TD>\n",
+			[s(FileName), i(LineNumber)]) ++
+		string__format("<TD CLASS=id>%s</TD>\n",
+			[s(proc_static_to_html_ref(Pref, Deep,
+				CallerPSPtr))]),
+	LineGroup = line_group(FileName, LineNumber, CallerProcName,
+		Own, Desc, HTML, unit).
+
+:- func proc_callers_module_to_html(preferences, deep, proc_static_ptr,
+	pair(string, list(call_site_dynamic_ptr))) = one_id_line.
+
+proc_callers_module_to_html(Pref, Deep, CalleePSPtr, ModuleName - CSDPtrs)
+		= LineGroup :-
+	list__foldl2(accumulate_parent_csd_prof_info(Deep, CalleePSPtr),
+		CSDPtrs,
+		zero_own_prof_info, Own, zero_inherit_prof_info, Desc),
+	HTML = string__format("<TD CLASS=id>%s</TD>\n",
+		[s(module_name_to_html_ref(Pref, Deep, ModuleName))]),
+	% We don't have filename information for modules, and line numbers
+	% are not meaningful for modules.
+	LineGroup = line_group(ModuleName, 0, ModuleName,
+		Own, Desc, HTML, unit).
+
+:- func proc_callers_clique_to_html(preferences, deep, proc_static_ptr,
+	pair(clique_ptr, list(call_site_dynamic_ptr))) = one_id_line.
+
+proc_callers_clique_to_html(Pref, Deep, CalleePSPtr, CliquePtr - CSDPtrs)
+		= LineGroup :-
+	list__foldl2(accumulate_parent_csd_prof_info(Deep, CalleePSPtr),
+		CSDPtrs,
+		zero_own_prof_info, Own, zero_inherit_prof_info, Desc),
+	deep_lookup_clique_parents(Deep, CliquePtr, EntryCSDPtr),
+	deep_lookup_call_site_dynamics(Deep, EntryCSDPtr, EntryCSD),
+	EntryPDPtr = EntryCSD ^ csd_callee,
+	proc_dynamic_context(Deep, EntryPDPtr, FileName, LineNumber),
+	ProcName = proc_dynamic_name(Deep, EntryPDPtr),
+	HTML = string__format("<TD CLASS=id>%s</TD>\n",
+		[s(clique_ptr_to_html_ref(Pref, Deep, ProcName, CliquePtr))]),
+	LineGroup = line_group(FileName, LineNumber, ProcName,
+		Own, Desc, HTML, unit).
+
+:- func accumulate_csds_by_call_site(deep, pair(call_site_dynamic_ptr),
+	map(call_site_static_ptr, list(call_site_dynamic_ptr))) =
+	map(call_site_static_ptr, list(call_site_dynamic_ptr)).
+
+accumulate_csds_by_call_site(Deep, GroupCSDPtr - CostCSDPtr, Map0) = Map :-
+	deep_lookup_call_site_static_map(Deep, GroupCSDPtr, GroupCSSPtr),
+	( map__search(Map0, GroupCSSPtr, CostCSDPtrs0) ->
+		map__det_update(Map0, GroupCSSPtr, [CostCSDPtr | CostCSDPtrs0],
+			Map)
+	;
+		map__det_insert(Map0, GroupCSSPtr, [CostCSDPtr], Map)
+	).
+
+:- func accumulate_csds_by_procedure(deep, pair(call_site_dynamic_ptr),
+	map(proc_static_ptr, list(call_site_dynamic_ptr))) =
+	map(proc_static_ptr, list(call_site_dynamic_ptr)).
+
+accumulate_csds_by_procedure(Deep, GroupCSDPtr - CostCSDPtr, Map0) = Map :-
+	deep_lookup_call_site_static_map(Deep, GroupCSDPtr, GroupCSSPtr),
+	deep_lookup_call_site_statics(Deep, GroupCSSPtr, GroupCSS),
+	GroupPSPtr = GroupCSS ^ css_container,
+	( map__search(Map0, GroupPSPtr, CostCSDPtrs0) ->
+		map__det_update(Map0, GroupPSPtr, [CostCSDPtr | CostCSDPtrs0],
+			Map)
+	;
+		map__det_insert(Map0, GroupPSPtr, [CostCSDPtr], Map)
+	).
+
+:- func accumulate_csds_by_module(deep, pair(call_site_dynamic_ptr),
+	map(string, list(call_site_dynamic_ptr))) =
+	map(string, list(call_site_dynamic_ptr)).
+
+accumulate_csds_by_module(Deep, GroupCSDPtr - CostCSDPtr, Map0) = Map :-
+	deep_lookup_call_site_static_map(Deep, GroupCSDPtr, GroupCSSPtr),
+	deep_lookup_call_site_statics(Deep, GroupCSSPtr, GroupCSS),
+	GroupPSPtr = GroupCSS ^ css_container,
+	deep_lookup_proc_statics(Deep, GroupPSPtr, GroupPS),
+	GroupModuleName = GroupPS ^ ps_decl_module,
+	( map__search(Map0, GroupModuleName, CostCSDPtrs0) ->
+		map__det_update(Map0, GroupModuleName,
+			[CostCSDPtr | CostCSDPtrs0], Map)
+	;
+		map__det_insert(Map0, GroupModuleName, [CostCSDPtr], Map)
+	).
+
+:- func accumulate_csds_by_clique(deep, pair(call_site_dynamic_ptr),
+	map(clique_ptr, list(call_site_dynamic_ptr))) =
+	map(clique_ptr, list(call_site_dynamic_ptr)).
+
+accumulate_csds_by_clique(Deep, GroupCSDPtr - CostCSDPtr, Map0) = Map :-
+	deep_lookup_call_site_dynamics(Deep, GroupCSDPtr, GroupCSD),
+	CallerPDPtr = GroupCSD ^ csd_caller,
+	deep_lookup_clique_index(Deep, CallerPDPtr, CliquePtr),
+	( map__search(Map0, CliquePtr, CostCSDPtrs0) ->
+		map__det_update(Map0, CliquePtr, [CostCSDPtr | CostCSDPtrs0],
+			Map)
+	;
+		map__det_insert(Map0, CliquePtr, [CostCSDPtr], Map)
+	).
+
+:- pred accumulate_parent_csd_prof_info(deep::in, proc_static_ptr::in,
+	call_site_dynamic_ptr::in,
+	own_prof_info::in, own_prof_info::out,
+	inherit_prof_info::in, inherit_prof_info::out) is det.
+
+accumulate_parent_csd_prof_info(Deep, CallerPSPtr, CSDPtr,
+		Own0, Own, Desc0, Desc) :-
+	deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
+	( CSD ^ csd_callee = CSD ^ csd_caller ->
+			% We want to sum only cross-clique callers.
+		Own = Own0,
+		Desc = Desc0
+	;
+		deep_lookup_csd_own(Deep, CSDPtr, CSDOwn),
+		deep_lookup_csd_desc(Deep, CSDPtr, CSDDesc),
+		add_own_to_own(Own0, CSDOwn) = Own,
+		add_inherit_to_inherit(Desc0, CSDDesc) = Desc1,
+
+		deep_lookup_clique_index(Deep, CSD ^ csd_callee,
+			CalleeCliquePtr),
+		deep_lookup_clique_members(Deep, CalleeCliquePtr,
+			CalleeCliquePDPtrs),
+		list__foldl(compensate_for_zero_map(Deep, CallerPSPtr),
+			CalleeCliquePDPtrs, Desc1, Desc)
+	).
+
+:- pred compensate_for_zero_map(deep::in, proc_static_ptr::in,
+	proc_dynamic_ptr::in, inherit_prof_info::in, inherit_prof_info::out)
+	is det.
+
+compensate_for_zero_map(Deep, CallerPSPtr, PDPtr, Desc0, Desc) :-
+	deep_lookup_pd_zero_map(Deep, PDPtr, ZeroMapArray),
+	( map__search(ZeroMapArray, CallerPSPtr, InnerTotal) ->
+		Desc = subtract_inherit_from_inherit(InnerTotal, Desc0)
+	;
+		Desc = Desc0
+	).
+
+:- func pair_self(call_site_dynamic_ptr) = pair(call_site_dynamic_ptr).
+
+pair_self(CSDPtr) = CSDPtr - CSDPtr.
+
+:- func pair_contour(deep, set(exclude_spec), call_site_dynamic_ptr)
+	= pair(call_site_dynamic_ptr).
+
+pair_contour(Deep, ExcludeSpec, CSDPtr) =
+	apply_contour_exclusion(Deep, ExcludeSpec, CSDPtr) - CSDPtr.
+
+%-----------------------------------------------------------------------------%
+
+:- func proc_summary_to_html(preferences, deep, proc_static_ptr) = string.
+
+proc_summary_to_html(Pref, Deep, PSPtr) = HTML :-
+	SumHTML = two_id_line_to_html(Pref, Deep, totals_meaningful,
+		lookup_proc_total_to_two_id_line(Pref, Deep, yes, "", PSPtr)),
+	deep_lookup_proc_statics(Deep, PSPtr, PS),
+	CSSPtrsArray = PS ^ ps_sites,
+	array__to_list(CSSPtrsArray, CSSPtrs),
+	CallSiteGroups = list__map(call_site_summary_to_html(Pref, Deep),
+		CSSPtrs),
+	SortedCallSiteGroups = sort_line_groups(Pref ^ pref_criteria,
+		CallSiteGroups),
+	BodyHTMLs = list__map(
+		two_id_line_group_to_html(Pref, Deep, totals_meaningful),
+		SortedCallSiteGroups),
+	string__append_list(BodyHTMLs, BodyHTML0),
+	( SortedCallSiteGroups = [] ->
+		BodyHTML = BodyHTML0
+	;
+		BodyHTML =
+			BodyHTML0 ++
+			separator_row(Pref, source_proc, totals_meaningful)
+	),
+	HTML =
+		SumHTML ++
+		separator_row(Pref, source_proc, totals_meaningful) ++
+		BodyHTML.
+
+:- func proc_summary_toggles_to_html(preferences, deep, proc_static_ptr)
+	= string.
+
+proc_summary_toggles_to_html(Pref, Deep, PSPtr) = HTML :-
+	PSPtr = proc_static_ptr(PSI),
+	Msg1 = "Parent call sites",
+	Cmd1 = proc_callers(PSI, group_by_call_site, 1),
+	Msg2 = "Parent procedures",
+	Cmd2 = proc_callers(PSI, group_by_proc, 1),
+	Msg3 = "Parent modules",
+	Cmd3 = proc_callers(PSI, group_by_module, 1),
+	Msg4 = "Parent cliques",
+	Cmd4 = proc_callers(PSI, group_by_clique, 1),
+	Link1 = string__format("<A HREF=""%s"">%s</A>\n",
+		[s(deep_cmd_pref_to_url(Pref, Deep, Cmd1)), s(Msg1)]),
+	Link2 = string__format("<A HREF=""%s"">%s</A>\n",
+		[s(deep_cmd_pref_to_url(Pref, Deep, Cmd2)), s(Msg2)]),
+	Link3 = string__format("<A HREF=""%s"">%s</A>\n",
+		[s(deep_cmd_pref_to_url(Pref, Deep, Cmd3)), s(Msg3)]),
+	Link4 = string__format("<A HREF=""%s"">%s</A>\n",
+		[s(deep_cmd_pref_to_url(Pref, Deep, Cmd4)), s(Msg4)]),
+	HTML =
+		Link1 ++
+		Link2 ++
+		Link3 ++
+		Link4.
+
+%-----------------------------------------------------------------------------%
+
+:- func wrap_clique_links(clique_ptr, preferences, deep, string,
+	order_criteria) = string.
+
+wrap_clique_links(CliquePtr, Pref0, Deep, Str0, Criteria) = Str :-
+	CliquePtr = clique_ptr(CI),
+	Cmd = clique(CI),
+	Pref = Pref0 ^ pref_criteria := Criteria,
+	URL = deep_cmd_pref_to_url(Pref, Deep, Cmd),
+	Str = string__format("<A HREF=%s>%s</A>",
+		[s(URL), s(Str0)]).
+
+:- func wrap_proc_links(proc_static_ptr, preferences, deep, string,
+	order_criteria) = string.
+
+wrap_proc_links(PSPtr, Pref0, Deep, Str0, Criteria) = Str :-
+	PSPtr = proc_static_ptr(PSI),
+	Cmd = proc(PSI),
+	Pref = Pref0 ^ pref_criteria := Criteria,
+	URL = deep_cmd_pref_to_url(Pref, Deep, Cmd),
+	Str = string__format("<A HREF=%s>%s</A>",
+		[s(URL), s(Str0)]).
+
+:- func wrap_proc_callers_links(proc_static_ptr, caller_groups, int,
+	preferences, deep, string, order_criteria) = string.
+
+wrap_proc_callers_links(PSPtr, CallerGroups, BunchNum, Pref0, Deep,
+		Str0, Criteria) = Str :-
+	PSPtr = proc_static_ptr(PSI),
+	Cmd = proc_callers(PSI, CallerGroups, BunchNum),
+	Pref = Pref0 ^ pref_criteria := Criteria,
+	URL = deep_cmd_pref_to_url(Pref, Deep, Cmd),
+	Str = string__format("<A HREF=%s>%s</A>",
+		[s(URL), s(Str0)]).
+
+:- func wrap_module_links(string, preferences, deep, string,
+	order_criteria) = string.
+
+wrap_module_links(ModuleName, Pref0, Deep, Str0, Criteria) = Str :-
+	Cmd = module(ModuleName),
+	Pref = Pref0 ^ pref_criteria := Criteria,
+	URL = deep_cmd_pref_to_url(Pref, Deep, Cmd),
+	Str = string__format("<A HREF=%s>%s</A>",
+		[s(URL), s(Str0)]).
+
+:- func wrap_modules_links(preferences, deep, string, order_criteria) = string.
+
+wrap_modules_links(Pref0, Deep, Str0, Criteria) = Str :-
+	Cmd = modules,
+	Pref = Pref0 ^ pref_criteria := Criteria,
+	URL = deep_cmd_pref_to_url(Pref, Deep, Cmd),
+	Str = string__format("<A HREF=%s>%s</A>",
+		[s(URL), s(Str0)]).
+
+:- func wrap_top_procs_links(display_limit, preferences, deep, string,
+	order_criteria) = string.
+
+wrap_top_procs_links(Limit, Pref, Deep, Str0, Criteria) = Str :-
+	(
+		Criteria = by_context,
+		Str = Str0
+	;
+		Criteria = by_name,
+		Str = Str0
+	;
+		Criteria = by_cost(CostKind, InclDesc, Scope),
+		Cmd = top_procs(Limit, CostKind, InclDesc, Scope),
+		URL = deep_cmd_pref_to_url(Pref, Deep, Cmd),
+		Str = string__format("<A HREF=%s>%s</A>",
+			[s(URL), s(Str0)])
+	).
Index: deep_profiler/read_profile.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/read_profile.m,v
retrieving revision 1.2
diff -u -b -r1.2 read_profile.m
--- deep_profiler/read_profile.m	2001/06/05 04:47:32	1.2
+++ deep_profiler/read_profile.m	2001/07/02 07:13:15
@@ -21,7 +21,7 @@
 :- implementation.
 
 :- import_module measurements, array_util.
-:- import_module array, char, string, int, float, list, require.
+:- import_module bool, char, string, int, array, list, require.
 :- import_module io_combinator.
 
 :- type maybe_error2(T1, T2)
@@ -41,7 +41,7 @@
 		read_id_string(Res1),
 		(
 			{ Res1 = ok(_) },
-			io_combinator__maybe_error_sequence_7(
+			io_combinator__maybe_error_sequence_10(
 				read_fixed_size_int,
 				read_fixed_size_int,
 				read_fixed_size_int,
@@ -49,16 +49,24 @@
 				read_num,
 				read_num,
 				read_num,
+				read_deep_byte,
+				read_deep_byte,
+				read_ptr(pd),
 				(pred(MaxCSD::in, MaxCSS::in,
 						MaxPD::in, MaxPS::in,
 						TicksPerSec::in,
 						InstrumentQuanta::in,
 						UserQuanta::in,
+						WordSize::in,
+						CanonicalFlag::in,
+						RootPDI::in,
 						ResInitDeep::out) is det :-
-					init_deep(MaxCSD, MaxCSS, MaxPD, MaxPS,
+					InitDeep0 = init_deep(MaxCSD, MaxCSS,
+						MaxPD, MaxPS,
 						TicksPerSec,
 						InstrumentQuanta, UserQuanta,
-						InitDeep0),
+						WordSize, CanonicalFlag,
+						RootPDI),
 					ResInitDeep = ok(InitDeep0)
 				),
 				Res2),
@@ -100,33 +108,38 @@
 :- func id_string = string.
 
 id_string = "Mercury deep profiler data".
-
-:- pred init_deep(int::in, int::in, int::in, int::in, int::in, int::in,
-	int::in, initial_deep::out) is det.
 
-init_deep(MaxCSD, MaxCSS, MaxPD, MaxPS, TicksPerSec,
-		InstrumentQuanta, UserQuanta, InitDeep) :-
-	InitStats = profile_stats(MaxCSD, MaxCSS, MaxPD, MaxPS,
-		TicksPerSec, InstrumentQuanta, UserQuanta),
+:- func init_deep(int, int, int, int, int, int, int, int, int, int)
+	= initial_deep.
 
+init_deep(MaxCSD, MaxCSS, MaxPD, MaxPS, TicksPerSec, InstrumentQuanta,
+		UserQuanta, WordSize, CanonicalByte, RootPDI) = InitDeep :-
+	( CanonicalByte = 0 ->
+		CanonicalFlag = no
+	;
+		CanonicalFlag = yes
+	),
+	InitStats = profile_stats(MaxCSD, MaxCSS, MaxPD, MaxPS, TicksPerSec,
+		InstrumentQuanta, UserQuanta, WordSize, CanonicalFlag),
 	InitDeep = initial_deep(
 		InitStats,
-		proc_dynamic_ptr(-1),
+		make_pdptr(RootPDI),
 		array__init(MaxCSD + 1,
 			call_site_dynamic(
-				proc_dynamic_ptr(-1),
-				proc_dynamic_ptr(-1),
+				make_dummy_pdptr,
+				make_dummy_pdptr,
 				zero_own_prof_info
 			)),
 		array__init(MaxPD + 1,
-			proc_dynamic(proc_static_ptr(-1), array([]))),
+			proc_dynamic(make_dummy_psptr, array([]))),
 		array__init(MaxCSS + 1,
 			call_site_static(
-				proc_static_ptr(-1), -1,
-				normal_call(proc_static_ptr(-1), ""), -1, ""
+				make_dummy_psptr, -1,
+				normal_call(make_dummy_psptr, ""), -1, ""
 			)),
 		array__init(MaxPS + 1,
-			proc_static(dummy_proc_id, "", "", "", array([])))
+			proc_static(dummy_proc_id, "", "", "", "", -1, no,
+				array([]), not_zeroed))
 	).
 
 :- pred read_nodes(initial_deep::in, maybe_error(initial_deep)::out,
@@ -192,16 +205,6 @@
 				{ Res1 = error2(Err) },
 				{ Res = error(Err) }
 			)
-		; { Byte = token_root } ->
-			read_root(Res1),
-			(
-				{ Res1 = ok(PDPtr) },
-				{ InitDeep1 = InitDeep0 ^ init_root := PDPtr },
-				read_nodes(InitDeep1, Res)
-			;
-				{ Res1 = error(Err) },
-				{ Res = error(Err) }
-			)
 		;
 			{ format("unexpected token %d", [i(Byte)], Msg) },
 			{ Res = error(Msg) }
@@ -215,26 +218,12 @@
 		{ Res = error(Msg) }
 	).
 
-:- pred read_root(maybe_error(proc_dynamic_ptr)::out,
-	io__state::di, io__state::uo) is det.
-
-read_root(Res) -->
-	% format("reading root.\n", []),
-	read_ptr(pd, Res0),
-	(
-		{ Res0 = ok(PDI) },
-		{ PDPtr = proc_dynamic_ptr(PDI) },
-		{ Res = ok(PDPtr) }
-	;
-		{ Res0 = error(Err) },
-		{ Res = error(Err) }
-	).
-
 :- pred read_call_site_static(maybe_error2(call_site_static, int)::out,
 	io__state::di, io__state::uo) is det.
 
 read_call_site_static(Res) -->
-	% format("reading call_site_static.\n", []),
+	% DEBUGSITE
+	% io__format("reading call_site_static.\n", []),
 	io_combinator__maybe_error_sequence_4(
 		read_ptr(css),
 		read_call_site_kind_and_callee,
@@ -242,7 +231,7 @@
 		read_string,
 		(pred(CSSI0::in, Kind::in, LineNumber::in, Str::in, Res0::out)
 				is det :-
-			DummyPSPtr = proc_static_ptr(-1),
+			DummyPSPtr = make_dummy_psptr,
 			DummySlotNum = -1,
 			CallSiteStatic0 = call_site_static(DummyPSPtr,
 				DummySlotNum, Kind, LineNumber, Str),
@@ -262,29 +251,41 @@
 	io__state::di, io__state::uo) is det.
 
 read_proc_static(Res) -->
-	% format("reading proc_static.\n", []),
-	io_combinator__maybe_error_sequence_4(
+	% DEBUGSITE
+	% io__format("reading proc_static.\n", []),
+	io_combinator__maybe_error_sequence_6(
 		read_ptr(ps),
 		read_proc_id,
 		read_string,
+		read_num,
+		read_deep_byte,
 		read_num,
-		(pred(PSI0::in, Id0::in, F0::in, N0::in, Stuff0::out) is det :-
-			Stuff0 = ok({PSI0, Id0, F0, N0})
+		(pred(PSI0::in, Id0::in, F0::in, L0::in, I0::in,
+				N0::in, Stuff0::out) is det :-
+			Stuff0 = ok({PSI0, Id0, F0, L0, I0, N0})
 		),
 		Res1),
 	(
-		{ Res1 = ok({PSI, Id, FileName, N}) },
+		{ Res1 = ok({PSI, Id, FileName, LineNumber, Interface, N}) },
 		read_n_things(N, read_ptr(css), Res2),
 		(
-			{ Res2 = ok(Ptrs0) },
-			{ map((pred(Ptr1::in, Ptr2::out) is det :-
-				Ptr2 = call_site_static_ptr(Ptr1)
-			), Ptrs0, Ptrs) },
+			{ Res2 = ok(CSSIs) },
+			{ CSSPtrs = list__map(make_cssptr, CSSIs) },
+			{ DeclModule = decl_module(Id) },
 			{ RefinedStr = refined_proc_id_to_string(Id) },
 			{ RawStr = raw_proc_id_to_string(Id) },
-			{ ProcStatic =
-				proc_static(Id, RefinedStr, RawStr,
-					FileName, array(Ptrs)) },
+			% The `not_zeroed' for whether the procedure's
+			% proc_static is ever zeroed is the default. The
+			% startup phase will set it to `zeroed' in the
+			% proc_statics which are ever zeroed.
+			{ Interface = 0 ->
+				IsInInterface = no
+			;
+				IsInInterface = yes
+			},
+			{ ProcStatic = proc_static(Id, DeclModule,
+				RefinedStr, RawStr, FileName, LineNumber,
+				IsInInterface, array(CSSPtrs), not_zeroed) },
 			{ Res = ok2(ProcStatic, PSI) }
 		;
 			{ Res2 = error(Err) },
@@ -503,7 +504,8 @@
 	io__state::di, io__state::uo) is det.
 
 read_proc_dynamic(Res) -->
-	% format("reading proc_dynamic.\n", []),
+	% DEBUGSITE
+	% io__format("reading proc_dynamic.\n", []),
 	io_combinator__maybe_error_sequence_3(
 		read_ptr(pd),
 		read_ptr(ps),
@@ -517,7 +519,7 @@
 		read_n_things(N, read_call_site_ref, Res2),
 		(
 			{ Res2 = ok(Refs) },
-			{ PSPtr = proc_static_ptr(PSI) },
+			{ PSPtr = make_psptr(PSI) },
 			{ ProcDynamic = proc_dynamic(PSPtr, array(Refs)) },
 			{ Res = ok2(ProcDynamic, PDI) }
 		;
@@ -533,7 +535,8 @@
 	io__state::di, io__state::uo) is det.
 
 read_call_site_dynamic(Res) -->
-	% format("reading call_site_dynamic.\n", []),
+	% DEBUGSITE
+	% io__format("reading call_site_dynamic.\n", []),
 	read_ptr(csd, Res1),
 	(
 		{ Res1 = ok(CSDI) },
@@ -543,10 +546,10 @@
 			read_profile(Res3),
 			(
 				{ Res3 = ok(Profile) },
-				{ PDPtr = proc_dynamic_ptr(PDI) },
-				{ DummyPDPtr = proc_dynamic_ptr(-1) },
+				{ PDPtr = make_pdptr(PDI) },
+				{ CallerPDPtr = make_dummy_pdptr },
 				{ CallSiteDynamic = call_site_dynamic(
-					DummyPDPtr, PDPtr, Profile) },
+					CallerPDPtr, PDPtr, Profile) },
 				{ Res = ok2(CallSiteDynamic, CSDI) }
 			;
 				{ Res3 = error(Err) },
@@ -570,7 +573,7 @@
 		{ Res0 = ok(Mask) },
 		{ MaybeError1 = no },
 		% { MaybeError0 = no },
-		% Calls are computed from the other counts below
+		% Calls are computed from the other counts in measurements.m
 		% ( { Mask /\ 0x0001 \= 0 } ->
 		% 	maybe_read_num_handle_error(Calls,
 		% 		MaybeError0, MaybeError1)
@@ -652,28 +655,32 @@
 	io__state::di, io__state::uo) is det.
 
 read_call_site_ref(Res) -->
-	% format("reading call_site_ref.\n", []),
+	% DEBUGSITE
+	% io__format("reading call_site_ref.\n", []),
 	read_call_site_kind(Res1),
 	(
 		{ Res1 = ok(Kind) },
 		( { Kind = normal_call } ->
 			read_ptr(csd, Res2),
 			(
-				{ Res2 = ok(Ptr) },
-				{ CDPtr = call_site_dynamic_ptr(Ptr) },
-				{ Res = ok(normal(CDPtr)) }
+				{ Res2 = ok(CSDI) },
+				{ CSDPtr = make_csdptr(CSDI) },
+				{ Res = ok(normal(CSDPtr)) }
 			;
 				{ Res2 = error(Err) },
 				{ Res = error(Err) }
 			)
 		;
+			{ ( Kind = higher_order_call ; Kind = method_call ) ->
+				Zeroed = zeroed
+			;
+				Zeroed = not_zeroed
+			},
 			read_things(read_ptr(csd), Res2),
 			(
-				{ Res2 = ok(Ptrs0) },
-				{ map((pred(PtrX::in, PtrY::out) is det :-
-					PtrY = call_site_dynamic_ptr(PtrX)
-				), Ptrs0, Ptrs) },
-				{ Res = ok(multi(array(Ptrs))) }
+				{ Res2 = ok(CSDIs) },
+				{ CSDPtrs = list__map(make_csdptr, CSDIs) },
+				{ Res = ok(multi(Zeroed, array(CSDPtrs))) }
 			;
 				{ Res2 = error(Err) },
 				{ Res = error(Err) }
@@ -706,8 +713,11 @@
 				[i(Byte)], Msg) },
 			{ Res = error(Msg) }
 		)
+		% DEBUGSITE
 		% io__write_string("call_site_kind "),
+		% DEBUGSITE
 		% io__write(Res),
+		% DEBUGSITE
 		% io__write_string("\n")
 	;
 		{ Res0 = error(Err) },
@@ -754,8 +764,11 @@
 				[i(Byte)], Msg) },
 			{ Res = error(Msg) }
 		)
+		% DEBUGSITE
 		% io__write_string("call_site_kind_and_callee "),
+		% DEBUGSITE
 		% io__write(Res),
+		% DEBUGSITE
 		% io__write_string("\n")
 	;
 		{ Res0 = error(Err) },
@@ -870,25 +883,34 @@
 		{ Res1 = error(Err) },
 		{ Res = error(Err) }
 	).
+	% DEBUGSITE
 	% io__write_string("string "),
+	% DEBUGSITE
 	% io__write(Res),
-	% io__write_string("\n")
+	% DEBUGSITE
+	% io__write_string("\n").
 
 :- pred read_ptr(ptr_kind::in, maybe_error(int)::out,
 	io__state::di, io__state::uo) is det.
 
 read_ptr(_Kind, Res) -->
 	read_num1(0, Res).
+	% DEBUGSITE
 	% io__write_string("ptr "),
+	% DEBUGSITE
 	% io__write(Res),
+	% DEBUGSITE
 	% io__write_string("\n").
 
 :- pred read_num(maybe_error(int)::out, io__state::di, io__state::uo) is det.
 
 read_num(Res) -->
 	read_num1(0, Res).
+	% DEBUGSITE
 	% io__write_string("num "),
+	% DEBUGSITE
 	% io__write(Res),
+	% DEBUGSITE
 	% io__write_string("\n").
 
 :- pred read_num1(int::in, maybe_error(int)::out,
@@ -981,8 +1003,11 @@
 
 read_deep_byte(Res) -->
 	read_byte(Res0),
+	% DEBUGSITE
 	% io__write_string("byte "),
+	% DEBUGSITE
 	% io__write(Res),
+	% DEBUGSITE
 	% io__write_string("\n"),
 	(
 		{ Res0 = ok(Byte) },
@@ -1003,23 +1028,41 @@
 deep_insert(A0, Ind, Thing, A) :-
 	array__max(A0, Max),
 	( Ind > Max ->
-		array__lookup(A0, 0, X),
-		array__resize(u(A0), 2 * (Max + 1), X, A1),
-		deep_insert(A1, Ind, Thing, A)
+		error("deep_insert: array bounds violation")
+		% array__lookup(A0, 0, X),
+		% array__resize(u(A0), 2 * (Max + 1), X, A1),
+		% deep_insert(A1, Ind, Thing, A)
 	;
 		set(u(A0), Ind, Thing, A)
 	).
 
 %------------------------------------------------------------------------------%
 
+:- func make_csdptr(int) = call_site_dynamic_ptr.
+:- func make_cssptr(int) = call_site_static_ptr.
+:- func make_pdptr(int) = proc_dynamic_ptr.
+:- func make_psptr(int) = proc_static_ptr.
+
+make_csdptr(CSDI) = call_site_dynamic_ptr(CSDI).
+make_cssptr(CSSI) = call_site_static_ptr(CSSI).
+make_pdptr(PDI) = proc_dynamic_ptr(PDI).
+make_psptr(PSI) = proc_static_ptr(PSI).
+
+:- func make_dummy_csdptr = call_site_dynamic_ptr.
+:- func make_dummy_cssptr = call_site_static_ptr.
+:- func make_dummy_pdptr = proc_dynamic_ptr.
+:- func make_dummy_psptr = proc_static_ptr.
+
+make_dummy_csdptr = call_site_dynamic_ptr(-1).
+make_dummy_cssptr = call_site_static_ptr(-1).
+make_dummy_pdptr = proc_dynamic_ptr(-1).
+make_dummy_psptr = proc_static_ptr(-1).
+
+%------------------------------------------------------------------------------%
+
 :- pragma c_header_code("
 #include ""mercury_deep_profiling.h""
 ").
-
-:- func token_root = int.
-:- pragma c_code(token_root = (X::out),
-	[will_not_call_mercury, thread_safe],
-	"X = MR_deep_token_root;").
 
 :- func token_call_site_static = int.
 :- pragma c_code(token_call_site_static = (X::out),
Index: deep_profiler/server.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/server.m,v
retrieving revision 1.3
diff -u -b -r1.3 server.m
--- deep_profiler/server.m	2001/06/12 01:17:42	1.3
+++ deep_profiler/server.m	2001/07/03 03:08:54
@@ -16,58 +16,51 @@
 
 :- interface.
 
-:- import_module profile.
+:- import_module profile, interface.
 :- import_module bool, io.
 
-:- pred test_server(string::in, deep::in, string::in,
+:- pred test_server(string::in, preferences::in, deep::in,
 	io__state::di, io__state::uo) is cc_multi.
 
-:- pred server(int::in, bool::in, deep::in,
+:- pred server(int::in, bool::in, bool::in, deep::in,
 	io__state::di, io__state::uo) is cc_multi.
 
 :- implementation.
 
-:- import_module interface, measurements, timeout.
-:- import_module std_util, int, float, char, string.
-:- import_module array, list, assoc_list, map.
-:- import_module exception, require.
-
-:- type call_site_line_number
-	--->	call_site_line_number
-	;	no_call_site_line_number.
+:- import_module query, startup, timeout.
+:- import_module std_util, int, string, array, list, exception, require.
 
 %-----------------------------------------------------------------------------%
 
-test_server(DirName, Deep, Fields) -->
+test_server(DirName, Pref, Deep) -->
 	{ string__format("test -d %s || mkdir -p %s",
 		[s(DirName), s(DirName)], Cmd) },
 	io__call_system(Cmd, _),
 	{ array__max(Deep ^ clique_members, NumCliques) },
-	test_cliques(1, NumCliques, DirName, Deep, Fields),
+	test_cliques(1, NumCliques, DirName, Pref, Deep),
 	{ array__max(Deep ^ proc_statics, NumProcStatics) },
-	test_procs(1, NumProcStatics, DirName, Deep, Fields).
+	test_procs(1, NumProcStatics, DirName, Pref, Deep).
 
-:- pred test_cliques(int::in, int::in, string::in, deep::in,
-	string::in, io__state::di, io__state::uo) is cc_multi.
+:- pred test_cliques(int::in, int::in, string::in, preferences::in, deep::in,
+	io__state::di, io__state::uo) is cc_multi.
 
-test_cliques(Cur, Max, DirName, Deep, Fields) -->
+test_cliques(Cur, Max, DirName, Pref, Deep) -->
 	( { Cur =< Max } ->
-		{ try_exec(clique(Cur, Fields, default_ancestor_limit),
-			Deep, HTML) },
+		try_exec(clique(Cur), Pref, Deep, HTML),
 		write_html(DirName, "clique", Cur, HTML),
-		test_cliques(Cur + 1, Max, DirName, Deep, Fields)
+		test_cliques(Cur + 1, Max, DirName, Pref, Deep)
 	;
 		[]
 	).
 
-:- pred test_procs(int::in, int::in, string::in, deep::in,
-	string::in, io__state::di, io__state::uo) is cc_multi.
+:- pred test_procs(int::in, int::in, string::in, preferences::in, deep::in,
+	io__state::di, io__state::uo) is cc_multi.
 
-test_procs(Cur, Max, DirName, Deep, Fields) -->
+test_procs(Cur, Max, DirName, Pref, Deep) -->
 	( { Cur =< Max } ->
-		{ try_exec(proc(Cur, Fields), Deep, HTML) },
+		try_exec(proc(Cur), Pref, Deep, HTML),
 		write_html(DirName, "proc", Cur, HTML),
-		test_procs(Cur + 1, Max, DirName, Deep, Fields)
+		test_procs(Cur + 1, Max, DirName, Pref, Deep)
 	;
 		[]
 	).
@@ -98,14 +91,16 @@
 
 %-----------------------------------------------------------------------------%
 
-server(TimeOut, Debug, Deep) -->
+server(TimeOut, Debug, CanonicalClique, Deep) -->
 	{ DataFileName = Deep ^ data_file_name },
 	{ InputPipe = to_server_pipe_name(DataFileName) },
 	{ OutputPipe = from_server_pipe_name(DataFileName) },
-	{ StartupFile = server_startup_name(DataFileName) },
+	% Comment out the following line if you want to debug query processing.
+	% Otherwise, the process started by mdb will exit before the first
+	% query is read.
 	detach_server_loop,
-	server_loop(InputPipe, OutputPipe, StartupFile, TimeOut,
-		Debug, 0, Deep).
+	server_loop(InputPipe, OutputPipe, TimeOut, Debug, CanonicalClique,
+		0, Deep).
 
 :- pragma foreign_decl("C", "
 #include <stdio.h>
@@ -128,7 +123,7 @@
 		/*
 		** The fork failed; we cannot detach the server loop from the
 		** startup process. The cgi script would therefore wait forever
-		** if we did not exit now
+		** if we did not exit now.
 		*/
 
 		exit(1);
@@ -138,6 +133,9 @@
 		** now to let the io__call_system in the cgi script succeed.
 		*/
 
+		extern	bool	process_is_detached_server;
+
+		process_is_detached_server = TRUE;
 		exit(0);
 	}
 
@@ -147,12 +145,12 @@
 	*/
 }").
 
-:- pred server_loop(string::in, string::in, string::in, int::in,
+:- pred server_loop(string::in, string::in, int::in, bool::in,
 	bool::in, int::in, deep::in, io__state::di, io__state::uo) is cc_multi.
 
-server_loop(InputPipe, OutputPipe, StartupFile, TimeOut,
-		Debug, QueryNum, Deep) -->
-	setup_timeout(InputPipe, OutputPipe, StartupFile, TimeOut),
+server_loop(InputPipe, OutputPipe, TimeOut, Debug, CanonicalClique,
+		QueryNum, Deep0) -->
+	setup_timeout(TimeOut),
 	io__see(InputPipe, SeeRes),
 	(
 		{ SeeRes = ok },
@@ -173,8 +171,8 @@
 			;
 				{ Debug = no }
 			),
-			server_loop(InputPipe, OutputPipe, StartupFile,
-				TimeOut, Debug, QueryNum + 1, Deep)
+			server_loop(InputPipe, OutputPipe, TimeOut, Debug,
+				CanonicalClique, QueryNum + 1, Deep0)
 		;
 			{ ReadRes = error(Msg, Line) },
 			(
@@ -185,11 +183,32 @@
 			;
 				{ Debug = no }
 			),
-			server_loop(InputPipe, OutputPipe, StartupFile,
-				TimeOut, Debug, QueryNum + 1, Deep)
+			server_loop(InputPipe, OutputPipe, TimeOut, Debug,
+				CanonicalClique, QueryNum + 1, Deep0)
+		;
+			{ ReadRes = ok(CmdPref0) },
+			{ CmdPref0 = cmd_pref(Cmd0, Pref0) },
+			( { Cmd0 = restart } ->
+				read_and_startup(Deep0 ^ server_name,
+					[Deep0 ^ data_file_name],
+					CanonicalClique, MaybeDeep),
+				(
+					{ MaybeDeep = ok(Deep) },
+					{ Cmd = menu }
+				;
+					{ MaybeDeep = error(Msg) },
+					io__tell(OutputPipe, _),
+					io__write(html(Msg)),
+					io__write_string(".\n"),
+					io__told,
+					{ Deep = Deep0 },
+					{ Cmd = quit }
+				)
 		;
-			{ ReadRes = ok(Cmd) },
-			{ try_exec(Cmd, Deep, HTML) },
+				{ Deep = Deep0 },
+				{ Cmd = Cmd0 }
+			),
+			try_exec(Cmd, Pref0, Deep, HTML),
 			(
 				{ Debug = yes },
 				io__format(StdErr, "query %d output:\n%s\n",
@@ -205,16 +224,17 @@
 			io__write_string(".\n"),
 			io__told,
 			( { Cmd = quit } ->
-				% This cleans up after the server.
-				execute_timeout_action
 				% The lack of a recursive call here shuts down
 				% the server process.
+				[]
 			; { Cmd = timeout(NewTimeOut) } ->
-				server_loop(InputPipe, OutputPipe, StartupFile,
-					NewTimeOut, Debug, QueryNum + 1, Deep)
-			;
-				server_loop(InputPipe, OutputPipe, StartupFile,
-					TimeOut, Debug, QueryNum + 1, Deep)
+				server_loop(InputPipe, OutputPipe, NewTimeOut,
+					Debug, CanonicalClique,
+					QueryNum + 1, Deep)
+			;
+				server_loop(InputPipe, OutputPipe, TimeOut,
+					Debug, CanonicalClique,
+					QueryNum + 1, Deep)
 			)
 		)
 	;
@@ -226,1778 +246,26 @@
 
 %-----------------------------------------------------------------------------%
 
-:- pred try_exec(cmd::in, deep::in, string::out) is cc_multi.
+:- pred try_exec(cmd::in, preferences::in, deep::in, string::out,
+	io__state::di, io__state::uo) is cc_multi.
 
-try_exec(Cmd, Deep, HTML) :-
-	try(exec(Cmd, Deep), Result),
+try_exec(Cmd, Pref, Deep, HTML, IO0, IO) :-
+	try_io(exec(Cmd, Pref, Deep), Result, IO0, IO),
 	(
 		Result = succeeded(HTML)
 	;
 		Result = exception(Exception),
 		( univ_to_type(Exception, MsgPrime) ->
 			Msg = MsgPrime
+		; univ_to_type(Exception, software_error(MsgPrime)) ->
+			Msg = MsgPrime
 		;
 			Msg = "unknown exception"
 		),
 		HTML =
 			string__format(
-				"<H1>AN EXCEPTION HAS OCCURRED: %s.</H1>\n",
+				"<H3>AN EXCEPTION HAS OCCURRED: %s</H3>\n",
 				[s(Msg)])
 	).
-
-:- pred exec(cmd::in, deep::in, string::out) is det.
-
-exec(Cmd, Deep, HTML) :-
-	Cmd = quit,
-	HTML =
-		string__format(
-			"<H1>Shutting down deep profile server for %s.</H1>\n",
-			[s(Deep ^ data_file_name)]).
-
-exec(Cmd, _Deep, HTML) :-
-	Cmd = timeout(TimeOut),
-	HTML = string__format("<H1>Timeout set to %d minutes</H1>\n",
-		[i(TimeOut)]).
-
-exec(Cmd, Deep, HTML) :-
-	Cmd = menu,
-	HTML =
-		banner ++
-		"<p>\n" ++
-		menu_text ++
-		"<ul>\n" ++
-		"<li>\n" ++
-		menu_item(Deep, root(default_fields),
-			"Exploring the call graph.") ++
-		"<li>\n" ++
-		menu_item(Deep, top_procs(time, self,
-			rank_range(1, 100), default_fields),
-			"Top 100 most expensive procedures: time, self.") ++
-		"<li>\n" ++
-		menu_item(Deep, top_procs(time, self_and_desc,
-			rank_range(1, 100), default_fields),
-			"Top 100 most expensive procedures: time, self+desc.")
-			++
-		"<li>\n" ++
-		menu_item(Deep, top_procs(words, self,
-			rank_range(1, 100), default_fields),
-			"Top 100 most expensive procedures: words, self.") ++
-		"<li>\n" ++
-		menu_item(Deep, top_procs(words, self_and_desc,
-			rank_range(1, 100), default_fields),
-			"Top 100 most expensive procedures: words, self+desc.")
-			++
-		"<li>\n" ++
-		menu_item(Deep, top_procs(time, self,
-			threshold(0.1), default_fields),
-			"Procedures above 0.1% threshold: time, self.") ++
-		"<li>\n" ++
-		menu_item(Deep, top_procs(time, self_and_desc,
-			threshold(1.0), default_fields),
-			"Procedures above 1% threshold: time, self+desc.")
-			++
-		"<li>\n" ++
-		menu_item(Deep, top_procs(words, self,
-			threshold(0.1), default_fields),
-			"Procedures above 0.1% threshold: words, self.") ++
-		"<li>\n" ++
-		menu_item(Deep, top_procs(words, self_and_desc,
-			threshold(1.0), default_fields),
-			"Procedures above 1% threshold: words, self+desc.")
-			++
-		"</ul>\n" ++
-		"<p>\n" ++
-		present_stats(Deep) ++
-		footer(Cmd, Deep).
-
-exec(Cmd, Deep, HTML) :-
-	Cmd = root(Fields),
-	deep_lookup_clique_index(Deep, Deep ^ root, RootCliquePtr),
-	RootCliquePtr = clique_ptr(RootCliqueNum),
-	exec(clique(RootCliqueNum, Fields, default_ancestor_limit),
-		Deep, HTML).
-
-exec(Cmd, Deep, HTML) :-
-	Cmd = clique(CliqueNum, Fields, MaybeAncestorLimit),
-	( valid_clique_ptr(Deep, clique_ptr(CliqueNum)) ->
-		HTML =
-			banner ++
-			"<TABLE border=1 rules=cols frame=void>\n" ++
-			fields_header(Fields) ++
-			clique_to_html(Deep, Fields, MaybeAncestorLimit,
-				clique_ptr(CliqueNum)) ++
-			"</TABLE>\n" ++
-			footer(Cmd, Deep)
-	;
-		HTML =
-			banner ++
-			"There is no clique with that number.\n" ++
-			footer(Cmd, Deep)
-	).
-
-exec(Cmd, Deep, HTML) :-
-	Cmd = top_procs(Sort, InclDesc, Limit, Fields),
-	find_top_procs(Sort, InclDesc, Limit, Deep, MaybeTopPSIs),
-	(
-		MaybeTopPSIs = error(ErrorMessage),
-		HTML =
-			banner ++
-			ErrorMessage ++ "\n" ++
-			footer(Cmd, Deep)
-	;
-		MaybeTopPSIs = ok(TopPSIs),
-		( TopPSIs = [] ->
-			HTML =
-				banner ++
-				"No procedures match the specification.\n" ++
-				footer(Cmd, Deep)
-		;
-			TopProcSummaries = list__map(
-				proc_total_summary_to_html(Deep, Fields),
-				TopPSIs),
-			HTML =
-				banner ++
-				"<TABLE>\n" ++
-				fields_header(Fields) ++
-				string__append_list(TopProcSummaries) ++
-				"</TABLE>\n" ++
-				footer(Cmd, Deep)
-		)
-	).
-
-exec(Cmd, Deep, HTML) :-
-	Cmd = proc(PSI, Fields),
-	HTML =
-		"<HTML>\n" ++
-		banner ++
-		"<TABLE>\n" ++
-		fields_header(Fields) ++
-		proc_summary_to_html(Deep, Fields, PSI) ++
-		"</TABLE>\n" ++
-		footer(Cmd, Deep).
-
-exec(Cmd, Deep, HTML) :-
-	Cmd = proc_static(PSI),
-	PSPtr = proc_static_ptr(PSI),
-	( valid_proc_static_ptr(Deep, PSPtr) ->
-		deep_lookup_proc_statics(Deep, PSPtr, PS),
-		Refined = PS ^ ps_refined_id,
-		Raw = PS ^ ps_raw_id,
-		FileName = PS ^ ps_filename,
-		HTML =
-			"<HTML>\n" ++
-			Refined ++ " " ++ Raw ++ " " ++ FileName ++ " " ++
-			string__int_to_string(array__max(PS ^ ps_sites)) ++
-			"</HTML>\n"
-	;
-		HTML =
-			"<HTML>\n" ++
-			"Invalid proc_static_ptr" ++
-			"</HTML>\n"
-	).
-
-exec(Cmd, Deep, HTML) :-
-	Cmd = proc_dynamic(PDI),
-	PDPtr = proc_dynamic_ptr(PDI),
-	( valid_proc_dynamic_ptr(Deep, PDPtr) ->
-		deep_lookup_proc_dynamics(Deep, PDPtr, PD),
-		PSPtr = PD ^ pd_proc_static,
-		PSPtr = proc_static_ptr(PSI),
-		HTML =
-			"<HTML>\n" ++
-			string__format("proc_static %d, ", [i(PSI)]) ++
-			array_slots_to_html(PD ^ pd_sites) ++
-			"</HTML>\n"
-	;
-		HTML =
-			"<HTML>\n" ++
-			"Invalid proc_dynamic_ptr" ++
-			"</HTML>\n"
-	).
-
-exec(Cmd, Deep, HTML) :-
-	Cmd = call_site_static(CSSI),
-	CSSPtr = call_site_static_ptr(CSSI),
-	( valid_call_site_static_ptr(Deep, CSSPtr) ->
-		deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
-		ContainerPtr = CSS ^ css_container,
-		ContainerPtr = proc_static_ptr(Container),
-		HTML =
-			"<HTML>\n" ++
-			string__int_to_string(Container) ++ " " ++
-			string__int_to_string(CSS ^ css_slot_num) ++ " " ++
-			string__int_to_string(CSS ^ css_line_num) ++ " " ++
-			kind_and_callee_to_string(CSS ^ css_kind) ++ " " ++
-			CSS ^ css_goal_path ++
-			"</HTML>\n"
-	;
-		HTML =
-			"<HTML>\n" ++
-			"Invalid call_site_static_ptr" ++
-			"</HTML>\n"
-	).
-
-exec(Cmd, Deep, HTML) :-
-	Cmd = call_site_dynamic(CSDI),
-	CSDPtr = call_site_dynamic_ptr(CSDI),
-	( valid_call_site_dynamic_ptr(Deep, CSDPtr) ->
-		deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
-		CSD ^ csd_caller = proc_dynamic_ptr(CallerPDI),
-		CSD ^ csd_callee = proc_dynamic_ptr(CalleePDI),
-		HTML =
-			"<HTML>\n" ++
-			string__int_to_string(CallerPDI) ++ " -> " ++
-			string__int_to_string(CalleePDI) ++ ": " ++
-			own_to_string(CSD ^ csd_own_prof) ++
-			"</HTML>\n"
-	;
-		HTML =
-			"<HTML>\n" ++
-			"Invalid call_site_dynamic_ptr" ++
-			"</HTML>\n"
-	).
-
-exec(Cmd, Deep, HTML) :-
-	Cmd = raw_clique(CI),
-	CliquePtr = clique_ptr(CI),
-	( valid_clique_ptr(Deep, CliquePtr) ->
-		deep_lookup_clique_parents(Deep, CliquePtr, Parent),
-		Parent = call_site_dynamic_ptr(ParentPDI),
-		ParentStr = string__format("%d ->", [i(ParentPDI)]),
-		deep_lookup_clique_members(Deep, CliquePtr, Members),
-		HTML =
-			"<HTML>\n" ++
-			ParentStr ++
-			list__foldl(append_pdi_to_string, Members, "") ++
-			"</HTML>\n"
-	;
-		HTML =
-			"<HTML>\n" ++
-			"Invalid call_site_dynamic_ptr" ++
-			"</HTML>\n"
-	).
-
-exec(Cmd, Deep, HTML) :-
-	Cmd = num_proc_dynamics,
-	HTML =
-		"<HTML>\n" ++
-		string__int_to_string(Deep ^ profile_stats ^ max_pd) ++
-		"</HTML>\n".
-
-exec(Cmd, Deep, HTML) :-
-	Cmd = num_call_site_dynamics,
-	HTML =
-		"<HTML>\n" ++
-		string__int_to_string(Deep ^ profile_stats ^ max_csd) ++
-		"</HTML>\n".
-
-exec(Cmd, Deep, HTML) :-
-	Cmd = num_proc_statics,
-	HTML =
-		"<HTML>\n" ++
-		string__int_to_string(Deep ^ profile_stats ^ max_ps) ++
-		"</HTML>\n".
-
-exec(Cmd, Deep, HTML) :-
-	Cmd = num_call_site_statics,
-	HTML =
-		"<HTML>\n" ++
-		string__int_to_string(Deep ^ profile_stats ^ max_css) ++
-		"</HTML>\n".
-
-%-----------------------------------------------------------------------------%
-
-:- func array_slots_to_html(array(call_site_array_slot)) = string.
-
-array_slots_to_html(SlotArray) = HTML :-
-	array__to_list(SlotArray, SlotList),
-	list__foldl(append_slot_to_string, SlotList, "multi", HTML).
-
-:- pred append_slot_to_string(call_site_array_slot::in,
-	string::in, string::out) is det.
-
-append_slot_to_string(Slot, Str0, Str) :-
-	Str = Str0 ++ " " ++ array_slot_to_html(Slot).
-
-:- func array_slot_to_html(call_site_array_slot) = string.
-
-array_slot_to_html(normal(CSDPtr)) = HTML :-
-	CSDPtr = call_site_dynamic_ptr(CSDI),
-	HTML = "normal " ++ string__int_to_string(CSDI).
-array_slot_to_html(multi(CSDPtrArray)) = HTML :-
-	array__to_list(CSDPtrArray, CSDPtrs),
-	list__foldl(append_csdi_to_string, CSDPtrs, "", CSDI_HTML),
-	list__length(CSDPtrs, CSDPtrCount),
-	HTML = string__format("multi(%d): [", [i(CSDPtrCount)])
-		++ CSDI_HTML ++ "]".
-
-:- pred append_csdi_to_string(call_site_dynamic_ptr::in,
-	string::in, string::out) is det.
-
-append_csdi_to_string(call_site_dynamic_ptr(CSDI), Str0, Str) :-
-	Str = Str0 ++ " " ++ string__int_to_string(CSDI).
-
-:- func append_pdi_to_string(proc_dynamic_ptr, string) = string.
-
-append_pdi_to_string(proc_dynamic_ptr(PDI), Str0) =
-	Str0 ++ " " ++ string__int_to_string(PDI).
-
-%-----------------------------------------------------------------------------%
-
-:- func kind_and_callee_to_string(call_site_kind_and_callee) = string.
-
-kind_and_callee_to_string(normal_call(proc_static_ptr(PSI), TypeSpec)) =
-	"normal " ++ string__int_to_string(PSI) ++ " " ++ TypeSpec.
-kind_and_callee_to_string(special_call) = "special_call".
-kind_and_callee_to_string(higher_order_call) = "higher_order_call".
-kind_and_callee_to_string(method_call) = "method_call".
-kind_and_callee_to_string(callback) = "callback".
-
-:- func present_stats(deep) = string.
-
-present_stats(Deep) = HTML :-
-	Stats = Deep ^ profile_stats,
-	lookup_ticks_per_sec(Stats, TicksPerSec, Assumed),
-	(
-		Assumed = yes,
-		AssumedStr = " (assumed)"
-	;
-		Assumed = no,
-		AssumedStr = ""
-	),
-	HTML =
-		"<TABLE>\n" ++
-		"<TR><TD ALIGN=left>Quanta per second:</TD>\n" ++
-		string__format("<TD ALIGN=right>%d%s</TD></TR>\n",
-			[i(TicksPerSec), s(AssumedStr)]) ++
-		"<TR><TD ALIGN=left>Quanta in user code:</TD>\n" ++
-		string__format("<TD ALIGN=right>%d</TD></TR>\n",
-			[i(Stats ^ user_quanta)]) ++
-		"<TR><TD ALIGN=left>Quanta in instrumentation:</TD>\n" ++
-		string__format("<TD ALIGN=right>%d</TD></TR>\n",
-			[i(Stats ^ instrument_quanta)]) ++
-		"<TR><TD ALIGN=left>CallSiteDynamic structures:</TD>\n" ++
-		string__format("<TD ALIGN=right>%d</TD></TR>\n",
-			[i(Stats ^ max_csd)]) ++
-		"<TR><TD ALIGN=left>ProcDynamic structures:</TD>\n" ++
-		string__format("<TD ALIGN=right>%d</TD></TR>\n",
-			[i(Stats ^ max_pd)]) ++
-		"<TR><TD ALIGN=left>CallSiteStatic structures:</TD>\n" ++
-		string__format("<TD ALIGN=right>%d</TD></TR>\n",
-			[i(Stats ^ max_css)]) ++
-		"<TR><TD ALIGN=left>ProcStatic structures:</TD>\n" ++
-		string__format("<TD ALIGN=right>%d</TD></TR>\n",
-			[i(Stats ^ max_ps)]) ++
-		"<TR><TD ALIGN=left>Cliques:</TD>\n" ++
-		string__format("<TD ALIGN=right>%d</TD></TR>\n",
-			[i(array__max(Deep ^ clique_members))]) ++
-		"</TABLE>\n".
-
-%-----------------------------------------------------------------------------%
-
-:- func clique_to_html(deep, fields, maybe(int), clique_ptr) = string.
-
-clique_to_html(Deep, Fields, MaybeAncestorLimit, CliquePtr) = HTML :-
-	(
-		MaybeAncestorLimit = yes(AncestorLimit),
-		RespectLimit = yes
-	;
-		MaybeAncestorLimit = no,
-		AncestorLimit = 0, % the value doesn't matter
-		RespectLimit = no
-	),
-	Ancestors = clique_ancestors_to_html(Deep, Fields, MaybeAncestorLimit,
-		AncestorLimit, RespectLimit, CliquePtr),
-	deep_lookup_clique_members(Deep, CliquePtr, PDPtrs),
-	list__foldl(group_proc_dynamics_by_proc_static(Deep), PDPtrs,
-		map__init, PStoPDsMap),
-	map__to_assoc_list(PStoPDsMap, PStoPDsList0),
-
-	deep_lookup_clique_parents(Deep, CliquePtr, EntryCSDPtr),
-	( valid_call_site_dynamic_ptr(Deep, EntryCSDPtr) ->
-		deep_lookup_call_site_dynamics(Deep, EntryCSDPtr, EntryCSD),
-		EntryPDPtr = EntryCSD ^ csd_callee,
-		list__filter(proc_group_contains(EntryPDPtr), PStoPDsList0,
-			EntryGroup, RestGroup),
-		list__append(EntryGroup, RestGroup, PStoPDsList)
-	;
-		PStoPDsList = PStoPDsList0
-	),
-
-	PDsStrs = list__map(procs_in_clique_to_html(Deep, Fields,
-		MaybeAncestorLimit, CliquePtr), PStoPDsList),
-	string__append_list(PDsStrs, ProcGroups),
-	HTML =
-		Ancestors ++
-		"<a name=""body"">\n" ++
-		ProcGroups ++
-		"</a>".
-
-:- pred proc_group_contains(proc_dynamic_ptr::in,
-	pair(proc_static_ptr, list(proc_dynamic_ptr))::in) is semidet.
-
-proc_group_contains(EntryPDPtr, _ - PDPtrs) :-
-	list__member(EntryPDPtr, PDPtrs).
-
-:- func clique_ancestors_to_html(deep, fields, maybe(int), int, bool,
-	clique_ptr) = string.
-
-clique_ancestors_to_html(Deep, Fields, OrigMaybeAncestorLimit,
-		AncestorLimit, RespectLimit, CliquePtr) = HTML :-
-	deep_lookup_clique_index(Deep, Deep ^ root, RootCliquePtr),
-	( CliquePtr = RootCliquePtr ->
-		HTML = ""
-	; RespectLimit = yes, AncestorLimit =< 0 ->
-		HTML = "<TR><TD>..."
-	;
-		deep_lookup_clique_parents(Deep, CliquePtr, EntryCSDPtr),
-		ThisHTML = call_site_dynamic_to_html(Deep, Fields,
-			OrigMaybeAncestorLimit, call_site_line_number,
-			no, EntryCSDPtr),
-		deep_lookup_call_site_dynamics(Deep, EntryCSDPtr, EntryCSD),
-		EntryPDPtr = EntryCSD ^ csd_caller,
-		require(valid_proc_dynamic_ptr(Deep, EntryPDPtr),
-			"clique_ancestors_to_html: invalid proc_dynamic"),
-		deep_lookup_clique_index(Deep, EntryPDPtr, EntryCliquePtr),
-		AncestorHTML = clique_ancestors_to_html(Deep, Fields,
-			OrigMaybeAncestorLimit,
-			AncestorLimit - 1, RespectLimit, EntryCliquePtr),
-		HTML =
-			AncestorHTML ++
-			ThisHTML
-	).
-
-:- pred group_proc_dynamics_by_proc_static(deep::in, proc_dynamic_ptr::in,
-	map(proc_static_ptr, list(proc_dynamic_ptr))::in,
-	map(proc_static_ptr, list(proc_dynamic_ptr))::out) is det.
-
-group_proc_dynamics_by_proc_static(Deep, PDPtr, PStoPDsMap0, PStoPDsMap) :-
-	require(valid_proc_dynamic_ptr(Deep, PDPtr),
-		"group_proc_dynamics_by_proc_static: invalid PDPtr"),
-	deep_lookup_proc_dynamics(Deep, PDPtr, PD),
-	PSPtr = PD ^ pd_proc_static,
-	( map__search(PStoPDsMap0, PSPtr, PSPDs0) ->
-		PSPDs = [PDPtr | PSPDs0],
-		map__det_update(PStoPDsMap0, PSPtr, PSPDs, PStoPDsMap)
-	;
-		map__det_insert(PStoPDsMap0, PSPtr, [PDPtr], PStoPDsMap)
-	).
-
-:- func procs_in_clique_to_html(deep, fields, maybe(int), clique_ptr,
-	pair(proc_static_ptr, list(proc_dynamic_ptr))) = string.
-
-procs_in_clique_to_html(Deep, Fields, OrigMaybeAncestorLimit, CliquePtr,
-		PSPtr - PDPtrs) = HTML :-
-	( PDPtrs = [] ->
-		HTML = ""
-	; PDPtrs = [PDPtr] ->
-		HTML = proc_in_clique_to_html(Deep, Fields,
-			OrigMaybeAncestorLimit, CliquePtr, PDPtr)
-	;
-		Separator = separator_row(Fields),
-		list__map(deep_lookup_pd_own(Deep), PDPtrs, ProcOwns),
-		list__map(deep_lookup_pd_desc(Deep), PDPtrs, ProcDescs),
-		ProcOwn = sum_own_infos(ProcOwns),
-		ProcDesc = sum_inherit_infos(ProcDescs),
-		ProcTotal = proc_total_in_clique(Deep, Fields,
-			PSPtr, no, ProcOwn, ProcDesc),
-		ComponentHTMLs = list__map(proc_in_clique_to_html(Deep, Fields,
-			OrigMaybeAncestorLimit, CliquePtr), PDPtrs),
-		string__append_list(ComponentHTMLs, ComponentHTML),
-		HTML =
-			Separator ++
-			ProcTotal ++
-			Separator ++
-			ComponentHTML
-	).
-
-:- func proc_in_clique_to_html(deep, fields, maybe(int), clique_ptr,
-	proc_dynamic_ptr) = string.
-
-proc_in_clique_to_html(Deep, Fields, OrigMaybeAncestorLimit, CliquePtr, PDPtr)
-		= HTML :-
-	( valid_proc_dynamic_ptr(Deep, PDPtr) ->
-		InitialSeparator = separator_row(Fields),
-		deep_lookup_pd_own(Deep, PDPtr, ProcOwn),
-		deep_lookup_pd_desc(Deep, PDPtr, ProcDesc),
-		deep_lookup_proc_dynamics(Deep, PDPtr, PD),
-		PSPtr = PD ^ pd_proc_static,
-		ProcTotal = proc_total_in_clique(Deep, Fields,
-			PSPtr, yes, ProcOwn, ProcDesc),
-		child_call_sites(Deep ^ proc_dynamics, Deep ^ proc_statics,
-			PDPtr, GroupPairs),
-		list__foldl(call_site_group_to_html(Deep, Fields,
-			OrigMaybeAncestorLimit, CliquePtr),
-			GroupPairs, map__init, GroupMap),
-		map__to_assoc_list(GroupMap, GroupPairLists),
-		assoc_list__values(GroupPairLists, GroupLists),
-		list__condense(GroupLists, GroupList),
-		string__append_list(GroupList, GroupStr0),
-		( GroupList = [] ->
-			GroupStr = GroupStr0
-		;
-			GroupStr = separator_row(Fields) ++ GroupStr0
-		),
-		HTML =
-			InitialSeparator ++
-			ProcTotal ++
-			GroupStr
-	;
-		HTML = ""
-	).
-
-:- pred child_call_sites(proc_dynamics::in, proc_statics::in,
-	proc_dynamic_ptr::in,
-	assoc_list(call_site_static_ptr, call_site_array_slot)::out) is det.
-
-child_call_sites(ProcDynamics, ProcStatics, PDPtr, PairedSlots) :-
-	lookup_proc_dynamics(ProcDynamics, PDPtr, PD),
-	PSPtr = PD ^ pd_proc_static,
-	CSDArray = PD ^ pd_sites,
-	lookup_proc_statics(ProcStatics, PSPtr, PS),
-	CSSArray = PS ^ ps_sites,
-	array__to_list(CSDArray, CSDSlots),
-	array__to_list(CSSArray, CSSSlots),
-	assoc_list__from_corresponding_lists(CSSSlots, CSDSlots, PairedSlots).
-
-:- func proc_total_in_clique(deep, fields, proc_static_ptr, bool,
-	own_prof_info, inherit_prof_info) = string.
-
-proc_total_in_clique(Deep, Fields, PSPtr, Only, Own, Desc) = HTML :-
-	ProcName = proc_static_to_html_ref(Deep, Fields, PSPtr),
-	(
-		Only = no,
-		OnlyStr = "summary "
-	;
-		Only = yes,
-		OnlyStr = ""
-	),
-	HTML = 
-		"<TR>\n" ++
-		string__format("<TD COLSPAN=2><B>%s%s</B></TD>\n",
-			[s(OnlyStr), s(ProcName)]) ++
-		own_and_desc_to_html(Own, Desc, Deep, Fields) ++
-		"</TR>\n".
-
-:- pred call_site_group_to_html(deep::in, fields::in, maybe(int)::in,
-	clique_ptr::in, pair(call_site_static_ptr, call_site_array_slot)::in,
-	map(pair(string, int), list(string))::in,
-	map(pair(string, int), list(string))::out) is det.
-
-call_site_group_to_html(Deep, Fields, OrigMaybeAncestorLimit, ThisCliquePtr,
-		Pair, GroupMap0, GroupMap) :-
-	Pair = CSSPtr - CallSiteArray,
-	deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
-	CSS = call_site_static(PSPtr, _SlotNum, Kind, LineNumber, _GoalPath),
-	deep_lookup_proc_statics(Deep, PSPtr, PS),
-	FileName = PS ^ ps_filename,
-	( Kind = normal_call(_CalleePSPtr, _) ->
-		( CallSiteArray = normal(CSDPtr0) ->
-			CSDPtr = CSDPtr0
-		;
-			error("call_site_group_to_html: normal_call error")
-		),
-		HTML = maybe_call_site_dynamic_to_html(Deep, Fields,
-			OrigMaybeAncestorLimit, call_site_line_number,
-			ThisCliquePtr, CSDPtr)
-	;
-		( CallSiteArray = multi(CSDPtrs0) ->
-			array__to_list(CSDPtrs0, CSDPtrs)
-		;
-			error("call_site_group_to_html: non-normal_call error")
-		),
-		Tuple0 = { "", zero_own_prof_info, zero_inherit_prof_info },
-		Tuple = list__foldl(call_site_array_to_html(Deep, Fields,
-			OrigMaybeAncestorLimit, no_call_site_line_number,
-			ThisCliquePtr), CSDPtrs, Tuple0),
-		Tuple = { GroupHTML, SumOwn, SumDesc },
-		CallSiteName0 = call_site_kind_and_callee_to_html(Kind),
-		( GroupHTML = "" ->
-			CallSiteName = CallSiteName0 ++ " (no calls made)"
-		;
-			CallSiteName = CallSiteName0
-		),
-		HTML =
-			"<TR>\n" ++
-			string__format("<TD>%s:%d</TD>\n",
-				[s(FileName), i(LineNumber)]) ++
-			string__format("<TD>%s</TD>\n", [s(CallSiteName)]) ++
-			own_and_desc_to_html(SumOwn, SumDesc, Deep, Fields) ++
-			"</TR>\n" ++
-			GroupHTML
-	),
-	Key = FileName - LineNumber,
-	( map__search(GroupMap0, Key, HTMLs0) ->
-		map__det_update(GroupMap0, Key, [HTML | HTMLs0], GroupMap)
-	;
-		map__det_insert(GroupMap0, Key, [HTML], GroupMap)
-	).
-
-:- func call_site_array_to_html(deep, fields, maybe(int),
-	call_site_line_number, clique_ptr, call_site_dynamic_ptr,
-	{string, own_prof_info, inherit_prof_info}) =
-	{string, own_prof_info, inherit_prof_info}.
-
-call_site_array_to_html(Deep, Fields, OrigMaybeAncestorLimit,
-		PrintCallSiteLineNmber, ThisCliquePtr, CSDPtr, Tuple0)
-		= Tuple :-
-	( valid_call_site_dynamic_ptr(Deep, CSDPtr) ->
-		Tuple0 = { HTML0, Own0, Desc0 },
-		HTML1 = call_site_dynamic_to_html(Deep, Fields,
-			OrigMaybeAncestorLimit, PrintCallSiteLineNmber,
-			yes(ThisCliquePtr), CSDPtr),
-		string__append(HTML0, HTML1, HTML),
-		deep_lookup_csd_own(Deep, CSDPtr, CallSiteOwn),
-		deep_lookup_csd_desc(Deep, CSDPtr, CallSiteDesc),
-		Own = add_own_to_own(Own0, CallSiteOwn),
-		Desc = add_inherit_to_inherit(Desc0, CallSiteDesc),
-		Tuple = { HTML, Own, Desc }
-	;
-		Tuple = Tuple0
-	).
-
-:- pred process_call_site_dynamics_group(list(call_site_dynamic_ptr)::in,
-	deep::in, proc_static_ptr::in,
-	maybe(clique_ptr)::in, maybe(clique_ptr)::out,
-	own_prof_info::in, own_prof_info::out,
-	inherit_prof_info::in, inherit_prof_info::out) is det.
-
-process_call_site_dynamics_group([], _, _, MaybeToCliquePtr, MaybeToCliquePtr,
-		Own, Own, Desc, Desc).
-process_call_site_dynamics_group([CSDPtr | CSDPtrs], Deep, CalleePSPtr,
-		MaybeToCliquePtr0, MaybeToCliquePtr, Own0, Own, Desc0, Desc) :-
-	deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
-	PDPtr = CSD ^ csd_callee,
-	deep_lookup_proc_dynamics(Deep, PDPtr, PD),
-	PSPtr = PD ^ pd_proc_static,
-	require(unify(CalleePSPtr, PSPtr),
-		"process_call_site_dynamics_group: callee mismatch"),
-	deep_lookup_clique_index(Deep, PDPtr, ToCliquePtr),
-	(
-		MaybeToCliquePtr0 = no,
-		MaybeToCliquePtr1 = yes(ToCliquePtr)
-	;
-		MaybeToCliquePtr0 = yes(PrevToCliquePtr),
-		MaybeToCliquePtr1 = MaybeToCliquePtr0,
-		require(unify(PrevToCliquePtr, ToCliquePtr),
-			"process_call_site_dynamics_group: clique mismatch")
-	),
-	deep_lookup_csd_own(Deep, CSDPtr, CSDOwn),
-	deep_lookup_csd_desc(Deep, CSDPtr, CSDDesc),
-	Own1 = add_own_to_own(Own0, CSDOwn),
-	Desc1 = add_inherit_to_inherit(Desc0, CSDDesc),
-	process_call_site_dynamics_group(CSDPtrs, Deep, CalleePSPtr,
-		MaybeToCliquePtr1, MaybeToCliquePtr, Own1, Own, Desc1, Desc).
-
-:- func call_site_dynamics_to_html(deep, fields, maybe(int),
-	maybe(pair(string, int)), clique_ptr, clique_ptr, proc_static_ptr,
-	own_prof_info, inherit_prof_info) = string.
-
-call_site_dynamics_to_html(Deep, Fields, OrigMaybeAncestorLimit,
-		MaybeFileNameLineNumber, ThisCliquePtr, ToCliquePtr,
-		PSPtr, Own, Desc) = HTML :-
-	deep_lookup_proc_statics(Deep, PSPtr, PS),
-	CalleeName = PS ^ ps_refined_id,
-	( ThisCliquePtr = ToCliquePtr ->
-		% We don't link recursive calls
-		ProcName = CalleeName
-	;
-		ToCliquePtr = clique_ptr(ToCliqueNum),
-		ToCliqueURL = deep_cmd_to_url(Deep,
-			clique(ToCliqueNum, Fields, OrigMaybeAncestorLimit)),
-		ProcName =
-			string__format("<A HREF=""%s"">%s</A>\n",
-				[s(ToCliqueURL), s(CalleeName)])
-	),
-	( MaybeFileNameLineNumber = yes(FileName - LineNumber) ->
-		SourceField =
-			string__format("<TD>%s:%d</TD>\n",
-				[s(FileName), i(LineNumber)])
-	;
-		SourceField = "<TD> </TD>\n"
-	),
-	HTML =
-		"<TR>\n" ++
-		SourceField ++
-		string__format("<TD>%s</TD>\n", [s(ProcName)]) ++
-		own_and_desc_to_html(Own, Desc, Deep, Fields) ++
-		"</TR>\n".
-
-:- func maybe_call_site_dynamic_to_html(deep, fields, maybe(int),
-	call_site_line_number, clique_ptr, call_site_dynamic_ptr) = string.
-
-maybe_call_site_dynamic_to_html(Deep, Fields, OrigMaybeAncestorLimit,
-		PrintCallSiteLineNmber, ThisCliquePtr, CSDPtr) = HTML :-
-	( valid_call_site_dynamic_ptr(Deep, CSDPtr) ->
-		HTML = call_site_dynamic_to_html(Deep, Fields,
-			OrigMaybeAncestorLimit, PrintCallSiteLineNmber,
-			yes(ThisCliquePtr), CSDPtr)
-	;
-		HTML = ""
-	).
-
-:- func call_site_dynamic_to_html(deep, fields, maybe(int),
-	call_site_line_number, maybe(clique_ptr), call_site_dynamic_ptr)
-	= string.
-
-call_site_dynamic_to_html(Deep, Fields, OrigMaybeAncestorLimit,
-		PrintCallSiteLineNmber, MaybeThisCliquePtr, CSDPtr) = HTML :-
-	require(valid_call_site_dynamic_ptr(Deep, CSDPtr),
-		"call_site_dynamic_to_html: invalid call_site_dynamic_ptr"),
-	deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
-	ToProcPtr = CSD ^ csd_callee,
-	CallSiteOwn = CSD ^ csd_own_prof,
-	deep_lookup_csd_desc(Deep, CSDPtr, CallSiteDesc),
-	( valid_proc_dynamic_ptr(Deep, ToProcPtr) ->
-		deep_lookup_clique_index(Deep, ToProcPtr, ToCliquePtr),
-		CalleeName = call_site_dynamic_label(Deep, CSDPtr),
-		(
-			MaybeThisCliquePtr = yes(ThisCliquePtr),
-			ThisCliquePtr = ToCliquePtr
-		->
-			% We don't link recursive calls
-			ProcName = CalleeName
-		;
-			ToCliquePtr = clique_ptr(ToCliqueNum),
-			ToCliqueURL = deep_cmd_to_url(Deep,
-				clique(ToCliqueNum, Fields,
-					OrigMaybeAncestorLimit)),
-			ProcName =
-				string__format("<A HREF=""%s"">%s</A>\n",
-					[s(ToCliqueURL), s(CalleeName)])
-		)
-	;
-		ProcName = "builtin special procedure"
-	),
-	( PrintCallSiteLineNmber = call_site_line_number ->
-		deep_lookup_call_site_static_map(Deep, CSDPtr, CSSPtr),
-		deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
-		CSS = call_site_static(PSPtr, _, _, LineNumber, _),
-		deep_lookup_proc_statics(Deep, PSPtr, PS),
-		SourceField =
-			string__format("<TD>%s:%d</TD>\n",
-				[s(PS ^ ps_filename), i(LineNumber)])
-	;
-		SourceField = "<TD> </TD>\n"
-	),
-	HTML =
-		"<TR>\n" ++
-		SourceField ++
-		string__format("<TD>%s</TD>\n", [s(ProcName)]) ++
-		own_and_desc_to_html(CallSiteOwn, CallSiteDesc,
-			Deep, Fields) ++
-		"</TR>\n".
-
-%-----------------------------------------------------------------------------%
-
-:- func proc_summary_to_html(deep, string, int) = string.
-
-proc_summary_to_html(Deep, Fields, PSI) = HTML :-
-	deep_lookup_proc_statics(Deep, proc_static_ptr(PSI), PS),
-	CSSPtrsArray = PS ^ ps_sites,
-	array__to_list(CSSPtrsArray, CSSPtrs),
-	CallSiteSummaryList =
-		list__map(call_site_summary_to_html(Deep, Fields), CSSPtrs),
-	string__append_list(CallSiteSummaryList, CallSiteSummaries),
-	HTML =
-		proc_total_summary_to_html(Deep, Fields, PSI) ++
-		CallSiteSummaries.
-
-:- func proc_total_summary_to_html(deep, string, int) = string.
-
-proc_total_summary_to_html(Deep, Fields, PSI) = HTML :-
-	PSPtr = proc_static_ptr(PSI),
-	deep_lookup_ps_own(Deep, PSPtr, Own),
-	deep_lookup_ps_desc(Deep, PSPtr, Desc),
-	HTML =
-		"<TR>\n" ++
-		string__format("<TD COLSPAN=2>%s</TD>\n",
-			[s(proc_static_to_html_ref(Deep, Fields,
-				proc_static_ptr(PSI)))]) ++
-		own_and_desc_to_html(Own, Desc, Deep, Fields) ++
-		"</TR>\n".
-
-%-----------------------------------------------------------------------------%
-
-:- func call_site_summary_to_html(deep, string, call_site_static_ptr) = string.
-
-call_site_summary_to_html(Deep, Fields, CSSPtr) = HTML :-
-	deep_lookup_css_own(Deep, CSSPtr, Own),
-	deep_lookup_css_desc(Deep, CSSPtr, Desc),
-	deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
-	CSS = call_site_static(PSPtr, _, Kind, LineNumber, _GoalPath),
-	deep_lookup_proc_statics(Deep, PSPtr, PS),
-	FileName = PS ^ ps_filename,
-	deep_lookup_call_site_calls(Deep, CSSPtr, CallSiteCallMap),
-	map__to_assoc_list(CallSiteCallMap, CallSiteCallList),
-	( Kind = normal_call(CalleePSPtr, _) ->
-		( CallSiteCallList = [] ->
-			deep_lookup_proc_statics(Deep, CalleePSPtr, CalleePS)
-		; CallSiteCallList = [CallSiteCall] ->
-			CallSiteCall = CalleePSPtr2 - _CallSet,
-			require(unify(CalleePSPtr, CalleePSPtr2),
-				"call_site_summary_to_html: callee mismatch"),
-			deep_lookup_proc_statics(Deep, CalleePSPtr, CalleePS)
-		;
-			error("normal call site calls more than one procedure")
-		),
-		MainLineRest =
-			string__format("<TD>%s</TD>\n",
-				[s(CalleePS ^ ps_refined_id)]) ++
-			own_and_desc_to_html(Own, Desc, Deep, Fields),
-		AdditionalLines = ""
-	;
-		CallSiteName0 = call_site_kind_and_callee_to_html(Kind),
-		( CallSiteCallList = [] ->
-			CallSiteName = CallSiteName0 ++
-				" (no&nbps;calls&nbps;made)"
-		;
-			CallSiteName = CallSiteName0
-		),
-		MainLineRest =
-			string__format("<TD>%s</TD>\n",
-				[s(CallSiteName)]) ++
-			own_and_desc_to_html(Own, Desc, Deep, Fields),
-		CallSiteCallLines = list__map(
-			call_site_summary_group_to_html(Deep, Fields),
-			CallSiteCallList),
-		string__append_list(CallSiteCallLines, AdditionalLines)
-	),
-	HTML =
-		"<TR>\n" ++
-		string__format("<TD>%s:%d</TD>\n",
-			[s(FileName), i(LineNumber)]) ++
-		MainLineRest ++
-		"</TR>\n" ++
-		AdditionalLines.
-
-:- func call_site_kind_and_callee_to_html(call_site_kind_and_callee) = string.
-
-call_site_kind_and_callee_to_html(normal_call(_, _)) = "normal_call".
-call_site_kind_and_callee_to_html(special_call) =      "special_call".
-call_site_kind_and_callee_to_html(higher_order_call) = "higher_order_call".
-call_site_kind_and_callee_to_html(method_call) =       "method_call".
-call_site_kind_and_callee_to_html(callback) =          "callback".
-
-:- func call_site_summary_group_to_html(deep, string,
-	pair(proc_static_ptr, list(call_site_dynamic_ptr))) = string.
-
-call_site_summary_group_to_html(Deep, Fields, PSPtr - CSDPtrs) = HTML :-
-	list__foldl2(accumulate_csd_prof_info(Deep), CSDPtrs,
-		zero_own_prof_info, Own, zero_inherit_prof_info, Desc),
-	HTML =
-		"<TR>\n" ++
-		string__format("<TD></TD><TD>%s</TD>\n",
-			[s(proc_static_to_html_ref(Deep, Fields, PSPtr))]) ++
-		own_and_desc_to_html(Own, Desc, Deep, Fields) ++
-		"</TR>\n".
-
-:- pred accumulate_csd_prof_info(deep::in, call_site_dynamic_ptr::in,
-	own_prof_info::in, own_prof_info::out,
-	inherit_prof_info::in, inherit_prof_info::out) is det.
-
-accumulate_csd_prof_info(Deep, CSDPtr, Own0, Own, Desc0, Desc) :-
-	deep_lookup_csd_own(Deep, CSDPtr, CSDOwn),
-	deep_lookup_csd_desc(Deep, CSDPtr, CSDDesc),
-
-	add_own_to_own(Own0, CSDOwn) = Own,
-	add_inherit_to_inherit(Desc0, CSDDesc) = Desc.
-
-%-----------------------------------------------------------------------------%
-
-:- func call_site_dynamic_label(deep, call_site_dynamic_ptr) = string.
-
-call_site_dynamic_label(Deep, CSDPtr) = Name :-
-	(
-		valid_call_site_dynamic_ptr(Deep, CSDPtr),
-		deep_lookup_call_site_dynamics(Deep, CSDPtr, CSD),
-		PDPtr = CSD ^ csd_callee,
-		valid_proc_dynamic_ptr(Deep, PDPtr),
-		deep_lookup_proc_dynamics(Deep, PDPtr, PD),
-		PSPtr = PD ^ pd_proc_static,
-		valid_proc_static_ptr(Deep, PSPtr),
-		deep_lookup_proc_statics(Deep, PSPtr, PS)
-	->
-		Name = PS ^ ps_refined_id
-	;
-		Name = "unknown procedure"
-	).
-
-:- func proc_static_to_html_ref(deep, string, proc_static_ptr) = string.
-
-proc_static_to_html_ref(Deep, Fields, PSPtr) = HTML :-
-	( valid_proc_static_ptr(Deep, PSPtr) ->
-		deep_lookup_proc_statics(Deep, PSPtr, PS),
-		PSPtr = proc_static_ptr(PSI),
-		PSURL = deep_cmd_to_url(Deep, proc(PSI, Fields)),
-		HTML = string__format("<A HREF=""%s"">%s</A>\n",
-				[s(PSURL), s(PS ^ ps_refined_id)])
-	;
-		HTML =
-			"mercury_runtime"
-	).
-
-%-----------------------------------------------------------------------------%
-
-	% The number of ticks per sec to assume if the profiling data file
-	% does not record the actual tick rate.
-:- func default_ticks_per_sec = int.
-
-default_ticks_per_sec = 100.
-
-:- pred lookup_ticks_per_sec(profile_stats::in, int::out, bool::out) is det.
-
-lookup_ticks_per_sec(Stats, TicksPerSec, Assumed) :-
-	TicksPerSec0 = Stats ^ ticks_per_sec,
-	( TicksPerSec0 = 0 ->
-		TicksPerSec = default_ticks_per_sec,
-		Assumed = yes
-	;
-		TicksPerSec = TicksPerSec0,
-		Assumed = no
-	).
-
-:- func quantum_time(deep, int) = string.
-
-quantum_time(Deep, Quanta) = TimeStr :-
-	lookup_ticks_per_sec(Deep ^ profile_stats, TicksPerSec, _Assumed),
-	% We display Time as seconds, with two digits after the decimal point.
-	% This is the most we can do, given clock granularity.
-	Time = 100.0 * float(Quanta) / float(TicksPerSec),
-	string__format("%d", [i(floor_to_int(Time))], Str0),
-	string__to_char_list(Str0, Chars0),
-	reverse(Chars0, RevChars0),
-	string__from_char_list(reverse(
-		centiseconds_to_seconds(RevChars0)), TimeStr).
-
-:- func commas(int) = string.
-
-commas(Num) = Str :-
-	string__format("%d", [i(Num)], Str0),
-	string__to_char_list(Str0, Chars0),
-	reverse(Chars0, RevChars0),
-	string__from_char_list(reverse(add_commas(RevChars0)), Str).
-
-:- func centiseconds_to_seconds(list(char)) = list(char).
-
-centiseconds_to_seconds([]) = ['0', '0', '.', '0'].
-centiseconds_to_seconds([D]) = [D, '0', '.', '0'].
-centiseconds_to_seconds([D, E]) = [D, E, '.', '0'].
-centiseconds_to_seconds([D, E, F | R]) = [D, E, '.' | add_commas([F | R])].
-
-:- func add_commas(list(char)) = list(char).
-
-add_commas([]) = [].
-add_commas([C]) = [C].
-add_commas([C, D]) = [C, D].
-add_commas([C, D, E]) = [C, D, E].
-add_commas([C, D, E, F | R]) = [C, D, E, (',') | add_commas([F | R])].
-
-%-----------------------------------------------------------------------------%
-
-:- pred show_port_counts(fields::in) is semidet.
-
-show_port_counts(Fields) :-
-	string__contains_char(Fields, 'p').
-
-:- pred show_quanta(fields::in) is semidet.
-
-show_quanta(Fields) :-
-	string__contains_char(Fields, 'q').
-
-:- pred show_times(fields::in) is semidet.
-
-show_times(Fields) :-
-	string__contains_char(Fields, 't').
-
-:- pred show_allocs(fields::in) is semidet.
-
-show_allocs(Fields) :-
-	string__contains_char(Fields, 'a').
-
-:- pred show_words(fields::in) is semidet.
-
-show_words(Fields) :-
-	string__contains_char(Fields, 'w').
-
-%-----------------------------------------------------------------------------%
-
-:- pred find_top_procs(sort_measurement::in, include_descendants::in,
-	display_limit::in, deep::in, maybe_error(list(int))::out) is det.
-
-find_top_procs(Sort, InclDesc, Limit, Deep, MaybeTopPSIs) :-
-	find_top_sort_predicate(Sort, InclDesc, SortCompatible, RawSortPred),
-	(
-		SortCompatible = no,
-		MaybeTopPSIs = error("bad sort specification")
-	;
-		SortCompatible = yes,
-		ProcStatics = Deep ^ proc_statics,
-		array__max(ProcStatics, MaxProcStatic),
-		PSIs = int_list_from_to(1, MaxProcStatic),
-		SortPred = (pred(PSI1::in, PSI2::in, ComparisonResult::out)
-				is det :-
-			call(RawSortPred, Deep, PSI1, PSI2, ComparisonResult)
-		),
-		list__sort(SortPred, PSIs, AscendingPSIs),
-		list__reverse(AscendingPSIs, DescendingPSIs),
-		(
-			Limit = rank_range(First, Last),
-			(
-				list__drop(First - 1, DescendingPSIs,
-					RemainingPSIs)
-			->
-				list__take_upto(Last - First + 1,
-					RemainingPSIs, TopPSIs),
-				MaybeTopPSIs = ok(TopPSIs)
-			;
-				MaybeTopPSIs = ok([])
-			)
-		;
-			Limit = threshold(Threshold),
-			find_threshold_predicate(Sort, InclDesc,
-				ThresholdCompatible, RawThresholdPred),
-			(
-				ThresholdCompatible = no,
-				MaybeTopPSIs =
-					error("bad threshold specification")
-			;
-				ThresholdCompatible = yes,
-				ThresholdPred = (pred(PSI::in) is semidet :-
-					call(RawThresholdPred, Deep, Threshold,
-						PSI)
-				),
-				list__takewhile(ThresholdPred, DescendingPSIs,
-					TopPSIs, _),
-				MaybeTopPSIs = ok(TopPSIs)
-			)
-		)
-	).
-
-:- func int_list_from_to(int, int) = list(int).
-
-int_list_from_to(From, To) = List :-
-	( From > To ->
-		List = []
-	;
-		List = [From | int_list_from_to(From + 1, To)]
-	).
-
-:- pred find_top_sort_predicate(sort_measurement, include_descendants,
-	bool, pred(deep, int, int, comparison_result)).
-:- mode find_top_sort_predicate(in, in, out, out(pred(in, in, in, out) is det))
-	is det.
-
-find_top_sort_predicate(calls,  self,          yes, compare_ps_calls_self).
-find_top_sort_predicate(calls,  self_and_desc, no,  compare_ps_calls_self).
-find_top_sort_predicate(time,   self,          yes, compare_ps_time_self).
-find_top_sort_predicate(time,   self_and_desc, yes, compare_ps_time_both).
-find_top_sort_predicate(allocs, self,          yes, compare_ps_allocs_self).
-find_top_sort_predicate(allocs, self_and_desc, yes, compare_ps_allocs_both).
-find_top_sort_predicate(words,  self,          yes, compare_ps_words_self).
-find_top_sort_predicate(words,  self_and_desc, yes, compare_ps_words_both).
-
-:- pred find_threshold_predicate(sort_measurement, include_descendants,
-	bool, pred(deep, float, int)).
-:- mode find_threshold_predicate(in, in, out, out(pred(in, in, in) is semidet))
-	is det.
-
-find_threshold_predicate(calls,  self,          no,  threshold_ps_time_self).
-find_threshold_predicate(calls,  self_and_desc, no,  threshold_ps_time_self).
-find_threshold_predicate(time,   self,          yes, threshold_ps_time_self).
-find_threshold_predicate(time,   self_and_desc, yes, threshold_ps_time_both).
-find_threshold_predicate(allocs, self,          yes, threshold_ps_allocs_self).
-find_threshold_predicate(allocs, self_and_desc, yes, threshold_ps_allocs_both).
-find_threshold_predicate(words,  self,          yes, threshold_ps_words_self).
-find_threshold_predicate(words,  self_and_desc, yes, threshold_ps_words_both).
-
-:- pred compare_ps_calls_self(deep::in, int::in, int::in,
-	comparison_result::out) is det.
-
-compare_ps_calls_self(Deep, PSI1, PSI2, Result) :-
-	PSOwn = Deep ^ ps_own,
-	array__lookup(PSOwn, PSI1, Own1),
-	array__lookup(PSOwn, PSI2, Own2),
-	OwnCalls1 = calls(Own1),
-	OwnCalls2 = calls(Own2),
-	compare(Result, OwnCalls1, OwnCalls2).
-
-:- pred compare_ps_time_self(deep::in, int::in, int::in,
-	comparison_result::out) is det.
-
-compare_ps_time_self(Deep, PSI1, PSI2, Result) :-
-	PSOwn = Deep ^ ps_own,
-	array__lookup(PSOwn, PSI1, Own1),
-	array__lookup(PSOwn, PSI2, Own2),
-	OwnQuanta1 = quanta(Own1),
-	OwnQuanta2 = quanta(Own2),
-	compare(Result, OwnQuanta1, OwnQuanta2).
-
-:- pred compare_ps_time_both(deep::in, int::in, int::in,
-	comparison_result::out) is det.
-
-compare_ps_time_both(Deep, PSI1, PSI2, Result) :-
-	PSOwn = Deep ^ ps_own,
-	PSDesc = Deep ^ ps_desc,
-	array__lookup(PSOwn, PSI1, Own1),
-	array__lookup(PSOwn, PSI2, Own2),
-	array__lookup(PSDesc, PSI1, Desc1),
-	array__lookup(PSDesc, PSI2, Desc2),
-	OwnQuanta1 = quanta(Own1),
-	OwnQuanta2 = quanta(Own2),
-	DescQuanta1 = inherit_quanta(Desc1),
-	DescQuanta2 = inherit_quanta(Desc2),
-	TotalQuanta1 = OwnQuanta1 + DescQuanta1,
-	TotalQuanta2 = OwnQuanta2 + DescQuanta2,
-	compare(Result, TotalQuanta1, TotalQuanta2).
-
-:- pred compare_ps_allocs_self(deep::in, int::in, int::in,
-	comparison_result::out) is det.
-
-compare_ps_allocs_self(Deep, PSI1, PSI2, Result) :-
-	PSOwn = Deep ^ ps_own,
-	array__lookup(PSOwn, PSI1, Own1),
-	array__lookup(PSOwn, PSI2, Own2),
-	OwnAllocs1 = allocs(Own1),
-	OwnAllocs2 = allocs(Own2),
-	compare(Result, OwnAllocs1, OwnAllocs2).
-
-:- pred compare_ps_allocs_both(deep::in, int::in, int::in,
-	comparison_result::out) is det.
-
-compare_ps_allocs_both(Deep, PSI1, PSI2, Result) :-
-	PSOwn = Deep ^ ps_own,
-	PSDesc = Deep ^ ps_desc,
-	array__lookup(PSOwn, PSI1, Own1),
-	array__lookup(PSOwn, PSI2, Own2),
-	array__lookup(PSDesc, PSI1, Desc1),
-	array__lookup(PSDesc, PSI2, Desc2),
-	OwnAllocs1 = allocs(Own1),
-	OwnAllocs2 = allocs(Own2),
-	DescAllocs1 = inherit_allocs(Desc1),
-	DescAllocs2 = inherit_allocs(Desc2),
-	TotalAllocs1 = OwnAllocs1 + DescAllocs1,
-	TotalAllocs2 = OwnAllocs2 + DescAllocs2,
-	compare(Result, TotalAllocs1, TotalAllocs2).
-
-:- pred compare_ps_words_self(deep::in, int::in, int::in,
-	comparison_result::out) is det.
-
-compare_ps_words_self(Deep, PSI1, PSI2, Result) :-
-	PSOwn = Deep ^ ps_own,
-	array__lookup(PSOwn, PSI1, Own1),
-	array__lookup(PSOwn, PSI2, Own2),
-	OwnWords1 = words(Own1),
-	OwnWords2 = words(Own2),
-	compare(Result, OwnWords1, OwnWords2).
-
-:- pred compare_ps_words_both(deep::in, int::in, int::in,
-	comparison_result::out) is det.
-
-compare_ps_words_both(Deep, PSI1, PSI2, Result) :-
-	PSOwn = Deep ^ ps_own,
-	PSDesc = Deep ^ ps_desc,
-	array__lookup(PSOwn, PSI1, Own1),
-	array__lookup(PSOwn, PSI2, Own2),
-	array__lookup(PSDesc, PSI1, Desc1),
-	array__lookup(PSDesc, PSI2, Desc2),
-	OwnWords1 = words(Own1),
-	OwnWords2 = words(Own2),
-	DescWords1 = inherit_words(Desc1),
-	DescWords2 = inherit_words(Desc2),
-	TotalWords1 = OwnWords1 + DescWords1,
-	TotalWords2 = OwnWords2 + DescWords2,
-	compare(Result, TotalWords1, TotalWords2).
-
-:- pred threshold_ps_time_self(deep::in, float::in, int::in) is semidet.
-
-threshold_ps_time_self(Deep, Threshold, PSI) :-
-	PSOwn = Deep ^ ps_own,
-	array__lookup(PSOwn, PSI, Own),
-	RootOwn = root_own_info(Deep),
-	RootDesc = root_desc_info(Deep),
-	OwnQuanta = quanta(Own),
-	RootOwnQuanta = quanta(RootOwn),
-	RootDescQuanta = inherit_quanta(RootDesc),
-	RootTotalQuanta = RootOwnQuanta + RootDescQuanta,
-	100.0 * float(OwnQuanta) > Threshold * float(RootTotalQuanta).
-
-:- pred threshold_ps_time_both(deep::in, float::in, int::in) is semidet.
-
-threshold_ps_time_both(Deep, Threshold, PSI) :-
-	PSOwn = Deep ^ ps_own,
-	PSDesc = Deep ^ ps_desc,
-	array__lookup(PSOwn, PSI, Own),
-	array__lookup(PSDesc, PSI, Desc),
-	RootOwn = root_own_info(Deep),
-	RootDesc = root_desc_info(Deep),
-	OwnQuanta = quanta(Own),
-	RootOwnQuanta = quanta(RootOwn),
-	DescQuanta = inherit_quanta(Desc),
-	RootDescQuanta = inherit_quanta(RootDesc),
-	TotalQuanta = OwnQuanta + DescQuanta,
-	RootTotalQuanta = RootOwnQuanta + RootDescQuanta,
-	100.0 * float(TotalQuanta) > Threshold * float(RootTotalQuanta).
-
-:- pred threshold_ps_allocs_self(deep::in, float::in, int::in) is semidet.
-
-threshold_ps_allocs_self(Deep, Threshold, PSI) :-
-	PSOwn = Deep ^ ps_own,
-	array__lookup(PSOwn, PSI, Own),
-	RootOwn = root_own_info(Deep),
-	RootDesc = root_desc_info(Deep),
-	OwnAllocs = allocs(Own),
-	RootOwnAllocs = allocs(RootOwn),
-	RootDescAllocs = inherit_allocs(RootDesc),
-	RootTotalAllocs = RootOwnAllocs + RootDescAllocs,
-	100.0 * float(OwnAllocs) > Threshold * float(RootTotalAllocs).
-
-:- pred threshold_ps_allocs_both(deep::in, float::in, int::in) is semidet.
-
-threshold_ps_allocs_both(Deep, Threshold, PSI) :-
-	PSOwn = Deep ^ ps_own,
-	PSDesc = Deep ^ ps_desc,
-	array__lookup(PSOwn, PSI, Own),
-	array__lookup(PSDesc, PSI, Desc),
-	RootOwn = root_own_info(Deep),
-	RootDesc = root_desc_info(Deep),
-	OwnAllocs = allocs(Own),
-	RootOwnAllocs = allocs(RootOwn),
-	DescAllocs = inherit_allocs(Desc),
-	RootDescAllocs = inherit_allocs(RootDesc),
-	TotalAllocs = OwnAllocs + DescAllocs,
-	RootTotalAllocs = RootOwnAllocs + RootDescAllocs,
-	100.0 * float(TotalAllocs) > Threshold * float(RootTotalAllocs).
-
-:- pred threshold_ps_words_self(deep::in, float::in, int::in) is semidet.
-
-threshold_ps_words_self(Deep, Threshold, PSI) :-
-	PSOwn = Deep ^ ps_own,
-	array__lookup(PSOwn, PSI, Own),
-	RootOwn = root_own_info(Deep),
-	RootDesc = root_desc_info(Deep),
-	OwnWords = words(Own),
-	RootOwnWords = words(RootOwn),
-	RootDescWords = inherit_words(RootDesc),
-	RootTotalWords = RootOwnWords + RootDescWords,
-	100.0 * float(OwnWords) > Threshold * float(RootTotalWords).
-
-:- pred threshold_ps_words_both(deep::in, float::in, int::in) is semidet.
-
-threshold_ps_words_both(Deep, Threshold, PSI) :-
-	PSOwn = Deep ^ ps_own,
-	PSDesc = Deep ^ ps_desc,
-	array__lookup(PSOwn, PSI, Own),
-	array__lookup(PSDesc, PSI, Desc),
-	RootOwn = root_own_info(Deep),
-	RootDesc = root_desc_info(Deep),
-	OwnWords = words(Own),
-	RootOwnWords = words(RootOwn),
-	DescWords = inherit_words(Desc),
-	RootDescWords = inherit_words(RootDesc),
-	TotalWords = OwnWords + DescWords,
-	RootTotalWords = RootOwnWords + RootDescWords,
-	100.0 * float(TotalWords) > Threshold * float(RootTotalWords).
-
-%-----------------------------------------------------------------------------%
-
-:- func banner = string.
-
-banner =
-	"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.01//EN""\n" ++
-	"""http://www.w3.org/TR/html4/strict.dtd"">\n" ++
-	"<HTML>\n" ++
-	"<HEAD>\n" ++
-	"<TITLE>Page created by the Mercury Deep Profiler.</TITLE>\n" ++
-	"</HEAD>\n" ++
-	"<BODY>\n".
-
-:- func footer(cmd, deep) = string.
-
-footer(Cmd, Deep) = HTML :-
-	% Link back to root,
-	% Search, etc, etc.
-	HTML =
-		footer_cmd_toggles(Cmd, Deep) ++
-		"<p>\n" ++
-		string__format("<A HREF=""%s"">Menu</A>\n",
-			[s(deep_cmd_to_url(Deep, menu))]) ++
-		string__format("<A HREF=""%s"">Quit</A>\n",
-			[s(deep_cmd_to_url(Deep, quit))]) ++
-		"</BODY>\n" ++
-		"</HTML>\n".
-
-:- func footer_cmd_toggles(cmd, deep) = string.
-
-footer_cmd_toggles(quit, _) = "".
-footer_cmd_toggles(timeout(_), _) = "".
-footer_cmd_toggles(menu, _) = "".
-footer_cmd_toggles(root(Fields), Deep) =
-	footer_field_toggle(Deep, Fields,
-		func(ArgFields) = root(ArgFields) :- true).
-footer_cmd_toggles(clique(CI, Fields, MaybeAncestorLimit), Deep) =
-	footer_field_toggle(Deep, Fields,
-		func(ArgFields) = clique(CI, ArgFields, MaybeAncestorLimit)
-			:- true) ++
-	"<p>\n" ++
-	footer_ancestor_toggle(Deep, MaybeAncestorLimit,
-		func(ArgMaybeAncestorLimit) =
-			clique(CI, Fields, ArgMaybeAncestorLimit) :- true).
-footer_cmd_toggles(proc(PSI, Fields), Deep) =
-	footer_field_toggle(Deep, Fields,
-		func(ArgFields) = proc(PSI, ArgFields) :- true).
-footer_cmd_toggles(top_procs(Sort, InclDesc, Limit, Fields), Deep) =
-	footer_field_toggle(Deep, Fields,
-		func(ArgFields) = top_procs(Sort, InclDesc, Limit, ArgFields)
-			:- true).
-footer_cmd_toggles(proc_static(_), _) = "".
-footer_cmd_toggles(proc_dynamic(_), _) = "".
-footer_cmd_toggles(call_site_static(_), _) = "".
-footer_cmd_toggles(call_site_dynamic(_), _) = "".
-footer_cmd_toggles(raw_clique(_), _) = "".
-footer_cmd_toggles(num_proc_statics, _) = "".
-footer_cmd_toggles(num_call_site_statics, _) = "".
-footer_cmd_toggles(num_proc_dynamics, _) = "".
-footer_cmd_toggles(num_call_site_dynamics, _) = "".
-
-:- func footer_ancestor_toggle(deep, maybe(int), func(maybe(int)) = cmd)
-	= string.
-
-footer_ancestor_toggle(Deep, MaybeAncestorLimit, MakeCmd) = HTML :-
-	(
-		MaybeAncestorLimit = no,
-		Display1 = yes,
-		Display2 = yes,
-		Msg1 = "One ancestor",
-		MaybeAncestorLimit1 = yes(1),
-		Msg2 = "Two ancestors",
-		MaybeAncestorLimit2 = yes(2),
-		Msg3 = "Three ancestors",
-		MaybeAncestorLimit3 = yes(3),
-		Msg4 = "Five ancestors",
-		MaybeAncestorLimit4 = yes(5),
-		Msg5 = "Ten ancestors",
-		MaybeAncestorLimit5 = yes(10)
-	;
-		MaybeAncestorLimit = yes(OldAncestorLimit),
-		( OldAncestorLimit > 2 ->
-			Display1 = yes
-		;
-			Display1 = no
-		),
-		( OldAncestorLimit > 1 ->
-			Display2 = yes
-		;
-			Display2 = no
-		),
-		Msg1 = "Halve ancestors",
-		MaybeAncestorLimit1 = yes(OldAncestorLimit // 2),
-		Msg2 = "Remove an ancestor",
-		MaybeAncestorLimit2 = yes(OldAncestorLimit - 1),
-		Msg3 = "Add an ancestor",
-		MaybeAncestorLimit3 = yes(OldAncestorLimit + 1),
-		Msg4 = "Double ancestors",
-		MaybeAncestorLimit4 = yes(OldAncestorLimit * 2),
-		Msg5 = "Unlimited ancestors",
-		MaybeAncestorLimit5 = no
-	),
-	Cmd1 = MakeCmd(MaybeAncestorLimit1),
-	Cmd2 = MakeCmd(MaybeAncestorLimit2),
-	Cmd3 = MakeCmd(MaybeAncestorLimit3),
-	Cmd4 = MakeCmd(MaybeAncestorLimit4),
-	Cmd5 = MakeCmd(MaybeAncestorLimit5),
-	Toggle1 = string__format("<A HREF=""%s"">%s</A>\n",
-			[s(deep_cmd_to_url(Deep, Cmd1)), s(Msg1)]),
-	Toggle2 = string__format("<A HREF=""%s"">%s</A>\n",
-			[s(deep_cmd_to_url(Deep, Cmd2)), s(Msg2)]),
-	Toggle3 = string__format("<A HREF=""%s"">%s</A>\n",
-			[s(deep_cmd_to_url(Deep, Cmd3)), s(Msg3)]),
-	Toggle4 = string__format("<A HREF=""%s"">%s</A>\n",
-			[s(deep_cmd_to_url(Deep, Cmd4)), s(Msg4)]),
-	Toggle5 = string__format("<A HREF=""%s"">%s</A>\n",
-			[s(deep_cmd_to_url(Deep, Cmd5)), s(Msg5)]),
-	(
-		Display1 = yes,
-		MaybeToggle1 = Toggle1
-	;
-		Display1 = no,
-		MaybeToggle1 = ""
-	),
-	(
-		Display2 = yes,
-		MaybeToggle2 = Toggle2
-	;
-		Display2 = no,
-		MaybeToggle2 = ""
-	),
-	HTML =
-		"<p>\n" ++
-		"Toggle ancestors: " ++
-		MaybeToggle1 ++ MaybeToggle2 ++ Toggle3 ++ Toggle4 ++ Toggle5.
-
-:- func footer_field_toggle(deep, string, func(string) = cmd) = string.
-
-footer_field_toggle(Deep, Fields, MakeCmd) = HTML :-
-	FieldsChars = string__to_char_list(Fields),
-	( show_port_counts(Fields) ->
-		PortChars = list__delete_all(FieldsChars, 'p'),
-		PortMsg = "Don't show port counts"
-	;
-		PortChars = ['p' | FieldsChars],
-		PortMsg = "Show port counts"
-	),
-	( show_quanta(Fields) ->
-		QuantaChars = list__delete_all(FieldsChars, 'q'),
-		QuantaMsg = "Don't show quanta"
-	;
-		QuantaChars = ['q' | FieldsChars],
-		QuantaMsg = "Show quanta"
-	),
-	( show_times(Fields) ->
-		TimesChars = list__delete_all(FieldsChars, 't'),
-		TimesMsg = "Don't show time"
-	;
-		TimesChars = ['t' | FieldsChars],
-		TimesMsg = "Show time"
-	),
-	( show_allocs(Fields) ->
-		AllocsChars = list__delete_all(FieldsChars, 'a'),
-		AllocsMsg = "Don't show allocations"
-	;
-		AllocsChars = ['a' | FieldsChars],
-		AllocsMsg = "Show allocations"
-	),
-	( show_words(Fields) ->
-		WordsChars = list__delete_all(FieldsChars, 'w'),
-		WordsMsg = "Don't show words"
-	;
-		WordsChars = ['w' | FieldsChars],
-		WordsMsg = "Show words"
-	),
-	CmdPort   = MakeCmd(string__from_char_list(list__sort(PortChars))),
-	CmdQuanta = MakeCmd(string__from_char_list(list__sort(QuantaChars))),
-	CmdTimes  = MakeCmd(string__from_char_list(list__sort(TimesChars))),
-	CmdAllocs = MakeCmd(string__from_char_list(list__sort(AllocsChars))),
-	CmdWords  = MakeCmd(string__from_char_list(list__sort(WordsChars))),
-	HTML =
-		"<p>\n" ++
-		"Toggle fields: " ++
-		string__format("<A HREF=""%s"">%s</A>\n",
-			[s(deep_cmd_to_url(Deep, CmdPort)), s(PortMsg)]) ++
-		string__format("<A HREF=""%s"">%s</A>\n",
-			[s(deep_cmd_to_url(Deep, CmdQuanta)), s(QuantaMsg)]) ++
-		string__format("<A HREF=""%s"">%s</A>\n",
-			[s(deep_cmd_to_url(Deep, CmdTimes)), s(TimesMsg)]) ++
-		string__format("<A HREF=""%s"">%s</A>\n",
-			[s(deep_cmd_to_url(Deep, CmdAllocs)), s(AllocsMsg)]) ++
-		string__format("<A HREF=""%s"">%s</A>\n",
-			[s(deep_cmd_to_url(Deep, CmdWords)), s(WordsMsg)]).
-
-:- func menu_text = string.
-
-menu_text =
-	"You can start exploring the deep profile at the following points.\n".
-
-:- func menu_item(deep, cmd, string) = string.
-
-menu_item(Deep, Cmd, Text) = 
-	string__format("<A HREF=""%s"">%s</A>\n",
-		[s(deep_cmd_to_url(Deep, Cmd)), s(Text)]).
-
-%-----------------------------------------------------------------------------%
-
-:- func root_total_info(deep) = inherit_prof_info.
-
-root_total_info(Deep) = RootTotal :-
-	deep_lookup_pd_own(Deep, Deep ^ root, RootOwn),
-	deep_lookup_pd_desc(Deep, Deep ^ root, RootDesc),
-	add_own_to_inherit(RootOwn, RootDesc) = RootTotal.
-
-:- func root_desc_info(deep) = inherit_prof_info.
-
-root_desc_info(Deep) = RootDesc :-
-	deep_lookup_pd_desc(Deep, Deep ^ root, RootDesc).
-
-:- func root_own_info(deep) = own_prof_info.
-
-root_own_info(Deep) = RootOwn :-
-	deep_lookup_pd_own(Deep, Deep ^ root, RootOwn).
-
-%-----------------------------------------------------------------------------%
-
-:- func fields_header(fields) = string.
-
-fields_header(Fields) = HTML :-
-	Groups0 = "<COLGROUP>\n" ++
-		"<COL ALIGN=LEFT>\n" ++
-		"<COL ALIGN=LEFT>\n",
-	FirstRow0 = "<TR>\n" ++
-		"<TH ALIGN=LEFT ROWSPAN=2>Source\n" ++
-		"<TH ALIGN=LEFT ROWSPAN=2>Procedure\n",
-	SecondRow0 = "<TR>\n",
-	( show_port_counts(Fields) ->
-		Groups1 = Groups0 ++
-			"<COLGROUP>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n",
-		FirstRow1 = FirstRow0 ++
-			"<TH COLSPAN=4>Port counts\n",
-		SecondRow1 = SecondRow0 ++
-			"<TH ALIGN=RIGHT>Calls\n" ++
-			"<TH ALIGN=RIGHT>Exits\n" ++
-			"<TH ALIGN=RIGHT>Fails\n" ++
-			"<TH ALIGN=RIGHT>Redos\n"
-	;
-		Groups1 = Groups0,
-		FirstRow1 = FirstRow0,
-		SecondRow1 = SecondRow0
-	),
-	( show_quanta(Fields), show_times(Fields) ->
-		Groups2 = Groups1 ++
-			"<COLGROUP>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n",
-		FirstRow2 = FirstRow1 ++
-			"<TH COLSPAN=6>Clock ticks and times\n",
-		SecondRow2 = SecondRow1 ++
-			"<TH ALIGN=RIGHT ROWSPAN=2>Self\n" ++
-			"<TH ALIGN=RIGHT>%\n" ++
-			"<TH ALIGN=RIGHT ROWSPAN=2>Total\n" ++
-			"<TH ALIGN=RIGHT>%\n"
-	; show_quanta(Fields) ->
-		Groups2 = Groups1 ++
-			"<COLGROUP>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n",
-		FirstRow2 = FirstRow1 ++
-			"<TH COLSPAN=4>Clock ticks\n",
-		SecondRow2 = SecondRow1 ++
-			"<TH ALIGN=RIGHT>Self\n" ++
-			"<TH ALIGN=RIGHT>%\n" ++
-			"<TH ALIGN=RIGHT>Total\n" ++
-			"<TH ALIGN=RIGHT>%\n"
-	; show_times(Fields) ->
-		Groups2 = Groups1 ++
-			"<COLGROUP>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n",
-		FirstRow2 = FirstRow1 ++
-			"<TH COLSPAN=4>Time\n",
-		SecondRow2 = SecondRow1 ++
-			"<TH ALIGN=RIGHT>Self\n" ++
-			"<TH ALIGN=RIGHT>%\n" ++
-			"<TH ALIGN=RIGHT>Total\n" ++
-			"<TH ALIGN=RIGHT>%\n"
-	;
-		Groups2 = Groups1,
-		FirstRow2 = FirstRow1,
-		SecondRow2 = SecondRow1
-	),
-	( show_allocs(Fields) ->
-		Groups3 = Groups2 ++
-			"<COLGROUP>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n",
-		FirstRow3 = FirstRow2 ++
-			"<TH COLSPAN=4>Memory allocations\n",
-		SecondRow3 = SecondRow2 ++
-			"<TH ALIGN=RIGHT>Self\n" ++
-			"<TH ALIGN=RIGHT>%\n" ++
-			"<TH ALIGN=RIGHT>Total\n" ++
-			"<TH ALIGN=RIGHT>%\n"
-	;
-		Groups3 = Groups2,
-		FirstRow3 = FirstRow2,
-		SecondRow3 = SecondRow2
-	),
-	( show_words(Fields) ->
-		Groups4 = Groups3 ++
-			"<COLGROUP>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n" ++
-			"<COL ALIGN=RIGHT>\n",
-		FirstRow4 = FirstRow3 ++
-			"<TH COLSPAN=4>Memory words\n",
-		SecondRow4 = SecondRow3 ++
-			"<TH ALIGN=RIGHT>Self\n" ++
-			"<TH ALIGN=RIGHT>%\n" ++
-			"<TH ALIGN=RIGHT>Total\n" ++
-			"<TH ALIGN=RIGHT>%\n"
-	;
-		Groups4 = Groups3,
-		FirstRow4 = FirstRow3,
-		SecondRow4 = SecondRow3
-	),
-	HTML = Groups4 ++ "<THEAD>\n" ++
-		FirstRow4 ++ SecondRow4 ++ "<TBODY>\n".
-
-:- func separator_row(fields) = string.
-
-separator_row(Fields) = Separator :-
-	Fixed = 2,	% Source, Procedure
-	( show_port_counts(Fields) ->
-		Port = 4
-	;
-		Port = 4
-	),
-	( show_quanta(Fields) ->
-		Quanta = 2
-	;
-		Quanta = 0
-	),
-	( show_times(Fields) ->
-		Times = 2
-	;
-		Times = 0
-	),
-	( (show_quanta(Fields) ; show_times(Fields)) ->
-		Percentage = 2
-	;
-		Percentage = 0
-	),
-	( show_allocs(Fields) ->
-		Allocs = 4
-	;
-		Allocs = 0
-	),
-	( show_words(Fields) ->
-		Words = 4
-	;
-		Words = 0
-	),
-	Count = Fixed + Port + Quanta + Times + Percentage + Allocs + Words,
-	Separator = string__format("<TR><TD COLSPAN=%d> </TD></TR>\n",
-		[i(Count)]).
-
-:- func own_and_desc_to_html(own_prof_info, inherit_prof_info,
-	deep, fields) = string.
-
-own_and_desc_to_html(Own, Desc, Deep, Fields) = HTML :-
-	add_own_to_inherit(Own, Desc) = OwnPlusDesc,
-	Root = root_total_info(Deep),
-	Calls = calls(Own),
-	Exits = exits(Own),
-	Fails = fails(Own),
-	Redos = redos(Own),
-
-	OwnQuanta = quanta(Own),
-	TotalQuanta = inherit_quanta(OwnPlusDesc),
-	RootQuanta = inherit_quanta(Root),
-	OwnQuantaProp = percentage(OwnQuanta, RootQuanta),
-	TotalQuantaProp = percentage(TotalQuanta, RootQuanta),
-
-	OwnAllocs = allocs(Own),
-	TotalAllocs = inherit_allocs(OwnPlusDesc),
-	RootAllocs = inherit_allocs(Root),
-	OwnAllocProp = percentage(OwnAllocs, RootAllocs),
-	TotalAllocProp = percentage(TotalAllocs, RootAllocs),
-
-	OwnWords = words(Own),
-	TotalWords = inherit_words(OwnPlusDesc),
-	RootWords = inherit_words(Root),
-	OwnWordProp = percentage(OwnWords, RootWords),
-	TotalWordProp = percentage(TotalWords, RootWords),
-
-	HTML =
-		( show_port_counts(Fields) ->
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(commas(Calls))]) ++
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(commas(Exits))]) ++
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(commas(Fails))]) ++
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(commas(Redos))])
-		;
-			""
-		) ++
-		( show_quanta(Fields) ->
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(commas(OwnQuanta))])
-		;
-			""
-		) ++
-		( show_times(Fields) ->
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(quantum_time(Deep, OwnQuanta))])
-		;
-			""
-		) ++
-		( (show_quanta(Fields) ; show_times(Fields)) ->
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(OwnQuantaProp)])
-		;
-			""
-		) ++
-		( show_quanta(Fields) ->
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(commas(TotalQuanta))])
-		;
-			""
-		) ++
-		( show_times(Fields) ->
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(quantum_time(Deep, TotalQuanta))])
-		;
-			""
-		) ++
-		( (show_quanta(Fields) ; show_times(Fields)) ->
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(TotalQuantaProp)])
-		;
-			""
-		) ++
-		( show_allocs(Fields) ->
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(commas(OwnAllocs))]) ++
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(OwnAllocProp)]) ++
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(commas(TotalAllocs))]) ++
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(TotalAllocProp)])
-		;
-			""
-		) ++
-		( show_words(Fields) ->
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(commas(OwnWords))]) ++
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(OwnWordProp)]) ++
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(commas(TotalWords))]) ++
-			string__format("<TD ALIGN=RIGHT>%s</TD>\n",
-				[s(TotalWordProp)])
-		;
-			""
-		).
-
-:- func percentage(int, int) = string.
-
-percentage(Fraction, Whole) = PercentageStr :-
-	( Whole = 0 ->
-		PercentageStr = "N/A"
-	;
-		Percentage = 100.0 * float(Fraction) / float(Whole),
-		PercentageStr = string__format("%5.2f", [f(Percentage)])
-	).
-
-:- func deep_cmd_to_url(deep, cmd) = string.
-
-deep_cmd_to_url(Deep, Cmd) = URL :-
-	cmd_to_url(Deep ^ server_name, Deep ^ data_file_name, Cmd, URL).
 
 %-----------------------------------------------------------------------------%
Index: deep_profiler/startup.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/startup.m,v
retrieving revision 1.3
diff -u -b -r1.3 startup.m
--- deep_profiler/startup.m	2001/06/05 07:37:04	1.3
+++ deep_profiler/startup.m	2001/06/25 06:31:44
@@ -25,7 +25,7 @@
 
 :- implementation.
 
-:- import_module profile, read_profile, callgraph.
+:- import_module profile, read_profile, callgraph, canonical.
 :- import_module measurements, array_util.
 :- import_module std_util, int, string, array, assoc_list, set, map, require.
 
@@ -59,26 +59,28 @@
 :- pred startup(string::in, string::in, bool::in, initial_deep::in, deep::out,
 	io__state::di, io__state::uo) is det.
 
-startup(Machine, DataFileName, _CanonicalClique, InitDeep0, Deep) -->
+startup(Machine, DataFileName, CanonicalClique, InitDeep0, Deep) -->
 	stderr_stream(StdErr),
 
 	{ InitDeep0 = initial_deep(InitStats, Root,
 		CallSiteDynamics0, ProcDynamics,
-		CallSiteStatics0, ProcStatics) },
+		CallSiteStatics0, ProcStatics0) },
 
 	io__format(StdErr,
 		"  Mapping static call sites to containing procedures...\n",
 		[]),
-	{ array_foldl_from_1(record_css_containers, ProcStatics,
-		u(CallSiteStatics0), CallSiteStatics) },
+	{ array_foldl2_from_1(record_css_containers_module_procs, ProcStatics0,
+		u(CallSiteStatics0), CallSiteStatics,
+		map__init, ModuleProcs) },
 	io__format(StdErr, "  Done.\n", []),
 	io__report_stats,
 
 	io__format(StdErr,
 		"  Mapping dynamic call sites to containing procedures...\n",
 		[]),
-	{ array_foldl_from_1(record_csd_containers, ProcDynamics,
-		u(CallSiteDynamics0), CallSiteDynamics) },
+	{ array_foldl2_from_1(record_csd_containers_zeroed_pss, ProcDynamics,
+		u(CallSiteDynamics0), CallSiteDynamics,
+		u(ProcStatics0), ProcStatics) },
 	io__format(StdErr, "  Done.\n", []),
 	io__report_stats,
 
@@ -86,17 +88,16 @@
 		CallSiteDynamics, ProcDynamics,
 		CallSiteStatics, ProcStatics) },
 
-	{ InitDeep = InitDeep1 },
-%	(
-%		{ CanonicalClique = no },
-%		{ InitDeep = InitDeep1 }
-%	;
-%		{ CanonicalClique = yes },
-%		io__format(StdErr, "  Canonicalizing cliques...\n", []),
-%		{ canonicalize_cliques(InitDeep1, InitDeep) },
-%		io__format(StdErr, "  Done.\n", []),
-%		io__report_stats
-%	),
+	(
+		{ CanonicalClique = no },
+		{ InitDeep = InitDeep1 }
+	;
+		{ CanonicalClique = yes },
+		io__format(StdErr, "  Canonicalizing cliques...\n", []),
+		{ canonicalize_cliques(InitDeep1, InitDeep) },
+		io__format(StdErr, "  Done.\n", []),
+		io__report_stats
+	),
 
 	{ array__max(InitDeep ^ init_proc_dynamics, PDMax) },
 	{ NPDs = PDMax + 1 },
@@ -172,13 +173,17 @@
 	{ array__init(NPSs, zero_inherit_prof_info, PSDesc0) },
 	{ array__init(NCSSs, zero_own_prof_info, CSSOwn0) },
 	{ array__init(NCSSs, zero_inherit_prof_info, CSSDesc0) },
+	{ array__init(NPDs, map__init, PDZeroMap0) },
+	{ array__init(NCSDs, map__init, CSDZeroMap0) },
 
+	{ ModuleData = map__map_values(initialize_module_data, ModuleProcs) },
 	{ Deep0 = deep(InitStats, Machine, DataFileName, Root,
 		CallSiteDynamics, ProcDynamics, CallSiteStatics, ProcStatics,
 		CliqueIndex, Cliques, CliqueParents, CliqueMaybeChildren,
 		ProcCallers, CallSiteStaticMap, CallSiteCalls,
 		PDOwn, PDDesc0, CSDDesc0,
-		PSOwn0, PSDesc0, CSSOwn0, CSSDesc0) },
+		PSOwn0, PSDesc0, CSSOwn0, CSSDesc0,
+		PDZeroMap0, CSDZeroMap0, ModuleData) },
 
 	{ array_foldl_from_1(propagate_to_clique, Cliques, Deep0, Deep1) },
 	io__format(StdErr, "  Done.\n", []),
@@ -186,7 +191,8 @@
 
 	io__format(StdErr, "  Summarizing information...\n", []),
 	{ summarize_proc_dynamics(Deep1, Deep2) },
-	{ summarize_call_site_dynamics(Deep2, Deep) },
+	{ summarize_call_site_dynamics(Deep2, Deep3) },
+	{ summarize_modules(Deep3, Deep) },
 	io__format(StdErr, "  Done.\n", []),
 	io__report_stats.
 
@@ -195,18 +201,34 @@
 count_quanta(_N, CSD, Quanta0, Quanta) :-
 	Quanta = Quanta0 + quanta(CSD ^ csd_own_prof).
 
+:- func initialize_module_data(string, list(proc_static_ptr)) = module_data.
+
+initialize_module_data(_ModuleName, PSPtrs) =
+	module_data(zero_own_prof_info, zero_inherit_prof_info, PSPtrs).
+
 %-----------------------------------------------------------------------------%
 
-:- pred record_css_containers(int::in, proc_static::in,
+:- pred record_css_containers_module_procs(int::in, proc_static::in,
 	array(call_site_static)::array_di,
-	array(call_site_static)::array_uo) is det.
+	array(call_site_static)::array_uo,
+	map(string, list(proc_static_ptr))::in,
+	map(string, list(proc_static_ptr))::out) is det.
 
-record_css_containers(PSI, PS, CallSiteStatics0, CallSiteStatics) :-
+record_css_containers_module_procs(PSI, PS, CallSiteStatics0, CallSiteStatics,
+		ModuleProcs0, ModuleProcs) :-
 	CSSPtrs = PS ^ ps_sites,
 	PSPtr = proc_static_ptr(PSI),
 	array__max(CSSPtrs, MaxCS),
 	record_css_containers_2(MaxCS, PSPtr, CSSPtrs,
-		CallSiteStatics0, CallSiteStatics).
+		CallSiteStatics0, CallSiteStatics),
+	DeclModule = PS ^ ps_decl_module,
+	( map__search(ModuleProcs0, DeclModule, PSPtrs0) ->
+		map__det_update(ModuleProcs0, DeclModule, [PSPtr | PSPtrs0],
+			ModuleProcs)
+	;
+		map__det_insert(ModuleProcs0, DeclModule, [PSPtr],
+			ModuleProcs)
+	).
 
 :- pred record_css_containers_2(int::in, proc_static_ptr::in,
 	array(call_site_static_ptr)::in,
@@ -236,16 +258,28 @@
 
 %-----------------------------------------------------------------------------%
 
-:- pred record_csd_containers(int::in, proc_dynamic::in,
+:- pred record_csd_containers_zeroed_pss(int::in, proc_dynamic::in,
 	array(call_site_dynamic)::array_di,
-	array(call_site_dynamic)::array_uo) is det.
+	array(call_site_dynamic)::array_uo,
+	array(proc_static)::array_di, array(proc_static)::array_uo) is det.
 
-record_csd_containers(PDI, PD, CallSiteDynamics0, CallSiteDynamics) :-
+record_csd_containers_zeroed_pss(PDI, PD, CallSiteDynamics0, CallSiteDynamics,
+		ProcStatics0, ProcStatics) :-
 	CSDArray = PD ^ pd_sites,
 	PDPtr = proc_dynamic_ptr(PDI),
-	flatten_call_sites(CSDArray, CSDPtrs),
+	flatten_call_sites(CSDArray, CSDPtrs, IsZeroed),
 	record_csd_containers_2(PDPtr, CSDPtrs,
-		CallSiteDynamics0, CallSiteDynamics).
+		CallSiteDynamics0, CallSiteDynamics),
+	(
+		IsZeroed = zeroed,
+		PSPtr = PD ^ pd_proc_static,
+		lookup_proc_statics(ProcStatics0, PSPtr, PS0),
+		PS = PS0 ^ ps_is_zeroed := zeroed,
+		update_proc_statics(ProcStatics0, PSPtr, PS, ProcStatics)
+	;
+		IsZeroed = not_zeroed,
+		ProcStatics = ProcStatics0
+	).
 
 :- pred record_csd_containers_2(proc_dynamic_ptr::in,
 	list(call_site_dynamic_ptr)::in,
@@ -326,44 +360,7 @@
 		CliqueMaybeChildren = CliqueMaybeChildren0
 	).
 
-:- pred flat_call_sites(proc_dynamics::in, proc_dynamic_ptr::in,
-	list(call_site_dynamic_ptr)::out) is det.
-
-flat_call_sites(ProcDynamics, PDPtr, CSDPtrs) :-
-	( PDPtr = proc_dynamic_ptr(PDI), PDI > 0 ->
-		array__lookup(ProcDynamics, PDI, PD),
-		CallSiteArray = PD ^ pd_sites,
-		flatten_call_sites(CallSiteArray, CSDPtrs)
-	;
-		CSDPtrs = []
-	).
-
-:- pred flatten_call_sites(array(call_site_array_slot)::in,
-	list(call_site_dynamic_ptr)::out) is det.
-
-flatten_call_sites(CallSiteArray, CSDPtrs) :-
-	array__to_list(CallSiteArray, CallSites),
-	list__foldl((pred(Slot::in, CSDPtrs0::in, CSDPtrs1::out) is det :-
-		(
-			Slot = normal(CSDPtr),
-			CSDPtr = call_site_dynamic_ptr(CSDI),
-			( CSDI > 0 ->
-				CSDPtrs1 = [[CSDPtr] | CSDPtrs0]
-			;
-				CSDPtrs1 = CSDPtrs0
-			)
-		;
-			Slot = multi(PtrArray),
-			array__to_list(PtrArray, PtrList0),
-			filter((pred(CSDPtr::in) is semidet :-
-				CSDPtr = call_site_dynamic_ptr(CSDI),
-				CSDI > 0
-			), PtrList0, PtrList1),
-			CSDPtrs1 = [PtrList1 | CSDPtrs0]
-		)
-	), CallSites, [], CSDPtrsList0),
-	list__reverse(CSDPtrsList0, CSDPtrsList),
-	list__condense(CSDPtrsList, CSDPtrs).
+%-----------------------------------------------------------------------------%
 
 :- pred construct_proc_callers(initial_deep::in, int::in,
 	call_site_dynamic::in,
@@ -372,14 +369,12 @@
 
 construct_proc_callers(InitDeep, CSDI, CSD, ProcCallers0, ProcCallers) :-
 	PDPtr = CSD ^ csd_callee,
-	PDPtr = proc_dynamic_ptr(PDI),
-	( PDI > 0, array__in_bounds(InitDeep ^ init_proc_dynamics, PDI) ->
-		array__lookup(InitDeep ^ init_proc_dynamics, PDI, PD),
+	( valid_proc_dynamic_ptr_raw(InitDeep ^ init_proc_dynamics, PDPtr) ->
+		lookup_proc_dynamics(InitDeep ^ init_proc_dynamics, PDPtr, PD),
 		PSPtr = PD ^ pd_proc_static,
-		PSPtr = proc_static_ptr(PSI),
-		array__lookup(ProcCallers0, PSI, Callers0),
+		lookup_proc_callers(ProcCallers0, PSPtr, Callers0),
 		Callers = [call_site_dynamic_ptr(CSDI) | Callers0],
-		array__set(ProcCallers0, PSI, Callers, ProcCallers)
+		update_proc_callers(ProcCallers0, PSPtr, Callers, ProcCallers)
 	;
 		ProcCallers = ProcCallers0
 	).
@@ -392,9 +387,8 @@
 		CallSiteStaticMap0, CallSiteStaticMap) :-
 	PSPtr = PD ^ pd_proc_static,
 	CSDArraySlots = PD ^ pd_sites,
-	PSPtr = proc_static_ptr(PSI),
-	array__lookup(InitDeep ^ init_proc_statics, PSI, PS),
-	PS = proc_static(_, _, _, _, CSSPtrs),
+	lookup_proc_statics(InitDeep ^ init_proc_statics, PSPtr, PS),
+	CSSPtrs = PS ^ ps_sites,
 	array__max(CSDArraySlots, MaxCS),
 	construct_call_site_caller_2(MaxCS,
 		InitDeep ^ init_call_site_dynamics, CSSPtrs, CSDArraySlots,
@@ -417,7 +411,7 @@
 				CallSiteStaticMap0, CallSiteStaticMap1)
 
 		;
-			CSDArraySlot = multi(CSDPtrs),
+			CSDArraySlot = multi(_, CSDPtrs),
 			array_foldl_from_0(
 				construct_call_site_caller_3(Deep, CSSPtr),
 				CSDPtrs,
@@ -443,6 +437,8 @@
 		CallSiteStaticMap = CallSiteStaticMap0
 	).
 
+%-----------------------------------------------------------------------------%
+
 :- pred construct_call_site_calls(initial_deep::in, int::in, proc_dynamic::in,
 	array(map(proc_static_ptr, list(call_site_dynamic_ptr)))::array_di,
 	array(map(proc_static_ptr, list(call_site_dynamic_ptr)))::array_uo)
@@ -478,7 +474,7 @@
 				ProcDynamics, CSSPtr, -1,
 				CSDPtr, CallSiteCalls0, CallSiteCalls1)
 		;
-			CSDArraySlot = multi(CSDPtrs),
+			CSDArraySlot = multi(_, CSDPtrs),
 			array_foldl_from_0(
 				construct_call_site_calls_3(CallSiteDynamics,
 					ProcDynamics, CSSPtr),
@@ -521,176 +517,300 @@
 		CallSiteCalls = CallSiteCalls0
 	).
 
+%-----------------------------------------------------------------------------%
+
 :- pred sum_call_sites_in_proc_dynamic(int::in, call_site_dynamic::in,
 	array(own_prof_info)::array_di, array(own_prof_info)::array_uo) is det.
 
-sum_call_sites_in_proc_dynamic(_, CSD, PDO0, PDO) :-
+sum_call_sites_in_proc_dynamic(_, CSD, PDOwnArray0, PDOwnArray) :-
+	CalleeOwn = CSD ^ csd_own_prof,
 	PDPtr = CSD ^ csd_callee,
-	PI = CSD ^ csd_own_prof,
 	PDPtr = proc_dynamic_ptr(PDI),
 	( PDI > 0 ->
-		array__lookup(PDO0, PDI, OwnPI0),
-		OwnPI = add_own_to_own(PI, OwnPI0),
-		array__set(PDO0, PDI, OwnPI, PDO)
+		array__lookup(PDOwnArray0, PDI, ProcOwn0),
+		ProcOwn = add_own_to_own(CalleeOwn, ProcOwn0),
+		array__set(PDOwnArray0, PDI, ProcOwn, PDOwnArray)
 	;
-		PDO = PDO0
+		error("sum_call_sites_in_proc_dynamic: invalid pdptr")
 	).
 
+%-----------------------------------------------------------------------------%
+
 :- pred summarize_proc_dynamics(deep::in, deep::out) is det.
 
 summarize_proc_dynamics(Deep0, Deep) :-
-	PSOwn0 = Deep0 ^ ps_own,
-	PSDesc0 = Deep0 ^ ps_desc,
+	PSOwnArray0 = Deep0 ^ ps_own,
+	PSDescArray0 = Deep0 ^ ps_desc,
 	array_foldl2_from_1(
-		summarize_proc_dynamic(Deep0 ^ pd_own, Deep0 ^ pd_desc),
+		summarize_proc_dynamic(Deep0 ^ pd_own, Deep0 ^ pd_desc,
+			Deep0 ^ pd_zero_total_map),
 		Deep0 ^ proc_dynamics,
-		copy(PSOwn0), PSOwn, copy(PSDesc0), PSDesc),
+		copy(PSOwnArray0), PSOwnArray,
+		copy(PSDescArray0), PSDescArray),
 	Deep = ((Deep0
-		^ ps_own := PSOwn)
-		^ ps_desc := PSDesc).
+		^ ps_own := PSOwnArray)
+		^ ps_desc := PSDescArray).
 
 :- pred summarize_proc_dynamic(array(own_prof_info)::in,
-	array(inherit_prof_info)::in, int::in, proc_dynamic::in,
+	array(inherit_prof_info)::in, array(zero_total_map)::in,
+	int::in, proc_dynamic::in,
 	array(own_prof_info)::array_di, array(own_prof_info)::array_uo,
 	array(inherit_prof_info)::array_di, array(inherit_prof_info)::array_uo)
 	is det.
 
-summarize_proc_dynamic(PDOwn, PDDesc, PDI, PD,
-		PSOwn0, PSOwn, PSDesc0, PSDesc) :-
+summarize_proc_dynamic(PDOwnArray, PDDescArray, PDZeroMapArray, PDI, PD,
+		PSOwnArray0, PSOwnArray, PSDescArray0, PSDescArray) :-
 	PSPtr = PD ^ pd_proc_static,
-	PSPtr = proc_static_ptr(PSI),
-	( PSI > 0 ->
-		array__lookup(PDOwn, PDI, PDOwnPI),
-		array__lookup(PDDesc, PDI, PDDescPI),
-
-		array__lookup(PSOwn0, PSI, PSOwnPI0),
-		array__lookup(PSDesc0, PSI, PSDescPI0),
-
-		add_own_to_own(PDOwnPI, PSOwnPI0) = PSOwnPI,
-		add_inherit_to_inherit(PDDescPI, PSDescPI0) = PSDescPI,
-		array__set(u(PSOwn0), PSI, PSOwnPI, PSOwn),
-		array__set(u(PSDesc0), PSI, PSDescPI, PSDesc)
+	PDPtr = proc_dynamic_ptr(PDI),
+	lookup_pd_own(PDOwnArray, PDPtr, PDOwn),
+	lookup_pd_desc(PDDescArray, PDPtr, PDDesc0),
+	lookup_pd_zero_map(PDZeroMapArray, PDPtr, PDZeroMap),
+	( map__search(PDZeroMap, PSPtr, InnerTotal) ->
+		PDDesc = subtract_inherit_from_inherit(InnerTotal, PDDesc0)
 	;
-		error("emit nasal devils")
-	).
+		PDDesc = PDDesc0
+	),
+	lookup_ps_own(PSOwnArray0, PSPtr, PSOwn0),
+	lookup_ps_desc(PSDescArray0, PSPtr, PSDesc0),
+	add_own_to_own(PDOwn, PSOwn0) = PSOwn,
+	add_inherit_to_inherit(PDDesc, PSDesc0) = PSDesc,
+	update_ps_own(u(PSOwnArray0), PSPtr, PSOwn, PSOwnArray),
+	update_ps_desc(u(PSDescArray0), PSPtr, PSDesc, PSDescArray).
+
+%-----------------------------------------------------------------------------%
 
 :- pred summarize_call_site_dynamics(deep::in, deep::out) is det.
 
 summarize_call_site_dynamics(Deep0, Deep) :-
-	CSSOwn0 = Deep0 ^ css_own,
-	CSSDesc0 = Deep0 ^ css_desc,
+	CSSOwnArray0 = Deep0 ^ css_own,
+	CSSDescArray0 = Deep0 ^ css_desc,
 	array_foldl2_from_1(
-		summarize_call_site_dynamic(Deep0 ^ call_site_static_map,
-		Deep0 ^ csd_desc),
+		summarize_call_site_dynamic(
+			Deep0 ^ call_site_static_map,
+			Deep0 ^ call_site_statics, Deep0 ^ csd_desc,
+			Deep0 ^ csd_zero_total_map),
 		Deep0 ^ call_site_dynamics,
-		copy(CSSOwn0), CSSOwn, copy(CSSDesc0), CSSDesc),
+		copy(CSSOwnArray0), CSSOwnArray,
+		copy(CSSDescArray0), CSSDescArray),
 	Deep = ((Deep0
-		^ css_own := CSSOwn)
-		^ css_desc := CSSDesc).
+		^ css_own := CSSOwnArray)
+		^ css_desc := CSSDescArray).
 
 :- pred summarize_call_site_dynamic(call_site_static_map::in,
-	array(inherit_prof_info)::in, int::in, call_site_dynamic::in,
+	call_site_statics::in, array(inherit_prof_info)::in,
+	array(zero_total_map)::in, int::in, call_site_dynamic::in,
 	array(own_prof_info)::array_di, array(own_prof_info)::array_uo,
 	array(inherit_prof_info)::array_di, array(inherit_prof_info)::array_uo)
 	is det.
 
-summarize_call_site_dynamic(CallSiteStaticMap, CSDDescs, CSDI, CSD,
-		CSSOwn0, CSSOwn, CSSDesc0, CSSDesc) :-
+summarize_call_site_dynamic(CallSiteStaticMap, CallSiteStatics,
+		CSDDescs, CSDZeroMapArray, CSDI, CSD,
+		CSSOwnArray0, CSSOwnArray, CSSDescArray0, CSSDescArray) :-
 	CSDPtr = call_site_dynamic_ptr(CSDI),
 	lookup_call_site_static_map(CallSiteStaticMap, CSDPtr, CSSPtr),
 	CSSPtr = call_site_static_ptr(CSSI),
 	( CSSI > 0 ->
-		CSDOwnPI = CSD ^ csd_own_prof,
-		array__lookup(CSDDescs, CSDI, CSDDescPI),
-
-		array__lookup(CSSOwn0, CSSI, CSSOwnPI0),
-		array__lookup(CSSDesc0, CSSI, CSSDescPI0),
-
-		add_own_to_own(CSDOwnPI, CSSOwnPI0)
-			= CSSOwnPI,
-		add_inherit_to_inherit(CSDDescPI, CSSDescPI0)
-			= CSSDescPI,
-		array__set(u(CSSOwn0), CSSI, CSSOwnPI, CSSOwn),
-		array__set(u(CSSDesc0), CSSI, CSSDescPI, CSSDesc)
+		CSDOwn = CSD ^ csd_own_prof,
+		lookup_csd_desc(CSDDescs, CSDPtr, CSDDesc0),
+		lookup_csd_zero_map(CSDZeroMapArray, CSDPtr, CSDZeroMap),
+		lookup_call_site_statics(CallSiteStatics, CSSPtr, CSS),
+		( map__search(CSDZeroMap, CSS ^ css_container, InnerTotal) ->
+			CSDDesc = subtract_inherit_from_inherit(InnerTotal,
+				CSDDesc0)
+		;
+			CSDDesc = CSDDesc0
+		),
+		lookup_css_own(CSSOwnArray0, CSSPtr, CSSOwn0),
+		lookup_css_desc(CSSDescArray0, CSSPtr, CSSDesc0),
+		add_own_to_own(CSDOwn, CSSOwn0) = CSSOwn,
+		add_inherit_to_inherit(CSDDesc, CSSDesc0) = CSSDesc,
+		update_css_own(u(CSSOwnArray0), CSSPtr, CSSOwn,
+			CSSOwnArray),
+		update_css_desc(u(CSSDescArray0), CSSPtr, CSSDesc,
+			CSSDescArray)
 	;
-		error("emit nasal gorgons")
+		error("summarize_call_site_dynamic: invalid css ptr")
 	).
 
+%-----------------------------------------------------------------------------%
+
+:- pred summarize_modules(deep::in, deep::out) is det.
+
+summarize_modules(Deep0, Deep) :-
+	ModuleData0 = Deep0 ^ module_data,
+	ModuleData = map__map_values(summarize_module_costs(Deep0),
+		ModuleData0),
+	Deep = Deep0 ^ module_data := ModuleData.
+
+:- func summarize_module_costs(deep, string, module_data) = module_data.
+
+summarize_module_costs(Deep, _ModuleName, 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).
+
+:- pred accumulate_ps_costs(deep::in, proc_static_ptr::in,
+	own_prof_info::in, own_prof_info::out,
+	inherit_prof_info::in, inherit_prof_info::out) is det.
+
+accumulate_ps_costs(Deep, PSPtr, Own0, Own, Desc0, Desc) :-
+	deep_lookup_ps_own(Deep, PSPtr, PSOwn),
+	deep_lookup_ps_desc(Deep, PSPtr, PSDesc),
+	Own = add_own_to_own(Own0, PSOwn),
+	Desc = add_inherit_to_inherit(Desc0, PSDesc).
+
+%-----------------------------------------------------------------------------%
+
 :- pred propagate_to_clique(int::in, list(proc_dynamic_ptr)::in,
 	deep::in, deep::out) is det.
 
 propagate_to_clique(CliqueNumber, Members, Deep0, Deep) :-
 	array__lookup(Deep0 ^ clique_parents, CliqueNumber, ParentCSDPtr),
-	list__foldl(propagate_to_proc_dynamic(CliqueNumber, ParentCSDPtr),
-		Members, Deep0, Deep1),
-	(
-		valid_call_site_dynamic_ptr_raw(Deep1 ^ call_site_dynamics,
-			ParentCSDPtr)
-	->
-		lookup_call_site_dynamics(Deep1 ^ call_site_dynamics,
-			ParentCSDPtr, ParentCSD),
-		ParentOwnPI = ParentCSD ^ csd_own_prof,
+	list__foldl2(propagate_to_proc_dynamic(CliqueNumber, ParentCSDPtr),
+		Members, Deep0, Deep1, map__init, CSDZeroMap),
+	( valid_call_site_dynamic_ptr(Deep1, ParentCSDPtr) ->
+		deep_lookup_call_site_dynamics(Deep1, ParentCSDPtr, ParentCSD),
+		ParentOwn = ParentCSD ^ csd_own_prof,
 		deep_lookup_csd_desc(Deep1, ParentCSDPtr, ParentDesc0),
-		subtract_own_from_inherit(ParentOwnPI, ParentDesc0) =
+		subtract_own_from_inherit(ParentOwn, ParentDesc0) =
 			ParentDesc,
-		deep_update_csd_desc(Deep1, ParentCSDPtr, ParentDesc, Deep)
+		deep_update_csd_desc(Deep1, ParentCSDPtr, ParentDesc, Deep2),
+		deep_update_csd_zero_map(Deep2, ParentCSDPtr, CSDZeroMap, Deep)
 	;
 		Deep = Deep1
 	).
 
 :- pred propagate_to_proc_dynamic(int::in, call_site_dynamic_ptr::in,
-	proc_dynamic_ptr::in, deep::in, deep::out) is det.
+	proc_dynamic_ptr::in, deep::in, deep::out,
+	zero_total_map::in, zero_total_map::out) is det.
 
-propagate_to_proc_dynamic(CliqueNumber, ParentCSDPtr, PDPtr,
-		Deep0, Deep) :-
+propagate_to_proc_dynamic(CliqueNumber, ParentCSDPtr, PDPtr, Deep0, Deep,
+		CSDZeroMap0, CSDZeroMap) :-
 	flat_call_sites(Deep0 ^ proc_dynamics, PDPtr, CSDPtrs),
-	list__foldl(propagate_to_call_site(CliqueNumber, PDPtr),
-		CSDPtrs, Deep0, Deep1),
-	(
-		valid_call_site_dynamic_ptr_raw(Deep1 ^ call_site_dynamics,
-			ParentCSDPtr)
-	->
-		deep_lookup_csd_desc(Deep1, ParentCSDPtr, ParentDesc0),
-		deep_lookup_pd_desc(Deep1, PDPtr, DescPI),
-		deep_lookup_pd_own(Deep1, PDPtr, OwnPI),
-		add_own_to_inherit(OwnPI, ParentDesc0) = ParentDesc1,
-		add_inherit_to_inherit(DescPI, ParentDesc1) = ParentDesc,
-		deep_update_csd_desc(Deep1, ParentCSDPtr, ParentDesc, Deep)
+	list__foldl2(propagate_to_call_site(CliqueNumber, PDPtr),
+		CSDPtrs, Deep0, Deep1, map__init, PDZeroMap),
+	deep_update_pd_zero_map(Deep1, PDPtr, PDZeroMap, Deep2),
+
+	deep_lookup_pd_desc(Deep2, PDPtr, ProcDesc),
+	deep_lookup_pd_own(Deep2, PDPtr, ProcOwn),
+	ProcTotal = add_own_to_inherit(ProcOwn, ProcDesc),
+
+	CSDZeroMap1 = add_zero_maps(CSDZeroMap0, PDZeroMap),
+	deep_lookup_proc_dynamics(Deep2, PDPtr, PD),
+	PSPtr = PD ^ pd_proc_static,
+	deep_lookup_proc_statics(Deep2, PSPtr, PS),
+	( PS ^ ps_is_zeroed = zeroed ->
+		CSDZeroMap = add_to_zero_map(CSDZeroMap1, PSPtr, ProcTotal)
 	;
-		Deep = Deep1
+		CSDZeroMap = CSDZeroMap1
+	),
+
+	( valid_call_site_dynamic_ptr(Deep1, ParentCSDPtr) ->
+		deep_lookup_csd_desc(Deep2, ParentCSDPtr, ParentDesc0),
+		ParentDesc = add_inherit_to_inherit(ParentDesc0, ProcTotal),
+		deep_update_csd_desc(Deep2, ParentCSDPtr, ParentDesc, Deep)
+	;
+		Deep = Deep2
 	).
 
 :- pred propagate_to_call_site(int::in, proc_dynamic_ptr::in,
-	call_site_dynamic_ptr::in, deep::in, deep::out) is det.
+	call_site_dynamic_ptr::in, deep::in, deep::out,
+	zero_total_map::in, zero_total_map::out) is det.
+
+propagate_to_call_site(CliqueNumber, PDPtr, CSDPtr, Deep0, Deep,
+		PDZeroMap0, PDZeroMap) :-
+	deep_lookup_call_site_dynamics(Deep0, CSDPtr, CSD),
+	CalleeOwn = CSD ^ csd_own_prof,
+	CalleePDPtr = CSD ^ csd_callee,
+	deep_lookup_clique_index(Deep0, CalleePDPtr, ChildCliquePtr),
+	ChildCliquePtr = clique_ptr(ChildCliqueNumber),
+	( ChildCliqueNumber \= CliqueNumber ->
+		deep_lookup_pd_desc(Deep0, PDPtr, ProcDesc0),
+		deep_lookup_csd_desc(Deep0, CSDPtr, CalleeDesc),
+		CalleeTotal = add_own_to_inherit(CalleeOwn, CalleeDesc),
+		ProcDesc = add_inherit_to_inherit(ProcDesc0, CalleeTotal),
+		deep_update_pd_desc(Deep0, PDPtr, ProcDesc, Deep),
+		deep_lookup_csd_zero_map(Deep, CSDPtr, CSDZeroMap),
+		PDZeroMap = add_zero_maps(PDZeroMap0, CSDZeroMap)
+	;
+		% We don't propagate profiling measurements
+		% along intra-clique calls.
+		Deep = Deep0,
+		PDZeroMap = PDZeroMap0
+	).
+
+%-----------------------------------------------------------------------------%
+
+:- func add_zero_maps(zero_total_map, zero_total_map) = zero_total_map.
+
+add_zero_maps(ZeroMap1, ZeroMap2) = ZeroMap :-
+	( map__is_empty(ZeroMap1) ->
+		ZeroMap = ZeroMap2
+	; map__is_empty(ZeroMap2) ->
+		ZeroMap = ZeroMap1
+	;
+		ZeroMap = map__union(add_inherit_to_inherit,
+			ZeroMap1, ZeroMap2)
+	).
+
+:- func add_to_zero_map(zero_total_map, proc_static_ptr, inherit_prof_info) =
+	zero_total_map.
+
+add_to_zero_map(ZeroMap0, PSPtr, PDTotal) = ZeroMap :-
+	% Even if the map already contained an entry for PSPtr, the costs
+	% incurred by the calls represented by that entry have already been
+	% propagated to PDTotal.
+	map__set(ZeroMap0, PSPtr, PDTotal, ZeroMap).
 
-propagate_to_call_site(CliqueNumber, PDPtr, CSDPtr, Deep0, Deep) :-
+%-----------------------------------------------------------------------------%
+
+:- pred flat_call_sites(proc_dynamics::in, proc_dynamic_ptr::in,
+	list(call_site_dynamic_ptr)::out) is det.
+
+flat_call_sites(ProcDynamics, PDPtr, CSDPtrs) :-
+	lookup_proc_dynamics(ProcDynamics, PDPtr, PD),
+	CallSiteArray = PD ^ pd_sites,
+	flatten_call_sites(CallSiteArray, CSDPtrs, _).
+
+:- pred flatten_call_sites(array(call_site_array_slot)::in,
+	list(call_site_dynamic_ptr)::out, is_zeroed::out) is det.
+
+flatten_call_sites(CallSiteArray, CSDPtrs, IsZeroed) :-
+	array__to_list(CallSiteArray, CallSites),
+	list__foldl2(gather_call_site_csdptrs, CallSites, [], CSDPtrsList0,
+		not_zeroed, IsZeroed),
+	list__reverse(CSDPtrsList0, CSDPtrsList),
+	list__condense(CSDPtrsList, CSDPtrs).
+
+:- pred gather_call_site_csdptrs(call_site_array_slot::in, 
+	list(list(call_site_dynamic_ptr))::in,
+	list(list(call_site_dynamic_ptr))::out,
+	is_zeroed::in, is_zeroed::out) is det.
+
+gather_call_site_csdptrs(Slot, CSDPtrs0, CSDPtrs1, IsZeroed0, IsZeroed) :-
+	(
+		Slot = normal(CSDPtr),
 	CSDPtr = call_site_dynamic_ptr(CSDI),
 	( CSDI > 0 ->
-		array__lookup(Deep0 ^ call_site_dynamics, CSDI, CSD),
-		CPDPtr = CSD ^ csd_callee,
-		CPI = CSD ^ csd_own_prof,
-		CPDPtr = proc_dynamic_ptr(CPDI),
-		( CPDI > 0 ->
-			array__lookup(Deep0 ^ clique_index, CPDI,
-				clique_ptr(ChildCliqueNumber)),
-			( ChildCliqueNumber \= CliqueNumber ->
-				PDPtr = proc_dynamic_ptr(PDI),
-				array__lookup(Deep0 ^ pd_desc, PDI, PDTotal0),
-				array__lookup(Deep0 ^ csd_desc, CSDI, CDesc),
-				add_own_to_inherit(CPI, PDTotal0) = PDTotal1,
-				add_inherit_to_inherit(CDesc, PDTotal1)
-					= PDTotal,
-				array__set(u(Deep0 ^ pd_desc), PDI, PDTotal,
-					PDDesc),
-				Deep = Deep0 ^ pd_desc := PDDesc
+			CSDPtrs1 = [[CSDPtr] | CSDPtrs0]
 			;
-				Deep = Deep0
-			)
+			CSDPtrs1 = CSDPtrs0
+		),
+		IsZeroed = IsZeroed0
 		;
-			Deep = Deep0
-		)
+		Slot = multi(IsZeroed1, PtrArray),
+		array__to_list(PtrArray, PtrList0),
+		list__filter((pred(CSDPtr::in) is semidet :-
+			CSDPtr = call_site_dynamic_ptr(CSDI),
+			CSDI > 0
+		), PtrList0, PtrList1),
+		CSDPtrs1 = [PtrList1 | CSDPtrs0],
+		( IsZeroed1 = zeroed ->
+			IsZeroed = zeroed
 	;
-		Deep = Deep0
+			IsZeroed = IsZeroed0
+		)
 	).
 
 %-----------------------------------------------------------------------------%
Index: deep_profiler/timeout.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/timeout.m,v
retrieving revision 1.1
diff -u -b -r1.1 timeout.m
--- deep_profiler/timeout.m	2001/05/31 05:59:59	1.1
+++ deep_profiler/timeout.m	2001/06/28 16:04:52
@@ -20,9 +20,18 @@
 % there is no simple, portable way to eliminate the race condition, and the
 % window of vulnerability is quite small.
 %
-% This module also provides a predicate for executing the timeout action
-% explicitly, for use when the user explicitly shuts down the server.
-% This avoids double maintenance of the shutdown actions.
+% This module also sets up the automatic execution of the timeout action
+% when the process exits, for use both when the user explicitly requests
+% the shutdown of the server (which will of course happen after startup)
+% and in case of program aborts (which may happen both before and after
+% startup). However, immediately after startup is complete, the server
+% process forks, with the parent exiting to let mdprof_cgi's wait finish,
+% and the child entering a loop waiting for requests.
+%
+% We establish the exit action to clean up the files as soon as they are
+% created, but we don't want the parent process after the fork to delete them
+% while they are still in use by the child process. This is prevented by the
+% boolean flag process_is_detached_server.
 
 :- module timeout.
 
@@ -30,10 +39,10 @@
 
 :- import_module io.
 
-:- pred setup_timeout(string::in, string::in, string::in, int::in,
+:- pred setup_exit(string::in, string::in, string::in,
 	io__state::di, io__state::uo) is det.
 
-:- pred execute_timeout_action(io__state::di, io__state::uo) is det.
+:- pred setup_timeout(int::in, io__state::di, io__state::uo) is det.
 
 :- implementation.
 
@@ -43,25 +52,65 @@
 :- pragma foreign_decl("C",
 "
 #include <stdio.h>
-#include <signal.h>
+#include <signal.h>	/* for signal numbers */
+#include <unistd.h>	/* for alarm() */
+#include <stdlib.h>	/* for atexit() */
 #include ""mercury_signal.h""
 
 extern	char	*MP_timeout_file1;
 extern	char	*MP_timeout_file2;
 extern	char	*MP_timeout_file3;
 
-extern	void	delete_timeout_files_and_exit(void);
+#define	MDPROF_NUM_SIGNAL_NUMBERS	12
+
+extern	const int	signal_numbers[MDPROF_NUM_SIGNAL_NUMBERS];
+
+extern	void		delete_timeout_files(void);
+extern	void		delete_timeout_files_and_exit_success(void);
+extern	void		delete_timeout_files_and_exit_failure(void);
 ").
 
 :- pragma foreign_code("C",
 "
+bool	process_is_detached_server = FALSE;
 char	*MP_timeout_file1;
 char	*MP_timeout_file2;
 char	*MP_timeout_file3;
 
+/*
+** SIGALRM alarm signal indicates a timeout. SIGTERM usually indicates the
+** machine is being shut down. The others are there to catch forceful shutdowns
+** during development, both intentional ones where the programmer sends the
+** signal and those caused by bugs in the server code. We would like to include
+** all catchable, fatal signals in this list, but that set is somewhat OS
+** dependent. The set here is the widely portable subset.
+**
+** We could avoid this problem if we had a version of atexit that executed
+** its actions even when the program exits after a signal.
+*/
+
+const int	signal_numbers[MDPROF_NUM_SIGNAL_NUMBERS] =
+{
+	SIGALRM,
+	SIGTERM,
+	SIGHUP,
+	SIGINT,
+	SIGQUIT,
+
+	SIGILL,
+	SIGABRT,
+	SIGIOT,
+	SIGBUS,
+	SIGFPE,
+
+	SIGSEGV,
+	SIGPIPE
+};
+
 void
-delete_timeout_files_and_exit(void)
+delete_timeout_files(void)
 {
+	if (! process_is_detached_server) {
 	if (remove(MP_timeout_file1) != 0) {
 		perror(MP_timeout_file1);
 	}
@@ -73,32 +122,60 @@
 	if (remove(MP_timeout_file3) != 0) {
 		perror(MP_timeout_file3);
 	}
+	}
+}
 
-	exit(0);
+void
+delete_timeout_files_and_exit_success(void)
+{
+	delete_timeout_files();
+	exit(EXIT_SUCCESS);
 }
+
+void
+delete_timeout_files_and_exit_failure(void)
+{
+	delete_timeout_files();
+	exit(EXIT_FAILURE);
+}
 ").
 
 :- pragma foreign_proc("C",
-	setup_timeout(File1::in, File2::in, File3::in, Minutes::in,
-		IO0::di, IO::uo),
+	setup_exit(File1::in, File2::in, File3::in, IO0::di, IO::uo),
 	[will_not_call_mercury],
 "
-	int	seconds;
+	int	i;
+	void	(*handler)(void);
 
-	seconds = Minutes * 60;
 	MP_timeout_file1 = File1;
 	MP_timeout_file2 = File2;
 	MP_timeout_file3 = File3;
-	MR_setup_signal(SIGALRM, delete_timeout_files_and_exit, FALSE,
-		""Mercury deep profiler: cannot setup timeout"");
-	(void) alarm(seconds);
+
+	for (i = 0; i < MDPROF_NUM_SIGNAL_NUMBERS; i++) {
+		if (signal_numbers[i] == SIGALRM) {
+			handler = delete_timeout_files_and_exit_success;
+		} else {
+			handler = delete_timeout_files_and_exit_failure;
+		}
+
+		MR_setup_signal(signal_numbers[i], handler, FALSE,
+			""Mercury deep profiler: cannot setup signal exit"");
+	}
+
+	if (atexit(delete_timeout_files) != 0) {
+		MR_fatal_error(""Mercury deep profiler: cannot setup exit"");
+	}
+
 	IO = IO0;
 ").
 
 :- pragma foreign_proc("C",
-	execute_timeout_action(IO0::di, IO::uo),
+	setup_timeout(Minutes::in, IO0::di, IO::uo),
 	[will_not_call_mercury],
 "
-	delete_timeout_files_and_exit();
+	int	seconds;
+
+	seconds = Minutes * 60;
+	(void) alarm(seconds);
 	IO = IO0;
 ").
Index: deep_profiler/top_procs.m
===================================================================
RCS file: top_procs.m
diff -N top_procs.m
--- /dev/null	Fri Dec  1 02:25:58 2000
+++ top_procs.m	Mon Jul  2 20:31:59 2001
@@ -0,0 +1,884 @@
+%-----------------------------------------------------------------------------%
+% Copyright (C) 2001 The University of Melbourne.
+% This file may only be copied under the terms of the GNU General
+% Public License - see the file COPYING in the Mercury distribution.
+%-----------------------------------------------------------------------------%
+%
+% Author: zs.
+%
+% This module contains code to find the top procedures by several criteria.
+%
+% For comparisons on costs, we sort highest first. For comparisons on names and
+% contexts, we sort lowest first. This is consistently what users want.
+
+:- module top_procs.
+
+:- interface.
+
+:- import_module profile, interface, measurements.
+:- import_module std_util, list.
+
+:- func find_top_procs(cost_kind, include_descendants, measurement_scope,
+	display_limit, deep) = maybe_error(list(int)).
+
+	% A line group consists of a first line, and optionally a group of
+	% later lines. The first line is structured as a sequence of fields,
+	% which may have one or two id fields but must have all the measurement
+	% fields demanded by the current preferences. The later lines must all
+	% be full rows.
+
+:- type line_group(FL, LL)
+	--->	line_group(
+			group_filename		:: string,
+			group_linenumber	:: int,
+			group_name		:: string,
+			group_own		:: own_prof_info,
+			group_desc		:: inherit_prof_info,
+			group_first_line_id	:: string,
+			group_later_lines	:: LL
+		).
+
+:- func sort_line_groups(order_criteria, list(line_group(FL, LL)))
+	= list(line_group(FL, LL)).
+
+:- pred sum_line_group_measurements(list(line_group(FL, LL))::in,
+	own_prof_info::out, inherit_prof_info::out) is det.
+
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module bool, int, float, array, require.
+
+find_top_procs(Sort, InclDesc, Scope, Limit, Deep) = MaybeTopPSIs :-
+	find_top_sort_predicate(Sort, InclDesc, Scope, SortCompatible,
+		RawSortFunc, FilterPred),
+	(
+		SortCompatible = no,
+		MaybeTopPSIs = error("bad sort specification")
+	;
+		SortCompatible = yes,
+		ProcStatics = Deep ^ proc_statics,
+		array__max(ProcStatics, MaxProcStatic),
+		PSIs0 = int_list_from_to(1, MaxProcStatic),
+		deep_lookup_proc_dynamics(Deep, Deep ^ root, RootPD),
+		RootPD ^ pd_proc_static = proc_static_ptr(RootPSI),
+		list__filter(filter_top_procs(Deep, RootPSI, FilterPred),
+			PSIs0, PSIs),
+		SortPred = (pred(PSI1::in, PSI2::in, ComparisonResult::out)
+				is det :-
+			ComparisonResult = compare_procs_fallback(RawSortFunc,
+				Deep, PSI1, PSI2)
+		),
+		list__sort(SortPred, PSIs, DescendingPSIs),
+		(
+			Limit = rank_range(First, Last),
+			(
+				list__drop(First - 1, DescendingPSIs,
+					RemainingPSIs)
+			->
+				list__take_upto(Last - First + 1,
+					RemainingPSIs, TopPSIs),
+				MaybeTopPSIs = ok(TopPSIs)
+			;
+				MaybeTopPSIs = ok([])
+			)
+		;
+			Limit = threshold(Threshold),
+			find_threshold_predicate(Sort, InclDesc,
+				ThresholdCompatible, RawThresholdPred),
+			(
+				ThresholdCompatible = no,
+				MaybeTopPSIs =
+					error("bad threshold specification")
+			;
+				ThresholdCompatible = yes,
+				ThresholdPred = (pred(PSI::in) is semidet :-
+					RawThresholdPred(Deep, Threshold, PSI)
+				),
+				list__takewhile(ThresholdPred, DescendingPSIs,
+					TopPSIs, _),
+				MaybeTopPSIs = ok(TopPSIs)
+			)
+		)
+	).
+
+:- func int_list_from_to(int, int) = list(int).
+
+int_list_from_to(From, To) = List :-
+	( From > To ->
+		List = []
+	;
+		List = [From | int_list_from_to(From + 1, To)]
+	).
+
+%-----------------------------------------------------------------------------%
+
+:- type compare_proc_statics ==
+	(func(deep, int, int) = comparison_result).
+
+:- func compare_procs_fallback(compare_proc_statics, deep, int, int)
+	= comparison_result.
+
+compare_procs_fallback(MainFunc, Deep, PSI1, PSI2) = Result :-
+	Result0 = MainFunc(Deep, PSI1, PSI2),
+	( Result0 \= (=) ->
+		Result = Result0
+	;
+		Result1 = compare_ps_time_both_overall(Deep, PSI1, PSI2),
+		( Result1 \= (=) ->
+			Result = Result1
+		;
+			Result = compare_ps_words_both_overall(Deep,
+				PSI1, PSI2)
+		)
+	).
+
+%-----------------------------------------------------------------------------%
+
+:- pred filter_top_procs(deep, int, pred(deep, int), int).
+:- mode filter_top_procs(in, in, pred(in, in) is semidet, in) is semidet.
+
+filter_top_procs(Deep, RootPSI, FilterPred, PSI) :-
+	PSI \= RootPSI,
+	FilterPred(Deep, PSI).
+
+:- pred find_top_sort_predicate(cost_kind, include_descendants,
+	measurement_scope, bool,
+	compare_proc_statics, pred(deep, int)).
+:- mode find_top_sort_predicate(in, in, in, out,
+	out(func(in, in, in) = out is det),
+	out(pred(in, in) is semidet)) is det.
+
+find_top_sort_predicate(calls,  self,          overall,  yes,
+	compare_ps_calls_self_overall,  filter_ps_calls_self).
+find_top_sort_predicate(calls,  self,          per_call, no,
+	compare_ps_calls_self_overall,  filter_ps_calls_self).
+find_top_sort_predicate(calls,  self_and_desc, overall,  no,
+	compare_ps_calls_self_overall,  filter_ps_calls_self).
+find_top_sort_predicate(calls,  self_and_desc, per_call, no,
+	compare_ps_calls_self_overall,  filter_ps_calls_self).
+find_top_sort_predicate(time,   self,          overall,  yes,
+	compare_ps_time_self_overall,   filter_ps_time_self).
+find_top_sort_predicate(time,   self,          per_call, yes,
+	compare_ps_time_self_percall,   filter_ps_time_self).
+find_top_sort_predicate(time,   self_and_desc, overall,  yes,
+	compare_ps_time_both_overall,   filter_ps_time_both).
+find_top_sort_predicate(time,   self_and_desc, per_call, yes,
+	compare_ps_time_both_percall,   filter_ps_time_both).
+find_top_sort_predicate(allocs, self,          overall,  yes,
+	compare_ps_allocs_self_overall, filter_ps_allocs_self).
+find_top_sort_predicate(allocs, self,          per_call, yes,
+	compare_ps_allocs_self_percall, filter_ps_allocs_self).
+find_top_sort_predicate(allocs, self_and_desc, overall,  yes,
+	compare_ps_allocs_both_overall, filter_ps_allocs_both).
+find_top_sort_predicate(allocs, self_and_desc, per_call, yes,
+	compare_ps_allocs_both_percall, filter_ps_allocs_both).
+find_top_sort_predicate(words,  self,          overall,  yes,
+	compare_ps_words_self_overall,  filter_ps_words_self).
+find_top_sort_predicate(words,  self,          per_call, yes,
+	compare_ps_words_self_percall,  filter_ps_words_self).
+find_top_sort_predicate(words,  self_and_desc, overall,  yes,
+	compare_ps_words_both_overall,  filter_ps_words_both).
+find_top_sort_predicate(words,  self_and_desc, per_call, yes,
+	compare_ps_words_both_percall,  filter_ps_words_both).
+
+:- pred find_threshold_predicate(cost_kind, include_descendants,
+	bool, pred(deep, float, int)).
+:- mode find_threshold_predicate(in, in, out, out(pred(in, in, in) is semidet))
+	is det.
+
+find_threshold_predicate(calls,  self,          no,  threshold_ps_time_self).
+find_threshold_predicate(calls,  self_and_desc, no,  threshold_ps_time_self).
+find_threshold_predicate(time,   self,          yes, threshold_ps_time_self).
+find_threshold_predicate(time,   self_and_desc, yes, threshold_ps_time_both).
+find_threshold_predicate(allocs, self,          yes, threshold_ps_allocs_self).
+find_threshold_predicate(allocs, self_and_desc, yes, threshold_ps_allocs_both).
+find_threshold_predicate(words,  self,          yes, threshold_ps_words_self).
+find_threshold_predicate(words,  self_and_desc, yes, threshold_ps_words_both).
+
+%-----------------------------------------------------------------------------%
+
+:- func compare_ps_calls_self_overall(deep, int, int) = comparison_result.
+
+compare_ps_calls_self_overall(Deep, PSI1, PSI2) = Result :-
+	PSOwn = Deep ^ ps_own,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSOwn, PSI2, Own2),
+	OwnCalls1 = calls(Own1),
+	OwnCalls2 = calls(Own2),
+	compare(Result, OwnCalls2, OwnCalls1).
+
+:- func compare_ps_time_self_overall(deep, int, int) = comparison_result.
+
+compare_ps_time_self_overall(Deep, PSI1, PSI2) = Result :-
+	PSOwn = Deep ^ ps_own,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSOwn, PSI2, Own2),
+	OwnQuanta1 = quanta(Own1),
+	OwnQuanta2 = quanta(Own2),
+	compare(Result, OwnQuanta2, OwnQuanta1).
+
+:- func compare_ps_time_self_percall(deep, int, int) = comparison_result.
+
+compare_ps_time_self_percall(Deep, PSI1, PSI2) = Result :-
+	PSOwn = Deep ^ ps_own,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSOwn, PSI2, Own2),
+	Calls1 = calls(Own1),
+	Calls2 = calls(Own2),
+	OwnQuanta1 = quanta(Own1),
+	OwnQuanta2 = quanta(Own2),
+	OwnQuantaPerCall1 = float(OwnQuanta1) / float(Calls1),
+	OwnQuantaPerCall2 = float(OwnQuanta2) / float(Calls2),
+	compare(Result, OwnQuantaPerCall2, OwnQuantaPerCall1).
+
+:- func compare_ps_time_both_overall(deep, int, int) = comparison_result.
+
+compare_ps_time_both_overall(Deep, PSI1, PSI2) = Result :-
+	PSOwn = Deep ^ ps_own,
+	PSDesc = Deep ^ ps_desc,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSOwn, PSI2, Own2),
+	array__lookup(PSDesc, PSI1, Desc1),
+	array__lookup(PSDesc, PSI2, Desc2),
+	OwnQuanta1 = quanta(Own1),
+	OwnQuanta2 = quanta(Own2),
+	DescQuanta1 = inherit_quanta(Desc1),
+	DescQuanta2 = inherit_quanta(Desc2),
+	TotalQuanta1 = OwnQuanta1 + DescQuanta1,
+	TotalQuanta2 = OwnQuanta2 + DescQuanta2,
+	compare(Result, TotalQuanta2, TotalQuanta1).
+
+:- func compare_ps_time_both_percall(deep, int, int) = comparison_result.
+
+compare_ps_time_both_percall(Deep, PSI1, PSI2) = Result :-
+	PSOwn = Deep ^ ps_own,
+	PSDesc = Deep ^ ps_desc,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSOwn, PSI2, Own2),
+	array__lookup(PSDesc, PSI1, Desc1),
+	array__lookup(PSDesc, PSI2, Desc2),
+	Calls1 = calls(Own1),
+	Calls2 = calls(Own2),
+	OwnQuanta1 = quanta(Own1),
+	OwnQuanta2 = quanta(Own2),
+	DescQuanta1 = inherit_quanta(Desc1),
+	DescQuanta2 = inherit_quanta(Desc2),
+	TotalQuanta1 = OwnQuanta1 + DescQuanta1,
+	TotalQuanta2 = OwnQuanta2 + DescQuanta2,
+	TotalQuantaPerCall1 = float(TotalQuanta1) / float(Calls1),
+	TotalQuantaPerCall2 = float(TotalQuanta2) / float(Calls2),
+	compare(Result, TotalQuantaPerCall2, TotalQuantaPerCall1).
+
+:- func compare_ps_allocs_self_overall(deep, int, int) = comparison_result.
+
+compare_ps_allocs_self_overall(Deep, PSI1, PSI2) = Result :-
+	PSOwn = Deep ^ ps_own,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSOwn, PSI2, Own2),
+	OwnAlloc1 = allocs(Own1),
+	OwnAlloc2 = allocs(Own2),
+	compare(Result, OwnAlloc2, OwnAlloc1).
+
+:- func compare_ps_allocs_self_percall(deep, int, int) = comparison_result.
+
+compare_ps_allocs_self_percall(Deep, PSI1, PSI2) = Result :-
+	PSOwn = Deep ^ ps_own,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSOwn, PSI2, Own2),
+	Calls1 = calls(Own1),
+	Calls2 = calls(Own2),
+	OwnAlloc1 = allocs(Own1),
+	OwnAlloc2 = allocs(Own2),
+	OwnAllocPerCall1 = float(OwnAlloc1) / float(Calls1),
+	OwnAllocPerCall2 = float(OwnAlloc2) / float(Calls2),
+	compare(Result, OwnAllocPerCall2, OwnAllocPerCall1).
+
+:- func compare_ps_allocs_both_overall(deep, int, int) = comparison_result.
+
+compare_ps_allocs_both_overall(Deep, PSI1, PSI2) = Result :-
+	PSOwn = Deep ^ ps_own,
+	PSDesc = Deep ^ ps_desc,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSOwn, PSI2, Own2),
+	array__lookup(PSDesc, PSI1, Desc1),
+	array__lookup(PSDesc, PSI2, Desc2),
+	OwnAlloc1 = allocs(Own1),
+	OwnAlloc2 = allocs(Own2),
+	DescAlloc1 = inherit_allocs(Desc1),
+	DescAlloc2 = inherit_allocs(Desc2),
+	TotalAlloc1 = OwnAlloc1 + DescAlloc1,
+	TotalAlloc2 = OwnAlloc2 + DescAlloc2,
+	compare(Result, TotalAlloc2, TotalAlloc1).
+
+:- func compare_ps_allocs_both_percall(deep, int, int) = comparison_result.
+
+compare_ps_allocs_both_percall(Deep, PSI1, PSI2) = Result :-
+	PSOwn = Deep ^ ps_own,
+	PSDesc = Deep ^ ps_desc,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSOwn, PSI2, Own2),
+	array__lookup(PSDesc, PSI1, Desc1),
+	array__lookup(PSDesc, PSI2, Desc2),
+	Calls1 = calls(Own1),
+	Calls2 = calls(Own2),
+	OwnAlloc1 = allocs(Own1),
+	OwnAlloc2 = allocs(Own2),
+	DescAlloc1 = inherit_allocs(Desc1),
+	DescAlloc2 = inherit_allocs(Desc2),
+	TotalAlloc1 = OwnAlloc1 + DescAlloc1,
+	TotalAlloc2 = OwnAlloc2 + DescAlloc2,
+	TotalAllocPerCall1 = float(TotalAlloc1) / float(Calls1),
+	TotalAllocPerCall2 = float(TotalAlloc2) / float(Calls2),
+	compare(Result, TotalAllocPerCall2, TotalAllocPerCall1).
+
+:- func compare_ps_words_self_overall(deep, int, int) = comparison_result.
+
+compare_ps_words_self_overall(Deep, PSI1, PSI2) = Result :-
+	PSOwn = Deep ^ ps_own,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSOwn, PSI2, Own2),
+	OwnWords1 = words(Own1),
+	OwnWords2 = words(Own2),
+	compare(Result, OwnWords2, OwnWords1).
+
+:- func compare_ps_words_self_percall(deep, int, int) = comparison_result.
+
+compare_ps_words_self_percall(Deep, PSI1, PSI2) = Result :-
+	PSOwn = Deep ^ ps_own,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSOwn, PSI2, Own2),
+	Calls1 = calls(Own1),
+	Calls2 = calls(Own2),
+	OwnWords1 = words(Own1),
+	OwnWords2 = words(Own2),
+	OwnWordsPerCall1 = float(OwnWords1) / float(Calls1),
+	OwnWordsPerCall2 = float(OwnWords2) / float(Calls2),
+	compare(Result, OwnWordsPerCall2, OwnWordsPerCall1).
+
+:- func compare_ps_words_both_overall(deep, int, int) = comparison_result.
+
+compare_ps_words_both_overall(Deep, PSI1, PSI2) = Result :-
+	PSOwn = Deep ^ ps_own,
+	PSDesc = Deep ^ ps_desc,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSOwn, PSI2, Own2),
+	array__lookup(PSDesc, PSI1, Desc1),
+	array__lookup(PSDesc, PSI2, Desc2),
+	OwnWords1 = words(Own1),
+	OwnWords2 = words(Own2),
+	DescWords1 = inherit_words(Desc1),
+	DescWords2 = inherit_words(Desc2),
+	TotalWords1 = OwnWords1 + DescWords1,
+	TotalWords2 = OwnWords2 + DescWords2,
+	compare(Result, TotalWords2, TotalWords1).
+
+:- func compare_ps_words_both_percall(deep, int, int) = comparison_result.
+
+compare_ps_words_both_percall(Deep, PSI1, PSI2) = Result :-
+	PSOwn = Deep ^ ps_own,
+	PSDesc = Deep ^ ps_desc,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSOwn, PSI2, Own2),
+	array__lookup(PSDesc, PSI1, Desc1),
+	array__lookup(PSDesc, PSI2, Desc2),
+	Calls1 = calls(Own1),
+	Calls2 = calls(Own2),
+	OwnWords1 = words(Own1),
+	OwnWords2 = words(Own2),
+	DescWords1 = inherit_words(Desc1),
+	DescWords2 = inherit_words(Desc2),
+	TotalWords1 = OwnWords1 + DescWords1,
+	TotalWords2 = OwnWords2 + DescWords2,
+	TotalWordsPerCall1 = float(TotalWords1) / float(Calls1),
+	TotalWordsPerCall2 = float(TotalWords2) / float(Calls2),
+	compare(Result, TotalWordsPerCall2, TotalWordsPerCall1).
+
+%-----------------------------------------------------------------------------%
+
+:- pred filter_ps_calls_self(deep::in, int::in) is semidet.
+
+filter_ps_calls_self(Deep, PSI1) :-
+	PSOwn = Deep ^ ps_own,
+	array__lookup(PSOwn, PSI1, Own1),
+	OwnCalls1 = calls(Own1),
+	OwnCalls1 > 0.
+
+:- pred filter_ps_time_self(deep::in, int::in) is semidet.
+
+filter_ps_time_self(Deep, PSI1) :-
+	PSOwn = Deep ^ ps_own,
+	array__lookup(PSOwn, PSI1, Own1),
+	OwnQuanta1 = quanta(Own1),
+	OwnQuanta1 > 0.
+
+:- pred filter_ps_time_both(deep::in, int::in) is semidet.
+
+filter_ps_time_both(Deep, PSI1) :-
+	PSOwn = Deep ^ ps_own,
+	PSDesc = Deep ^ ps_desc,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSDesc, PSI1, Desc1),
+	OwnQuanta1 = quanta(Own1),
+	DescQuanta1 = inherit_quanta(Desc1),
+	TotalQuanta1 = OwnQuanta1 + DescQuanta1,
+	TotalQuanta1 > 0.
+
+:- pred filter_ps_allocs_self(deep::in, int::in) is semidet.
+
+filter_ps_allocs_self(Deep, PSI1) :-
+	PSOwn = Deep ^ ps_own,
+	array__lookup(PSOwn, PSI1, Own1),
+	OwnAlloc1 = allocs(Own1),
+	OwnAlloc1 > 0.
+
+:- pred filter_ps_allocs_both(deep::in, int::in) is semidet.
+
+filter_ps_allocs_both(Deep, PSI1) :-
+	PSOwn = Deep ^ ps_own,
+	PSDesc = Deep ^ ps_desc,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSDesc, PSI1, Desc1),
+	OwnAlloc1 = allocs(Own1),
+	DescAlloc1 = inherit_allocs(Desc1),
+	TotalAlloc1 = OwnAlloc1 + DescAlloc1,
+	TotalAlloc1 > 0.
+
+:- pred filter_ps_words_self(deep::in, int::in) is semidet.
+
+filter_ps_words_self(Deep, PSI1) :-
+	PSOwn = Deep ^ ps_own,
+	array__lookup(PSOwn, PSI1, Own1),
+	OwnWords1 = words(Own1),
+	OwnWords1 > 0.
+
+:- pred filter_ps_words_both(deep::in, int::in) is semidet.
+
+filter_ps_words_both(Deep, PSI1) :-
+	PSOwn = Deep ^ ps_own,
+	PSDesc = Deep ^ ps_desc,
+	array__lookup(PSOwn, PSI1, Own1),
+	array__lookup(PSDesc, PSI1, Desc1),
+	OwnWords1 = words(Own1),
+	DescWords1 = inherit_words(Desc1),
+	TotalWords1 = OwnWords1 + DescWords1,
+	TotalWords1 > 0.
+
+%-----------------------------------------------------------------------------%
+
+:- pred threshold_ps_time_self(deep::in, float::in, int::in) is semidet.
+
+threshold_ps_time_self(Deep, Threshold, PSI) :-
+	PSOwn = Deep ^ ps_own,
+	array__lookup(PSOwn, PSI, Own),
+	RootOwn = root_own_info(Deep),
+	RootDesc = root_desc_info(Deep),
+	OwnQuanta = quanta(Own),
+	RootOwnQuanta = quanta(RootOwn),
+	RootDescQuanta = inherit_quanta(RootDesc),
+	RootTotalQuanta = RootOwnQuanta + RootDescQuanta,
+	100.0 * float(OwnQuanta) > Threshold * float(RootTotalQuanta).
+
+:- pred threshold_ps_time_both(deep::in, float::in, int::in) is semidet.
+
+threshold_ps_time_both(Deep, Threshold, PSI) :-
+	PSOwn = Deep ^ ps_own,
+	PSDesc = Deep ^ ps_desc,
+	array__lookup(PSOwn, PSI, Own),
+	array__lookup(PSDesc, PSI, Desc),
+	RootOwn = root_own_info(Deep),
+	RootDesc = root_desc_info(Deep),
+	OwnQuanta = quanta(Own),
+	RootOwnQuanta = quanta(RootOwn),
+	DescQuanta = inherit_quanta(Desc),
+	RootDescQuanta = inherit_quanta(RootDesc),
+	TotalQuanta = OwnQuanta + DescQuanta,
+	RootTotalQuanta = RootOwnQuanta + RootDescQuanta,
+	100.0 * float(TotalQuanta) > Threshold * float(RootTotalQuanta).
+
+:- pred threshold_ps_allocs_self(deep::in, float::in, int::in) is semidet.
+
+threshold_ps_allocs_self(Deep, Threshold, PSI) :-
+	PSOwn = Deep ^ ps_own,
+	array__lookup(PSOwn, PSI, Own),
+	RootOwn = root_own_info(Deep),
+	RootDesc = root_desc_info(Deep),
+	OwnAlloc = allocs(Own),
+	RootOwnAlloc = allocs(RootOwn),
+	RootDescAlloc = inherit_allocs(RootDesc),
+	RootTotalAlloc = RootOwnAlloc + RootDescAlloc,
+	100.0 * float(OwnAlloc) > Threshold * float(RootTotalAlloc).
+
+:- pred threshold_ps_allocs_both(deep::in, float::in, int::in) is semidet.
+
+threshold_ps_allocs_both(Deep, Threshold, PSI) :-
+	PSOwn = Deep ^ ps_own,
+	PSDesc = Deep ^ ps_desc,
+	array__lookup(PSOwn, PSI, Own),
+	array__lookup(PSDesc, PSI, Desc),
+	RootOwn = root_own_info(Deep),
+	RootDesc = root_desc_info(Deep),
+	OwnAlloc = allocs(Own),
+	RootOwnAlloc = allocs(RootOwn),
+	DescAlloc = inherit_allocs(Desc),
+	RootDescAlloc = inherit_allocs(RootDesc),
+	TotalAlloc = OwnAlloc + DescAlloc,
+	RootTotalAlloc = RootOwnAlloc + RootDescAlloc,
+	100.0 * float(TotalAlloc) > Threshold * float(RootTotalAlloc).
+
+:- pred threshold_ps_words_self(deep::in, float::in, int::in) is semidet.
+
+threshold_ps_words_self(Deep, Threshold, PSI) :-
+	PSOwn = Deep ^ ps_own,
+	array__lookup(PSOwn, PSI, Own),
+	RootOwn = root_own_info(Deep),
+	RootDesc = root_desc_info(Deep),
+	OwnWords = words(Own),
+	RootOwnWords = words(RootOwn),
+	RootDescWords = inherit_words(RootDesc),
+	RootTotalWords = RootOwnWords + RootDescWords,
+	100.0 * float(OwnWords) > Threshold * float(RootTotalWords).
+
+:- pred threshold_ps_words_both(deep::in, float::in, int::in) is semidet.
+
+threshold_ps_words_both(Deep, Threshold, PSI) :-
+	PSOwn = Deep ^ ps_own,
+	PSDesc = Deep ^ ps_desc,
+	array__lookup(PSOwn, PSI, Own),
+	array__lookup(PSDesc, PSI, Desc),
+	RootOwn = root_own_info(Deep),
+	RootDesc = root_desc_info(Deep),
+	OwnWords = words(Own),
+	RootOwnWords = words(RootOwn),
+	DescWords = inherit_words(Desc),
+	RootDescWords = inherit_words(RootDesc),
+	TotalWords = OwnWords + DescWords,
+	RootTotalWords = RootOwnWords + RootDescWords,
+	100.0 * float(TotalWords) > Threshold * float(RootTotalWords).
+
+%-----------------------------------------------------------------------------%
+
+sort_line_groups(Criteria, Groups) = SortedGroups :-
+	(
+		Criteria = by_context,
+		CompFunc = compare_line_groups_by_context
+	;
+		Criteria = by_name,
+		CompFunc = compare_line_groups_by_name
+	;
+		Criteria = by_cost(Measurement, InclDesc, Scope),
+		(
+			Measurement = calls,
+			% We ignore the setting of InclDesc because
+			% calls are not inherited from descendants,
+			% and we ignore the setting of Scope because
+			% sorting on "calls per call" is not useful.
+			CompFunc = compare_line_groups_by_calls
+		;
+			Measurement = time,
+			InclDesc = self,
+			Scope = overall,
+			CompFunc = compare_line_groups_by_time_self_overall
+		;
+			Measurement = time,
+			InclDesc = self,
+			Scope = per_call,
+			CompFunc = compare_line_groups_by_time_self_percall
+		;
+			Measurement = time,
+			InclDesc = self_and_desc,
+			Scope = overall,
+			CompFunc = compare_line_groups_by_time_total_overall
+		;
+			Measurement = time,
+			InclDesc = self_and_desc,
+			Scope = per_call,
+			CompFunc = compare_line_groups_by_time_total_percall
+		;
+			Measurement = allocs,
+			InclDesc = self,
+			Scope = overall,
+			CompFunc = compare_line_groups_by_allocs_self_overall
+		;
+			Measurement = allocs,
+			InclDesc = self,
+			Scope = per_call,
+			CompFunc = compare_line_groups_by_allocs_self_percall
+		;
+			Measurement = allocs,
+			InclDesc = self_and_desc,
+			Scope = overall,
+			CompFunc = compare_line_groups_by_allocs_total_overall
+		;
+			Measurement = allocs,
+			InclDesc = self_and_desc,
+			Scope = per_call,
+			CompFunc = compare_line_groups_by_allocs_total_percall
+		;
+			Measurement = words,
+			InclDesc = self,
+			Scope = overall,
+			CompFunc = compare_line_groups_by_words_self_overall
+		;
+			Measurement = words,
+			InclDesc = self,
+			Scope = per_call,
+			CompFunc = compare_line_groups_by_words_self_percall
+		;
+			Measurement = words,
+			InclDesc = self_and_desc,
+			Scope = overall,
+			CompFunc = compare_line_groups_by_words_total_overall
+		;
+			Measurement = words,
+			InclDesc = self_and_desc,
+			Scope = per_call,
+			CompFunc = compare_line_groups_by_words_total_percall
+		)
+	),
+	SortedGroups = list__sort(compare_groups_fallback(CompFunc), Groups).
+
+:- type compare_line_groups_func(FL, LL) ==
+	(func(line_group(FL, LL), line_group(FL, LL)) = comparison_result).
+
+:- func compare_groups_fallback(compare_line_groups_func(FL, LL),
+	line_group(FL, LL), line_group(FL, LL)) = comparison_result.
+
+compare_groups_fallback(MainFunc, Group1, Group2) = Result :-
+	Result0 = MainFunc(Group1, Group2),
+	( Result0 \= (=) ->
+		Result = Result0
+	;
+		Result1 = compare_line_groups_by_context(Group1, Group2),
+		( Result1 \= (=) ->
+			Result = Result1
+		;
+			Result = compare_line_groups_by_name(Group1, Group2)
+		)
+	).
+
+%-----------------------------------------------------------------------------%
+
+:- func compare_line_groups_by_context(line_group(FL, LL), line_group(FL, LL))
+	= comparison_result.
+
+compare_line_groups_by_context(Group1, Group2) = Result :-
+	compare(ResultFilenames,
+	 	Group1 ^ group_filename, Group2 ^ group_filename),
+	( ResultFilenames = (=) ->
+		compare(Result,
+			Group1 ^ group_linenumber, Group2 ^ group_linenumber)
+	;
+		Result = ResultFilenames
+	).
+
+:- func compare_line_groups_by_name(line_group(FL, LL), line_group(FL, LL))
+	= comparison_result.
+
+compare_line_groups_by_name(Group1, Group2) = Result :-
+	compare(Result, Group1 ^ group_name, Group2 ^ group_name).
+
+:- func compare_line_groups_by_calls(line_group(FL, LL), line_group(FL, LL))
+	= comparison_result.
+
+compare_line_groups_by_calls(Group1, Group2) = Result :-
+	Calls1 = calls(Group1 ^ group_own),
+	Calls2 = calls(Group2 ^ group_own),
+	compare(Result, Calls2, Calls1).
+
+:- func compare_line_groups_by_time_self_overall(line_group(FL, LL),
+	line_group(FL, LL)) = comparison_result.
+
+compare_line_groups_by_time_self_overall(Group1, Group2) = Result :-
+	Quanta1 = quanta(Group1 ^ group_own),
+	Quanta2 = quanta(Group2 ^ group_own),
+	compare(Result, Quanta2, Quanta1).
+
+:- func compare_line_groups_by_time_self_percall(line_group(FL, LL),
+	line_group(FL, LL)) = comparison_result.
+
+compare_line_groups_by_time_self_percall(Group1, Group2) = Result :-
+	Calls1 = calls(Group1 ^ group_own),
+	Calls2 = calls(Group2 ^ group_own),
+	Quanta1 = quanta(Group1 ^ group_own),
+	Quanta2 = quanta(Group2 ^ group_own),
+	( Calls1 = 0 ->
+		QuantaPerCall1 = 0.0
+	;
+		QuantaPerCall1 = float(Quanta1) / float(Calls1)
+	),
+	( Calls2 = 0 ->
+		QuantaPerCall2 = 0.0
+	;
+		QuantaPerCall2 = float(Quanta2) / float(Calls2)
+	),
+	compare(Result, QuantaPerCall2, QuantaPerCall1).
+
+:- func compare_line_groups_by_time_total_overall(line_group(FL, LL),
+	line_group(FL, LL)) = comparison_result.
+
+compare_line_groups_by_time_total_overall(Group1, Group2) = Result :-
+	Quanta1 = quanta(Group1 ^ group_own) +
+		inherit_quanta(Group1 ^ group_desc),
+	Quanta2 = quanta(Group2 ^ group_own) +
+		inherit_quanta(Group2 ^ group_desc),
+	compare(Result, Quanta2, Quanta1).
+
+:- func compare_line_groups_by_time_total_percall(line_group(FL, LL),
+	line_group(FL, LL)) = comparison_result.
+
+compare_line_groups_by_time_total_percall(Group1, Group2) = Result :-
+	Calls1 = calls(Group1 ^ group_own),
+	Calls2 = calls(Group2 ^ group_own),
+	Quanta1 = quanta(Group1 ^ group_own) +
+		inherit_quanta(Group1 ^ group_desc),
+	Quanta2 = quanta(Group2 ^ group_own) +
+		inherit_quanta(Group2 ^ group_desc),
+	( Calls1 = 0 ->
+		QuantaPerCall1 = 0.0
+	;
+		QuantaPerCall1 = float(Quanta1) / float(Calls1)
+	),
+	( Calls2 = 0 ->
+		QuantaPerCall2 = 0.0
+	;
+		QuantaPerCall2 = float(Quanta2) / float(Calls2)
+	),
+	compare(Result, QuantaPerCall2, QuantaPerCall1).
+
+:- func compare_line_groups_by_allocs_self_overall(line_group(FL, LL),
+	line_group(FL, LL)) = comparison_result.
+
+compare_line_groups_by_allocs_self_overall(Group1, Group2) = Result :-
+	Alloc1 = allocs(Group1 ^ group_own),
+	Alloc2 = allocs(Group2 ^ group_own),
+	compare(Result, Alloc2, Alloc1).
+
+:- func compare_line_groups_by_allocs_self_percall(line_group(FL, LL),
+	line_group(FL, LL)) = comparison_result.
+
+compare_line_groups_by_allocs_self_percall(Group1, Group2) = Result :-
+	Calls1 = calls(Group1 ^ group_own),
+	Calls2 = calls(Group2 ^ group_own),
+	Alloc1 = allocs(Group1 ^ group_own),
+	Alloc2 = allocs(Group2 ^ group_own),
+	( Calls1 = 0 ->
+		AllocPerCall1 = 0.0
+	;
+		AllocPerCall1 = float(Alloc1) / float(Calls1)
+	),
+	( Calls2 = 0 ->
+		AllocPerCall2 = 0.0
+	;
+		AllocPerCall2 = float(Alloc2) / float(Calls2)
+	),
+	compare(Result, AllocPerCall2, AllocPerCall1).
+
+:- func compare_line_groups_by_allocs_total_overall(line_group(FL, LL),
+	line_group(FL, LL)) = comparison_result.
+
+compare_line_groups_by_allocs_total_overall(Group1, Group2) = Result :-
+	Alloc1 = allocs(Group1 ^ group_own) +
+		inherit_allocs(Group1 ^ group_desc),
+	Alloc2 = allocs(Group2 ^ group_own) +
+		inherit_allocs(Group2 ^ group_desc),
+	compare(Result, Alloc2, Alloc1).
+
+:- func compare_line_groups_by_allocs_total_percall(line_group(FL, LL),
+	line_group(FL, LL)) = comparison_result.
+
+compare_line_groups_by_allocs_total_percall(Group1, Group2) = Result :-
+	Calls1 = calls(Group1 ^ group_own),
+	Calls2 = calls(Group2 ^ group_own),
+	Alloc1 = allocs(Group1 ^ group_own) +
+		inherit_allocs(Group1 ^ group_desc),
+	Alloc2 = allocs(Group2 ^ group_own) +
+		inherit_allocs(Group2 ^ group_desc),
+	( Calls1 = 0 ->
+		AllocPerCall1 = 0.0
+	;
+		AllocPerCall1 = float(Alloc1) / float(Calls1)
+	),
+	( Calls2 = 0 ->
+		AllocPerCall2 = 0.0
+	;
+		AllocPerCall2 = float(Alloc2) / float(Calls2)
+	),
+	compare(Result, AllocPerCall2, AllocPerCall1).
+
+:- func compare_line_groups_by_words_self_overall(line_group(FL, LL),
+	line_group(FL, LL)) = comparison_result.
+
+compare_line_groups_by_words_self_overall(Group1, Group2) = Result :-
+	Words1 = words(Group1 ^ group_own),
+	Words2 = words(Group2 ^ group_own),
+	compare(Result, Words2, Words1).
+
+:- func compare_line_groups_by_words_self_percall(line_group(FL, LL),
+	line_group(FL, LL)) = comparison_result.
+
+compare_line_groups_by_words_self_percall(Group1, Group2) = Result :-
+	Calls1 = calls(Group1 ^ group_own),
+	Calls2 = calls(Group2 ^ group_own),
+	Words1 = words(Group1 ^ group_own),
+	Words2 = words(Group2 ^ group_own),
+	( Calls1 = 0 ->
+		WordsPerCall1 = 0.0
+	;
+		WordsPerCall1 = float(Words1) / float(Calls1)
+	),
+	( Calls2 = 0 ->
+		WordsPerCall2 = 0.0
+	;
+		WordsPerCall2 = float(Words2) / float(Calls2)
+	),
+	compare(Result, WordsPerCall2, WordsPerCall1).
+
+:- func compare_line_groups_by_words_total_overall(line_group(FL, LL),
+	line_group(FL, LL)) = comparison_result.
+
+compare_line_groups_by_words_total_overall(Group1, Group2) = Result :-
+	Words1 = words(Group1 ^ group_own) +
+		inherit_words(Group1 ^ group_desc),
+	Words2 = words(Group2 ^ group_own) +
+		inherit_words(Group2 ^ group_desc),
+	compare(Result, Words2, Words1).
+
+:- func compare_line_groups_by_words_total_percall(line_group(FL, LL),
+	line_group(FL, LL)) = comparison_result.
+
+compare_line_groups_by_words_total_percall(Group1, Group2) = Result :-
+	Calls1 = calls(Group1 ^ group_own),
+	Calls2 = calls(Group2 ^ group_own),
+	Words1 = words(Group1 ^ group_own) +
+		inherit_words(Group1 ^ group_desc),
+	Words2 = words(Group2 ^ group_own) +
+		inherit_words(Group2 ^ group_desc),
+	( Calls1 = 0 ->
+		WordsPerCall1 = 0.0
+	;
+		WordsPerCall1 = float(Words1) / float(Calls1)
+	),
+	( Calls2 = 0 ->
+		WordsPerCall2 = 0.0
+	;
+		WordsPerCall2 = float(Words2) / float(Calls2)
+	),
+	compare(Result, WordsPerCall2, WordsPerCall1).
+
+%-----------------------------------------------------------------------------%
+
+sum_line_group_measurements(LineGroups, Own, Desc) :-
+	list__foldl2(accumulate_line_group_measurements, LineGroups,
+		zero_own_prof_info, Own, zero_inherit_prof_info, Desc).
+
+:- pred accumulate_line_group_measurements(line_group(FL, LL)::in,
+	own_prof_info::in, own_prof_info::out,
+	inherit_prof_info::in, inherit_prof_info::out) is det.
+
+accumulate_line_group_measurements(LineGroup, Own0, Own, Desc0, Desc) :-
+	Own = add_own_to_own(Own0, LineGroup ^ group_own),
+	Desc = add_inherit_to_inherit(Desc0, LineGroup ^ group_desc).
+
+%-----------------------------------------------------------------------------%
Index: deep_profiler/util.m
===================================================================
RCS file: /home/mercury1/repository/mercury/deep_profiler/util.m,v
retrieving revision 1.1
diff -u -b -r1.1 util.m
--- deep_profiler/util.m	2001/05/31 05:59:59	1.1
+++ deep_profiler/util.m	2001/06/05 16:42:36
@@ -15,45 +15,52 @@
 
 :- import_module char, list.
 
-% split(Str, Char, Pieces): splits Str into pieces at every occurrence of Char,
-% and returns the pieces in order. No piece will contain Char.
-
+	% split(Str, Char, Pieces): splits Str into pieces at every occurrence
+	% of Char, and returns the pieces in order. No piece will contain Char.
+	% If two Chars occur in a row, split will return the empty string as
+	% the piece between them.
 :- pred split(string::in, char::in, list(string)::out) is det.
 
 :- implementation.
 
 :- import_module string, require.
 
-split(Str0, SChar, Strs) :-
+split(Str0, SplitChar, Strs) :-
 	string__to_char_list(Str0, Chars0),
-	split(Chars0, SChar, [], [], Strs0),
-	list__reverse(Strs0, Strs).
+	split_2(Chars0, SplitChar, Strs).
 
-:- pred split(list(char)::in, char::in, list(char)::in,
-	list(string)::in, list(string)::out) is det.
+:- pred split_2(list(char)::in, char::in, list(string)::out) is det.
 
-split([], _SChar, Chars0, Strs0, Strs) :-
-	(
-		Chars0 = [],
-		Strs = Strs0
+split_2(Chars, SplitChar, PieceStrs) :-
+	( find_split_char(Chars, SplitChar, Before, After) ->
+		string__from_char_list(Before, BeforeStr),
+		split_2(After, SplitChar, TailStrs),
+		PieceStrs = [BeforeStr | TailStrs]
 	;
-		Chars0 = [_ | _],
-		list__reverse(Chars0, Chars),
-		string__from_char_list(Chars, Str),
-		Strs = [Str | Strs0]
+		string__from_char_list(Chars, PieceStr),
+		PieceStrs = [PieceStr]
 	).
-split([C | Cs], SChar, Chars0, Strs0, Strs) :-
-	( C = SChar ->
-		(
-			Chars0 = [],
-			Strs1 = Strs0
-		;
-			Chars0 = [_ | _],
-			list__reverse(Chars0, Chars),
-			string__from_char_list(Chars, Str),
-			Strs1 = [Str | Strs0]
-		),
-		split(Cs, SChar, [], Strs1, Strs)
+
+	% find_split_char(Chars, SplitChar, Before, After):
+	% If SplitChar occurs in Chars, it returns all the characters in Chars
+	% before the first occurrence of SplitChar in Chars in Before, and all
+	% the characters after the first occurrence of SplitChar in Chars in
+	% After. The first occurrence of SplitChar itself is not returned.
+:- pred find_split_char(list(char)::in, char::in,
+	list(char)::out, list(char)::out) is semidet.
+
+find_split_char(Chars, SplitChar, Before, After) :-
+	find_split_char_2(Chars, SplitChar, [], BeforeRev, After),
+	list__reverse(BeforeRev, Before).
+
+:- pred find_split_char_2(list(char)::in, char::in, list(char)::in,
+	list(char)::out, list(char)::out) is semidet.
+
+find_split_char_2([Char | Chars], SplitChar, BeforeRev0, BeforeRev, After) :-
+	( Char = SplitChar ->
+		BeforeRev = BeforeRev0,
+		After = Chars
 	;
-		split(Cs, SChar, [C | Chars0], Strs0, Strs)
+		find_split_char_2(Chars, SplitChar, [Char | BeforeRev0],
+			BeforeRev, After)
 	).
cvs diff: Diffing deep_profiler/notes
cvs diff: Diffing doc
cvs diff: Diffing extras
cvs diff: Diffing extras/aditi
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/graphics
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/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/logged_output
cvs diff: Diffing extras/moose
cvs diff: Diffing extras/moose/samples
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/odbc
cvs diff: Diffing extras/posix
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/stream
cvs diff: Diffing extras/trailed_update
cvs diff: Diffing extras/trailed_update/samples
cvs diff: Diffing extras/trailed_update/tests
cvs diff: Diffing extras/xml
cvs diff: Diffing extras/xml/samples
cvs diff: Diffing java
cvs diff: Diffing library
Index: library/array.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/array.m,v
retrieving revision 1.93
diff -u -b -r1.93 array.m
--- library/array.m	2001/06/04 13:39:13	1.93
+++ library/array.m	2001/06/29 01:01:28
@@ -464,9 +464,9 @@
 
 #ifdef	MR_DEEP_PROFILING
 MR_proc_static_compiler_plain(array, __Unify__,   array, 1, 0,
-	array, array_equal,   2, 0, ""array.m"", 99);
+	array, array_equal,   2, 0, ""array.m"", 99, TRUE);
 MR_proc_static_compiler_plain(array, __Compare__, array, 1, 0,
-	array, array_compare, 3, 0, ""array.m"", 99);
+	array, array_compare, 3, 0, ""array.m"", 99, TRUE);
 #endif
 
 MR_DEFINE_BUILTIN_TYPE_CTOR_INFO(array, array, 1, MR_TYPECTOR_REP_ARRAY);
Index: library/builtin.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/builtin.m,v
retrieving revision 1.57
diff -u -b -r1.57 builtin.m
--- library/builtin.m	2001/06/18 08:18:02	1.57
+++ library/builtin.m	2001/06/29 01:00:17
@@ -311,7 +311,7 @@
 
 	/* 
 	** One of the following two is used for all higher-order types.
-	** Note that they use the same three predicates.
+	** Note that they use the same unify and compare predicates.
 	*/
 
 MR_DEFINE_BUILTIN_TYPE_CTOR_INFO_NOCM(builtin, func, 0,
@@ -993,8 +993,8 @@
 #else /* ! MR_HIGHLEVEL_CODE */
 
 #ifdef	MR_DEEP_PROFILING
-MR_proc_static_user_builtin_empty(copy, 2, 0, ""builtin.m"");
-MR_proc_static_user_builtin_empty(copy, 2, 1, ""builtin.m"");
+MR_proc_static_user_builtin_empty(copy, 2, 0, ""builtin.m"", 0, TRUE);
+MR_proc_static_user_builtin_empty(copy, 2, 1, ""builtin.m"", 0, TRUE);
 #endif
 
 MR_define_extern_entry(mercury__copy_2_0);
@@ -1145,9 +1145,9 @@
 
 #ifdef	MR_DEEP_PROFILING
 MR_proc_static_compiler_empty(builtin, __Unify__,   c_pointer,
-	0, 0, ""builtin.m"");
+	0, 0, ""builtin.m"", 0, TRUE);
 MR_proc_static_compiler_empty(builtin, __Compare__, c_pointer,
-	0, 0, ""builtin.m"");
+	0, 0, ""builtin.m"", 0, TRUE);
 #endif
 
 MR_DEFINE_BUILTIN_TYPE_CTOR_INFO_PRED(builtin, c_pointer, 0,
Index: library/exception.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/exception.m,v
retrieving revision 1.48
diff -u -b -r1.48 exception.m
--- library/exception.m	2001/05/31 06:00:04	1.48
+++ library/exception.m	2001/07/03 03:14:25
@@ -1437,18 +1437,25 @@
 MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_throw_1_0, 1);
 
 #ifdef	MR_DEEP_PROFILING
-/* XXX the final 0s are fake line numbers */
-MR_proc_static_user_ho(exception, builtin_catch, 3, 0, ""exception.m"", 0);
-MR_proc_static_user_ho(exception, builtin_catch, 3, 1, ""exception.m"", 0);
-MR_proc_static_user_ho(exception, builtin_catch, 3, 2, ""exception.m"", 0);
-MR_proc_static_user_ho(exception, builtin_catch, 3, 3, ""exception.m"", 0);
-MR_proc_static_user_ho(exception, builtin_catch, 3, 4, ""exception.m"", 0);
-MR_proc_static_user_ho(exception, builtin_catch, 3, 5, ""exception.m"", 0);
+/* XXX the 0s are fake line numbers */
+MR_proc_static_user_ho(exception, builtin_catch, 3, 0,
+	""exception.m"", 0, TRUE);
+MR_proc_static_user_ho(exception, builtin_catch, 3, 1,
+	""exception.m"", 0, TRUE);
+MR_proc_static_user_ho(exception, builtin_catch, 3, 2,
+	""exception.m"", 0, TRUE);
+MR_proc_static_user_ho(exception, builtin_catch, 3, 3,
+	""exception.m"", 0, TRUE);
+MR_proc_static_user_ho(exception, builtin_catch, 3, 4,
+	""exception.m"", 0, TRUE);
+MR_proc_static_user_ho(exception, builtin_catch, 3, 5,
+	""exception.m"", 0, TRUE);
 /*
 ** XXX Builtin_throw will eventually be able to make calls in deep profiling
 ** grades. In the meantime, we need its proc_static structure for its callers.
 */
-MR_proc_static_user_empty(exception, builtin_throw, 1, 0, ""exception.m"");
+MR_proc_static_user_empty(exception, builtin_throw, 1, 0,
+	""exception.m"", 0, FALSE);
 #endif
 
 MR_BEGIN_MODULE(exceptions_module)
Index: library/std_util.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/std_util.m,v
retrieving revision 1.234
diff -u -b -r1.234 std_util.m
--- library/std_util.m	2001/06/27 05:04:40	1.234
+++ library/std_util.m	2001/06/29 01:02:03
@@ -1305,9 +1305,9 @@
 
 #ifdef	MR_DEEP_PROFILING
 MR_proc_static_compiler_empty(std_util, __Unify__,   type_desc, 0, 0,
-	""std_util.m"");
+	""std_util.m"", 0, TRUE);
 MR_proc_static_compiler_empty(std_util, __Compare__, type_desc, 0, 0,
-	""std_util.m"");
+	""std_util.m"", 0, TRUE);
 #endif
 
 MR_DEFINE_BUILTIN_TYPE_CTOR_INFO(std_util, type_desc, 0,
cvs diff: Diffing profiler
cvs diff: Diffing robdd
cvs diff: Diffing runtime
Index: runtime/mercury_deep_profiling.c
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_deep_profiling.c,v
retrieving revision 1.4
diff -u -b -r1.4 mercury_deep_profiling.c
--- runtime/mercury_deep_profiling.c	2001/06/06 06:40:53	1.4
+++ runtime/mercury_deep_profiling.c	2001/07/02 07:20:39
@@ -32,6 +32,8 @@
 	{ MR_PREDICATE, "Mercury runtime", "Mercury runtime",
 	  "Mercury runtime", 0, 0 },
 	"Mercury runtime",
+	0,
+	TRUE,
 	1,
 	&MR_main_parent_call_site_statics[0],
 #ifdef	MR_USE_ACTIVATION_COUNTS
@@ -61,7 +63,7 @@
   #else
 	/* the call count is computed from the other counts */
   #endif
-	0, 0, 0,
+	1, 0, 0,
 #endif
 #ifdef MR_DEEP_PROFILING_TIMING
 	0,
@@ -80,8 +82,8 @@
 				(MR_CallSiteDynList **)
 				&MR_main_parent_call_site_dynamics[0];
 volatile bool	 	MR_inside_deep_profiling_code = FALSE;
-volatile unsigned	MR_quanta_inside_deep_profiling_code = 0L;
-volatile unsigned	MR_quanta_outside_deep_profiling_code = 0L;
+volatile unsigned	MR_quanta_inside_deep_profiling_code = 0;
+volatile unsigned	MR_quanta_outside_deep_profiling_code = 0;
 
 #ifdef	MR_DEEP_PROFILING_STATISTICS
 
@@ -325,6 +327,8 @@
 	MR_write_num(fp, ticks_per_sec);
 	MR_write_num(fp, MR_quanta_inside_deep_profiling_code);
 	MR_write_num(fp, MR_quanta_outside_deep_profiling_code);
+	MR_write_byte(fp, sizeof(MR_Word));
+	MR_write_byte(fp, 0); /* the canonical flag is FALSE = 0 */
 
 	MR_call_site_dynamic_table = MR_create_hash_table(MR_hash_table_size);
 	MR_call_site_static_table  = MR_create_hash_table(MR_hash_table_size);
@@ -343,7 +347,6 @@
 		&MR_main_parent_proc_dynamic, root_pd_id);
 #endif
 
-	MR_write_byte(fp, MR_deep_token_root);
 	MR_write_ptr(fp, kind_pd, root_pd_id);
 
 	MR_write_out_proc_dynamic(fp, &MR_main_parent_proc_dynamic);
@@ -576,8 +579,10 @@
 
 #ifdef MR_DEEP_PROFILING_DEBUG
 	fprintf(debug_fp, "proc_static %p/%d\n", ptr, ps_id);
-	fprintf(debug_fp, "  filename \"%s\", %d call sites\n",
-		ptr->MR_ps_file_name, ptr->MR_ps_num_call_sites);
+	fprintf(debug_fp, "  filename \"%s\", linenumber %d, "
+			"interface %d, %d call sites\n",
+		ptr->MR_ps_file_name, ptr->MR_ps_line_number,
+		ptr->MR_ps_is_in_interface, ptr->MR_ps_num_call_sites);
 #endif
 
 	if (already_written) {
@@ -645,6 +650,8 @@
 	}
 
 	MR_write_string(fp, ptr->MR_ps_file_name);
+	MR_write_num(fp, ptr->MR_ps_line_number);
+	MR_write_byte(fp, ptr->MR_ps_is_in_interface);
 	MR_write_num(fp, ptr->MR_ps_num_call_sites);
 
 	for (i = 0; i < ptr->MR_ps_num_call_sites; i++) {
Index: runtime/mercury_deep_profiling.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_deep_profiling.h,v
retrieving revision 1.4
diff -u -b -r1.4 mercury_deep_profiling.h
--- runtime/mercury_deep_profiling.h	2001/06/05 12:36:59	1.4
+++ runtime/mercury_deep_profiling.h	2001/06/28 14:06:25
@@ -70,6 +70,8 @@
 struct MR_ProcStatic_Struct {
 	MR_Proc_Id				MR_ps_proc_id;
 	MR_ConstString				MR_ps_file_name;
+	int					MR_ps_line_number;
+	int					MR_ps_is_in_interface;
 	int					MR_ps_num_call_sites;
 	const MR_CallSiteStatic			*MR_ps_call_sites;
 #ifdef MR_USE_ACTIVATION_COUNTS
@@ -81,6 +83,8 @@
 struct MR_User_ProcStatic_Struct {
 	MR_User_Proc_Id				MR_ps_proc_id;
 	MR_ConstString				MR_ps_file_name;
+	int					MR_ps_line_number;
+	int					MR_ps_is_in_interface;
 	int					MR_ps_num_call_sites;
 	const MR_CallSiteStatic			*MR_ps_call_sites;
 #ifdef MR_USE_ACTIVATION_COUNTS
@@ -92,6 +96,8 @@
 struct MR_Compiler_ProcStatic_Struct {
 	MR_Compiler_Proc_Id			MR_ps_proc_id;
 	MR_ConstString				MR_ps_file_name;
+	int					MR_ps_line_number;
+	int					MR_ps_is_in_interface;
 	int					MR_ps_num_call_sites;
 	const MR_CallSiteStatic			*MR_ps_call_sites;
 #ifdef MR_USE_ACTIVATION_COUNTS
@@ -119,7 +125,6 @@
 
 typedef enum {
 	MR_deep_token_end = 0,
-	MR_deep_token_root,
 	MR_deep_token_call_site_static,
 	MR_deep_token_call_site_dynamic,
 	MR_deep_token_proc_static,
Index: runtime/mercury_deep_profiling_hand.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_deep_profiling_hand.h,v
retrieving revision 1.3
diff -u -b -r1.3 mercury_deep_profiling_hand.h
--- runtime/mercury_deep_profiling_hand.h	2001/06/05 04:57:09	1.3
+++ runtime/mercury_deep_profiling_hand.h	2001/06/29 00:59:02
@@ -42,7 +42,7 @@
   #define	MR_maybe_activation_count_field
 #endif
 
-#define	MR_proc_static_user_builtin_empty(name, arity, mode, file)	\
+#define	MR_proc_static_user_builtin_empty(name, arity, mode, file, line, interface)\
 	MR_User_ProcStatic						\
 	MR_proc_static_user_builtin_name(name, arity, mode) = {		\
 		{							\
@@ -54,13 +54,15 @@
 			mode, 						\
 		},							\
 		file,							\
+		line,							\
+		interface,						\
 		0,							\
 		NULL,							\
 		MR_maybe_activation_count_field				\
 		NULL							\
 	}
 
-#define	MR_proc_static_compiler_empty(module, name, type, arity, mode, file) \
+#define	MR_proc_static_compiler_empty(module, name, type, arity, mode, file, line, interface) \
 	MR_Compiler_ProcStatic						\
 	MR_proc_static_compiler_name(module, name, type, arity, mode) = { \
 		{							\
@@ -72,13 +74,15 @@
 			mode, 						\
 		},							\
 		file,							\
+		line,							\
+		interface,						\
 		0,							\
 		NULL,							\
 		MR_maybe_activation_count_field				\
 		NULL							\
 	}
 
-#define	MR_proc_static_user_empty(module, name, arity, mode, file)	\
+#define	MR_proc_static_user_empty(module, name, arity, mode, file, line, interface)\
 	MR_User_ProcStatic						\
 	MR_proc_static_user_name(module, name, arity, mode) = {		\
 		{							\
@@ -90,13 +94,15 @@
 			mode, 						\
 		},							\
 		file,							\
+		line,							\
+		interface,						\
 		0,							\
 		NULL,							\
 		MR_maybe_activation_count_field				\
 		NULL							\
 	}
 
-#define	MR_proc_static_user_plain(module, name, arity, mode, cmodule, cname, carity, cmode, file, line)\
+#define	MR_proc_static_user_plain(module, name, arity, mode, cmodule, cname, carity, cmode, file, line, interface)\
 	static const MR_CallSiteStatic					\
 	MR_call_sites_user_name(module, name, arity, mode)[] = {	\
 		{ MR_normal_call, (MR_ProcStatic *)			\
@@ -115,13 +121,15 @@
 			mode, 						\
 		},							\
 		file,							\
+		line,							\
+		interface,						\
 		1,							\
 		MR_call_sites_user_name(module, name, arity, mode),	\
 		MR_maybe_activation_count_field				\
 		NULL							\
 	}
 
-#define	MR_proc_static_compiler_plain(module, name, type, arity, mode, cmodule, cname, carity, cmode, file, line)\
+#define	MR_proc_static_compiler_plain(module, name, type, arity, mode, cmodule, cname, carity, cmode, file, line, interface)\
 	static const MR_CallSiteStatic					\
 	MR_call_sites_compiler_name(module, name, type, arity, mode)[] = {\
 		{ MR_normal_call, (MR_ProcStatic *)			\
@@ -140,13 +148,15 @@
 			mode, 						\
 		},							\
 		file,							\
+		line,							\
+		interface,						\
 		1,							\
 		MR_call_sites_compiler_name(module, name, type, arity, mode),\
 		MR_maybe_activation_count_field				\
 		NULL							\
 	}
 
-#define	MR_proc_static_user_ho(module, name, arity, mode, file, line)	\
+#define	MR_proc_static_user_ho(module, name, arity, mode, file, line, interface)	\
 	static const MR_CallSiteStatic					\
 	MR_call_sites_user_name(module, name, arity, mode)[] = {	\
 		{ MR_higher_order_call, NULL,				\
@@ -164,6 +174,8 @@
 			mode, 						\
 		},							\
 		file,							\
+		line,							\
+		interface,						\
 		1,							\
 		MR_call_sites_user_name(module, name, arity, mode),	\
 		MR_maybe_activation_count_field				\
Index: runtime/mercury_ho_call.c
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_ho_call.c,v
retrieving revision 1.45
diff -u -b -r1.45 mercury_ho_call.c
--- runtime/mercury_ho_call.c	2001/06/05 04:57:09	1.45
+++ runtime/mercury_ho_call.c	2001/06/29 00:58:52
@@ -78,16 +78,26 @@
 		MR_proc_static_user_builtin_name(predname, 3, 0),	\
 		MR_own_exits)
 
-MR_proc_static_user_builtin_empty(integer_unify, 2, 0,    "mercury_ho_call.c");
-MR_proc_static_user_builtin_empty(integer_compare, 3, 0,  "mercury_ho_call.c");
-MR_proc_static_user_builtin_empty(float_unify, 2, 0,      "mercury_ho_call.c");
-MR_proc_static_user_builtin_empty(float_compare, 3, 0,    "mercury_ho_call.c");
-MR_proc_static_user_builtin_empty(string_unify, 2, 0,     "mercury_ho_call.c");
-MR_proc_static_user_builtin_empty(string_compare, 3, 0,   "mercury_ho_call.c");
-MR_proc_static_user_builtin_empty(c_pointer_unify, 2, 0 , "mercury_ho_call.c");
-MR_proc_static_user_builtin_empty(c_pointer_compare, 3, 0,"mercury_ho_call.c");
-MR_proc_static_user_builtin_empty(typeinfo_unify, 2, 0,   "mercury_ho_call.c");
-MR_proc_static_user_builtin_empty(typeinfo_compare, 3, 0, "mercury_ho_call.c");
+MR_proc_static_user_builtin_empty(integer_unify, 2, 0,
+	"mercury_ho_call.c", 0, TRUE);
+MR_proc_static_user_builtin_empty(integer_compare, 3, 0,
+	"mercury_ho_call.c", 0, TRUE);
+MR_proc_static_user_builtin_empty(float_unify, 2, 0,
+	"mercury_ho_call.c", 0, TRUE);
+MR_proc_static_user_builtin_empty(float_compare, 3, 0,
+	"mercury_ho_call.c", 0, TRUE);
+MR_proc_static_user_builtin_empty(string_unify, 2, 0,
+	"mercury_ho_call.c", 0, TRUE);
+MR_proc_static_user_builtin_empty(string_compare, 3, 0,
+	"mercury_ho_call.c", 0, TRUE);
+MR_proc_static_user_builtin_empty(c_pointer_unify, 2, 0,
+	"mercury_ho_call.c", 0, TRUE);
+MR_proc_static_user_builtin_empty(c_pointer_compare, 3, 0,
+	"mercury_ho_call.c", 0, TRUE);
+MR_proc_static_user_builtin_empty(typeinfo_unify, 2, 0,
+	"mercury_ho_call.c", 0, TRUE);
+MR_proc_static_user_builtin_empty(typeinfo_compare, 3, 0,
+	"mercury_ho_call.c", 0, TRUE);
 
 #endif
 
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/diff
cvs diff: Diffing samples/muz
cvs diff: Diffing samples/rot13
cvs diff: Diffing samples/solutions
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 tests
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/structure_reuse
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/recompilation
cvs diff: Diffing tests/tabling
cvs diff: Diffing tests/term
cvs diff: Diffing tests/valid
cvs diff: Diffing tests/warnings
cvs diff: Diffing tools
cvs diff: Diffing trace
cvs diff: Diffing util
--------------------------------------------------------------------------
mercury-reviews mailing list
post:  mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the reviews mailing list