mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-13 21:04:00 +00:00
Estimated hours taken: 1 Branches: main trace/mercury_trace_declarative.c: Fix a bug that always caused the "dd_dd" command to abort. When we reach the original event at which the dd_dd command was issued, switch back from the mode in which append new events to the annotated trace to interactive mode.
1519 lines
39 KiB
C
1519 lines
39 KiB
C
/*
|
|
** Copyright (C) 1998-2003 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.
|
|
*/
|
|
|
|
/*
|
|
** Main author: Mark Brown
|
|
**
|
|
** This file implements the back end of the declarative debugger. The
|
|
** back end is an extension to the internal debugger which collects
|
|
** related trace events and builds them into an annotated trace. Once
|
|
** built, the structure is passed to the front end where it can be
|
|
** analysed to find bugs. The front end is implemented in
|
|
** browse/declarative_debugger.m.
|
|
**
|
|
** The interface between the front and back ends is via the
|
|
** annotated_trace/2 typeclass, which is documented in
|
|
** browse/declarative_debugger.m. It would be possible to replace
|
|
** the front end or the back end with an alternative implementation
|
|
** which also conforms to the typeclass constraints. For example:
|
|
** - An alternative back end could generate the same tree
|
|
** structure in a different way, such as via program
|
|
** transformation.
|
|
** - An alternative front end could graphically display the
|
|
** generated trees as part of a visualization tool rather
|
|
** than analyzing them for bugs.
|
|
*/
|
|
|
|
#include "mercury_imp.h"
|
|
#include "mercury_trace_declarative.h"
|
|
|
|
#include "mercury_trace.h"
|
|
#include "mercury_trace_browse.h"
|
|
#include "mercury_trace_internal.h"
|
|
#include "mercury_trace_tables.h"
|
|
#include "mercury_trace_util.h"
|
|
#include "mercury_trace_vars.h"
|
|
|
|
#include "mercury_layout_util.h"
|
|
#include "mercury_deep_copy.h"
|
|
#include "mercury_stack_trace.h"
|
|
#include "mercury_string.h"
|
|
#include "mercury_trace_base.h"
|
|
|
|
#include "mdb.declarative_debugger.mh"
|
|
#include "mdb.declarative_execution.mh"
|
|
|
|
#include "std_util.mh"
|
|
|
|
#include <errno.h>
|
|
|
|
/*
|
|
** We only build the annotated trace for events down to a certain
|
|
** depth. The following macro gives the default depth limit (relative
|
|
** to the starting depth). In future it would be nice to dynamically
|
|
** adjust this factor based on profiling information.
|
|
*/
|
|
|
|
#define MR_EDT_DEPTH_STEP_SIZE 3
|
|
|
|
/*
|
|
** These macros are to aid debugging of the code which constructs
|
|
** the annotated trace.
|
|
*/
|
|
|
|
#ifdef MR_DEBUG_DD_BACK_END
|
|
|
|
#define MR_decl_checkpoint_event(event_info) \
|
|
MR_decl_checkpoint_event_imp("EVENT", event_info)
|
|
|
|
#define MR_decl_checkpoint_filter(event_info) \
|
|
MR_decl_checkpoint_event_imp("FILTER", event_info)
|
|
|
|
#define MR_decl_checkpoint_find(location) \
|
|
MR_decl_checkpoint_loc("FIND", location)
|
|
|
|
#define MR_decl_checkpoint_step(location) \
|
|
MR_decl_checkpoint_loc("STEP", location)
|
|
|
|
#define MR_decl_checkpoint_match(location) \
|
|
MR_decl_checkpoint_loc("MATCH", location)
|
|
|
|
#define MR_decl_checkpoint_alloc(location) \
|
|
MR_decl_checkpoint_loc("ALLOC", location)
|
|
|
|
#else /* !MR_DEBUG_DD_BACK_END */
|
|
|
|
#define MR_decl_checkpoint_event(event_info)
|
|
#define MR_decl_checkpoint_filter(event_info)
|
|
#define MR_decl_checkpoint_find(location)
|
|
#define MR_decl_checkpoint_step(location)
|
|
#define MR_decl_checkpoint_match(location)
|
|
#define MR_decl_checkpoint_alloc(location)
|
|
|
|
#endif
|
|
|
|
/*
|
|
** The declarative debugger back end is controlled by the
|
|
** settings of the following variables. They are set in
|
|
** MR_trace_start_decl_debug when the back end is started. They
|
|
** are used by MR_trace_decl_debug to decide what action to
|
|
** take for a particular trace event.
|
|
**
|
|
** Events that are deeper than the maximum depth, or which are
|
|
** outside the top call being debugged, are ignored. Events which
|
|
** are beyond the given last event cause the internal debugger to
|
|
** be switched back into interactive mode.
|
|
*/
|
|
|
|
static MR_Unsigned MR_edt_max_depth;
|
|
static MR_Unsigned MR_edt_last_event;
|
|
static MR_bool MR_edt_inside;
|
|
static MR_Unsigned MR_edt_start_seqno;
|
|
static MR_Unsigned MR_edt_start_io_counter;
|
|
|
|
/*
|
|
** The declarative debugger ignores modules that were not compiled with
|
|
** the required information. However, this may result in incorrect
|
|
** assumptions being made about the code, so the debugger gives a warning
|
|
** if this happens. The following flag indicates whether a warning
|
|
** should be printed before calling the front end.
|
|
*/
|
|
|
|
static MR_bool MR_edt_compiler_flag_warning;
|
|
|
|
/*
|
|
** This is used as the abstract map from node identifiers to nodes
|
|
** in the data structure passed to the front end. It should be
|
|
** incremented each time the data structure is destructively
|
|
** updated, before being passed to Mercury code again.
|
|
*/
|
|
|
|
static MR_Unsigned MR_trace_node_store;
|
|
|
|
/*
|
|
** The front end state is stored here in between calls to it.
|
|
** MR_trace_decl_ensure_init should be called before using the state.
|
|
*/
|
|
|
|
static MR_Word MR_trace_front_end_state;
|
|
|
|
static void
|
|
MR_trace_decl_ensure_init(void);
|
|
|
|
/*
|
|
** MR_trace_current_node always contains the last node allocated,
|
|
** or NULL if the collection has just started.
|
|
*/
|
|
|
|
static MR_Trace_Node MR_trace_current_node;
|
|
|
|
/*
|
|
** When in test mode, MR_trace_store_file points to an open file to
|
|
** which the store should be written when built. This global is
|
|
** set in MR_trace_start_decl_debug, and keeps the same value
|
|
** throughout the declarative debugging session.
|
|
*/
|
|
|
|
static FILE *MR_trace_store_file;
|
|
|
|
static MR_Trace_Node MR_trace_decl_call(MR_Event_Info *event_info,
|
|
MR_Trace_Node prev);
|
|
static MR_Trace_Node MR_trace_decl_exit(MR_Event_Info *event_info,
|
|
MR_Trace_Node prev);
|
|
static MR_Trace_Node MR_trace_decl_redo(MR_Event_Info *event_info,
|
|
MR_Trace_Node prev);
|
|
static MR_Trace_Node MR_trace_decl_fail(MR_Event_Info *event_info,
|
|
MR_Trace_Node prev);
|
|
static MR_Trace_Node MR_trace_decl_excp(MR_Event_Info *event_info,
|
|
MR_Trace_Node prev);
|
|
static MR_Trace_Node MR_trace_decl_switch(MR_Event_Info *event_info,
|
|
MR_Trace_Node prev);
|
|
static MR_Trace_Node MR_trace_decl_disj(MR_Event_Info *event_info,
|
|
MR_Trace_Node prev);
|
|
static MR_Trace_Node MR_trace_decl_cond(MR_Event_Info *event_info,
|
|
MR_Trace_Node prev);
|
|
static MR_Trace_Node MR_trace_decl_then(MR_Event_Info *event_info,
|
|
MR_Trace_Node prev);
|
|
static MR_Trace_Node MR_trace_decl_else(MR_Event_Info *event_info,
|
|
MR_Trace_Node prev);
|
|
static MR_Trace_Node MR_trace_decl_neg_enter(MR_Event_Info *event_info,
|
|
MR_Trace_Node prev);
|
|
static MR_Trace_Node MR_trace_decl_neg_success(MR_Event_Info *event_info,
|
|
MR_Trace_Node prev);
|
|
static MR_Trace_Node MR_trace_decl_neg_failure(MR_Event_Info *event_info,
|
|
MR_Trace_Node prev);
|
|
static MR_Trace_Node MR_trace_matching_call(MR_Trace_Node node);
|
|
static MR_bool MR_trace_first_disjunct(MR_Event_Info *event_info);
|
|
static MR_bool MR_trace_matching_cond(const char *path,
|
|
MR_Trace_Node node);
|
|
static MR_bool MR_trace_matching_neg(const char *path,
|
|
MR_Trace_Node node);
|
|
static MR_bool MR_trace_matching_disj(const char *path,
|
|
MR_Trace_Node node);
|
|
static MR_bool MR_trace_same_construct(const char *p1,
|
|
const char *p2);
|
|
static MR_bool MR_trace_single_component(const char *path);
|
|
static MR_Word MR_decl_make_atom(const MR_Label_Layout *layout,
|
|
MR_Word *saved_regs, MR_Trace_Port port);
|
|
static MR_ConstString MR_decl_atom_name(const MR_Proc_Layout *entry);
|
|
static MR_Word MR_decl_atom_args(const MR_Label_Layout *layout,
|
|
MR_Word *saved_regs);
|
|
static const char *MR_trace_start_collecting(MR_Unsigned event,
|
|
MR_Unsigned seqno, MR_Unsigned maxdepth,
|
|
MR_Trace_Cmd_Info *cmd,
|
|
MR_Event_Info *event_info,
|
|
MR_Event_Details *event_details,
|
|
MR_Code **jumpaddr);
|
|
static MR_Code *MR_trace_restart_decl_debug(MR_Unsigned event,
|
|
MR_Unsigned seqno, MR_Trace_Cmd_Info *cmd,
|
|
MR_Event_Info *event_info,
|
|
MR_Event_Details *event_details);
|
|
static MR_Code *MR_decl_diagnosis(MR_Trace_Node root,
|
|
MR_Trace_Cmd_Info *cmd,
|
|
MR_Event_Info *event_info,
|
|
MR_Event_Details *event_details);
|
|
static MR_Code *MR_decl_go_to_selected_event(MR_Unsigned event,
|
|
MR_Trace_Cmd_Info *cmd,
|
|
MR_Event_Info *event_info,
|
|
MR_Event_Details *event_details);
|
|
static MR_String MR_trace_node_path(MR_Trace_Node node);
|
|
static MR_Trace_Port MR_trace_node_port(MR_Trace_Node node);
|
|
static MR_Unsigned MR_trace_node_seqno(MR_Trace_Node node);
|
|
static MR_Trace_Node MR_trace_node_first_disj(MR_Trace_Node node);
|
|
static MR_Trace_Node MR_trace_step_left_in_contour(MR_Trace_Node node);
|
|
static MR_Trace_Node MR_trace_find_prev_contour(MR_Trace_Node node);
|
|
static void MR_decl_checkpoint_event_imp(const char *str,
|
|
MR_Event_Info *event_info);
|
|
static void MR_decl_checkpoint_loc(const char *str,
|
|
MR_Trace_Node node);
|
|
|
|
MR_bool MR_trace_decl_assume_all_io_is_tabled = MR_FALSE;
|
|
|
|
MR_Code *
|
|
MR_trace_decl_debug(MR_Trace_Cmd_Info *cmd, MR_Event_Info *event_info)
|
|
{
|
|
const MR_Proc_Layout *entry;
|
|
MR_Unsigned depth;
|
|
MR_Trace_Node trace;
|
|
MR_Event_Details event_details;
|
|
MR_Integer trace_suppress;
|
|
|
|
entry = event_info->MR_event_sll->MR_sll_entry;
|
|
depth = event_info->MR_call_depth;
|
|
|
|
if (event_info->MR_event_number > MR_edt_last_event) {
|
|
/* This shouldn't ever be reached. */
|
|
fprintf(MR_mdb_err, "Warning: missed final event.\n");
|
|
fprintf(MR_mdb_err, "event %lu\nlast event %lu\n",
|
|
(unsigned long) event_info->MR_event_number,
|
|
(unsigned long) MR_edt_last_event);
|
|
MR_trace_decl_mode = MR_TRACE_INTERACTIVE;
|
|
return MR_trace_event_internal(cmd, MR_TRUE, event_info);
|
|
}
|
|
|
|
if (!MR_PROC_LAYOUT_HAS_EXEC_TRACE(entry)) {
|
|
/* XXX this should be handled better. */
|
|
MR_fatal_error("layout has no execution tracing");
|
|
}
|
|
|
|
if (depth > MR_edt_max_depth) {
|
|
/*
|
|
** We filter out events which are deeper than a certain
|
|
** limit given by MR_edt_max_depth. These events are
|
|
** implicitly represented in the structure being built.
|
|
*/
|
|
return NULL;
|
|
}
|
|
|
|
if (MR_edt_inside) {
|
|
if (event_info->MR_call_seqno == MR_edt_start_seqno &&
|
|
MR_port_is_final(event_info->MR_trace_port))
|
|
{
|
|
/*
|
|
** We are leaving the topmost call.
|
|
*/
|
|
MR_edt_inside = MR_FALSE;
|
|
}
|
|
} else {
|
|
if (event_info->MR_call_seqno == MR_edt_start_seqno) {
|
|
/*
|
|
** The port must be either CALL or REDO;
|
|
** we are (re)entering the topmost call.
|
|
*/
|
|
MR_edt_inside = MR_TRUE;
|
|
} else {
|
|
/*
|
|
** Ignore this event---it is outside the
|
|
** topmost call.
|
|
*/
|
|
MR_decl_checkpoint_filter(event_info);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (MR_PROC_LAYOUT_COMPILER_GENERATED(entry)) {
|
|
/*
|
|
** Filter out events for compiler generated procedures.
|
|
*/
|
|
return NULL;
|
|
}
|
|
|
|
trace_suppress = entry->MR_sle_module_layout->MR_ml_suppressed_events;
|
|
if (trace_suppress != 0) {
|
|
/*
|
|
** We ignore events from modules that were not compiled
|
|
** with the necessary information. Procedures in those
|
|
** modules are effectively assumed correct, so we give
|
|
** the user a warning.
|
|
*/
|
|
MR_edt_compiler_flag_warning = MR_TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
event_details.MR_call_seqno = MR_trace_call_seqno;
|
|
event_details.MR_call_depth = MR_trace_call_depth;
|
|
event_details.MR_event_number = MR_trace_event_number;
|
|
|
|
MR_trace_enabled = MR_FALSE;
|
|
MR_decl_checkpoint_event(event_info);
|
|
trace = MR_trace_current_node;
|
|
switch (event_info->MR_trace_port) {
|
|
case MR_PORT_CALL:
|
|
trace = MR_trace_decl_call(event_info, trace);
|
|
break;
|
|
case MR_PORT_EXIT:
|
|
trace = MR_trace_decl_exit(event_info, trace);
|
|
break;
|
|
case MR_PORT_REDO:
|
|
trace = MR_trace_decl_redo(event_info, trace);
|
|
break;
|
|
case MR_PORT_FAIL:
|
|
trace = MR_trace_decl_fail(event_info, trace);
|
|
break;
|
|
case MR_PORT_DISJ:
|
|
trace = MR_trace_decl_disj(event_info, trace);
|
|
break;
|
|
case MR_PORT_SWITCH:
|
|
trace = MR_trace_decl_switch(event_info, trace);
|
|
break;
|
|
case MR_PORT_COND:
|
|
trace = MR_trace_decl_cond(event_info, trace);
|
|
break;
|
|
case MR_PORT_THEN:
|
|
trace = MR_trace_decl_then(event_info, trace);
|
|
break;
|
|
case MR_PORT_ELSE:
|
|
trace = MR_trace_decl_else(event_info, trace);
|
|
break;
|
|
case MR_PORT_NEG_ENTER:
|
|
trace = MR_trace_decl_neg_enter(event_info, trace);
|
|
break;
|
|
case MR_PORT_NEG_SUCCESS:
|
|
trace = MR_trace_decl_neg_success(event_info, trace);
|
|
break;
|
|
case MR_PORT_NEG_FAILURE:
|
|
trace = MR_trace_decl_neg_failure(event_info, trace);
|
|
break;
|
|
case MR_PORT_PRAGMA_FIRST:
|
|
case MR_PORT_PRAGMA_LATER:
|
|
MR_fatal_error("MR_trace_decl_debug: "
|
|
"foreign language code is not handled (yet)");
|
|
case MR_PORT_EXCEPTION:
|
|
trace = MR_trace_decl_excp(event_info, trace);
|
|
break;
|
|
default:
|
|
MR_fatal_error("MR_trace_decl_debug: unknown port");
|
|
}
|
|
MR_decl_checkpoint_alloc(trace);
|
|
MR_trace_current_node = trace;
|
|
|
|
/*
|
|
** Restore globals from the saved copies.
|
|
*/
|
|
MR_trace_call_seqno = event_details.MR_call_seqno;
|
|
MR_trace_call_depth = event_details.MR_call_depth;
|
|
MR_trace_event_number = event_details.MR_event_number;
|
|
|
|
if (MR_trace_event_number == MR_edt_last_event) {
|
|
/*
|
|
** Call the front end.
|
|
*/
|
|
return MR_decl_diagnosis(MR_trace_current_node, cmd,
|
|
event_info, &event_details);
|
|
}
|
|
|
|
MR_trace_enabled = MR_TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_decl_call(MR_Event_Info *event_info, MR_Trace_Node prev)
|
|
{
|
|
MR_Trace_Node node;
|
|
MR_Word atom;
|
|
MR_bool at_depth_limit;
|
|
const MR_Label_Layout *event_label_layout;
|
|
const MR_Proc_Layout *event_proc_layout;
|
|
const MR_Label_Layout *return_label_layout;
|
|
MR_Word proc_rep;
|
|
MR_Stack_Walk_Step_Result result;
|
|
MR_ConstString problem;
|
|
MR_String goal_path;
|
|
MR_Word *base_sp;
|
|
MR_Word *base_curfr;
|
|
|
|
if (event_info->MR_call_depth == MR_edt_max_depth) {
|
|
at_depth_limit = MR_TRUE;
|
|
} else {
|
|
at_depth_limit = MR_FALSE;
|
|
}
|
|
|
|
event_label_layout = event_info->MR_event_sll;
|
|
event_proc_layout = event_label_layout->MR_sll_entry;
|
|
proc_rep = (MR_Word) event_proc_layout->MR_sle_proc_rep;
|
|
atom = MR_decl_make_atom(event_label_layout, event_info->MR_saved_regs,
|
|
MR_PORT_CALL);
|
|
base_sp = MR_saved_sp(event_info->MR_saved_regs);
|
|
base_curfr = MR_saved_curfr(event_info->MR_saved_regs);
|
|
result = MR_stack_walk_step(event_proc_layout, &return_label_layout,
|
|
&base_sp, &base_curfr, &problem);
|
|
|
|
/*
|
|
** We pass goal_path to Mercury code, which expects its type to be
|
|
** MR_String, not MR_ConstString, even though it treats the string as
|
|
** constant.
|
|
**
|
|
** return_label_layout may be NULL even if result is MR_STEP_OK, if
|
|
** the current event is inside the code of main/2.
|
|
*/
|
|
|
|
if (result == MR_STEP_OK && return_label_layout != NULL) {
|
|
goal_path = (MR_String) (MR_Integer)
|
|
MR_label_goal_path(return_label_layout);
|
|
} else {
|
|
goal_path = (MR_String) (MR_Integer) "";
|
|
}
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
if (proc_rep) {
|
|
node = (MR_Trace_Node)
|
|
MR_DD_construct_call_node_with_goal(
|
|
(MR_Word) prev, atom,
|
|
(MR_Word) event_info->MR_call_seqno,
|
|
(MR_Word) event_info->MR_event_number,
|
|
(MR_Word) at_depth_limit, proc_rep,
|
|
goal_path, MR_io_tabling_counter);
|
|
} else {
|
|
node = (MR_Trace_Node)
|
|
MR_DD_construct_call_node((MR_Word) prev, atom,
|
|
(MR_Word) event_info->MR_call_seqno,
|
|
(MR_Word) event_info->MR_event_number,
|
|
(MR_Word) at_depth_limit, goal_path,
|
|
MR_io_tabling_counter);
|
|
}
|
|
);
|
|
|
|
return node;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_decl_exit(MR_Event_Info *event_info, MR_Trace_Node prev)
|
|
{
|
|
MR_Trace_Node node;
|
|
MR_Trace_Node call;
|
|
MR_Word last_interface;
|
|
MR_Word atom;
|
|
|
|
atom = MR_decl_make_atom(event_info->MR_event_sll,
|
|
event_info->MR_saved_regs,
|
|
MR_PORT_EXIT);
|
|
|
|
call = MR_trace_matching_call(prev);
|
|
MR_decl_checkpoint_match(call);
|
|
MR_TRACE_CALL_MERCURY(
|
|
last_interface = MR_DD_call_node_get_last_interface(
|
|
(MR_Word) call);
|
|
node = (MR_Trace_Node) MR_DD_construct_exit_node(
|
|
(MR_Word) prev, (MR_Word) call, last_interface,
|
|
atom, (MR_Word) event_info->MR_event_number,
|
|
MR_io_tabling_counter);
|
|
MR_DD_call_node_set_last_interface((MR_Word) call,
|
|
(MR_Word) node);
|
|
);
|
|
|
|
return node;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_decl_redo(MR_Event_Info *event_info, MR_Trace_Node prev)
|
|
{
|
|
MR_Trace_Node node;
|
|
MR_Trace_Node call;
|
|
MR_Trace_Node next;
|
|
MR_Word last_interface;
|
|
|
|
/*
|
|
** Search through previous contour for a matching EXIT event.
|
|
*/
|
|
next = MR_trace_find_prev_contour(prev);
|
|
while (MR_trace_node_port(next) != MR_PORT_EXIT
|
|
|| MR_trace_node_seqno(next) != event_info->MR_call_seqno)
|
|
{
|
|
next = MR_trace_step_left_in_contour(next);
|
|
}
|
|
MR_decl_checkpoint_match(next);
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
MR_trace_node_store++;
|
|
if (!MR_DD_trace_node_call(MR_trace_node_store, (MR_Word) next,
|
|
(MR_Word *) &call))
|
|
{
|
|
MR_fatal_error("MR_trace_decl_redo: no matching EXIT");
|
|
}
|
|
);
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
last_interface = MR_DD_call_node_get_last_interface(
|
|
(MR_Word) call);
|
|
node = (MR_Trace_Node) MR_DD_construct_redo_node(
|
|
(MR_Word) prev,
|
|
last_interface);
|
|
MR_DD_call_node_set_last_interface((MR_Word) call,
|
|
(MR_Word) node);
|
|
);
|
|
|
|
return node;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_decl_fail(MR_Event_Info *event_info, MR_Trace_Node prev)
|
|
{
|
|
MR_Trace_Node node;
|
|
MR_Trace_Node next;
|
|
MR_Trace_Node call;
|
|
MR_Word redo;
|
|
|
|
if (MR_trace_node_port(prev) == MR_PORT_CALL) {
|
|
/*
|
|
** We are already at the corresponding call, so there
|
|
** is no need to search for it.
|
|
*/
|
|
call = prev;
|
|
} else {
|
|
next = MR_trace_find_prev_contour(prev);
|
|
call = MR_trace_matching_call(next);
|
|
}
|
|
MR_decl_checkpoint_match(call);
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
redo = MR_DD_call_node_get_last_interface((MR_Word) call);
|
|
node = (MR_Trace_Node) MR_DD_construct_fail_node(
|
|
(MR_Word) prev, (MR_Word) call,
|
|
(MR_Word) redo,
|
|
(MR_Word) event_info->MR_event_number);
|
|
MR_DD_call_node_set_last_interface((MR_Word) call,
|
|
(MR_Word) node);
|
|
);
|
|
return node;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_decl_excp(MR_Event_Info *event_info, MR_Trace_Node prev)
|
|
{
|
|
MR_Trace_Node node;
|
|
MR_Trace_Node call;
|
|
MR_Word last_interface;
|
|
|
|
call = MR_trace_matching_call(prev);
|
|
MR_decl_checkpoint_match(call);
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
last_interface = MR_DD_call_node_get_last_interface(
|
|
(MR_Word) call);
|
|
node = (MR_Trace_Node) MR_DD_construct_excp_node(
|
|
(MR_Word) prev, (MR_Word) call, last_interface,
|
|
MR_trace_get_exception_value(),
|
|
(MR_Word) event_info->MR_event_number);
|
|
MR_DD_call_node_set_last_interface(
|
|
(MR_Word) call, (MR_Word) node);
|
|
);
|
|
|
|
return node;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_decl_cond(MR_Event_Info *event_info, MR_Trace_Node prev)
|
|
{
|
|
MR_Trace_Node node;
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
node = (MR_Trace_Node) MR_DD_construct_cond_node(
|
|
(MR_Word) prev,
|
|
(MR_String) event_info->MR_event_path);
|
|
);
|
|
return node;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_decl_then(MR_Event_Info *event_info, MR_Trace_Node prev)
|
|
{
|
|
MR_Trace_Node node;
|
|
MR_Trace_Node next;
|
|
MR_Trace_Node cond;
|
|
const char *path = event_info->MR_event_path;
|
|
|
|
/*
|
|
** Search through current contour for a matching COND event.
|
|
*/
|
|
next = prev;
|
|
while (!MR_trace_matching_cond(path, next))
|
|
{
|
|
next = MR_trace_step_left_in_contour(next);
|
|
}
|
|
cond = next;
|
|
MR_decl_checkpoint_match(cond);
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
MR_DD_cond_node_set_status((MR_Word) cond,
|
|
MR_TRACE_STATUS_SUCCEEDED);
|
|
node = (MR_Trace_Node) MR_DD_construct_then_node(
|
|
(MR_Word) prev,
|
|
(MR_Word) cond);
|
|
);
|
|
return node;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_decl_else(MR_Event_Info *event_info, MR_Trace_Node prev)
|
|
{
|
|
MR_Trace_Node node;
|
|
MR_Trace_Node cond;
|
|
const char *path = event_info->MR_event_path;
|
|
|
|
/*
|
|
** Search through previous contour for a matching COND event.
|
|
*/
|
|
if (MR_trace_matching_cond(path, prev))
|
|
{
|
|
cond = prev;
|
|
}
|
|
else
|
|
{
|
|
MR_Trace_Node next;
|
|
|
|
next = prev;
|
|
while (!MR_trace_matching_cond(path, next))
|
|
{
|
|
next = MR_trace_step_left_in_contour(next);
|
|
}
|
|
cond = next;
|
|
}
|
|
MR_decl_checkpoint_match(cond);
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
MR_DD_cond_node_set_status((MR_Word) cond,
|
|
MR_TRACE_STATUS_FAILED);
|
|
node = (MR_Trace_Node) MR_DD_construct_else_node(
|
|
(MR_Word) prev,
|
|
(MR_Word) cond);
|
|
);
|
|
return node;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_decl_neg_enter(MR_Event_Info *event_info, MR_Trace_Node prev)
|
|
{
|
|
MR_Trace_Node node;
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
node = (MR_Trace_Node) MR_DD_construct_neg_node(
|
|
(MR_Word) prev,
|
|
(MR_String) event_info->MR_event_path);
|
|
);
|
|
return node;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_decl_neg_success(MR_Event_Info *event_info, MR_Trace_Node prev)
|
|
{
|
|
MR_Trace_Node node;
|
|
MR_Trace_Node nege;
|
|
const char *path = event_info->MR_event_path;
|
|
|
|
/*
|
|
** Search through previous contour for a matching NEGE event.
|
|
*/
|
|
if (MR_trace_matching_neg(path, prev))
|
|
{
|
|
nege = MR_trace_current_node;
|
|
}
|
|
else
|
|
{
|
|
MR_Trace_Node next;
|
|
|
|
next = prev;
|
|
while (!MR_trace_matching_neg(path, next))
|
|
{
|
|
next = MR_trace_step_left_in_contour(next);
|
|
}
|
|
nege = next;
|
|
}
|
|
MR_decl_checkpoint_match(nege);
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
MR_DD_neg_node_set_status((MR_Word) nege,
|
|
MR_TRACE_STATUS_SUCCEEDED);
|
|
node = (MR_Trace_Node) MR_DD_construct_neg_succ_node(
|
|
(MR_Word) prev,
|
|
(MR_Word) nege);
|
|
);
|
|
return node;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_decl_neg_failure(MR_Event_Info *event_info, MR_Trace_Node prev)
|
|
{
|
|
MR_Trace_Node node;
|
|
MR_Trace_Node next;
|
|
|
|
/*
|
|
** Search through current contour for a matching NEGE event.
|
|
*/
|
|
next = prev;
|
|
while (!MR_trace_matching_neg(event_info->MR_event_path, next))
|
|
{
|
|
next = MR_trace_step_left_in_contour(next);
|
|
}
|
|
MR_decl_checkpoint_match(next);
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
MR_DD_neg_node_set_status((MR_Word) next,
|
|
MR_TRACE_STATUS_FAILED);
|
|
node = (MR_Trace_Node) MR_DD_construct_neg_fail_node(
|
|
(MR_Word) prev,
|
|
(MR_Word) next);
|
|
);
|
|
return node;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_decl_switch(MR_Event_Info *event_info, MR_Trace_Node prev)
|
|
{
|
|
MR_Trace_Node node;
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
node = (MR_Trace_Node) MR_DD_construct_switch_node(
|
|
(MR_Word) prev,
|
|
(MR_String) event_info->MR_event_path);
|
|
);
|
|
return node;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_decl_disj(MR_Event_Info *event_info, MR_Trace_Node prev)
|
|
{
|
|
MR_Trace_Node node;
|
|
const char *path = event_info->MR_event_path;
|
|
|
|
if (MR_trace_first_disjunct(event_info))
|
|
{
|
|
MR_TRACE_CALL_MERCURY(
|
|
node = (MR_Trace_Node) MR_DD_construct_first_disj_node(
|
|
(MR_Word) prev,
|
|
(MR_String) path);
|
|
);
|
|
}
|
|
else
|
|
{
|
|
MR_Trace_Node next;
|
|
MR_Trace_Node first;
|
|
|
|
/*
|
|
** Search through previous nodes for a matching DISJ event.
|
|
*/
|
|
next = MR_trace_find_prev_contour(prev);
|
|
while (!MR_trace_matching_disj(path, next))
|
|
{
|
|
next = MR_trace_step_left_in_contour(next);
|
|
}
|
|
MR_decl_checkpoint_match(next);
|
|
|
|
/*
|
|
** Find the first disj event of this disjunction.
|
|
*/
|
|
first = MR_trace_node_first_disj(next);
|
|
if (first == (MR_Trace_Node) NULL)
|
|
{
|
|
first = next;
|
|
}
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
node = (MR_Trace_Node) MR_DD_construct_later_disj_node(
|
|
MR_trace_node_store,
|
|
(MR_Word) prev,
|
|
(MR_String) path,
|
|
(MR_Word) first);
|
|
);
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_matching_call(MR_Trace_Node node)
|
|
{
|
|
MR_Trace_Node next;
|
|
|
|
/*
|
|
** Search through contour for any CALL event. Since there
|
|
** is only one CALL event which can be reached, we assume it
|
|
** is the correct one.
|
|
*/
|
|
next = node;
|
|
while (MR_trace_node_port(next) != MR_PORT_CALL)
|
|
{
|
|
next = MR_trace_step_left_in_contour(next);
|
|
}
|
|
return next;
|
|
}
|
|
|
|
static MR_bool
|
|
MR_trace_first_disjunct(MR_Event_Info *event_info)
|
|
{
|
|
const char *path;
|
|
|
|
/*
|
|
** Return MR_TRUE iff the last component of the path is "d1;".
|
|
*/
|
|
path = event_info->MR_event_path;
|
|
while (*path)
|
|
{
|
|
if (MR_string_equal(path, "d1;"))
|
|
{
|
|
return MR_TRUE;
|
|
}
|
|
path++;
|
|
}
|
|
|
|
return MR_FALSE;
|
|
}
|
|
|
|
static MR_bool
|
|
MR_trace_matching_cond(const char *path, MR_Trace_Node node)
|
|
{
|
|
MR_Trace_Port port;
|
|
const char *node_path;
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
port = (MR_Trace_Port) MR_DD_trace_node_port(node);
|
|
);
|
|
if (port != MR_PORT_COND)
|
|
{
|
|
return MR_FALSE;
|
|
}
|
|
node_path = MR_trace_node_path(node);
|
|
|
|
return MR_trace_same_construct(path, node_path);
|
|
}
|
|
|
|
static MR_bool
|
|
MR_trace_matching_neg(const char *path, MR_Trace_Node node)
|
|
{
|
|
MR_Trace_Port port;
|
|
const char *node_path;
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
port = (MR_Trace_Port) MR_DD_trace_node_port(node);
|
|
);
|
|
if (port != MR_PORT_NEG_ENTER) {
|
|
return MR_FALSE;
|
|
}
|
|
node_path = MR_trace_node_path(node);
|
|
|
|
return MR_trace_same_construct(path, node_path);
|
|
}
|
|
|
|
static MR_bool
|
|
MR_trace_matching_disj(const char *path, MR_Trace_Node node)
|
|
{
|
|
MR_Trace_Port port;
|
|
const char *node_path;
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
port = (MR_Trace_Port) MR_DD_trace_node_port(node);
|
|
);
|
|
if (port == MR_PORT_DISJ) {
|
|
node_path = MR_trace_node_path(node);
|
|
return MR_trace_same_construct(path, node_path);
|
|
} else {
|
|
return MR_FALSE;
|
|
}
|
|
}
|
|
|
|
static MR_bool
|
|
MR_trace_same_construct(const char *p1, const char *p2)
|
|
{
|
|
/*
|
|
** Checks if the two arguments represent goals in the same
|
|
** construct. If both strings are identical up to the last
|
|
** component, return MR_TRUE, otherwise return MR_FALSE.
|
|
** If the arguments point to identical strings, return MR_TRUE.
|
|
*/
|
|
while (*p1 == *p2) {
|
|
if (*p1 == '\0' && *p2 == '\0') {
|
|
return MR_TRUE; /* They are identical. */
|
|
}
|
|
if (*p1 == '\0' || *p2 == '\0') {
|
|
return MR_FALSE; /* Different number of elements. */
|
|
}
|
|
|
|
p1++;
|
|
p2++;
|
|
}
|
|
|
|
/*
|
|
** If there is exactly one component left in each string, then
|
|
** the goal paths match, otherwise they don't.
|
|
*/
|
|
return MR_trace_single_component(p1) && MR_trace_single_component(p2);
|
|
}
|
|
|
|
static MR_bool
|
|
MR_trace_single_component(const char *path)
|
|
{
|
|
while (*path != ';') {
|
|
if (*path == '\0') {
|
|
return MR_FALSE;
|
|
}
|
|
path++;
|
|
}
|
|
path++;
|
|
return (*path == '\0');
|
|
}
|
|
|
|
static MR_Word
|
|
MR_decl_make_atom(const MR_Label_Layout *layout, MR_Word *saved_regs,
|
|
MR_Trace_Port port)
|
|
{
|
|
MR_PredFunc pred_or_func;
|
|
MR_ConstString name;
|
|
int arity;
|
|
MR_Word atom;
|
|
int hv; /* any head variable */
|
|
int num_added_args;
|
|
MR_TypeInfoParams type_params;
|
|
const MR_Proc_Layout *entry = layout->MR_sll_entry;
|
|
|
|
MR_trace_init_point_vars(layout, saved_regs, port, MR_TRUE);
|
|
|
|
name = MR_decl_atom_name(entry);
|
|
MR_proc_id_arity_addedargs_predfunc(entry, &arity, &num_added_args,
|
|
&pred_or_func);
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
atom = MR_DD_construct_trace_atom(
|
|
(MR_Word) pred_or_func,
|
|
(MR_String) name,
|
|
(MR_Word) entry->MR_sle_num_head_vars);
|
|
);
|
|
|
|
for (hv = 0; hv < entry->MR_sle_num_head_vars; hv++) {
|
|
int hlds_num;
|
|
MR_Word arg;
|
|
MR_TypeInfo arg_type;
|
|
MR_Word arg_value;
|
|
MR_bool is_prog_visible_headvar;
|
|
const char *problem;
|
|
|
|
hlds_num = entry->MR_sle_head_var_nums[hv];
|
|
|
|
is_prog_visible_headvar =
|
|
hv >= num_added_args ? MR_TRUE : MR_FALSE;
|
|
|
|
problem = MR_trace_return_hlds_var_info(hlds_num, &arg_type,
|
|
&arg_value);
|
|
if (problem != NULL) {
|
|
/* this head variable is not live at this port */
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
atom = MR_DD_add_trace_atom_arg_no_value(atom,
|
|
(MR_Word) hv + 1, hlds_num,
|
|
is_prog_visible_headvar);
|
|
);
|
|
} else {
|
|
MR_TRACE_USE_HP(
|
|
MR_new_univ_on_hp(arg, arg_type, arg_value);
|
|
);
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
atom = MR_DD_add_trace_atom_arg_value(atom,
|
|
(MR_Word) hv + 1, hlds_num,
|
|
is_prog_visible_headvar, arg);
|
|
);
|
|
}
|
|
}
|
|
|
|
return atom;
|
|
}
|
|
|
|
static MR_ConstString
|
|
MR_decl_atom_name(const MR_Proc_Layout *entry)
|
|
{
|
|
MR_ConstString name;
|
|
|
|
if (MR_PROC_LAYOUT_HAS_PROC_ID(entry)) {
|
|
if (MR_PROC_LAYOUT_COMPILER_GENERATED(entry)) {
|
|
MR_TRACE_USE_HP(
|
|
MR_make_aligned_string(name, "<<internal>>");
|
|
);
|
|
} else {
|
|
name = entry->MR_sle_proc_id.MR_proc_user.MR_user_name;
|
|
}
|
|
} else {
|
|
MR_TRACE_USE_HP(
|
|
MR_make_aligned_string(name, "<<unknown>>");
|
|
);
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
static void
|
|
MR_trace_decl_ensure_init(void)
|
|
{
|
|
static MR_bool done = MR_FALSE;
|
|
static MercuryFile mdb_in;
|
|
static MercuryFile mdb_out;
|
|
|
|
MR_mercuryfile_init(MR_mdb_in, 1, &mdb_in);
|
|
MR_mercuryfile_init(MR_mdb_out, 1, &mdb_out);
|
|
|
|
if (! done) {
|
|
MR_TRACE_CALL_MERCURY(
|
|
MR_trace_node_store = 0;
|
|
MR_DD_decl_diagnosis_state_init(
|
|
(MR_Word) &mdb_in,
|
|
(MR_Word) &mdb_out,
|
|
&MR_trace_front_end_state);
|
|
);
|
|
done = MR_TRUE;
|
|
}
|
|
}
|
|
|
|
MR_bool
|
|
MR_trace_start_decl_debug(MR_Trace_Mode trace_mode, const char *outfile,
|
|
MR_Trace_Cmd_Info *cmd, MR_Event_Info *event_info,
|
|
MR_Event_Details *event_details, MR_Code **jumpaddr)
|
|
{
|
|
MR_Retry_Result result;
|
|
const MR_Proc_Layout *entry;
|
|
FILE *out;
|
|
MR_Unsigned depth_limit;
|
|
const char *message;
|
|
MR_Trace_Level trace_level;
|
|
|
|
if (!MR_port_is_final(event_info->MR_trace_port)) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err,
|
|
"mdb: declarative debugging is only available"
|
|
" from EXIT, FAIL or EXCP events.\n");
|
|
return MR_FALSE;
|
|
}
|
|
|
|
entry = event_info->MR_event_sll->MR_sll_entry;
|
|
if (!MR_PROC_LAYOUT_HAS_EXEC_TRACE(entry)) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err,
|
|
"mdb: cannot start declarative debugging, "
|
|
"because this procedure was not\n"
|
|
"compiled with execution tracing enabled.\n");
|
|
return MR_FALSE;
|
|
}
|
|
|
|
if (MR_PROC_LAYOUT_COMPILER_GENERATED(entry)) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err,
|
|
"mdb: cannot start declarative debugging "
|
|
"at compiler generated procedures.\n");
|
|
return MR_FALSE;
|
|
}
|
|
|
|
trace_level = entry->MR_sle_module_layout->MR_ml_trace_level;
|
|
if (trace_level != MR_TRACE_LEVEL_DEEP
|
|
&& trace_level != MR_TRACE_LEVEL_DECL_REP)
|
|
{
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err,
|
|
"mdb: cannot start declarative debugging, "
|
|
"because this procedure was not\n"
|
|
"compiled with trace level `deep' or `rep'.\n");
|
|
return MR_FALSE;
|
|
}
|
|
|
|
if (entry->MR_sle_module_layout->MR_ml_suppressed_events != 0) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err,
|
|
"mdb: cannot start declarative debugging, "
|
|
"because some event types were\n"
|
|
"suppressed when this procedure was compiled.\n");
|
|
return MR_FALSE;
|
|
}
|
|
|
|
if (trace_mode == MR_TRACE_DECL_DEBUG_DUMP) {
|
|
out = fopen(outfile, "w");
|
|
if (out == NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err,
|
|
"mdb: cannot open file `%s' for output: %s.\n",
|
|
outfile, strerror(errno));
|
|
return MR_FALSE;
|
|
} else {
|
|
MR_trace_store_file = out;
|
|
}
|
|
}
|
|
|
|
MR_trace_decl_mode = trace_mode;
|
|
|
|
MR_trace_decl_ensure_init();
|
|
depth_limit = event_info->MR_call_depth + MR_EDT_DEPTH_STEP_SIZE;
|
|
message = MR_trace_start_collecting(event_info->MR_event_number,
|
|
event_info->MR_call_seqno, depth_limit, cmd, event_info,
|
|
event_details, jumpaddr);
|
|
|
|
if (message == NULL) {
|
|
return MR_TRUE;
|
|
} else {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err,
|
|
"mdb: failed to start collecting events:\n%s\n",
|
|
message);
|
|
|
|
return MR_FALSE;
|
|
}
|
|
}
|
|
|
|
static MR_Code *
|
|
MR_trace_restart_decl_debug(MR_Unsigned event, MR_Unsigned seqno,
|
|
MR_Trace_Cmd_Info *cmd, MR_Event_Info *event_info,
|
|
MR_Event_Details *event_details)
|
|
{
|
|
MR_Unsigned depth_limit;
|
|
const char *message;
|
|
MR_Code *jumpaddr;
|
|
|
|
depth_limit = MR_edt_max_depth + MR_EDT_DEPTH_STEP_SIZE;
|
|
message = MR_trace_start_collecting(event, seqno, depth_limit,
|
|
cmd, event_info, event_details, &jumpaddr);
|
|
|
|
if (message != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: diagnosis aborted:\n%s\n", message);
|
|
MR_trace_decl_mode = MR_TRACE_INTERACTIVE;
|
|
MR_trace_enabled = MR_TRUE;
|
|
return MR_trace_event_internal(cmd, MR_TRUE, event_info);
|
|
}
|
|
|
|
return jumpaddr;
|
|
}
|
|
|
|
static const char *
|
|
MR_trace_start_collecting(MR_Unsigned event, MR_Unsigned seqno,
|
|
MR_Unsigned maxdepth, MR_Trace_Cmd_Info *cmd, MR_Event_Info *event_info,
|
|
MR_Event_Details *event_details, MR_Code **jumpaddr)
|
|
{
|
|
const char *problem;
|
|
MR_Retry_Result retry_result;
|
|
|
|
/*
|
|
** Go back to an event before the topmost call.
|
|
*/
|
|
retry_result = MR_trace_retry(event_info, event_details, 0,
|
|
MR_RETRY_IO_ONLY_IF_SAFE,
|
|
MR_trace_decl_assume_all_io_is_tabled, &problem, NULL, NULL,
|
|
jumpaddr);
|
|
if (retry_result != MR_RETRY_OK_DIRECT) {
|
|
if (retry_result == MR_RETRY_ERROR) {
|
|
return problem;
|
|
} else {
|
|
return "internal error: direct retry impossible";
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Clear any warnings.
|
|
*/
|
|
MR_edt_compiler_flag_warning = MR_FALSE;
|
|
|
|
/*
|
|
** Start collecting the trace from the desired call, with the
|
|
** desired depth bound.
|
|
*/
|
|
MR_edt_last_event = event;
|
|
MR_edt_inside = MR_FALSE;
|
|
MR_edt_start_seqno = seqno;
|
|
MR_edt_start_io_counter = MR_io_tabling_counter;
|
|
MR_edt_max_depth = maxdepth;
|
|
MR_trace_current_node = (MR_Trace_Node) NULL;
|
|
|
|
/*
|
|
** Restore globals from the saved copies.
|
|
*/
|
|
MR_trace_call_seqno = event_details->MR_call_seqno;
|
|
MR_trace_call_depth = event_details->MR_call_depth;
|
|
MR_trace_event_number = event_details->MR_event_number;
|
|
|
|
/*
|
|
** Single step through every event.
|
|
*/
|
|
cmd->MR_trace_cmd = MR_CMD_GOTO;
|
|
cmd->MR_trace_stop_event = 0;
|
|
cmd->MR_trace_strict = MR_TRUE;
|
|
cmd->MR_trace_print_level = MR_PRINT_LEVEL_NONE;
|
|
cmd->MR_trace_must_check = MR_FALSE;
|
|
|
|
MR_trace_enabled = MR_TRUE;
|
|
return NULL;
|
|
}
|
|
|
|
static MR_bool MR_io_action_map_cache_is_valid = MR_FALSE;
|
|
static MR_Unsigned MR_io_action_map_cache_start;
|
|
static MR_Unsigned MR_io_action_map_cache_end;
|
|
|
|
static MR_Code *
|
|
MR_decl_diagnosis(MR_Trace_Node root, MR_Trace_Cmd_Info *cmd,
|
|
MR_Event_Info *event_info, MR_Event_Details *event_details)
|
|
{
|
|
MR_Word response;
|
|
MR_bool bug_found;
|
|
MR_bool symptom_found;
|
|
MR_bool no_bug_found;
|
|
MR_bool require_subtree;
|
|
MR_Unsigned bug_event;
|
|
MR_Unsigned symptom_event;
|
|
MR_Unsigned final_event;
|
|
MR_Unsigned topmost_seqno;
|
|
MercuryFile stream;
|
|
MR_Integer use_old_io_map;
|
|
MR_Unsigned io_start;
|
|
MR_Unsigned io_end;
|
|
|
|
event_details->MR_call_seqno = MR_trace_call_seqno;
|
|
event_details->MR_call_depth = MR_trace_call_depth;
|
|
event_details->MR_event_number = MR_trace_event_number;
|
|
|
|
if (MR_edt_compiler_flag_warning) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "Warning: some modules were compiled with"
|
|
" a trace level lower than `decl'.\n"
|
|
"This may result in calls being omitted from"
|
|
" the debugging tree.\n");
|
|
}
|
|
|
|
if (MR_trace_decl_mode == MR_TRACE_DECL_DEBUG_DUMP) {
|
|
MR_mercuryfile_init(MR_trace_store_file, 1, &stream);
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
MR_DD_save_trace((MR_Word) &stream,
|
|
MR_trace_node_store, root);
|
|
);
|
|
|
|
fclose(MR_trace_store_file);
|
|
MR_trace_decl_mode = MR_TRACE_INTERACTIVE;
|
|
MR_trace_enabled = MR_TRUE;
|
|
MR_trace_call_seqno = event_details->MR_call_seqno;
|
|
MR_trace_call_depth = event_details->MR_call_depth;
|
|
MR_trace_event_number = event_details->MR_event_number;
|
|
|
|
return MR_trace_event_internal(cmd, MR_TRUE, event_info);
|
|
}
|
|
|
|
if (MR_trace_decl_mode == MR_TRACE_DECL_DEBUG_DEBUG) {
|
|
/*
|
|
** This is a quick and dirty way to debug the front end.
|
|
*/
|
|
MR_trace_enabled = MR_TRUE;
|
|
MR_trace_decl_mode = MR_TRACE_INTERACTIVE;
|
|
}
|
|
|
|
io_start = MR_edt_start_io_counter;
|
|
io_end = MR_io_tabling_counter;
|
|
|
|
if (MR_io_action_map_cache_is_valid
|
|
&& MR_io_action_map_cache_start <= io_start
|
|
&& io_end <= MR_io_action_map_cache_end)
|
|
{
|
|
use_old_io_map = MR_TRUE;
|
|
} else {
|
|
use_old_io_map = MR_FALSE;
|
|
|
|
MR_io_action_map_cache_is_valid = MR_TRUE;
|
|
MR_io_action_map_cache_start = io_start;
|
|
MR_io_action_map_cache_end = io_end;
|
|
}
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
MR_DD_decl_diagnosis(MR_trace_node_store, root, use_old_io_map,
|
|
MR_io_action_map_cache_start,
|
|
MR_io_action_map_cache_end,
|
|
&response, MR_trace_front_end_state,
|
|
&MR_trace_front_end_state
|
|
);
|
|
bug_found = MR_DD_diagnoser_bug_found(response,
|
|
(MR_Integer *) &bug_event);
|
|
symptom_found = MR_DD_diagnoser_symptom_found(response,
|
|
(MR_Integer *) &symptom_event);
|
|
no_bug_found = MR_DD_diagnoser_no_bug_found(response);
|
|
require_subtree = MR_DD_diagnoser_require_subtree(response,
|
|
(MR_Integer *) &final_event,
|
|
(MR_Integer *) &topmost_seqno);
|
|
);
|
|
|
|
MR_trace_call_seqno = event_details->MR_call_seqno;
|
|
MR_trace_call_depth = event_details->MR_call_depth;
|
|
MR_trace_event_number = event_details->MR_event_number;
|
|
|
|
if (bug_found) {
|
|
return MR_decl_go_to_selected_event(bug_event, cmd,
|
|
event_info, event_details);
|
|
}
|
|
|
|
if (symptom_found) {
|
|
return MR_decl_go_to_selected_event(symptom_event, cmd,
|
|
event_info, event_details);
|
|
}
|
|
|
|
if (no_bug_found) {
|
|
/*
|
|
** No bug found. Return to the procedural debugger at the
|
|
** current event, which was the event it was left from.
|
|
*/
|
|
MR_trace_decl_mode = MR_TRACE_INTERACTIVE;
|
|
MR_trace_enabled = MR_TRUE;
|
|
return MR_trace_event_internal(cmd, MR_TRUE, event_info);
|
|
}
|
|
|
|
if (require_subtree) {
|
|
/*
|
|
** Front end requires a subtree to be made explicit.
|
|
** Restart the declarative debugger with deeper
|
|
** depth limit.
|
|
*/
|
|
return MR_trace_restart_decl_debug(final_event, topmost_seqno,
|
|
cmd, event_info, event_details);
|
|
}
|
|
|
|
/* We shouldn't ever get here. */
|
|
MR_fatal_error("unknown diagnoser response");
|
|
}
|
|
|
|
static MR_Code *
|
|
MR_decl_go_to_selected_event(MR_Unsigned event, MR_Trace_Cmd_Info *cmd,
|
|
MR_Event_Info *event_info, MR_Event_Details *event_details)
|
|
{
|
|
const char *problem;
|
|
MR_Retry_Result retry_result;
|
|
MR_Code *jumpaddr;
|
|
|
|
/*
|
|
** Perform a retry to get to somewhere before the
|
|
** bug event. Then set the command to go to the bug
|
|
** event and return to interactive mode.
|
|
*/
|
|
#ifdef MR_DEBUG_RETRY
|
|
MR_print_stack_regs(stdout, event_info->MR_saved_regs);
|
|
MR_print_succip_reg(stdout, event_info->MR_saved_regs);
|
|
#endif
|
|
retry_result = MR_trace_retry(event_info, event_details, 0,
|
|
MR_RETRY_IO_ONLY_IF_SAFE,
|
|
MR_trace_decl_assume_all_io_is_tabled,
|
|
&problem, NULL, NULL, &jumpaddr);
|
|
#ifdef MR_DEBUG_RETRY
|
|
MR_print_stack_regs(stdout, event_info->MR_saved_regs);
|
|
MR_print_succip_reg(stdout, event_info->MR_saved_regs);
|
|
MR_print_r_regs(stdout, event_info->MR_saved_regs);
|
|
#endif
|
|
if (retry_result != MR_RETRY_OK_DIRECT) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: diagnosis aborted:\n");
|
|
if (retry_result == MR_RETRY_ERROR) {
|
|
fprintf(MR_mdb_err, "%s\n", problem);
|
|
} else {
|
|
fprintf(MR_mdb_err, "direct retry impossible\n");
|
|
}
|
|
MR_trace_decl_mode = MR_TRACE_INTERACTIVE;
|
|
MR_trace_enabled = MR_TRUE;
|
|
return MR_trace_event_internal(cmd, MR_TRUE, event_info);
|
|
}
|
|
|
|
cmd->MR_trace_cmd = MR_CMD_GOTO;
|
|
cmd->MR_trace_stop_event = event;
|
|
cmd->MR_trace_print_level = MR_PRINT_LEVEL_NONE;
|
|
cmd->MR_trace_strict = MR_TRUE;
|
|
cmd->MR_trace_must_check = MR_FALSE;
|
|
MR_trace_decl_mode = MR_TRACE_INTERACTIVE;
|
|
MR_trace_enabled = MR_TRUE;
|
|
return jumpaddr;
|
|
}
|
|
|
|
static MR_String
|
|
MR_trace_node_path(MR_Trace_Node node)
|
|
{
|
|
MR_String path;
|
|
|
|
MR_trace_node_store++;
|
|
MR_TRACE_CALL_MERCURY(
|
|
path = MR_DD_trace_node_path(MR_trace_node_store,
|
|
(MR_Word) node);
|
|
);
|
|
return path;
|
|
}
|
|
|
|
static MR_Trace_Port
|
|
MR_trace_node_port(MR_Trace_Node node)
|
|
{
|
|
MR_Trace_Port port;
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
port = (MR_Trace_Port) MR_DD_trace_node_port((MR_Word) node);
|
|
);
|
|
return port;
|
|
}
|
|
|
|
static MR_Unsigned
|
|
MR_trace_node_seqno(MR_Trace_Node node)
|
|
{
|
|
MR_Unsigned seqno;
|
|
|
|
MR_trace_node_store++;
|
|
MR_TRACE_CALL_MERCURY(
|
|
if (!MR_DD_trace_node_seqno(MR_trace_node_store,
|
|
(MR_Word) node,
|
|
(MR_Integer *) &seqno))
|
|
{
|
|
MR_fatal_error("MR_trace_node_seqno: "
|
|
"not an interface event");
|
|
}
|
|
);
|
|
return seqno;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_node_first_disj(MR_Trace_Node node)
|
|
{
|
|
MR_Trace_Node first;
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
if (!MR_DD_trace_node_first_disj((MR_Word) node,
|
|
(MR_Word *) &first))
|
|
{
|
|
MR_fatal_error("MR_trace_node_first_disj: "
|
|
"not a DISJ event");
|
|
}
|
|
);
|
|
return first;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_step_left_in_contour(MR_Trace_Node node)
|
|
{
|
|
MR_Trace_Node next;
|
|
|
|
MR_decl_checkpoint_step(node);
|
|
|
|
MR_trace_node_store++;
|
|
MR_TRACE_CALL_MERCURY(
|
|
next = (MR_Trace_Node) MR_DD_step_left_in_contour(
|
|
MR_trace_node_store, node);
|
|
);
|
|
return next;
|
|
}
|
|
|
|
static MR_Trace_Node
|
|
MR_trace_find_prev_contour(MR_Trace_Node node)
|
|
{
|
|
MR_Trace_Node next;
|
|
|
|
MR_decl_checkpoint_find(node);
|
|
|
|
MR_trace_node_store++;
|
|
MR_TRACE_CALL_MERCURY(
|
|
next = (MR_Trace_Node) MR_DD_find_prev_contour(
|
|
MR_trace_node_store, node);
|
|
);
|
|
return next;
|
|
}
|
|
|
|
#ifdef MR_DEBUG_DD_BACK_END
|
|
|
|
static void
|
|
MR_decl_checkpoint_event_imp(const char *str, MR_Event_Info *event_info)
|
|
{
|
|
fprintf(MR_mdb_out, "DD %s %ld: #%ld %ld %s ",
|
|
str,
|
|
(long) event_info->MR_event_number,
|
|
(long) event_info->MR_call_seqno,
|
|
(long) event_info->MR_call_depth,
|
|
MR_port_names[event_info->MR_trace_port]);
|
|
MR_print_proc_id(MR_mdb_out, event_info->MR_event_sll->MR_sll_entry);
|
|
fprintf(MR_mdb_out, "\n");
|
|
}
|
|
|
|
static void
|
|
MR_decl_checkpoint_loc(const char *str, MR_Trace_Node node)
|
|
{
|
|
MercuryFile mdb_out;
|
|
|
|
MR_mercuryfile_init(MR_mdb_out, 1, &mdb_out);
|
|
|
|
fprintf(MR_mdb_out, "DD %s: %ld ", str, (long) node);
|
|
MR_TRACE_CALL_MERCURY(
|
|
MR_DD_print_trace_node((MR_Word) &mdb_out, (MR_Word) node);
|
|
);
|
|
fprintf(MR_mdb_out, "\n");
|
|
}
|
|
|
|
#endif /* MR_DEBUG_DD_BACK_END */
|