%---------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %---------------------------------------------------------------------------% % Copyright (C) 2001-2002, 2004-2012 The University of Melbourne. % This file may only be copied under the terms of the GNU General % Public License - see the file COPYING in the Mercury distribution. %---------------------------------------------------------------------------% % % File: startup.m. % Authors: conway, zs. % % This module contains the code for turning the raw list of nodes read in by % read_profile.m into the data structure that mdprof_cgi.m needs to service % requests for web pages. The algorithm it implements is documented in the % deep profiling paper. % %---------------------------------------------------------------------------% :- module startup. :- interface. :- import_module dump. :- import_module profile. :- import_module bool. :- import_module io. :- import_module list. :- import_module maybe. %---------------------------------------------------------------------------% :- pred read_and_startup_default_deep_options(string::in, string::in, string::in, bool::in, maybe(io.text_output_stream)::in, list(string)::in, maybe_error(deep)::out, io::di, io::uo) is det. :- pred read_and_startup(string::in, string::in, string::in, bool::in, maybe(io.text_output_stream)::in, list(string)::in, dump_options::in, maybe_error(deep)::out, io::di, io::uo) is det. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- implementation. :- import_module array_util. :- import_module callgraph. :- import_module canonical. :- import_module exclude. :- import_module mdbcomp. :- import_module mdbcomp.program_representation. :- import_module mdbcomp.shared_utilities. :- import_module measurements. :- import_module read_profile. :- import_module array. :- import_module int. :- import_module map. :- import_module require. :- import_module string. %---------------------------------------------------------------------------% read_and_startup_default_deep_options(Machine, ScriptName, DataFileName, Canonical, MaybeOutputStream, DumpStages, Res, !IO) :- read_and_startup(Machine, ScriptName, DataFileName, Canonical, MaybeOutputStream, DumpStages, default_dump_options, Res, !IO). read_and_startup(Machine, ScriptName, DataFileName, Canonical, MaybeOutputStream, DumpStages, DumpOptions, Result, !IO) :- % Any limits on stack size bite mostly during startup, which is why % this call is here. The alternative design would be to invoke % unlimit_stack separately in every program in this directory. % That would be error-prone, since adding the call would be easy to miss % when adding a new program. unlimit_stack(!IO), maybe_report_stats(MaybeOutputStream, !IO), maybe_report_msg(MaybeOutputStream, "% Reading graph data...\n", !IO), read_call_graph(DataFileName, DataFileResult, !IO), maybe_report_msg(MaybeOutputStream, "% Done.\n", !IO), maybe_report_stats(MaybeOutputStream, !IO), ( DataFileResult = ok(InitDeep), startup(Machine, ScriptName, DataFileName, Canonical, MaybeOutputStream, DumpStages, DumpOptions, InitDeep, Deep, !IO), Result = ok(Deep) ; DataFileResult = error(Error), Result = error(Error) ). :- func make_progrep_filename(string) = string. make_progrep_filename(DataFileName) = ProgrepFileName :- ( if string.remove_suffix(DataFileName, ".data", BaseFileName) then ProgrepFileName = BaseFileName ++ ".procrep" else error("Couldn't remove suffix from deep file name: " ++ DataFileName) ). :- pred startup(string::in, string::in, string::in, bool::in, maybe(io.text_output_stream)::in, list(string)::in, dump_options::in, initial_deep::in, deep::out, io::di, io::uo) is det. startup(Machine, ScriptName, DataFileName, Canonical, MaybeOutputStream, DumpStages, DumpOptions, InitDeep0, !:Deep, !IO) :- InitDeep0 = initial_deep(InitStats, Root, CallSiteDynamics0, ProcDynamics, CallSiteStatics0, ProcStatics0), maybe_dump(MaybeOutputStream, DataFileName, DumpStages, 0, dump_initial_deep(InitDeep0, default_dump_options), !IO), maybe_report_msg(MaybeOutputStream, "% Mapping static call sites to containing procedures...\n", !IO), array_foldl2_from_1(record_css_containers_module_procs, ProcStatics0, u(CallSiteStatics0), CallSiteStatics, map.init, ModuleProcs), ModuleDataMap0 = map.map_values_only(initialize_module_data, ModuleProcs), maybe_report_msg(MaybeOutputStream, "% Done.\n", !IO), maybe_report_stats(MaybeOutputStream, !IO), maybe_report_msg(MaybeOutputStream, "% Mapping dynamic call sites to containing procedures...\n", !IO), array_foldl2_from_1(record_csd_containers_zeroed_pss, ProcDynamics, u(CallSiteDynamics0), CallSiteDynamics, u(ProcStatics0), ProcStatics), maybe_report_msg(MaybeOutputStream, "% Done.\n", !IO), maybe_report_stats(MaybeOutputStream, !IO), InitDeep1 = initial_deep(InitStats, Root, CallSiteDynamics, ProcDynamics, CallSiteStatics, ProcStatics), maybe_dump(MaybeOutputStream, DataFileName, DumpStages, 10, dump_initial_deep(InitDeep1, DumpOptions), !IO), ( Canonical = no, InitDeep = InitDeep1 ; Canonical = yes, maybe_report_msg(MaybeOutputStream, "% Canonicalizing cliques...\n", !IO), canonicalize_cliques(InitDeep1, InitDeep), maybe_report_msg(MaybeOutputStream, "% Done.\n", !IO), maybe_report_stats(MaybeOutputStream, !IO) ), maybe_dump(MaybeOutputStream, DataFileName, DumpStages, 20, dump_initial_deep(InitDeep, DumpOptions), !IO), array.max(InitDeep ^ init_proc_dynamics, PDMax), NPDs = PDMax + 1, array.max(InitDeep ^ init_call_site_dynamics, CSDMax), NCSDs = CSDMax + 1, array.max(InitDeep ^ init_proc_statics, PSMax), NPSs = PSMax + 1, array.max(InitDeep ^ init_call_site_statics, CSSMax), NCSSs = CSSMax + 1, maybe_report_msg(MaybeOutputStream, "% Finding cliques...\n", !IO), find_cliques(InitDeep, CliqueList), maybe_report_msg(MaybeOutputStream, "% Done.\n", !IO), maybe_report_stats(MaybeOutputStream, !IO), maybe_report_msg(MaybeOutputStream, "% Constructing clique indexes...\n", !IO), make_clique_indexes(NPDs, CliqueList, Cliques, CliqueIndex), maybe_report_msg(MaybeOutputStream, "% Done.\n", !IO), maybe_report_stats(MaybeOutputStream, !IO), maybe_report_msg(MaybeOutputStream, "% Constructing clique parent map...\n", !IO), % For each CallSiteDynamic pointer, if it points to a ProcDynamic % which is in a different clique to the one from which the % CallSiteDynamic's parent came, then this CallSiteDynamic is the entry to % the [lower] clique. We need to compute this information so that % we can print clique-based timing summaries in the browser. array.max(Cliques, CliqueMax), NCliques = CliqueMax + 1, array.init(NCliques, call_site_dynamic_ptr(-1), CliqueParents0), array.init(NCSDs, no, CliqueMaybeChildren0), array_foldl2_from_1(construct_clique_parents(InitDeep, CliqueIndex), CliqueIndex, CliqueParents0, CliqueParents, CliqueMaybeChildren0, CliqueMaybeChildren), maybe_report_msg(MaybeOutputStream, "% Done.\n", !IO), maybe_report_stats(MaybeOutputStream, !IO), maybe_report_msg(MaybeOutputStream, "% Finding procedure callers...\n", !IO), array.init(NPSs, [], ProcCallers0), array_foldl_from_1(construct_proc_callers(InitDeep), CallSiteDynamics, ProcCallers0, ProcCallers), maybe_report_msg(MaybeOutputStream, "% Done.\n", !IO), maybe_report_stats(MaybeOutputStream, !IO), maybe_report_msg(MaybeOutputStream, "% Constructing call site static map...\n", !IO), array.init(NCSDs, call_site_static_ptr(-1), CallSiteStaticMap0), array_foldl_from_1(construct_call_site_caller(InitDeep), ProcDynamics, CallSiteStaticMap0, CallSiteStaticMap), maybe_report_msg(MaybeOutputStream, "% Done.\n", !IO), maybe_report_stats(MaybeOutputStream, !IO), maybe_report_msg(MaybeOutputStream, "% Finding call site calls...\n", !IO), array.init(NCSSs, map.init, CallSiteCalls0), array_foldl_from_1(construct_call_site_calls(InitDeep), ProcDynamics, CallSiteCalls0, CallSiteCalls), maybe_report_msg(MaybeOutputStream, "% Done.\n", !IO), maybe_report_stats(MaybeOutputStream, !IO), ProgRepFileName = make_progrep_filename(DataFileName), maybe_report_msg(MaybeOutputStream, "% Reading program representation...\n", !IO), read_prog_rep_file(ProgRepFileName, ProgRepResult, !IO), ( ProgRepResult = ok(ProgRep), maybe_report_msg(MaybeOutputStream, "% Done.\n", !IO), MaybeProcRepFile = yes(ok(ProgRep)), ProgRep = prog_rep(ModuleMap), map.keys(ModuleMap, ProgRepModules), ( MaybeOutputStream = yes(OutputStream), io.write(OutputStream, ProgRepModules, !IO), io.nl(OutputStream, !IO) ; MaybeOutputStream = no ), list.foldl(ensure_module_has_module_data, ProgRepModules, ModuleDataMap0, ModuleDataMap) ; ProgRepResult = error(Error), ( io.open_input(ProgRepFileName, OpenProgRepResult, !IO), ( OpenProgRepResult = ok(ProgRepStream), % The file exists, so the error message describes something % wrong with the file. io.close_input(ProgRepStream, !IO), ErrorMessage = io.error_message(Error), maybe_report_msg(MaybeOutputStream, "% Error: " ++ ErrorMessage ++ "\n", !IO), MaybeProcRepFile = yes(error(ErrorMessage)), ModuleDataMap = ModuleDataMap0 ; OpenProgRepResult = error(_), % The file does not exist. MaybeProcRepFile = no, ModuleDataMap = ModuleDataMap0 ) ) ), ContourFileName = contour_file_name(DataFileName), string.format("%% Trying to read contour exclusion file `%s'...\n", [s(ContourFileName)], TryMsg), maybe_report_msg(MaybeOutputStream, TryMsg, !IO), read_exclude_file(ContourFileName, ModuleDataMap, ExcludeFile, !IO), ExcludeFile = exclude_file(_, ExcludeContents), ( ExcludeContents = no_exclude_file, maybe_report_msg(MaybeOutputStream, "% Couldn't open file.\n", !IO) ; ExcludeContents = unreadable_exclude_file(ExcludeError), string.format("%% File had unrecoverable errors:\n%% %s.\n", [s(ExcludeError)], ExcludeErrorMsg), maybe_report_msg(MaybeOutputStream, ExcludeErrorMsg, !IO) ; ExcludeContents = readable_exclude_file(_, _), maybe_report_msg(MaybeOutputStream, "% Done.\n", !IO) ), maybe_report_msg(MaybeOutputStream, "% Propagating measurements up call graph...\n", !IO), array.init(NCSDs, zero_inherit_prof_info, CSDDesc0), array.init(NPDs, zero_own_prof_info, PDOwn0), array_foldl_from_1(sum_call_sites_in_proc_dynamic, CallSiteDynamics, PDOwn0, PDOwn), array.init(NPDs, zero_inherit_prof_info, PDDesc0), array.init(NPSs, zero_own_prof_info, PSOwn0), 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, PDCompTable0), array.init(NCSDs, map.init, CSDCompTable0), CoverageDataType = InitStats ^ prs_deep_flags ^ df_coverage_data_type, ( CoverageDataType = no_coverage_data, MaybeStaticCoverage0 = no ; ( CoverageDataType = static_coverage_data ; CoverageDataType = dynamic_coverage_data ), array.init(NPSs, zero_static_coverage, StaticCoverage0), MaybeStaticCoverage0 = yes(StaticCoverage0) ), !:Deep = deep(InitStats, Machine, ScriptName, DataFileName, Root, CallSiteDynamics, ProcDynamics, CallSiteStatics, ProcStatics, CliqueIndex, Cliques, CliqueParents, CliqueMaybeChildren, ProcCallers, CallSiteStaticMap, CallSiteCalls, PDOwn, PDDesc0, CSDDesc0, PSOwn0, PSDesc0, CSSOwn0, CSSDesc0, PDCompTable0, CSDCompTable0, ModuleDataMap, MaybeStaticCoverage0, ExcludeFile, MaybeProcRepFile, MaybeOutputStream), array_foldl_from_1(propagate_to_clique, Cliques, !Deep), maybe_report_msg(MaybeOutputStream, "% Done.\n", !IO), maybe_report_stats(MaybeOutputStream, !IO), maybe_dump(MaybeOutputStream, DataFileName, DumpStages, 30, dump_deep(!.Deep, DumpOptions), !IO), maybe_report_msg(MaybeOutputStream, "% Summarizing information...\n", !IO), ( ( CoverageDataType = no_coverage_data ; CoverageDataType = static_coverage_data ), ( CoverageDataType = static_coverage_data, maybe_report_msg(MaybeOutputStream, "\t% Summarizing static coverage...\n", !IO), summarize_proc_statics_coverage(!Deep) ; CoverageDataType = no_coverage_data ), maybe_report_msg(MaybeOutputStream, "\t% Summarizing proc dynamics...\n", !IO), summarize_proc_dynamics(!Deep) ; CoverageDataType = dynamic_coverage_data, maybe_report_msg(MaybeOutputStream, "\t% Summarizing proc dynamics with coverage...\n", !IO), summarize_proc_dynamics_with_coverage_data(!Deep) ), maybe_report_msg(MaybeOutputStream, "\t% Summarizing call site dynamics...\n", !IO), summarize_call_site_dynamics(!Deep), maybe_report_msg(MaybeOutputStream, "\t% Summarizing modules...\n", !IO), summarize_modules(!Deep), maybe_report_msg(MaybeOutputStream, "% Done.\n", !IO), maybe_report_stats(MaybeOutputStream, !IO), maybe_dump(MaybeOutputStream, DataFileName, DumpStages, 40, dump_deep(!.Deep, DumpOptions), !IO). :- func contour_file_name(string) = string. contour_file_name(DataFileName) = ContourFileName :- ( if string.remove_suffix(DataFileName, ".data", BaseFileName) then ContourFileName = BaseFileName ++ ".contour" else error("Couldn't remove suffix from deep file name: " ++ DataFileName) ). :- pred count_quanta(int::in, call_site_dynamic::in, int::in, int::out) is det. count_quanta(_N, CSD, Quanta0, Quanta) :- Quanta = Quanta0 + quanta(CSD ^ csd_own_prof). :- func initialize_module_data(list(proc_static_ptr)) = module_data. initialize_module_data(PSPtrs) = module_data(zero_own_prof_info, zero_inherit_prof_info, PSPtrs). :- pred ensure_module_has_module_data(string::in, map(string, module_data)::in, map(string, module_data)::out) is det. ensure_module_has_module_data(Module, !ModuleDataMap) :- ( if map.search(!.ModuleDataMap, Module, _) then true else Data = module_data(zero_own_prof_info, zero_inherit_prof_info, []), map.det_insert(Module, Data, !ModuleDataMap) ). :- pred maybe_dump(maybe(io.text_output_stream)::in, string::in, list(string)::in, int::in, pred(io.text_output_stream, io, io)::in(pred(in, di, uo) is det), io::di, io::uo) is det. maybe_dump(MaybeOutputStream, BaseName, DumpStages, ThisStageNum, Action, !IO) :- string.int_to_string(ThisStageNum, ThisStageStr), ( if ( list.member("all", DumpStages) ; list.member(ThisStageStr, DumpStages) ) then FileName = BaseName ++ ".deepdump." ++ ThisStageStr, io.open_output(FileName, OpenRes, !IO), ( OpenRes = ok(FileStream), Action(FileStream, !IO), io.close_output(FileStream, !IO) ; OpenRes = error(Error), io.error_message(Error, Msg), ( MaybeOutputStream = yes(OutputStream) ; MaybeOutputStream = no, io.stderr_stream(OutputStream, !IO) ), io.format(OutputStream, "%s: %s\n", [s(FileName), s(Msg)], !IO) ) else true ). %---------------------------------------------------------------------------% :- pred record_css_containers_module_procs(int::in, proc_static::in, array(call_site_static)::array_di, 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_module_procs(PSI, PS, !CallSiteStatics, !ModuleProcs) :- CSSPtrs = PS ^ ps_sites, PSPtr = proc_static_ptr(PSI), array.max(CSSPtrs, MaxCS), record_css_containers_2(MaxCS, PSPtr, CSSPtrs, !CallSiteStatics), DeclModule = PS ^ ps_decl_module, ( if map.search(!.ModuleProcs, DeclModule, PSPtrs0) then map.det_update(DeclModule, [PSPtr | PSPtrs0], !ModuleProcs) else map.det_insert(DeclModule, [PSPtr], !ModuleProcs) ). :- pred record_css_containers_2(int::in, proc_static_ptr::in, array(call_site_static_ptr)::in, array(call_site_static)::array_di, array(call_site_static)::array_uo) is det. record_css_containers_2(SlotNum, PSPtr, CSSPtrs, !CallSiteStatics) :- ( if SlotNum >= 0 then array.lookup(CSSPtrs, SlotNum, CSSPtr), lookup_call_site_statics(!.CallSiteStatics, CSSPtr, CSS0), CSS0 = call_site_static(PSPtr0, SlotNum0, Kind, LineNumber, GoalPath), require(unify(PSPtr0, proc_static_ptr(-1)), "record_css_containers_2: real proc_static_ptr"), require(unify(SlotNum0, -1), "record_css_containers_2: real slot_num"), CSS = call_site_static(PSPtr, SlotNum, Kind, LineNumber, GoalPath), update_call_site_statics(CSSPtr, CSS, !CallSiteStatics), record_css_containers_2(SlotNum - 1, PSPtr, CSSPtrs, !CallSiteStatics) else true ). %---------------------------------------------------------------------------% :- pred record_csd_containers_zeroed_pss(int::in, proc_dynamic::in, array(call_site_dynamic)::array_di, array(call_site_dynamic)::array_uo, array(proc_static)::array_di, array(proc_static)::array_uo) is det. record_csd_containers_zeroed_pss(PDI, PD, !CallSiteDynamics, !ProcStatics) :- CSDArray = PD ^ pd_sites, PDPtr = proc_dynamic_ptr(PDI), flatten_call_sites(CSDArray, CSDPtrs, IsZeroed), record_csd_containers_2(PDPtr, CSDPtrs, !CallSiteDynamics), ( IsZeroed = zeroed, PSPtr = PD ^ pd_proc_static, lookup_proc_statics(!.ProcStatics, PSPtr, PS0), PS = PS0 ^ ps_is_zeroed := zeroed, update_proc_statics(PSPtr, PS, !ProcStatics) ; IsZeroed = not_zeroed ). :- pred record_csd_containers_2(proc_dynamic_ptr::in, list(call_site_dynamic_ptr)::in, array(call_site_dynamic)::array_di, array(call_site_dynamic)::array_uo) is det. record_csd_containers_2(_, [], !CallSiteDynamics). record_csd_containers_2(PDPtr, [CSDPtr | CSDPtrs], !CallSiteDynamics) :- lookup_call_site_dynamics(!.CallSiteDynamics, CSDPtr, CSD0), CSD0 = call_site_dynamic(CallerPDPtr0, CalleePDPtr, Own), require(unify(CallerPDPtr0, proc_dynamic_ptr(-1)), "record_csd_containers_2: real proc_dynamic_ptr"), CSD = call_site_dynamic(PDPtr, CalleePDPtr, Own), update_call_site_dynamics(CSDPtr, CSD, !CallSiteDynamics), record_csd_containers_2(PDPtr, CSDPtrs, !CallSiteDynamics). %---------------------------------------------------------------------------% :- pred construct_clique_parents(initial_deep::in, array(clique_ptr)::in, int::in, clique_ptr::in, array(call_site_dynamic_ptr)::array_di, array(call_site_dynamic_ptr)::array_uo, array(maybe(clique_ptr))::array_di, array(maybe(clique_ptr))::array_uo) is det. construct_clique_parents(InitDeep, CliqueIndex, PDI, CliquePtr, !CliqueParents, !CliqueMaybeChildren) :- ( if PDI > 0 then flat_call_sites(InitDeep ^ init_proc_dynamics, proc_dynamic_ptr(PDI), CSDPtrs), array_list_foldl2( construct_clique_parents_2(InitDeep, CliqueIndex, CliquePtr), CSDPtrs, !CliqueParents, !CliqueMaybeChildren) else error("construct_clique_parents: invalid pdi") ). :- pred construct_clique_parents_2(initial_deep::in, array(clique_ptr)::in, clique_ptr::in, call_site_dynamic_ptr::in, array(call_site_dynamic_ptr)::array_di, array(call_site_dynamic_ptr)::array_uo, array(maybe(clique_ptr))::array_di, array(maybe(clique_ptr))::array_uo) is det. construct_clique_parents_2(InitDeep, CliqueIndex, ParentCliquePtr, CSDPtr, !CliqueParents, !CliqueMaybeChildren) :- CSDPtr = call_site_dynamic_ptr(CSDI), ( if CSDI > 0 then array.lookup(InitDeep ^ init_call_site_dynamics, CSDI, CSD), ChildPDPtr = CSD ^ csd_callee, ChildPDPtr = proc_dynamic_ptr(ChildPDI), ( if ChildPDI > 0 then array.lookup(CliqueIndex, ChildPDI, ChildCliquePtr), ( if ChildCliquePtr \= ParentCliquePtr then ChildCliquePtr = clique_ptr(ChildCliqueNum), array.set(ChildCliqueNum, CSDPtr, !CliqueParents), array.set(CSDI, yes(ChildCliquePtr), !CliqueMaybeChildren) else true ) else true ) else true ). %---------------------------------------------------------------------------% :- pred construct_proc_callers(initial_deep::in, int::in, call_site_dynamic::in, array(list(call_site_dynamic_ptr))::array_di, array(list(call_site_dynamic_ptr))::array_uo) is det. construct_proc_callers(InitDeep, CSDI, CSD, !ProcCallers) :- PDPtr = CSD ^ csd_callee, ( if valid_proc_dynamic_ptr_raw(InitDeep ^ init_proc_dynamics, PDPtr) then lookup_proc_dynamics(InitDeep ^ init_proc_dynamics, PDPtr, PD), PSPtr = PD ^ pd_proc_static, lookup_proc_callers(!.ProcCallers, PSPtr, Callers0), Callers = [call_site_dynamic_ptr(CSDI) | Callers0], update_proc_callers(PSPtr, Callers, !ProcCallers) else true ). :- pred construct_call_site_caller(initial_deep::in, int::in, proc_dynamic::in, array(call_site_static_ptr)::array_di, array(call_site_static_ptr)::array_uo) is det. construct_call_site_caller(InitDeep, _PDI, PD, !CallSiteStaticMap) :- PSPtr = PD ^ pd_proc_static, CSDArraySlots = PD ^ pd_sites, 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, !CallSiteStaticMap). :- pred construct_call_site_caller_2(int::in, call_site_dynamics::in, array(call_site_static_ptr)::in, array(call_site_array_slot)::in, array(call_site_static_ptr)::array_di, array(call_site_static_ptr)::array_uo) is det. construct_call_site_caller_2(SlotNum, Deep, CSSPtrs, CSDArraySlots, !CallSiteStaticMap) :- ( if SlotNum >= 0 then array.lookup(CSDArraySlots, SlotNum, CSDArraySlot), array.lookup(CSSPtrs, SlotNum, CSSPtr), ( CSDArraySlot = slot_normal(CSDPtr), construct_call_site_caller_3(Deep, CSSPtr, -1, CSDPtr, !CallSiteStaticMap) ; CSDArraySlot = slot_multi(_, CSDPtrs), array_foldl_from_0(construct_call_site_caller_3(Deep, CSSPtr), CSDPtrs, !CallSiteStaticMap) ), construct_call_site_caller_2(SlotNum - 1, Deep, CSSPtrs, CSDArraySlots, !CallSiteStaticMap) else true ). :- pred construct_call_site_caller_3(call_site_dynamics::in, call_site_static_ptr::in, int::in, call_site_dynamic_ptr::in, array(call_site_static_ptr)::array_di, array(call_site_static_ptr)::array_uo) is det. construct_call_site_caller_3(CallSiteDynamics, CSSPtr, _Dummy, CSDPtr, !CallSiteStaticMap) :- ( if valid_call_site_dynamic_ptr_raw(CallSiteDynamics, CSDPtr) then update_call_site_static_map(CSDPtr, CSSPtr, !CallSiteStaticMap) else true ). %---------------------------------------------------------------------------% :- 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) is det. construct_call_site_calls(InitDeep, _PDI, PD, !CallSiteCalls) :- PSPtr = PD ^ pd_proc_static, CSDArraySlots = PD ^ pd_sites, array.max(CSDArraySlots, MaxCS), PSPtr = proc_static_ptr(PSI), array.lookup(InitDeep ^ init_proc_statics, PSI, PS), CSSPtrs = PS ^ ps_sites, CallSiteDynamics = InitDeep ^ init_call_site_dynamics, ProcDynamics = InitDeep ^ init_proc_dynamics, construct_call_site_calls_2(CallSiteDynamics, ProcDynamics, MaxCS, CSSPtrs, CSDArraySlots, !CallSiteCalls). :- pred construct_call_site_calls_2(call_site_dynamics::in, proc_dynamics::in, int::in, array(call_site_static_ptr)::in, array(call_site_array_slot)::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) is det. construct_call_site_calls_2(CallSiteDynamics, ProcDynamics, SlotNum, CSSPtrs, CSDArraySlots, !CallSiteCalls) :- ( if SlotNum >= 0 then array.lookup(CSDArraySlots, SlotNum, CSDArraySlot), array.lookup(CSSPtrs, SlotNum, CSSPtr), ( CSDArraySlot = slot_normal(CSDPtr), construct_call_site_calls_3(CallSiteDynamics, ProcDynamics, CSSPtr, -1, CSDPtr, !CallSiteCalls) ; CSDArraySlot = slot_multi(_, CSDPtrs), array_foldl_from_0( construct_call_site_calls_3(CallSiteDynamics, ProcDynamics, CSSPtr), CSDPtrs, !CallSiteCalls) ), construct_call_site_calls_2(CallSiteDynamics, ProcDynamics, SlotNum - 1, CSSPtrs, CSDArraySlots, !CallSiteCalls) else true ). :- pred construct_call_site_calls_3(call_site_dynamics::in, proc_dynamics::in, call_site_static_ptr::in, int::in, call_site_dynamic_ptr::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) is det. construct_call_site_calls_3(CallSiteDynamics, ProcDynamics, CSSPtr, _Dummy, CSDPtr, !CallSiteCalls) :- CSDPtr = call_site_dynamic_ptr(CSDI), ( if CSDI > 0 then array.lookup(CallSiteDynamics, CSDI, CSD), PDPtr = CSD ^ csd_callee, PDPtr = proc_dynamic_ptr(PDI), array.lookup(ProcDynamics, PDI, PD), PSPtr = PD ^ pd_proc_static, CSSPtr = call_site_static_ptr(CSSI), array.lookup(!.CallSiteCalls, CSSI, CallMap0), ( if map.search(CallMap0, PSPtr, CallList0) then CallList = [CSDPtr | CallList0], map.det_update(PSPtr, CallList, CallMap0, CallMap) else CallList = [CSDPtr], map.det_insert(PSPtr, CallList, CallMap0, CallMap) ), array.set(CSSI, CallMap, !CallSiteCalls) else true ). %---------------------------------------------------------------------------% :- 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, !PDOwnArray) :- CalleeOwn = CSD ^ csd_own_prof, PDPtr = CSD ^ csd_callee, PDPtr = proc_dynamic_ptr(PDI), ( if PDI > 0 then array.lookup(!.PDOwnArray, PDI, ProcOwn0), ProcOwn = add_own_to_own(CalleeOwn, ProcOwn0), array.set(PDI, ProcOwn, !PDOwnArray) else error("sum_call_sites_in_proc_dynamic: invalid pdptr") ). %---------------------------------------------------------------------------% :- pred summarize_proc_dynamics(deep::in, deep::out) is det. summarize_proc_dynamics(Deep0, Deep) :- PSOwnArray0 = Deep0 ^ ps_own, PSDescArray0 = Deep0 ^ ps_desc, array_foldl2_from_1( summarize_proc_dynamic(Deep0 ^ pd_own, Deep0 ^ pd_desc, Deep0 ^ pd_comp_table), Deep0 ^ proc_dynamics, copy(PSOwnArray0), PSOwnArray, copy(PSDescArray0), PSDescArray), Deep = ((Deep0 ^ ps_own := PSOwnArray) ^ ps_desc := PSDescArray). :- pred summarize_proc_dynamic(array(own_prof_info)::in, array(inherit_prof_info)::in, array(compensation_table)::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(PDOwnArray, PDDescArray, PDCompTableArray, PDI, PD, PSOwnArray0, PSOwnArray, PSDescArray0, PSDescArray) :- PSPtr = PD ^ pd_proc_static, PDPtr = proc_dynamic_ptr(PDI), lookup_pd_own(PDOwnArray, PDPtr, PDOwn), lookup_pd_desc(PDDescArray, PDPtr, PDDesc0), lookup_pd_comp_table(PDCompTableArray, PDPtr, PDCompTable), ( if map.search(PDCompTable, PSPtr, InnerTotal) then PDDesc = subtract_inherit_from_inherit(InnerTotal, PDDesc0) else 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(PSPtr, PSOwn, u(PSOwnArray0), PSOwnArray), update_ps_desc(PSPtr, PSDesc, u(PSDescArray0), PSDescArray). :- pred summarize_proc_dynamics_with_coverage_data(deep::in, deep::out) is det. summarize_proc_dynamics_with_coverage_data(!Deep) :- % These arrays are one based, the +1 here is necessary to allocate the % correct amount of storage. NPS = !.Deep ^ profile_stats ^ prs_num_ps + 1, array_foldl3_from_1( summarize_proc_dynamic_with_coverage(!.Deep ^ pd_own, !.Deep ^ pd_desc, !.Deep ^ pd_comp_table), !.Deep ^ proc_dynamics, init(NPS, zero_own_prof_info), PSOwnArray, init(NPS, zero_inherit_prof_info), PSDescArray, init(NPS, zero_static_coverage), CoverageArray), !Deep ^ ps_own := PSOwnArray, !Deep ^ ps_desc := PSDescArray, !Deep ^ maybe_static_coverage := yes(CoverageArray). :- pred summarize_proc_dynamic_with_coverage(array(own_prof_info)::in, array(inherit_prof_info)::in, array(compensation_table)::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, array(static_coverage_info)::array_di, array(static_coverage_info)::array_uo) is det. summarize_proc_dynamic_with_coverage(PDOwnArray, PDDescArray, PDCompTableArray, PDI, PD, !PSOwnArray, !PSDescArray, !CoverageArray) :- summarize_proc_dynamic(PDOwnArray, PDDescArray, PDCompTableArray, PDI, PD, !PSOwnArray, !PSDescArray), PSPtr = PD ^ pd_proc_static, MaybeDynamicCoverage = PD ^ pd_maybe_coverage_points, ( MaybeDynamicCoverage = yes(DynamicCoverage), some [!StaticCoverage] ( lookup_ps_coverage(!.CoverageArray, PSPtr, !:StaticCoverage), add_coverage_arrays(DynamicCoverage, !StaticCoverage), update_ps_coverage(PSPtr, !.StaticCoverage, !CoverageArray) ) ; MaybeDynamicCoverage = no, unexpected($pred, "no coverage point array in proc dynamic") ). %---------------------------------------------------------------------------% :- pred summarize_call_site_dynamics(deep::in, deep::out) is det. summarize_call_site_dynamics(!Deep) :- CSSOwnArray0 = !.Deep ^ css_own, CSSDescArray0 = !.Deep ^ css_desc, array_foldl2_from_1( summarize_call_site_dynamic( !.Deep ^ call_site_static_map, !.Deep ^ call_site_statics, !.Deep ^ csd_desc, !.Deep ^ csd_comp_table), !.Deep ^ call_site_dynamics, copy(CSSOwnArray0), CSSOwnArray, copy(CSSDescArray0), CSSDescArray), !Deep ^ css_own := CSSOwnArray, !Deep ^ css_desc := CSSDescArray. :- pred summarize_call_site_dynamic(call_site_static_map::in, call_site_statics::in, array(inherit_prof_info)::in, array(compensation_table)::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, CallSiteStatics, CSDDescs, CSDCompTableArray, 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), ( if CSSI > 0 then CSDOwn = CSD ^ csd_own_prof, lookup_csd_desc(CSDDescs, CSDPtr, CSDDesc0), lookup_csd_comp_table(CSDCompTableArray, CSDPtr, CSDCompTable), lookup_call_site_statics(CallSiteStatics, CSSPtr, CSS), ( if map.search(CSDCompTable, CSS ^ css_container, InnerTotal) then CSDDesc = subtract_inherit_from_inherit(InnerTotal, CSDDesc0) else 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(CSSPtr, CSSOwn, u(CSSOwnArray0), CSSOwnArray), update_css_desc(CSSPtr, CSSDesc, u(CSSDescArray0), CSSDescArray) else 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_only(summarize_module_costs(Deep0), ModuleData0), Deep = Deep0 ^ module_data := ModuleData. :- func summarize_module_costs(deep, module_data) = module_data. summarize_module_costs(Deep, ModuleData0) = ModuleData :- ModuleData0 = module_data(Own0, Desc0, PSPtrs), list.foldl2(accumulate_ps_costs(Deep), PSPtrs, Own0, Own, Desc0, Desc), ModuleData = module_data(Own, Desc, PSPtrs). %---------------------------------------------------------------------------% :- pred summarize_proc_statics_coverage(deep::in, deep::out) is det. summarize_proc_statics_coverage(!Deep) :- NPS = !.Deep ^ profile_stats ^ prs_num_ps, array_foldl_from_1( summarize_proc_static_coverage, !.Deep ^ proc_statics, init(NPS, zero_static_coverage), CoverageArray), !Deep ^ maybe_static_coverage := yes(CoverageArray). :- pred summarize_proc_static_coverage(int::in, proc_static::in, array(static_coverage_info)::array_di, array(static_coverage_info)::array_uo) is det. summarize_proc_static_coverage(Index, PS, !CoverageArray) :- MaybeCoverage = PS ^ ps_maybe_coverage_points, ( MaybeCoverage = yes(Coverage0), array_to_static_coverage(Coverage0, Coverage), array.set(Index, Coverage, !CoverageArray) ; MaybeCoverage = no, unexpected($pred, "no coverage data in proc static") ). %---------------------------------------------------------------------------% :- 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, !Deep) :- array.lookup(!.Deep ^ clique_parents, CliqueNumber, ParentCSDPtr), list.foldl3(propagate_to_proc_dynamic(CliqueNumber, ParentCSDPtr), Members, !Deep, map.init, SumTable, map.init, OverrideMap), ( if valid_call_site_dynamic_ptr(!.Deep, ParentCSDPtr) then deep_lookup_call_site_dynamics(!.Deep, ParentCSDPtr, ParentCSD), ParentOwn = ParentCSD ^ csd_own_prof, deep_lookup_csd_desc(!.Deep, ParentCSDPtr, ParentDesc0), subtract_own_from_inherit(ParentOwn, ParentDesc0) = ParentDesc, deep_update_csd_desc(ParentCSDPtr, ParentDesc, !Deep), CSDCompTable = apply_override(OverrideMap, SumTable), deep_update_csd_comp_table(ParentCSDPtr, CSDCompTable, !Deep) else true ). :- pred propagate_to_proc_dynamic(int::in, call_site_dynamic_ptr::in, proc_dynamic_ptr::in, deep::in, deep::out, compensation_table::in, compensation_table::out, compensation_table::in, compensation_table::out) is det. propagate_to_proc_dynamic(CliqueNumber, ParentCSDPtr, PDPtr, !Deep, !SumTable, !OverrideTable) :- flat_call_sites(!.Deep ^ proc_dynamics, PDPtr, CSDPtrs), list.foldl2(propagate_to_call_site(CliqueNumber, PDPtr), CSDPtrs, !Deep, map.init, PDCompTable), deep_update_pd_comp_table(PDPtr, PDCompTable, !Deep), deep_lookup_pd_desc(!.Deep, PDPtr, ProcDesc), deep_lookup_pd_own(!.Deep, PDPtr, ProcOwn), ProcTotal = add_own_to_inherit(ProcOwn, ProcDesc), !:SumTable = add_comp_tables(!.SumTable, PDCompTable), deep_lookup_proc_dynamics(!.Deep, PDPtr, PD), PSPtr = PD ^ pd_proc_static, deep_lookup_proc_statics(!.Deep, PSPtr, PS), ( if PS ^ ps_is_zeroed = zeroed then !:OverrideTable = add_to_override(!.OverrideTable, PSPtr, ProcTotal) else true ), ( if valid_call_site_dynamic_ptr(!.Deep, ParentCSDPtr) then deep_lookup_csd_desc(!.Deep, ParentCSDPtr, ParentDesc0), ParentDesc = add_inherit_to_inherit(ParentDesc0, ProcTotal), deep_update_csd_desc(ParentCSDPtr, ParentDesc, !Deep) else true ). :- pred propagate_to_call_site(int::in, proc_dynamic_ptr::in, call_site_dynamic_ptr::in, deep::in, deep::out, compensation_table::in, compensation_table::out) is det. propagate_to_call_site(CliqueNumber, PDPtr, CSDPtr, !Deep, !PDCompTable) :- deep_lookup_call_site_dynamics(!.Deep, CSDPtr, CSD), CalleeOwn = CSD ^ csd_own_prof, CalleePDPtr = CSD ^ csd_callee, deep_lookup_clique_index(!.Deep, CalleePDPtr, ChildCliquePtr), ChildCliquePtr = clique_ptr(ChildCliqueNumber), ( if ChildCliqueNumber = CliqueNumber then % We don't propagate profiling measurements along intra-clique calls. true else deep_lookup_pd_desc(!.Deep, PDPtr, ProcDesc0), deep_lookup_csd_desc(!.Deep, CSDPtr, CalleeDesc), CalleeTotal = add_own_to_inherit(CalleeOwn, CalleeDesc), ProcDesc = add_inherit_to_inherit(ProcDesc0, CalleeTotal), deep_update_pd_desc(PDPtr, ProcDesc, !Deep), deep_lookup_csd_comp_table(!.Deep, CSDPtr, CSDCompTable), !:PDCompTable = add_comp_tables(!.PDCompTable, CSDCompTable) ). %---------------------------------------------------------------------------% :- func add_comp_tables(compensation_table, compensation_table) = compensation_table. add_comp_tables(CompTableA, CompTableB) = CompTable :- ( if map.is_empty(CompTableA) then CompTable = CompTableB else if map.is_empty(CompTableB) then CompTable = CompTableA else CompTable = map.union(add_inherit_to_inherit, CompTableA, CompTableB) ). :- func apply_override(compensation_table, compensation_table) = compensation_table. apply_override(CompTableA, CompTableB) = CompTable :- ( if map.is_empty(CompTableA) then CompTable = CompTableB else if map.is_empty(CompTableB) then CompTable = CompTableA else CompTable = map.union(select_override_comp, CompTableA, CompTableB) ). :- func select_override_comp(inherit_prof_info, inherit_prof_info) = inherit_prof_info. select_override_comp(OverrideComp, _) = OverrideComp. :- func add_to_override(compensation_table, proc_static_ptr, inherit_prof_info) = compensation_table. add_to_override(!.CompTable, PSPtr, PDTotal) = !:CompTable :- ( if map.search(!.CompTable, PSPtr, Comp0) then Comp = add_inherit_to_inherit(Comp0, PDTotal), map.det_update(PSPtr, Comp, !CompTable) else map.det_insert(PSPtr, PDTotal, !CompTable) ). %---------------------------------------------------------------------------% :- 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 = slot_normal(CSDPtr), CSDPtr = call_site_dynamic_ptr(CSDI), ( if CSDI > 0 then CSDPtrs1 = [[CSDPtr] | CSDPtrs0] else CSDPtrs1 = CSDPtrs0 ), IsZeroed = IsZeroed0 ; Slot = 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 ; IsZeroed1 = not_zeroed, IsZeroed = IsZeroed0 ) ). %---------------------------------------------------------------------------% :- pred maybe_report_stats(maybe(io.text_output_stream)::in, io::di, io::uo) is det. % XXX: io.report_stats writes to stderr, which mdprof_cgi has closed. % We want to write the report to _OutputStream, but the library doesn't % support that yet. % % The stats are needed only when writing the deep profiling paper anyway. maybe_report_stats(yes(_OutputStream), !IO). % io.report_stats("standard", !IO). maybe_report_stats(no, !IO). :- pred maybe_report_msg(maybe(io.text_output_stream)::in, string::in, io::di, io::uo) is det. maybe_report_msg(yes(OutputStream), Msg, !IO) :- io.write_string(OutputStream, Msg, !IO), flush_output(OutputStream, !IO). maybe_report_msg(no, _, !IO). %---------------------------------------------------------------------------% :- end_module startup. %---------------------------------------------------------------------------%