[m-rev.] for review: call contexts in ssdebug
Peter Wang
novalazy at gmail.com
Wed Jun 2 16:13:36 AEST 2010
Branches: main, 10.04
Modify the ssdebug transformation so that the context of procedure calls is
available. Before each call, store the caller's file name and line number in
globals by calling a predicate `ssdb.set_context'. Then in the following call
to `ssdb.handle_event_call' we know where the call came from.
compiler/ssdebug.m:
Add the calls to `ssdb.set_context' in the same initial pass that
creates proxies for standard library predicates.
Update the documentation on the transformation.
ssdb/ssdb.m:
Add the `set_context' predicate and globals.
Use the information from the globals to retain the call context in
shadow stack frames.
Print out the context where appropriate.
diff --git a/compiler/ssdebug.m b/compiler/ssdebug.m
index 56d0046..d134d26 100755
--- a/compiler/ssdebug.m
+++ b/compiler/ssdebug.m
@@ -12,22 +12,38 @@
% The ssdebug module does a source to source tranformation on each procedure
% which allows the procedure to be debugged.
%
-% Here is the transformation:
+% The ssdebug transformation is disabled on standard library predicates,
+% because it would introduce cyclic dependencies between ssdb.m and the
+% standard library. Disabling the transformation on the standard library is
+% also useful for maintaining decent performance.
%
-% original:
+% The tranformation is divided into two passes.
%
-% p(...) :-
-% <original body>
+% The first pass replaces calls to standard library predicates, and closure
+% constructions referring to standard library predicates, by calls to and
+% closures over proxy predicates. The proxy predicates generate events on
+% behalf of the standard library predicates. There will be no events for
+% further calls within the standard library, but that is better for
+% performance.
+%
+% The first pass also inserts calls to a context update procedure before every
+% procedure call (first or higher order). This will update global variables
+% with the location of the next call site, which will be used by the CALL event
+% handler. Context update calls are not required within proxy predicates.
%
-% model_det transformed:
+% The second pass performs the main ssdebug transformation, adding calls to
+% procedures to handle debugger events. The transformation depends on the
+% determinism of the procedure.
+%
+% det/cc_multi:
%
% p(...) :-
% promise_<original_purity> (
% CallVarDescs = [ ... ],
-% impure call_port(ProcId, CallVarDescs),
+% impure handle_event_call(ProcId, CallVarDescs),
% <original body>, % renaming outputs
% ExitVarDescs = [ ... | CallVarDescs ],
-% impure exit_port(ProcId, ExitVarDescs, DoRetry),
+% impure handle_event_exit(ProcId, ExitVarDescs, DoRetry),
% (
% DoRetry = do_retry,
% p(...)
@@ -37,17 +53,17 @@
% )
% ).
%
-% model_semi transformed:
+% semidet/cc_nondet:
%
% p(...) :-
% promise_<original_purity> (
% CallVarDescs = [ ... ],
% (
-% impure call_port(ProcId, CallVarDescs),
+% impure handle_event_call(ProcId, CallVarDescs),
% <original body> % renaming outputs
% ->
% ExitVarDescs = [ ... | CallVarDescs ],
-% impure exit_port(ProcId, ExitVarDescs, DoRetryA),
+% impure handle_event_exit(ProcId, ExitVarDescs, DoRetryA),
% (
% DoRetryA = do_retry,
% p(...)
@@ -56,7 +72,7 @@
% % bind outputs
% )
% ;
-% impure fail_port(ProcId, CallVarDescs, DoRetryB),
+% impure handle_event_fail(ProcId, CallVarDescs, DoRetryB),
% (
% DoRetryB = do_retry,
% p(...)
@@ -67,26 +83,26 @@
% )
% ).
%
-% model_non transformed:
+% nondet:
%
% p(...) :-
% promise_<original_purity> (
% (
% CallVarDescs = [ ... ],
-% impure call_port(ProcId, CallVarDescs),
+% impure handle_event_call(ProcId, CallVarDescs),
% <original body>,
% ExitVarDescs = [ ... | CallVarDescs ],
% (
-% impure exit_port(ProcId, ExitVarDescs)
+% impure handle_event_exit(ProcId, ExitVarDescs)
% % Go to fail port if retry.
% ;
% % preserve_backtrack_into,
-% impure redo_port(ProcId, ExitVarDescs),
+% impure handle_event_redo(ProcId, ExitVarDescs),
% fail
% )
% ;
% % preserve_backtrack_into
-% impure fail_port(ProcId, CallVarDescs, DoRetryB),
+% impure handle_event_fail(ProcId, CallVarDescs, DoRetryB),
% (
% DoRetryB = do_retry,
% p(...)
@@ -97,17 +113,17 @@
% )
% ).
%
-% detism_failure:
+% failure:
%
% p(...) :-
% promise_<original_purity> (
% (
% CallVarDescs = [ ... ],
-% impure call_port(ProcId, CallVarDescs),
+% impure handle_event_call(ProcId, CallVarDescs),
% <original body>
% ;
% % preserve_backtrack_into
-% impure fail_port(ProcId, CallVarDescs, DoRetry),
+% impure handle_event_fail(ProcId, CallVarDescs, DoRetry),
% (
% DoRetry = do_retry,
% p(...)
@@ -118,12 +134,12 @@
% )
% ).
%
-% detism_erroneous:
+% erroneous:
%
% p(...) :-
% promise_<original_purity> (
% CallVarDescs = [ ... ],
-% impure call_port(ProcId, CallVarDescs),
+% impure handle_event_call(ProcId, CallVarDescs),
% <original body>
% ).
%
@@ -146,17 +162,6 @@
%
% The ProcId is of type ssdb.ssdb_proc_id.
%
-% The transformation is disabled on standard library predicates, because it
-% would introduce cyclic dependencies between ssdb.m and the standard library.
-% Disabling the transformation on the standard library is also useful
-% for maintaining decent performance.
-%
-% Instead, for calls to standard library predicates, we create proxy predicates
-% in the calling module and transform those instead. The proxy predicates
-% generate events on behalf of the standard library predicates. Obviously
-% there will be no events for further calls within the standard library, but
-% that is better for performance.
-%
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
@@ -206,49 +211,50 @@
%-----------------------------------------------------------------------------%
ssdebug.transform_module(!ModuleInfo, !IO) :-
- create_proxy_predicates(!ModuleInfo),
+ ssdebug.first_pass(!ModuleInfo),
process_all_nonimported_procs(update_module(ssdebug.process_proc),
!ModuleInfo, !IO).
%-----------------------------------------------------------------------------%
%
-% Creating proxies for standard library predicates
+% Create proxies for standard library predicates and insert context updates
%
:- type proxy_map == map(pred_id, maybe(pred_id)).
-:- pred create_proxy_predicates(module_info::in, module_info::out) is det.
+:- pred first_pass(module_info::in, module_info::out) is det.
-create_proxy_predicates(!ModuleInfo) :-
+first_pass(!ModuleInfo) :-
module_info_predids(PredIds, !ModuleInfo),
- list.foldl2(create_proxies_in_pred, PredIds, map.init, _ProxyMap,
- !ModuleInfo).
+ list.foldl2(first_pass_in_pred, PredIds, map.init, _ProxyMap, !ModuleInfo).
-:- pred create_proxies_in_pred(pred_id::in, proxy_map::in, proxy_map::out,
+:- pred first_pass_in_pred(pred_id::in, proxy_map::in, proxy_map::out,
module_info::in, module_info::out) is det.
-create_proxies_in_pred(PredId, !ProxyMap, !ModuleInfo) :-
+first_pass_in_pred(PredId, !ProxyMap, !ModuleInfo) :-
module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
- ProcIds = pred_info_procids(PredInfo),
- list.foldl2(create_proxies_in_proc(PredId), ProcIds, !ProxyMap,
- !ModuleInfo).
+ ProcIds = pred_info_all_non_imported_procids(PredInfo),
+ list.foldl2(first_pass_in_proc(PredId), ProcIds, !ProxyMap, !ModuleInfo).
-:- pred create_proxies_in_proc(pred_id::in, proc_id::in,
+:- pred first_pass_in_proc(pred_id::in, proc_id::in,
proxy_map::in, proxy_map::out, module_info::in, module_info::out) is det.
-create_proxies_in_proc(PredId, ProcId, !ProxyMap, !ModuleInfo) :-
- module_info_pred_proc_info(!.ModuleInfo, PredId, ProcId, PredInfo,
- ProcInfo0),
- proc_info_get_goal(ProcInfo0, Goal0),
- create_proxies_in_goal(Goal0, Goal, !ProxyMap, !ModuleInfo),
- proc_info_set_goal(Goal, ProcInfo0, ProcInfo),
- module_info_set_pred_proc_info(PredId, ProcId, PredInfo, ProcInfo,
- !ModuleInfo).
+first_pass_in_proc(PredId, ProcId, !ProxyMap, !ModuleInfo) :-
+ some [!ProcInfo] (
+ module_info_pred_proc_info(!.ModuleInfo, PredId, ProcId, PredInfo,
+ !:ProcInfo),
+ proc_info_get_goal(!.ProcInfo, Goal0),
+ first_pass_in_goal(Goal0, Goal, !ProcInfo, !ProxyMap, !ModuleInfo),
+ proc_info_set_goal(Goal, !ProcInfo),
+ module_info_set_pred_proc_info(PredId, ProcId, PredInfo, !.ProcInfo,
+ !ModuleInfo)
+ ).
-:- pred create_proxies_in_goal(hlds_goal::in, hlds_goal::out,
- proxy_map::in, proxy_map::out, module_info::in, module_info::out) is det.
+:- pred first_pass_in_goal(hlds_goal::in, hlds_goal::out,
+ proc_info::in, proc_info::out, proxy_map::in, proxy_map::out,
+ module_info::in, module_info::out) is det.
-create_proxies_in_goal(!Goal, !ProxyMap, !ModuleInfo) :-
+first_pass_in_goal(!Goal, !ProcInfo, !ProxyMap, !ModuleInfo) :-
!.Goal = hlds_goal(GoalExpr0, GoalInfo0),
(
GoalExpr0 = unify(_, _, _, Unification0, _),
@@ -290,49 +296,53 @@ create_proxies_in_goal(!Goal, !ProxyMap, !ModuleInfo) :-
!:Goal = hlds_goal(GoalExpr, GoalInfo0)
;
MaybeNewPredId = no
- )
+ ),
+ insert_context_update_call(!.ModuleInfo, !Goal, !ProcInfo)
;
Builtin = inline_builtin
;
Builtin = out_of_line_builtin
)
;
- GoalExpr0 = generic_call(_, _, _, _)
+ GoalExpr0 = generic_call(_, _, _, _),
+ insert_context_update_call(!.ModuleInfo, !Goal, !ProcInfo)
;
GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
;
GoalExpr0 = conj(ConjType, Goals0),
- list.map_foldl2(create_proxies_in_goal, Goals0, Goals, !ProxyMap,
- !ModuleInfo),
+ list.map_foldl3(first_pass_in_goal, Goals0, Goals, !ProcInfo,
+ !ProxyMap, !ModuleInfo),
GoalExpr = conj(ConjType, Goals),
!:Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = disj(Goals0),
- list.map_foldl2(create_proxies_in_goal, Goals0, Goals, !ProxyMap,
- !ModuleInfo),
+ list.map_foldl3(first_pass_in_goal, Goals0, Goals, !ProcInfo,
+ !ProxyMap, !ModuleInfo),
GoalExpr = disj(Goals),
!:Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = switch(Var, CanFail, Cases0),
- list.map_foldl2(create_proxies_in_case, Cases0, Cases, !ProxyMap,
- !ModuleInfo),
+ list.map_foldl3(first_pass_in_case, Cases0, Cases, !ProcInfo,
+ !ProxyMap, !ModuleInfo),
GoalExpr = switch(Var, CanFail, Cases),
!:Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = negation(SubGoal0),
- create_proxies_in_goal(SubGoal0, SubGoal, !ProxyMap, !ModuleInfo),
+ first_pass_in_goal(SubGoal0, SubGoal, !ProcInfo, !ProxyMap,
+ !ModuleInfo),
GoalExpr = negation(SubGoal),
!:Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = scope(Reason, SubGoal0),
- create_proxies_in_goal(SubGoal0, SubGoal, !ProxyMap, !ModuleInfo),
+ first_pass_in_goal(SubGoal0, SubGoal, !ProcInfo, !ProxyMap,
+ !ModuleInfo),
GoalExpr = scope(Reason, SubGoal),
!:Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
- create_proxies_in_goal(Cond0, Cond, !ProxyMap, !ModuleInfo),
- create_proxies_in_goal(Then0, Then, !ProxyMap, !ModuleInfo),
- create_proxies_in_goal(Else0, Else, !ProxyMap, !ModuleInfo),
+ first_pass_in_goal(Cond0, Cond, !ProcInfo, !ProxyMap, !ModuleInfo),
+ first_pass_in_goal(Then0, Then, !ProcInfo, !ProxyMap, !ModuleInfo),
+ first_pass_in_goal(Else0, Else, !ProcInfo, !ProxyMap, !ModuleInfo),
GoalExpr = if_then_else(Vars, Cond, Then, Else),
!:Goal = hlds_goal(GoalExpr, GoalInfo0)
;
@@ -341,12 +351,13 @@ create_proxies_in_goal(!Goal, !ProxyMap, !ModuleInfo) :-
unexpected(this_file, "create_proxies_in_goal: unexpected shorthand")
).
-:- pred create_proxies_in_case(case::in, case::out,
+:- pred first_pass_in_case(case::in, case::out,
+ proc_info::in, proc_info::out,
proxy_map::in, proxy_map::out, module_info::in, module_info::out) is det.
-create_proxies_in_case(Case0, Case, !ProxyMap, !ModuleInfo) :-
+first_pass_in_case(Case0, Case, !ProcInfo, !ProxyMap, !ModuleInfo) :-
Case0 = case(MainConsId, OtherConsIds, Goal0),
- create_proxies_in_goal(Goal0, Goal, !ProxyMap, !ModuleInfo),
+ first_pass_in_goal(Goal0, Goal, !ProcInfo, !ProxyMap, !ModuleInfo),
Case = case(MainConsId, OtherConsIds, Goal).
% Look up the proxy for a predicate, creating one if appropriate.
@@ -414,6 +425,35 @@ create_proxy_proc(PredId, ProcId, !PredInfo, !ModuleInfo) :-
pred_info_set_proc_info(ProcId, !.ProcInfo, !PredInfo)
).
+:- pred insert_context_update_call(module_info::in,
+ hlds_goal::in, hlds_goal::out, proc_info::in, proc_info::out) is det.
+
+insert_context_update_call(ModuleInfo, Goal0, Goal, !ProcInfo) :-
+ Goal0 = hlds_goal(_, GoalInfo),
+ Context = goal_info_get_context(GoalInfo),
+ Context = term.context(FileName, LineNumber),
+
+ some [!VarSet, !VarTypes] (
+ proc_info_get_varset(!.ProcInfo, !:VarSet),
+ proc_info_get_vartypes(!.ProcInfo, !:VarTypes),
+ make_string_const_construction_alloc(FileName, yes("FileName"),
+ MakeFileName, FileNameVar, !VarSet, !VarTypes),
+ make_int_const_construction_alloc(LineNumber, yes("LineNumber"),
+ MakeLineNumber, LineNumberVar, !VarSet, !VarTypes),
+ proc_info_set_varset(!.VarSet, !ProcInfo),
+ proc_info_set_vartypes(!.VarTypes, !ProcInfo)
+ ),
+
+ Args = [FileNameVar, LineNumberVar],
+ Features = [],
+ instmap_delta_init_reachable(InstMapDelta),
+ generate_simple_call(mercury_ssdb_builtin_module, "set_context",
+ pf_predicate, only_mode, detism_det, purity_impure, Args, Features,
+ InstMapDelta, ModuleInfo, Context, SetContextGoal),
+
+ conj_list_to_goal([MakeFileName, MakeLineNumber, SetContextGoal, Goal0],
+ GoalInfo, Goal).
+
%-----------------------------------------------------------------------------%
%
% The main transformation
diff --git a/ssdb/ssdb.m b/ssdb/ssdb.m
index 65f9d1e..e2509b7 100755
--- a/ssdb/ssdb.m
+++ b/ssdb/ssdb.m
@@ -65,6 +65,10 @@
%
:- type pos == int.
+ % Update globals recording the context of the upcoming call.
+ %
+:- impure pred set_context(string::in, int::in) is det.
+
% This routine is called at each call event that occurs.
%
:- impure pred handle_event_call(ssdb_proc_id::in, list_var_value::in) is det.
@@ -155,6 +159,10 @@
% The goal's module name and procedure name.
sf_proc_id :: ssdb_proc_id,
+ % The call site.
+ sf_call_site_file :: string,
+ sf_call_site_line :: int,
+
% The list of the procedure's arguments.
sf_list_var_value :: list(var_value)
).
@@ -226,8 +234,10 @@
%----------------------------------------------------------------------------%
- % Initialization of the mutable variables.
- %
+:- mutable(cur_filename, string, "", ground,
+ [untrailed, attach_to_io_state]).
+:- mutable(cur_line_number, int, 0, ground,
+ [untrailed, attach_to_io_state]).
:- mutable(cur_ssdb_event_number, int, 0, ground,
[untrailed, attach_to_io_state]).
@@ -365,6 +375,12 @@ public static class SigIntHandler implements sun.misc.SignalHandler {
step_next_stop(!IO) :-
set_cur_ssdb_next_stop(ns_step, !IO).
+%-----------------------------------------------------------------------------%
+
+set_context(FileName, Line) :-
+ impure set_cur_filename(FileName),
+ impure set_cur_line_number(Line).
+
%----------------------------------------------------------------------------%
handle_event_call(ProcId, ListVarValue) :-
@@ -405,7 +421,10 @@ handle_event_call_2(Event, ProcId, ListVarValue, !IO) :-
Depth = OldDepth + 1,
% Push the new stack frame on top of the shadow stack(s).
- StackFrame = stack_frame(EventNum, CSN, Depth, ProcId, ListVarValue),
+ get_cur_filename(SiteFile, !IO),
+ get_cur_line_number(SiteLine, !IO),
+ StackFrame = stack_frame(EventNum, CSN, Depth, ProcId, SiteFile, SiteLine,
+ ListVarValue),
stack_push(StackFrame, !IO),
(
Event = ssdb_call
@@ -690,7 +709,10 @@ handle_event_excp_2(ProcId, ListVarValue, !IO) :-
Depth = OldDepth + 1,
% Push the new stack frame on top of the shadow stack(s).
- StackFrame = stack_frame(EventNum, CSN, Depth, ProcId, ListVarValue),
+ get_cur_filename(SiteFile, !IO),
+ get_cur_line_number(SiteLine, !IO),
+ StackFrame = stack_frame(EventNum, CSN, Depth, ProcId, SiteFile, SiteLine,
+ ListVarValue),
stack_push(StackFrame, !IO),
Event = ssdb_excp,
@@ -1919,6 +1941,8 @@ print_event_info(Event, EventNum, !IO) :-
CSN = StackFrame ^ sf_csn,
ProcId = StackFrame ^ sf_proc_id,
PrintDepth = StackFrame ^ sf_depth,
+ SiteFile = StackFrame ^ sf_call_site_file,
+ SiteLine = StackFrame ^ sf_call_site_line,
% Should right align these numbers.
io.write_string("\t", !IO),
@@ -1956,7 +1980,7 @@ print_event_info(Event, EventNum, !IO) :-
io.write_string(".", !IO),
io.write_string(ProcId ^ proc_name, !IO),
% mdb writes arity, mode, determinism and context here.
- io.nl(!IO).
+ io.format(" (%s:%d)\n", [s(SiteFile), i(SiteLine)], !IO).
%-----------------------------------------------------------------------------%
@@ -1970,8 +1994,12 @@ print_frame_info(StackFrame, StackDepth, !IO) :-
Depth = StackFrame ^ sf_depth,
ProcId = StackFrame ^ sf_proc_id,
ProcId = ssdb_proc_id(ModuleName, ProcName),
+ SiteFile = StackFrame ^ sf_call_site_file,
+ SiteLine = StackFrame ^ sf_call_site_line,
RevDepth = StackDepth - Depth,
- io.format("%4d %s.%s\n", [i(RevDepth), s(ModuleName), s(ProcName)], !IO).
+ io.format("%4d %s.%s (%s:%d)\n",
+ [i(RevDepth), s(ModuleName), s(ProcName), s(SiteFile), i(SiteLine)],
+ !IO).
%-----------------------------------------------------------------------------%
@@ -2035,6 +2063,8 @@ print_stack_trace(Level, Depth, !IO) :-
print_stack_frame(Starred, Level, Frame, !IO) :-
Module = Frame ^ sf_proc_id ^ module_name,
Procedure = Frame ^ sf_proc_id ^ proc_name,
+ SiteFile = Frame ^ sf_call_site_file,
+ SiteLine = Frame ^ sf_call_site_line,
(
Starred = yes,
io.write_char('*', !IO)
@@ -2042,7 +2072,8 @@ print_stack_frame(Starred, Level, Frame, !IO) :-
Starred = no,
io.write_char(' ', !IO)
),
- io.format("%5d\t%s.%s\n", [i(Level), s(Module), s(Procedure)], !IO).
+ io.format("%5d\t%s.%s (%s:%d)\n",
+ [i(Level), s(Module), s(Procedure), s(SiteFile), i(SiteLine)], !IO).
%-----------------------------------------------------------------------------%
--------------------------------------------------------------------------
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