[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