mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-15 05:44:58 +00:00
Estimated hours taken: 60
Branches: main
Redesign the way the declarative debugger tracks dependencies, to avoid bugs
and make the code comprehensible. This required tackling an issue we could
ignore before: the typeinfos added to procedure arguments by the compiler.
browsers/declarative_debugger.m:
Rewrite the dependency algorithm from scratch. It now has three phases:
materializing the contour leading up to the relevent point in the
procedure body, using that contour to construct a list of the conjoined
primitive operations executed up to that point by the procedure body,
and tracking the source of the marked subterm in this list of
primitives.
Add a mechanism to print out the result of the dependency tracking
algorithm if a flag is set, for testing and debugging.
browsers/declarative_analyser.m:
Transmit the result of the dependency tracking algorithm to where
it may be printed out.
browsers/declarative_user.m:
Update the user interface to make it switchable between viewing atoms
from the user's perspective (with compiler-generated arguments hidden)
and the implementor's perspective (with compiler-generated arguments
visible). The default view is the user view.
browsers/declarative_execution.m:
Add the immediate parent's goal path to the representation of call
events; this allows us to place the call in the body of its parent.
Expand the representation of atom arguments to include their HLDS
variable numbers, and a boolean that says whether the argument
is a programmer-visible headvar.
Use this extra information to add support for indexing lists of
arguments from either the user view or the implementor view.
Add field names to several types.
browsers/program_representation.m:
Add a field to plain calls, giving the name of the module defining
the called procedure. This is necessary to reliably distinguish
the builtin unify and compare procedures, calls to which must be
handled specially because they generate no events. (They don't need to,
since they are correct by construction.)
Add mechanisms for converting goal paths from strings to structured
terms, for use by the dependency tracking code.
Add tests on atomic goals, for use by the dependency tracking code.
Add a mechanism to let C code retrieve the types of proc_reps as well
as goal_reps.
compiler/prog_rep.m:
Fill in the module name field in plain calls.
trace/mercury_trace_vars.[ch]:
Add functions to get information about a variable specified by HLDS
number.
trace/mercury_trace_declarative.c:
Include typeinfos in the atoms constructed at interface events.
(The same code will work for typeclassinfos as well, once they
become deconstructable and hence printable.)
Fill in the extra slot in call events, and the extra slots in
representations of atom arguments.
trace/mercury_trace_internal.c:
Fix a bug in the implementation of the proc_body command: the
type of the proc_rep slot is proc_rep, not goal_rep.
tests/debugger/declarative/dependency.{m,inp,exp}:
A new test case to exercise dependency tracking. It cooperates with
instrumentation code in the browser directory to print out the result
of each trace_dependency operation.
The test case also tests the proc_body command.
tests/debugger/declarative/Mmakefile:
Enable the new test case.
291 lines
9.2 KiB
Mathematica
291 lines
9.2 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2001-2002 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU Library General
|
|
% Public License - see the file COPYING.LIB in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: program_representation.m
|
|
% Authors: zs, dougl
|
|
%
|
|
% This module defines the representation of procedure bodies
|
|
% used by the declarative debugger.
|
|
%
|
|
% One of the things we want the declarative debugger to be able to do
|
|
% is to let the user specify which part of which output argument of an
|
|
% incorrect or inadmissible atom is suspicious, and then find out where
|
|
% that particular subterm came from, i.e. where it was bound. Doing this
|
|
% requires knowing what the bodies of that procedure and its descendants are.
|
|
%
|
|
% If the Mercury compiler is invoked with options requesting declarative
|
|
% debugging, it will include in each procedure layout a pointer to a simplified
|
|
% representation of the goal that is the body of the corresponding procedure.
|
|
% We use a simplified representation partly because we want to insulate the
|
|
% code of the declarative debugger from irrelevant changes in HLDS types,
|
|
% and partly because we want to minimize the space taken in up in executables
|
|
% by these representations.
|
|
%
|
|
% The current representation is intended to contain all the information
|
|
% we are pretty sure can be usefully exploited by the declarative debugger.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module mdb__program_representation.
|
|
|
|
:- interface.
|
|
|
|
:- import_module mdb__browser_info.
|
|
:- import_module char, list, std_util.
|
|
|
|
% A representation of the goal we execute. These need to be
|
|
% generated statically and stored inside the executable.
|
|
%
|
|
% Each element of this structure will correspond one-to-one
|
|
% to the original stage 90 HLDS.
|
|
|
|
:- type proc_rep
|
|
---> proc_rep(
|
|
list(var_rep), % The head variables, in order,
|
|
% including the ones introduced
|
|
% by the compiler.
|
|
goal_rep % The procedure body.
|
|
).
|
|
|
|
:- type goal_rep
|
|
---> conj_rep(
|
|
list(goal_rep) % The conjuncts in the original
|
|
% order.
|
|
)
|
|
; disj_rep(
|
|
list(goal_rep) % The disjuncts in the original
|
|
% order.
|
|
)
|
|
; switch_rep(
|
|
list(goal_rep) % The switch arms in the
|
|
% original order.
|
|
)
|
|
; ite_rep(
|
|
goal_rep, % Condition.
|
|
goal_rep, % Then branch.
|
|
goal_rep % Else branch.
|
|
)
|
|
; negation_rep(
|
|
goal_rep % The negated goal.
|
|
)
|
|
; some_rep(
|
|
goal_rep, % The quantified goal.
|
|
maybe_cut
|
|
)
|
|
; atomic_goal_rep(
|
|
detism_rep,
|
|
string, % Filename of context.
|
|
int, % Line number of context.
|
|
list(var_rep), % The sorted list of the
|
|
% variables bound by the
|
|
% atomic goal.
|
|
atomic_goal_rep
|
|
).
|
|
|
|
:- type atomic_goal_rep
|
|
---> unify_construct_rep(
|
|
var_rep,
|
|
cons_id_rep,
|
|
list(var_rep)
|
|
)
|
|
; unify_deconstruct_rep(
|
|
var_rep,
|
|
cons_id_rep,
|
|
list(var_rep)
|
|
)
|
|
; unify_assign_rep(
|
|
var_rep, % target
|
|
var_rep % source
|
|
)
|
|
; unify_simple_test_rep(
|
|
var_rep,
|
|
var_rep
|
|
)
|
|
; pragma_foreign_code_rep(
|
|
list(var_rep) % arguments
|
|
)
|
|
; higher_order_call_rep(
|
|
var_rep, % the closure to call
|
|
list(var_rep) % the call's plain arguments
|
|
)
|
|
; method_call_rep(
|
|
var_rep, % typeclass info var
|
|
int, % method number
|
|
list(var_rep) % the call's plain arguments
|
|
)
|
|
; plain_call_rep(
|
|
string, % name of called pred's module
|
|
string, % name of the called pred
|
|
list(var_rep) % the call's arguments
|
|
).
|
|
|
|
:- type var_rep == int.
|
|
|
|
:- type cons_id_rep == string.
|
|
|
|
:- type detism_rep
|
|
---> det_rep
|
|
; semidet_rep
|
|
; nondet_rep
|
|
; multidet_rep
|
|
; cc_nondet_rep
|
|
; cc_multidet_rep
|
|
; erroneous_rep
|
|
; failure_rep.
|
|
|
|
% If the given atomic goal behaves like a call in the sense that it
|
|
% generates events, then return the list of variables that are passed
|
|
% as arguments.
|
|
%
|
|
:- func atomic_goal_generates_event(atomic_goal_rep) = maybe(list(var_rep)).
|
|
|
|
% call_is_primitive(ModuleName, PredName): succeeds iff a call to the
|
|
% named predicate behaves like a primitive operation, in the sense that
|
|
% it does not generate events.
|
|
:- pred call_is_primitive(string::in, string::in) is semidet.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The following three types are derived from compiler/hlds_goal.m.
|
|
|
|
:- type goal_path == list(goal_path_step).
|
|
|
|
:- type goal_path_step ---> conj(int)
|
|
; disj(int)
|
|
; switch(int)
|
|
; ite_cond
|
|
; ite_then
|
|
; ite_else
|
|
; neg
|
|
; exist(maybe_cut)
|
|
; first
|
|
; later.
|
|
|
|
% Does `some G' have a different determinism from plain `G'?
|
|
:- type maybe_cut ---> cut ; no_cut.
|
|
|
|
:- pred path_from_string_det(string, goal_path).
|
|
:- mode path_from_string_det(in, out) is det.
|
|
|
|
:- pred path_from_string(string, goal_path).
|
|
:- mode path_from_string(in, out) is semidet.
|
|
|
|
:- pred path_step_from_string(string, goal_path_step).
|
|
:- mode path_step_from_string(in, out) is semidet.
|
|
|
|
:- pred is_path_separator(char).
|
|
:- mode is_path_separator(in) is semidet.
|
|
|
|
% User-visible head variables are represented by a number from 1..N,
|
|
% where N is the user-visible arity.
|
|
%
|
|
% Both user-visible and compiler-generated head variables can be
|
|
% referred to via their position in the full list of head variables;
|
|
% the first head variable is at position 1.
|
|
|
|
:- type arg_pos
|
|
---> user_head_var(int) % Nth in the list of arguments after
|
|
% filtering out non-user-visible vars.
|
|
; any_head_var(int). % Nth in the list of all arguments.
|
|
|
|
% A particular subterm within a term is represented by a term_path.
|
|
% This is the list of argument positions that need to be followed
|
|
% in order to travel from the root to the subterm. In contrast to
|
|
% goal_paths, this list is in top-down order.
|
|
|
|
:- type term_path == list(int).
|
|
|
|
:- pred convert_dirs_to_term_path(list(dir), term_path).
|
|
:- mode convert_dirs_to_term_path(in, out) is det.
|
|
|
|
% Returns type_of(_ `with_type` proc_rep), for use in C code.
|
|
:- func proc_rep_type = type_desc.
|
|
|
|
% Returns type_of(_ `with_type` goal_rep), for use in C code.
|
|
:- func goal_rep_type = type_desc.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
:- import_module string, char, require.
|
|
|
|
atomic_goal_generates_event(unify_construct_rep(_, _, _)) = no.
|
|
atomic_goal_generates_event(unify_deconstruct_rep(_, _, _)) = no.
|
|
atomic_goal_generates_event(unify_assign_rep(_, _)) = no.
|
|
atomic_goal_generates_event(unify_simple_test_rep(_, _)) = no.
|
|
atomic_goal_generates_event(pragma_foreign_code_rep(_)) = no.
|
|
atomic_goal_generates_event(higher_order_call_rep(_, Args)) = yes(Args).
|
|
atomic_goal_generates_event(method_call_rep(_, _, Args)) = yes(Args).
|
|
atomic_goal_generates_event(plain_call_rep(ModuleName, PredName, Args)) =
|
|
( call_is_primitive(ModuleName, PredName) ->
|
|
% These calls behave as primitives and do not generate events.
|
|
no
|
|
;
|
|
yes(Args)
|
|
).
|
|
|
|
call_is_primitive(ModuleName, PredName) :-
|
|
ModuleName = "builtin",
|
|
( PredName = "unify"
|
|
; PredName = "compare"
|
|
).
|
|
|
|
convert_dirs_to_term_path([], []).
|
|
convert_dirs_to_term_path([child_num(N) | Dirs], [N | TermPath]) :-
|
|
convert_dirs_to_term_path(Dirs, TermPath).
|
|
convert_dirs_to_term_path([child_name(_) | _], _) :-
|
|
error("convert_dirs_to_term_path: not in canonical form").
|
|
convert_dirs_to_term_path([parent | _], _) :-
|
|
error("convert_dirs_to_term_path: not in canonical form").
|
|
|
|
:- pragma export(proc_rep_type = out, "ML_proc_rep_type").
|
|
|
|
proc_rep_type = type_of(_ `with_type` proc_rep).
|
|
|
|
:- pragma export(goal_rep_type = out, "ML_goal_rep_type").
|
|
|
|
goal_rep_type = type_of(_ `with_type` goal_rep).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
path_from_string_det(GoalPathStr, GoalPath) :-
|
|
( path_from_string(GoalPathStr, GoalPathPrime) ->
|
|
GoalPath = GoalPathPrime
|
|
;
|
|
error("path_from_string_det: path_from_string failed")
|
|
).
|
|
|
|
path_from_string(GoalPathStr, GoalPath) :-
|
|
StepStrs = string__words(is_path_separator, GoalPathStr),
|
|
list__map(path_step_from_string, StepStrs, GoalPath).
|
|
|
|
path_step_from_string(String, Step) :-
|
|
string__first_char(String, First, Rest),
|
|
path_step_from_string_2(First, Rest, Step).
|
|
|
|
:- pred path_step_from_string_2(char, string, goal_path_step).
|
|
:- mode path_step_from_string_2(in, in, out) is semidet.
|
|
|
|
path_step_from_string_2('c', NStr, conj(N)) :-
|
|
string__to_int(NStr, N).
|
|
path_step_from_string_2('d', NStr, disj(N)) :-
|
|
string__to_int(NStr, N).
|
|
path_step_from_string_2('s', NStr, switch(N)) :-
|
|
string__to_int(NStr, N).
|
|
path_step_from_string_2('?', "", ite_cond).
|
|
path_step_from_string_2('t', "", ite_then).
|
|
path_step_from_string_2('e', "", ite_else).
|
|
path_step_from_string_2('~', "", neg).
|
|
path_step_from_string_2('q', "!", exist(cut)).
|
|
path_step_from_string_2('q', "", exist(no_cut)).
|
|
path_step_from_string_2('f', "", first).
|
|
path_step_from_string_2('l', "", later).
|
|
|
|
is_path_separator(';').
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|