[m-rev.] for post-commit review: getters and setters in deep profiler
Zoltan Somogyi
zs at csse.unimelb.edu.au
Fri Jan 2 13:58:52 AEDT 2009
Add a query to the deep profiler for reporting on the getter and setter
predicates in a module. This can be used to decide whether a data structure
should be split in two, and if so, which fields should go into the
substructure.
At the same time, improve the displays we generate for some other queries
by making more table column headers capable of sorting the table.
deep_profiler/query.m:
Add the new query type.
deep_profiler/report.m:
Add the definition of the new report.
Include the identity of the callee, if known, in call site
descriptions, to allow sorting on this.
deep_profiler/create_report.m:
Add code to execute the new kind of query.
Include the new field in call site descriptions.
Remove "dump" from the name of the predicate that builds
procrep_coverage reports, since the result isn't a raw dump.
deep_profiler/display_report.m:
Add code to display the new kind of report.
Add a link to the new report to display we generate for the module
query.
Allow the table in the procedure report to be sorted by context
or by callee.
deep_profiler/display.m:
Add a new table column class for displaying field names.
deep_profiler/html_format.m:
Handle the new table column class.
deep_profiler/old_html_format.m:
deep_profiler/old_query.m:
"Handle" the new query.
Zoltan.
cvs diff: Diffing .
Index: create_report.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/create_report.m,v
retrieving revision 1.15
diff -u -b -r1.15 create_report.m
--- create_report.m 5 Nov 2008 03:38:40 -0000 1.15
+++ create_report.m 30 Dec 2008 08:14:10 -0000
@@ -58,6 +58,7 @@
:- import_module array.
:- import_module assoc_list.
+:- import_module char.
:- import_module exception.
:- import_module float.
:- import_module int.
@@ -69,6 +70,7 @@
:- import_module set.
:- import_module string.
:- import_module svmap.
+:- import_module unit.
:- import_module univ.
%-----------------------------------------------------------------------------%
@@ -113,6 +115,11 @@
create_module_report(Deep, ModuleName, MaybeModuleReport),
Report = report_module(MaybeModuleReport)
;
+ Cmd = deep_cmd_module_getter_setters(ModuleName),
+ create_module_getter_setter_report(Deep, ModuleName,
+ MaybeModuleGetterSettersReport),
+ Report = report_module_getter_setters(MaybeModuleGetterSettersReport)
+ ;
Cmd = deep_cmd_top_procs(Limit, CostKind, InclDesc, Scope),
create_top_procs_report(Deep, Limit, CostKind, InclDesc, Scope,
MaybeTopProcsReport),
@@ -121,7 +128,7 @@
Cmd = deep_cmd_procrep_coverage(PSPtr),
create_procrep_coverage_report(Deep, PSPtr,
MaybeProcrepCoverageReport),
- Report = report_procrep_coverage_dump(MaybeProcrepCoverageReport)
+ Report = report_procrep_coverage(MaybeProcrepCoverageReport)
;
Cmd = deep_cmd_proc(PSPtr),
create_proc_report(Deep, PSPtr, MaybeProcReport),
@@ -564,6 +571,184 @@
%-----------------------------------------------------------------------------%
%
+% Code to build a module_getter_setters report.
+%
+
+:- type gs_field_raw_data
+ ---> gs_field_raw_data(
+ gs_raw_proc :: proc_desc,
+ gs_raw_own :: own_prof_info,
+ gs_raw_desc :: inherit_prof_info
+ ).
+
+:- type raw_gs_field_info == gs_field_info(gs_field_raw_data, unit).
+:- type raw_gs_field_map == gs_field_map(raw_gs_field_info).
+:- type raw_gs_ds_map == gs_ds_map(raw_gs_field_info).
+
+ % Create a module_getter_setters report, from the given data
+ % with the specified parameters.
+ %
+:- pred create_module_getter_setter_report(deep::in, string::in,
+ maybe_error(module_getter_setters_report)::out) is det.
+
+create_module_getter_setter_report(Deep, ModuleName,
+ MaybeModuleGetterSettersReport) :-
+ ( map.search(Deep ^ module_data, ModuleName, ModuleData) ->
+ PSPtrs = ModuleData ^ module_procs,
+ list.foldl(gather_getters_setters(Deep), PSPtrs,
+ map.init, GetterSetterDataMap),
+ map.map_values(getter_setter_raw_map_to_info_map(Deep),
+ GetterSetterDataMap, GetterSetterInfoMap),
+ ModuleGetterSettersReport = module_getter_setters_report(ModuleName,
+ GetterSetterInfoMap),
+ MaybeModuleGetterSettersReport = ok(ModuleGetterSettersReport)
+ ;
+ Msg = string.format("There is no module named `%s'.\n",
+ [s(ModuleName)]),
+ MaybeModuleGetterSettersReport = error(Msg)
+ ).
+
+:- pred getter_setter_raw_map_to_info_map(deep::in, data_struct_name::in,
+ raw_gs_field_map::in, gs_field_map::out) is det.
+
+getter_setter_raw_map_to_info_map(Deep, _DataStructName, RawMap, Map) :-
+ map.map_values(getter_setter_raw_data_to_info(Deep), RawMap, Map).
+
+:- pred getter_setter_raw_data_to_info(deep::in, field_name::in,
+ raw_gs_field_info::in, gs_field_info::out) is det.
+
+getter_setter_raw_data_to_info(Deep, _FieldName, RawData, Data) :-
+ (
+ RawData = gs_field_both(RawGetter, RawSetter, _),
+ RawGetter = gs_field_raw_data(GetterProcDesc, GetterOwn, GetterDesc),
+ RawSetter = gs_field_raw_data(SetterProcDesc, SetterOwn, SetterDesc),
+ own_and_inherit_to_perf_row_data(Deep, GetterProcDesc,
+ GetterOwn, GetterDesc, GetterRowData),
+ own_and_inherit_to_perf_row_data(Deep, SetterProcDesc,
+ SetterOwn, SetterDesc, SetterRowData),
+ SumOwn = add_own_to_own(GetterOwn, SetterOwn),
+ SumDesc = add_inherit_to_inherit(GetterDesc, SetterDesc),
+ own_and_inherit_to_perf_row_data(Deep, unit, SumOwn, SumDesc,
+ SumRowData),
+ Data = gs_field_both(GetterRowData, SetterRowData, SumRowData)
+ ;
+ RawData = gs_field_getter(RawGetter),
+ RawGetter = gs_field_raw_data(GetterProcDesc, GetterOwn, GetterDesc),
+ own_and_inherit_to_perf_row_data(Deep, GetterProcDesc, GetterOwn,
+ GetterDesc, GetterRowData),
+ Data = gs_field_getter(GetterRowData)
+ ;
+ RawData = gs_field_setter(RawSetter),
+ RawSetter = gs_field_raw_data(SetterProcDesc, SetterOwn, SetterDesc),
+ own_and_inherit_to_perf_row_data(Deep, SetterProcDesc, SetterOwn,
+ SetterDesc, SetterRowData),
+ Data = gs_field_setter(SetterRowData)
+ ).
+
+:- pred gather_getters_setters(deep::in, proc_static_ptr::in,
+ raw_gs_ds_map::in, raw_gs_ds_map::out) is det.
+
+gather_getters_setters(Deep, PSPtr, !GSDSRawMap) :-
+ ( valid_proc_static_ptr(Deep, PSPtr) ->
+ deep_lookup_proc_statics(Deep, PSPtr, PS),
+ Id = PS ^ ps_id,
+ ( is_getter_or_setter(Id, GetterSetter, DataStructName, FieldName) ->
+ deep_lookup_ps_own(Deep, PSPtr, Own),
+ deep_lookup_ps_desc(Deep, PSPtr, Desc),
+ ProcDesc = describe_proc(Deep, PSPtr),
+ RawData = gs_field_raw_data(ProcDesc, Own, Desc),
+ ( map.search(!.GSDSRawMap, DataStructName, FieldMap0Prime) ->
+ FieldMap0 = FieldMap0Prime
+ ;
+ map.init(FieldMap0)
+ ),
+ ( map.search(FieldMap0, FieldName, FieldData0) ->
+ (
+ GetterSetter = getter,
+ (
+ ( FieldData0 = gs_field_both(_, _, _)
+ ; FieldData0 = gs_field_getter(_)
+ ),
+ error("gather_getters_setters: redundant getter")
+ ;
+ FieldData0 = gs_field_setter(SetterRawData),
+ FieldData = gs_field_both(RawData, SetterRawData, unit)
+ )
+ ;
+ GetterSetter = setter,
+ (
+ ( FieldData0 = gs_field_both(_, _, _)
+ ; FieldData0 = gs_field_setter(_)
+ ),
+ error("gather_getters_setters: redundant setter")
+ ;
+ FieldData0 = gs_field_getter(GetterRawData),
+ FieldData = gs_field_both(GetterRawData, RawData, unit)
+ )
+ ),
+ map.det_update(FieldMap0, FieldName, FieldData, FieldMap)
+ ;
+ (
+ GetterSetter = getter,
+ FieldData = gs_field_getter(RawData)
+ ;
+ GetterSetter = setter,
+ FieldData = gs_field_setter(RawData)
+ ),
+ map.det_insert(FieldMap0, FieldName, FieldData, FieldMap)
+ ),
+ svmap.set(DataStructName, FieldMap, !GSDSRawMap)
+ ;
+ true
+ )
+ ;
+ true
+ ).
+
+:- pred is_getter_or_setter(string_proc_label::in, getter_or_setter::out,
+ data_struct_name::out, field_name::out) is semidet.
+
+is_getter_or_setter(StringProcLabel, GetterSetter, DataStructName, FieldName) :-
+ StringProcLabel = str_ordinary_proc_label(_PorF, DeclModule, DefModule,
+ Name, Arity, _Mode),
+ DeclModule = DefModule,
+ string.to_char_list(Name, NameChars),
+ is_getter_or_setter_2(NameChars, GetterSetter, DataStructNameChars,
+ FieldNameChars),
+ (
+ GetterSetter = getter,
+ Arity = 2
+ ;
+ GetterSetter = setter,
+ Arity = 3
+ ),
+ string.from_char_list(DataStructNameChars, DataStructNameStr),
+ string.from_char_list(FieldNameChars, FieldNameStr),
+ DataStructName = data_struct_name(DataStructNameStr),
+ FieldName = field_name(FieldNameStr).
+
+:- pred is_getter_or_setter_2(list(char)::in, getter_or_setter::out,
+ list(char)::out, list(char)::out) is semidet.
+
+is_getter_or_setter_2(NameChars, GetterSetter, DataStructNameChars,
+ FieldNameChars) :-
+ ( NameChars = ['_', 'g', 'e', 't', '_' | FieldNameCharsPrime] ->
+ GetterSetter = getter,
+ DataStructNameChars = [],
+ FieldNameChars = FieldNameCharsPrime
+ ; NameChars = ['_', 's', 'e', 't', '_' | FieldNameCharsPrime] ->
+ GetterSetter = setter,
+ DataStructNameChars = [],
+ FieldNameChars = FieldNameCharsPrime
+ ;
+ NameChars = [FirstNameChar | LaterNameChars],
+ is_getter_or_setter_2(LaterNameChars, GetterSetter,
+ LaterDataStructNameChars, FieldNameChars),
+ DataStructNameChars = [FirstNameChar | LaterDataStructNameChars]
+ ).
+
+%-----------------------------------------------------------------------------%
+%
% Code to build a top_procs report.
%
@@ -1204,22 +1389,36 @@
describe_call_site(Deep, CSSPtr) = CallSiteDesc :-
( valid_call_site_static_ptr(Deep, CSSPtr) ->
deep_lookup_call_site_statics(Deep, CSSPtr, CSS),
- CSS = call_site_static(ContainingPSPtr, SlotNumber, _Kind, LineNumber,
+ CSS = call_site_static(ContainingPSPtr, SlotNumber, Kind, LineNumber,
GoalPathString),
deep_lookup_proc_statics(Deep, ContainingPSPtr, ContainingPS),
FileName = ContainingPS ^ ps_file_name,
RefinedName = ContainingPS ^ ps_refined_id,
- goal_path_from_string_det(GoalPathString, GoalPath)
+ goal_path_from_string_det(GoalPathString, GoalPath),
+ (
+ Kind = normal_call_and_callee(CalleePSPtr, _TypeSubst),
+ CalleeDesc = describe_proc(Deep, CalleePSPtr),
+ MaybeCalleeDesc = yes(CalleeDesc)
+ ;
+ ( Kind = special_call_and_no_callee
+ ; Kind = higher_order_call_and_no_callee
+ ; Kind = method_call_and_no_callee
+ ; Kind = callback_and_no_callee
+ ),
+ MaybeCalleeDesc = no
+ )
;
ContainingPSPtr = dummy_proc_static_ptr,
FileName = "",
LineNumber = 0,
RefinedName = "mercury_runtime",
SlotNumber = -1,
- GoalPath = empty_goal_path
+ GoalPath = empty_goal_path,
+ MaybeCalleeDesc = no
),
CallSiteDesc = call_site_desc(CSSPtr, ContainingPSPtr,
- FileName, LineNumber, RefinedName, SlotNumber, GoalPath).
+ FileName, LineNumber, RefinedName, SlotNumber, GoalPath,
+ MaybeCalleeDesc).
% Create a clique_desc structure for a given clique.
%
Index: display.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/display.m,v
retrieving revision 1.10
diff -u -b -r1.10 display.m
--- display.m 25 Sep 2008 03:47:03 -0000 1.10
+++ display.m 29 Dec 2008 14:04:35 -0000
@@ -66,7 +66,7 @@
)
; display_verbatim(
% A string to be displayed verbatim. It should be displayed
- % with a fixed with font and line breaks should be honoured.
+ % with a fixed width font and line breaks should be honoured.
string
).
@@ -161,6 +161,7 @@
---> table_column_class_allocations
; table_column_class_callseqs
; table_column_class_clique
+ ; table_column_class_field_name
; table_column_class_memory
; table_column_class_module_name
; table_column_class_no_class
Index: display_report.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/display_report.m,v
retrieving revision 1.18
diff -u -b -r1.18 display_report.m
--- display_report.m 1 Dec 2008 00:22:33 -0000 1.18
+++ display_report.m 29 Dec 2008 15:52:07 -0000
@@ -50,10 +50,12 @@
:- import_module float.
:- import_module int.
:- import_module list.
+:- import_module map.
:- import_module maybe.
:- import_module pair.
:- import_module require.
:- import_module string.
+:- import_module unit.
%-----------------------------------------------------------------------------%
@@ -99,6 +101,16 @@
Display = display(no, [display_heading(Msg)])
)
;
+ Report = report_module_getter_setters(MaybeModuleGetterSettersReport),
+ (
+ MaybeModuleGetterSettersReport = ok(ModuleGetterSettersReport),
+ display_report_module_getter_setters(Prefs,
+ ModuleGetterSettersReport, Display)
+ ;
+ MaybeModuleGetterSettersReport = error(Msg),
+ Display = display(no, [display_heading(Msg)])
+ )
+ ;
Report = report_top_procs(MaybeTopProcsReport),
(
MaybeTopProcsReport = ok(TopProcsReport),
@@ -117,16 +129,7 @@
Display = display(no, [display_heading(Msg)])
)
;
- Report = report_proc_callers(MaybeProcCallersReport),
- (
- MaybeProcCallersReport = ok(ProcCallersReport),
- display_report_proc_callers(Prefs, ProcCallersReport, Display)
- ;
- MaybeProcCallersReport = error(Msg),
- Display = display(no, [display_heading(Msg)])
- )
- ;
- Report = report_procrep_coverage_dump(MaybeProcrepCoverageInfo),
+ Report = report_procrep_coverage(MaybeProcrepCoverageInfo),
(
MaybeProcrepCoverageInfo = ok(ProcrepCoverageInfo),
display_report_procrep_coverage_info(Prefs, ProcrepCoverageInfo,
@@ -136,6 +139,15 @@
Display = display(no, [display_text(Msg)])
)
;
+ Report = report_proc_callers(MaybeProcCallersReport),
+ (
+ MaybeProcCallersReport = ok(ProcCallersReport),
+ display_report_proc_callers(Prefs, ProcCallersReport, Display)
+ ;
+ MaybeProcCallersReport = error(Msg),
+ Display = display(no, [display_heading(Msg)])
+ )
+ ;
Report = report_proc_static_dump(MaybeProcStaticDumpInfo),
(
MaybeProcStaticDumpInfo = ok(ProcStaticDumpInfo),
@@ -688,6 +700,10 @@
DisplayTable = display_table(Table),
% Build controls at the bottom of the page.
+ GetterSetterCmd = deep_cmd_module_getter_setters(ModuleName),
+ GetterSetterControl = display_link(deep_link(GetterSetterCmd, yes(Prefs),
+ attr_str([], "Show field getters and setters"), link_class_link)),
+
InactiveControls = inactive_proc_controls(Prefs, Cmd),
FieldControls = field_controls(Prefs, Cmd),
FormatControls = format_controls(Prefs, Cmd),
@@ -695,6 +711,7 @@
Display = display(yes(Title),
[DisplayTable,
+ display_paragraph_break, GetterSetterControl,
display_paragraph_break, InactiveControls,
display_paragraph_break, FieldControls,
display_paragraph_break, FormatControls,
@@ -708,6 +725,128 @@
%-----------------------------------------------------------------------------%
%
+% Code to display a module_getter_setters report.
+%
+
+ % Create a display_report structure for a top_procedures report.
+ %
+:- pred display_report_module_getter_setters(preferences::in,
+ module_getter_setters_report::in, display::out) is det.
+
+display_report_module_getter_setters(Prefs, Report, Display) :-
+ Report = module_getter_setters_report(ModuleName, GSMap),
+ Title = string.format("The getters and setters of module %s:",
+ [s(ModuleName)]),
+ map.to_assoc_list(GSMap, GSPairs),
+ RowLists = list.map(display_data_struct_getter_setters(Prefs, ModuleName),
+ GSPairs),
+ list.condense(RowLists, Rows),
+
+ SortByNamePrefs = Prefs ^ pref_criteria := by_name,
+ FieldNameHeaderCell = td_l(deep_link(Cmd, yes(SortByNamePrefs),
+ attr_str([], "FieldName"), link_class_link)),
+
+ RankHeaderGroup = make_single_table_header_group(td_s("Rank"),
+ table_column_class_ordinal_rank, column_do_not_colour),
+ FieldNameHeaderGroup = make_single_table_header_group(FieldNameHeaderCell,
+ table_column_class_field_name, column_do_not_colour),
+ Cmd = deep_cmd_module_getter_setters(ModuleName),
+ MakeHeaderData = override_order_criteria_header_data(Cmd),
+ perf_table_header(total_columns_meaningful, Prefs, MakeHeaderData,
+ PerfHeaderGroups),
+ header_groups_to_header([RankHeaderGroup, FieldNameHeaderGroup
+ | PerfHeaderGroups], NumColumns, Header),
+
+ Table = table(table_class_box_if_pref, NumColumns, yes(Header), Rows),
+ TableItem = display_table(Table),
+
+ ModuleCmd = deep_cmd_module(ModuleName),
+ ModuleControl = display_link(deep_link(ModuleCmd, yes(Prefs),
+ attr_str([], "Show all the procedures of the module"),
+ link_class_link)),
+ MenuResetQuitControls = cmds_menu_restart_quit(yes(Prefs)),
+ Controls =
+ [display_paragraph_break, ModuleControl,
+ display_paragraph_break, MenuResetQuitControls],
+
+ Display = display(yes(Title), [TableItem] ++ Controls).
+
+:- func display_data_struct_getter_setters(preferences, string,
+ pair(data_struct_name, gs_field_map)) = list(table_row).
+
+display_data_struct_getter_setters(Prefs, ModuleName,
+ DataStructName - FieldMap) = Rows :-
+ DataStructName = data_struct_name(Name),
+ Title = string.format("The getters and setters of %s:", [s(Name)]),
+ TitleHeader = table_section_header(td_as(attr_str([attr_bold], Title))),
+ map.to_assoc_list(FieldMap, FieldPairs0),
+ sort_getter_setter_fields(Prefs, FieldPairs0, FieldPairs),
+ list.map_foldl(display_field_getter_setters(Prefs, ModuleName),
+ FieldPairs, DataRowLists, 1, _),
+ list.condense(DataRowLists, DataRows),
+ Rows = [table_separator_row, TitleHeader, table_separator_row | DataRows].
+
+:- pred display_field_getter_setters(preferences::in, string::in,
+ pair(field_name, gs_field_info)::in, list(table_row)::out,
+ int::in, int::out) is det.
+
+display_field_getter_setters(Prefs, _ModuleName, FieldName - FieldInfo, Rows,
+ !Rank):-
+ Fields = Prefs ^ pref_fields,
+ FieldName = field_name(Name),
+ RankCell = table_cell(td_i(!.Rank)),
+ (
+ FieldInfo = gs_field_getter(GetterRowData),
+ perf_table_row(total_columns_meaningful, Fields, GetterRowData,
+ GetterPerfCells),
+ GetterProcDesc = GetterRowData ^ perf_row_subject,
+ GetterFieldNameCell = proc_desc_to_proc_name_cell(Prefs,
+ GetterProcDesc),
+ GetterRow =
+ table_row([RankCell, GetterFieldNameCell | GetterPerfCells]),
+ Rows = [table_separator_row, GetterRow]
+ ;
+ FieldInfo = gs_field_setter(SetterRowData),
+ perf_table_row(total_columns_meaningful, Fields, SetterRowData,
+ SetterPerfCells),
+ SetterProcDesc = SetterRowData ^ perf_row_subject,
+ SetterFieldNameCell = proc_desc_to_proc_name_cell(Prefs,
+ SetterProcDesc),
+ SetterRow =
+ table_row([RankCell, SetterFieldNameCell | SetterPerfCells]),
+ Rows = [table_separator_row, SetterRow]
+ ;
+ FieldInfo = gs_field_both(GetterRowData, SetterRowData, SumRowData),
+ EmptyCell = table_cell(td_s("")),
+
+ perf_table_row(total_columns_meaningful, Fields, SumRowData,
+ SumPerfCells),
+ SummaryName = string.format("%s summary", [s(Name)]),
+ SumFieldNameCell = table_cell(td_s(SummaryName)),
+ SumRow = table_row([RankCell, SumFieldNameCell | SumPerfCells]),
+
+ perf_table_row(total_columns_meaningful, Fields, GetterRowData,
+ GetterPerfCells),
+ GetterProcDesc = GetterRowData ^ perf_row_subject,
+ GetterFieldNameCell = proc_desc_to_proc_name_cell(Prefs,
+ GetterProcDesc),
+ GetterRow =
+ table_row([EmptyCell, GetterFieldNameCell | GetterPerfCells]),
+
+ perf_table_row(total_columns_meaningful, Fields, SetterRowData,
+ SetterPerfCells),
+ SetterProcDesc = SetterRowData ^ perf_row_subject,
+ SetterFieldNameCell = proc_desc_to_proc_name_cell(Prefs,
+ SetterProcDesc),
+ SetterRow =
+ table_row([EmptyCell, SetterFieldNameCell | SetterPerfCells]),
+
+ Rows = [table_separator_row, SumRow, GetterRow, SetterRow]
+ ),
+ !:Rank = !.Rank + 1.
+
+%-----------------------------------------------------------------------------%
+%
% Code to display a top procedures report.
%
@@ -805,9 +944,17 @@
PSPtr = ProcDesc ^ pdesc_ps_ptr,
Cmd = deep_cmd_proc(PSPtr),
- SourceHeaderGroup = make_single_table_header_group(td_s("Source"),
+ SortByContextPrefs = Prefs ^ pref_criteria := by_context,
+ SourceHeaderCell = td_l(deep_link(Cmd, yes(SortByContextPrefs),
+ attr_str([], "Source"), link_class_link)),
+
+ SortByNamePrefs = Prefs ^ pref_criteria := by_name,
+ ProcHeaderCell = td_l(deep_link(Cmd, yes(SortByNamePrefs),
+ attr_str([], "Procedure"), link_class_link)),
+
+ SourceHeaderGroup = make_single_table_header_group(SourceHeaderCell,
table_column_class_source_context, column_do_not_colour),
- ProcHeaderGroup = make_single_table_header_group(td_s("Procedure"),
+ ProcHeaderGroup = make_single_table_header_group(ProcHeaderCell,
table_column_class_proc, column_do_not_colour),
MakeHeaderData = override_order_criteria_header_data(Cmd),
perf_table_header(total_columns_meaningful, Prefs, MakeHeaderData,
@@ -949,17 +1096,12 @@
Prefs = Prefs0 ^ pref_contour := ContourExcl,
PSPtr = ProcDesc ^ pdesc_ps_ptr,
- Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum, ContourExcl),
- MakeHeaderData = override_order_criteria_header_data(Cmd),
- perf_table_header(total_columns_meaningful, Prefs, MakeHeaderData,
- PerfHeaderGroups),
-
- RankHeaderGroup = make_single_table_header_group(td_s("Rank"),
- table_column_class_ordinal_rank, column_do_not_colour),
(
CallerRowDatas = proc_caller_call_sites(CallSiteRowDatas),
CallerGroups = group_by_call_site,
+ Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum,
+ ContourExcl),
Title = "The call sites calling " ++ RefinedName,
sort_call_site_desc_rows_by_preferences(Prefs, CallSiteRowDatas,
SortedCallSiteRowDatas),
@@ -968,14 +1110,24 @@
DisplayedBunchNum, MaybeFirstAndLastBunchNum),
list.map_foldl(display_caller_call_site(Prefs),
DisplayedCallSiteRowDatas, Rows, FirstRowNum, AfterLastRowNum),
- SourceHeaderGroup = make_single_table_header_group(td_s("Source"),
+
+ SortByContextPrefs = Prefs ^ pref_criteria := by_context,
+ SourceHeaderCell = td_l(deep_link(Cmd, yes(SortByContextPrefs),
+ attr_str([], "Source"), link_class_link)),
+ SortByNamePrefs = Prefs ^ pref_criteria := by_context,
+ ProcHeaderCell = td_l(deep_link(Cmd, yes(SortByNamePrefs),
+ attr_str([], "In procedure"), link_class_link)),
+
+ SourceHeaderGroup = make_single_table_header_group(SourceHeaderCell,
table_column_class_source_context, column_do_not_colour),
- ProcHeaderGroup = make_single_table_header_group(td_s("In procedure"),
+ ProcHeaderGroup = make_single_table_header_group(ProcHeaderCell,
table_column_class_proc, column_do_not_colour),
IdHeaderGroups = [SourceHeaderGroup, ProcHeaderGroup]
;
CallerRowDatas = proc_caller_procedures(ProcRowDatas),
CallerGroups = group_by_proc,
+ Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum,
+ ContourExcl),
Title = "The procedures calling " ++ RefinedName,
sort_proc_desc_rows_by_preferences(Prefs, ProcRowDatas,
SortedProcRowDatas),
@@ -984,14 +1136,24 @@
DisplayedBunchNum, MaybeFirstAndLastBunchNum),
list.map_foldl(display_caller_proc(Prefs),
DisplayedProcRowDatas, Rows, FirstRowNum, AfterLastRowNum),
- SourceHeaderGroup = make_single_table_header_group(td_s("Source"),
+
+ SortByContextPrefs = Prefs ^ pref_criteria := by_context,
+ SourceHeaderCell = td_l(deep_link(Cmd, yes(SortByContextPrefs),
+ attr_str([], "Source"), link_class_link)),
+ SortByNamePrefs = Prefs ^ pref_criteria := by_context,
+ ProcHeaderCell = td_l(deep_link(Cmd, yes(SortByNamePrefs),
+ attr_str([], "Procedure"), link_class_link)),
+
+ SourceHeaderGroup = make_single_table_header_group(SourceHeaderCell,
table_column_class_source_context, column_do_not_colour),
- ProcHeaderGroup = make_single_table_header_group(td_s("Procedure"),
+ ProcHeaderGroup = make_single_table_header_group(ProcHeaderCell,
table_column_class_proc, column_do_not_colour),
IdHeaderGroups = [SourceHeaderGroup, ProcHeaderGroup]
;
CallerRowDatas = proc_caller_modules(ModuleRowDatas),
CallerGroups = group_by_module,
+ Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum,
+ ContourExcl),
Title = "The modules calling " ++ RefinedName,
sort_module_name_rows_by_preferences(Prefs, ModuleRowDatas,
SortedModuleRowDatas),
@@ -1000,12 +1162,19 @@
DisplayedBunchNum, MaybeFirstAndLastBunchNum),
list.map_foldl(display_caller_module(Prefs),
DisplayedModuleRowDatas, Rows, FirstRowNum, AfterLastRowNum),
- ModuleHeaderGroup = make_single_table_header_group(td_s("Module"),
+
+ SortByNamePrefs = Prefs ^ pref_criteria := by_name,
+ ModuleHeaderCell = td_l(deep_link(Cmd, yes(SortByNamePrefs),
+ attr_str([], "Module"), link_class_link)),
+
+ ModuleHeaderGroup = make_single_table_header_group(ModuleHeaderCell,
table_column_class_source_context, column_do_not_colour),
IdHeaderGroups = [ModuleHeaderGroup]
;
CallerRowDatas = proc_caller_cliques(CliqueRowDatas),
CallerGroups = group_by_clique,
+ Cmd = deep_cmd_proc_callers(PSPtr, CallerGroups, BunchNum,
+ ContourExcl),
Title = "The cliques calling " ++ RefinedName,
sort_clique_rows_by_preferences(Prefs, CliqueRowDatas,
SortedCliqueRowDatas),
@@ -1014,13 +1183,23 @@
DisplayedBunchNum, MaybeFirstAndLastBunchNum),
list.map_foldl(display_caller_clique(Prefs),
DisplayedCliqueRowDatas, Rows, FirstRowNum, AfterLastRowNum),
- CliqueHeaderGroup = make_single_table_header_group(td_s("Clique"),
+
+ SortByNamePrefs = Prefs ^ pref_criteria := by_name,
+ CliqueHeaderCell = td_l(deep_link(Cmd, yes(SortByNamePrefs),
+ attr_str([], "Clique"), link_class_link)),
+
+ CliqueHeaderGroup = make_single_table_header_group(CliqueHeaderCell,
table_column_class_clique, column_do_not_colour),
MembersHeaderGroup = make_single_table_header_group(td_s("Members"),
table_column_class_clique, column_do_not_colour),
IdHeaderGroups = [CliqueHeaderGroup, MembersHeaderGroup]
),
+ RankHeaderGroup = make_single_table_header_group(td_s("Rank"),
+ table_column_class_ordinal_rank, column_do_not_colour),
+ MakeHeaderData = override_order_criteria_header_data(Cmd),
+ perf_table_header(total_columns_meaningful, Prefs, MakeHeaderData,
+ PerfHeaderGroups),
AllHeaderGroups = [RankHeaderGroup] ++ IdHeaderGroups ++ PerfHeaderGroups,
header_groups_to_header(AllHeaderGroups, NumColumns, Header),
@@ -1233,7 +1412,7 @@
%-----------------------------------------------------------------------------%
%
-% Code to display procrep_coverage dumps
+% Code to display procrep_coverage information.
%
:- pred display_report_procrep_coverage_info(preferences::in,
@@ -1248,10 +1427,11 @@
Cmd = deep_cmd_procrep_coverage(PSPtr),
ProcReportControls = proc_reports_controls(Prefs, PSPtr, Cmd),
MenuResetQuitControls = cmds_menu_restart_quit(yes(Prefs)),
- Controls = [display_paragraph_break, ProcReportControls,
+ Controls =
+ [display_paragraph_break, ProcReportControls,
display_paragraph_break, MenuResetQuitControls],
- Title = "Procrep coverage dump",
+ Title = "Procedure coverage:",
Display = display(yes(Title), [CoverageInfoItem] ++ Controls).
:- instance goal_annotation(coverage_info) where [
@@ -3288,7 +3468,8 @@
call_site_desc_to_name_path_slot_cell(Prefs, CallSiteDesc) = Cell :-
CallSiteDesc = call_site_desc(CSSPtr, _ContainerPSPtr,
- _FileName, _LineNumber, RefinedName, SlotNumber, GoalPath),
+ _FileName, _LineNumber, RefinedName, SlotNumber, GoalPath,
+ _MaybeCallee),
GoalPathStr = goal_path_to_string(GoalPath),
string.format("%s @ %s #%d",
[s(RefinedName), s(GoalPathStr), i(SlotNumber)], Name),
@@ -3534,7 +3715,7 @@
list.sort(compare_call_site_perfs_by_context, !CallSitePerfs)
;
OrderCriteria = by_name,
- list.sort(compare_call_site_perfs_by_name, !CallSitePerfs)
+ list.sort(compare_call_site_perfs_by_callee_name, !CallSitePerfs)
;
OrderCriteria = by_cost(CostKind, InclDesc, Scope),
list.sort(compare_call_site_perfs_by_cost(CostKind, InclDesc, Scope),
@@ -3551,13 +3732,14 @@
CallSiteDescB = CallSitePerfB ^ csf_summary_perf ^ perf_row_subject,
compare_call_site_descs_by_context(CallSiteDescA, CallSiteDescB, Result).
-:- pred compare_call_site_perfs_by_name(
+:- pred compare_call_site_perfs_by_callee_name(
call_site_perf::in, call_site_perf::in, comparison_result::out) is det.
-compare_call_site_perfs_by_name(CallSitePerfA, CallSitePerfB, Result) :-
+compare_call_site_perfs_by_callee_name(CallSitePerfA, CallSitePerfB, Result) :-
CallSiteDescA = CallSitePerfA ^ csf_summary_perf ^ perf_row_subject,
CallSiteDescB = CallSitePerfB ^ csf_summary_perf ^ perf_row_subject,
- compare_call_site_descs_by_name(CallSiteDescA, CallSiteDescB, Result).
+ compare_call_site_descs_by_callee_name(CallSiteDescA, CallSiteDescB,
+ Result).
:- pred compare_call_site_perfs_by_cost(
cost_kind::in, include_descendants::in, measurement_scope::in,
@@ -3704,6 +3886,81 @@
%-----------------------------------------------------------------------------%
%
+% Sort perf_data_rows of module_getter_setters by the preferred criteria.
+%
+
+:- pred sort_getter_setter_fields(preferences::in,
+ assoc_list(field_name, gs_field_info)::in,
+ assoc_list(field_name, gs_field_info)::out) is det.
+
+sort_getter_setter_fields(Prefs, !FieldPairs) :-
+ OrderCriteria = Prefs ^ pref_criteria,
+ (
+ % In the common case, each FieldPair has two contexts, one each from
+ % the getter and the setter. Neither context is all that useful to sort
+ % by, so we sort by the field name instead.
+ ( OrderCriteria = by_context
+ ; OrderCriteria = by_name
+ ),
+ list.sort(compare_getter_setters_by_name, !FieldPairs)
+ ;
+ OrderCriteria = by_cost(CostKind, InclDesc, Scope),
+ list.sort(compare_getter_setters_by_cost(CostKind, InclDesc, Scope),
+ !FieldPairs),
+ % We want the most expensive fields to appear first.
+ list.reverse(!FieldPairs)
+ ).
+
+:- pred compare_getter_setters_by_name(
+ pair(field_name, gs_field_info)::in, pair(field_name, gs_field_info)::in,
+ comparison_result::out) is det.
+
+compare_getter_setters_by_name(PairA, PairB, Result) :-
+ PairA = FieldNameA - _,
+ PairB = FieldNameB - _,
+ compare(Result, FieldNameA, FieldNameB).
+
+:- pred compare_getter_setters_by_cost(
+ cost_kind::in, include_descendants::in, measurement_scope::in,
+ pair(field_name, gs_field_info)::in, pair(field_name, gs_field_info)::in,
+ comparison_result::out) is det.
+
+compare_getter_setters_by_cost(CostKind, InclDesc, Scope, PairA, PairB,
+ Result) :-
+ PairA = FieldNameA - FieldInfoA,
+ PairB = FieldNameB - FieldInfoB,
+ PerfA = representative_field_perf_row(FieldInfoA),
+ PerfB = representative_field_perf_row(FieldInfoB),
+ compare_perf_row_datas_by_cost(CostKind, InclDesc, Scope, PerfA, PerfB,
+ PerfResult),
+ (
+ ( PerfResult = (<)
+ ; PerfResult = (>)
+ ),
+ Result = PerfResult
+ ;
+ PerfResult = (=),
+ % We switch the field names to rank in reverse alphabetical order.
+ % We do this because our caller will reverse the final sorted list,
+ % and *this* reversal is needed to undo the effects of *that* reversal.
+ compare(Result, FieldNameB, FieldNameA)
+ ).
+
+:- func representative_field_perf_row(gs_field_info) = perf_row_data(unit).
+
+representative_field_perf_row(FieldInfo) = Perf :-
+ (
+ FieldInfo = gs_field_getter(Perf0),
+ Perf = Perf0 ^ perf_row_subject := unit
+ ;
+ FieldInfo = gs_field_setter(Perf0),
+ Perf = Perf0 ^ perf_row_subject := unit
+ ;
+ FieldInfo = gs_field_both(_, _, Perf)
+ ).
+
+%-----------------------------------------------------------------------------%
+%
% Sort perf_data_rows of module_actives by the preferred criteria.
%
@@ -3879,6 +4136,30 @@
NameB = CallSiteDescB ^ csdesc_caller_refined_name,
compare(Result, NameA, NameB).
+:- pred compare_call_site_descs_by_callee_name(
+ call_site_desc::in, call_site_desc::in, comparison_result::out) is det.
+
+compare_call_site_descs_by_callee_name(CallSiteDescA, CallSiteDescB, Result) :-
+ MaybeCalleeA = CallSiteDescA ^ csdesc_maybe_callee,
+ MaybeCalleeB = CallSiteDescB ^ csdesc_maybe_callee,
+ (
+ MaybeCalleeA = no,
+ MaybeCalleeB = no,
+ Result = (=)
+ ;
+ MaybeCalleeA = no,
+ MaybeCalleeB = yes(_),
+ Result = (<)
+ ;
+ MaybeCalleeA = yes(_),
+ MaybeCalleeB = no,
+ Result = (>)
+ ;
+ MaybeCalleeA = yes(CalleeNameA),
+ MaybeCalleeB = yes(CalleeNameB),
+ compare_proc_descs_by_name(CalleeNameA, CalleeNameB, Result)
+ ).
+
:- pred compare_proc_descs_by_name(proc_desc::in, proc_desc::in,
comparison_result::out) is det.
Index: html_format.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/html_format.m,v
retrieving revision 1.33
diff -u -b -r1.33 html_format.m
--- html_format.m 25 Sep 2008 07:33:14 -0000 1.33
+++ html_format.m 29 Dec 2008 14:12:14 -0000
@@ -588,6 +588,7 @@
table_column_class_to_string(table_column_class_allocations) = "allocations".
table_column_class_to_string(table_column_class_clique) = "clique".
table_column_class_to_string(table_column_class_callseqs) = "callseqs".
+table_column_class_to_string(table_column_class_field_name) = "field_name".
table_column_class_to_string(table_column_class_memory) = "memory".
table_column_class_to_string(table_column_class_module_name) = "module_name".
table_column_class_to_string(table_column_class_number) = "number".
@@ -663,6 +664,11 @@
style_element("text-align") - "right"
])
),
+ ( style_control("td.field_name") -
+ map.from_assoc_list([
+ style_element("text-align") - "left"
+ ])
+ ),
( style_control("td.memory") -
map.from_assoc_list([
style_element("text-align") - "right"
Index: old_html_format.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/old_html_format.m,v
retrieving revision 1.2
diff -u -b -r1.2 old_html_format.m
--- old_html_format.m 16 Oct 2008 01:16:36 -0000 1.2
+++ old_html_format.m 29 Dec 2008 08:59:56 -0000
@@ -143,6 +143,7 @@
:- import_module float.
:- import_module int.
:- import_module maybe.
+:- import_module require.
:- import_module string.
%-----------------------------------------------------------------------------%
@@ -402,13 +403,16 @@
toggle_time_format, toggle_inactive_procs].
command_relevant_toggles(deep_cmd_top_procs(_, _, _, _)) =
[toggle_fields, toggle_box, toggle_colour, toggle_time_format].
-command_relevant_toggles(deep_cmd_procrep_coverage(_)) = [].
command_relevant_toggles(deep_cmd_dump_proc_static(_)) = [].
command_relevant_toggles(deep_cmd_dump_proc_dynamic(_)) = [].
command_relevant_toggles(deep_cmd_dump_call_site_static(_)) = [].
command_relevant_toggles(deep_cmd_dump_call_site_dynamic(_)) = [].
command_relevant_toggles(deep_cmd_dump_clique(_)) = [].
command_relevant_toggles(deep_cmd_dump_proc_var_use(_)) = [].
+command_relevant_toggles(deep_cmd_procrep_coverage(_)) = [] :-
+ error("unexpected command in command_relevant_toggles").
+command_relevant_toggles(deep_cmd_module_getter_setters(_)) = [] :-
+ error("unexpected command in command_relevant_toggles").
:- func footer_field_toggle(cmd, preferences, deep) = string.
Index: old_query.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/old_query.m,v
retrieving revision 1.2
diff -u -b -r1.2 old_query.m
--- old_query.m 16 Oct 2008 01:16:36 -0000 1.2
+++ old_query.m 29 Dec 2008 09:00:45 -0000
@@ -143,6 +143,9 @@
old_exec(deep_cmd_dump_proc_var_use(_), _, _, HTML, !IO) :-
HTML =
"old_query.m: deep_cmd_dump_proc_var_use is unsupported by old_exec\n".
+old_exec(deep_cmd_module_getter_setters(_), _, _, HTML, !IO) :-
+ HTML = "old_query.m: " ++
+ "deep_cmd_module_getter_setters is unsupported by old_exec\n".
%-----------------------------------------------------------------------------%
Index: query.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/query.m,v
retrieving revision 1.32
diff -u -b -r1.32 query.m
--- query.m 16 Oct 2008 01:16:36 -0000 1.32
+++ query.m 29 Dec 2008 14:46:41 -0000
@@ -110,6 +110,9 @@
; deep_cmd_procrep_coverage(
cmd_procrep_coverage_proc_id :: proc_static_ptr
)
+ ; deep_cmd_module_getter_setters(
+ cmd_mgs_module_name :: string
+ )
% The following commands are for debugging.
@@ -417,6 +420,7 @@
)
;
( Cmd = deep_cmd_procrep_coverage(_)
+ ; Cmd = deep_cmd_module_getter_setters(_)
; Cmd = deep_cmd_dump_proc_var_use(_)
),
new_exec(Cmd, Prefs, Deep, HTMLStr, !IO)
@@ -575,6 +579,11 @@
CmdStr = string.format("%s%c%s",
[s(cmd_str_module), c(cmd_separator_char), s(ModuleName)])
;
+ Cmd = deep_cmd_module_getter_setters(ModuleName),
+ CmdStr = string.format("%s%c%s",
+ [s(cmd_str_module_getter_setters), c(cmd_separator_char),
+ s(ModuleName)])
+ ;
Cmd = deep_cmd_top_procs(Limit, CostKind, InclDesc, Scope),
LimitStr = limit_to_string(Limit),
CostKindStr = cost_kind_to_string(CostKind),
@@ -709,6 +718,11 @@
Cmd = deep_cmd_module(ModuleName),
MaybeCmd = yes(Cmd)
;
+ Pieces = [cmd_str_module_getter_setters, ModuleName]
+ ->
+ Cmd = deep_cmd_module_getter_setters(ModuleName),
+ MaybeCmd = yes(Cmd)
+ ;
Pieces = [cmd_str_top_procs, LimitStr, CostKindStr, InclDescStr,
ScopeStr],
string_to_limit(LimitStr, Limit),
@@ -1208,6 +1222,9 @@
:- func cmd_str_module = string.
cmd_str_module = "module".
+:- func cmd_str_module_getter_setters = string.
+cmd_str_module_getter_setters = "module_getter_setters".
+
:- func cmd_str_top_procs = string.
cmd_str_top_procs = "top_procs".
Index: report.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/deep_profiler/report.m,v
retrieving revision 1.16
diff -u -b -r1.16 report.m
--- report.m 5 Nov 2008 03:38:40 -0000 1.16
+++ report.m 30 Dec 2008 08:20:11 -0000
@@ -32,29 +32,62 @@
:- import_module var_use_analysis.
:- import_module list.
+:- import_module map.
:- import_module maybe.
:- import_module string.
+:- import_module unit.
%-----------------------------------------------------------------------------%
:- type deep_report
- ---> report_message(message_report)
- ; report_menu(maybe_error(menu_report))
- ; report_clique(maybe_error(clique_report))
- ; report_program_modules(maybe_error(program_modules_report))
- ; report_module(maybe_error(module_report))
- ; report_top_procs(maybe_error(top_procs_report))
- ; report_proc(maybe_error(proc_report))
- ; report_procrep_coverage_dump(maybe_error(procrep_coverage_info))
- ; report_proc_callers(maybe_error(proc_callers_report))
- ; report_proc_static_dump(maybe_error(proc_static_dump_info))
- ; report_proc_dynamic_dump(maybe_error(proc_dynamic_dump_info))
+ ---> report_message(
+ message_report
+ )
+ ; report_menu(
+ maybe_error(menu_report)
+ )
+ ; report_clique(
+ maybe_error(clique_report)
+ )
+ ; report_program_modules(
+ maybe_error(program_modules_report)
+ )
+ ; report_module(
+ maybe_error(module_report)
+ )
+ ; report_module_getter_setters(
+ maybe_error(module_getter_setters_report)
+ )
+ ; report_top_procs(
+ maybe_error(top_procs_report)
+ )
+ ; report_proc(
+ maybe_error(proc_report)
+ )
+ ; report_procrep_coverage(
+ maybe_error(procrep_coverage_info)
+ )
+ ; report_proc_callers(
+ maybe_error(proc_callers_report)
+ )
+ ; report_proc_static_dump(
+ maybe_error(proc_static_dump_info)
+ )
+ ; report_proc_dynamic_dump(
+ maybe_error(proc_dynamic_dump_info)
+ )
; report_call_site_static_dump(
- maybe_error(call_site_static_dump_info))
+ maybe_error(call_site_static_dump_info)
+ )
; report_call_site_dynamic_dump(
- maybe_error(call_site_dynamic_dump_info))
- ; report_clique_dump(maybe_error(clique_dump_info))
- ; report_proc_var_use_dump(maybe_error(proc_var_use_dump_info)).
+ maybe_error(call_site_dynamic_dump_info)
+ )
+ ; report_clique_dump(
+ maybe_error(clique_dump_info)
+ )
+ ; report_proc_var_use_dump(
+ maybe_error(proc_var_use_dump_info)
+ ).
:- type message_report
---> message_report(
@@ -152,6 +185,47 @@
mr_procs :: list(perf_row_data(proc_active))
).
+:- type data_struct_name
+ ---> data_struct_name(string).
+
+:- type field_name
+ ---> field_name(string).
+
+:- type getter_or_setter
+ ---> getter
+ ; setter.
+
+:- type gs_field_info(I, S)
+ ---> gs_field_both(
+ gsf_both_getter :: I,
+ gsf_both_setter :: I,
+ gsf_both_summary :: S
+ )
+ ; gs_field_getter(
+ gsf_getter :: I
+ )
+ ; gs_field_setter(
+ gsf_setter :: I
+ ).
+
+:- type gs_field_map(T) == map(field_name, T).
+:- type gs_ds_map(T) == map(data_struct_name, gs_field_map(T)).
+
+:- type gs_field_info == gs_field_info(
+ perf_row_data(proc_desc),
+ perf_row_data(unit)
+ ).
+:- type gs_field_map == gs_field_map(gs_field_info).
+:- type gs_ds_map == gs_ds_map(gs_field_info).
+
+:- type module_getter_setters_report
+ ---> module_getter_setters_report(
+ % Summary information about all the procedures in one module
+ % of the program.
+ mgsr_module_name :: string,
+ mgsr_procs :: gs_ds_map
+ ).
+
:- type top_procs_report
---> top_procs_report(
% Information about the most expensive procedures. The ordering
@@ -413,7 +487,8 @@
csdesc_line_number :: int,
csdesc_caller_refined_name :: string,
csdesc_slot_number :: int,
- csdesc_goal_path :: goal_path
+ csdesc_goal_path :: goal_path,
+ csdesc_maybe_callee :: maybe(proc_desc)
).
:- type ancestor_desc
cvs diff: Diffing notes
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to: mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions: mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------
More information about the reviews
mailing list