Files
mercury/runtime/mercury_trace_base.h
Mark Brown d465fa53cb Update the COPYING.LIB file and references to it.
Discussion of these changes can be found on the Mercury developers
mailing list archives from June 2018.

COPYING.LIB:
    Add a special linking exception to the LGPL.

*:
    Update references to COPYING.LIB.

    Clean up some minor errors that have accumulated in copyright
    messages.
2018-06-09 17:43:12 +10:00

708 lines
33 KiB
C

// vim: ts=4 sw=4 expandtab ft=c
// Copyright (C) 1997-2009, 2011 The University of Melbourne.
// Copyright (C) 2014, 2016, 2018 The Mercury team.
// This file is distributed under the terms specified in COPYING.LIB.
// mercury_trace_base.h defines the interface between the main part
// of the runtime system (mainly mercury_wrapper.c) and the part of the
// tracing subsystem that has to be present even if tracing is not enabled.
// The part of the tracing system that required only when tracing is enabled
// is in the trace directory.
#ifndef MERCURY_TRACE_BASE_H
#define MERCURY_TRACE_BASE_H
#include "mercury_engine.h" // for MR_MAXFLAG
#include "mercury_stack_layout.h"
#include "mercury_std.h"
#include "mercury_tabling.h" // for MR_TableNode
#include "mercury_goto.h" // for MR_declare_entry
#include <stdio.h> // for FILE; should be after mercury headers
// This enum should EXACTLY match
// - the macro `MR_TRACE_PORT_ACTUAL_NAMES' below,
// - the macro `MR_TRACE_PORT_SIMPLIFIED_NAMES' below,
// - the type `trace_port' in mdbcomp/prim_data.m,
// - the predicate `trace_port_to_string' in compiler/layout_out.m, and
// - the function `port_number' in compiler/trace_params.m.
typedef enum {
MR_PORT_CALL,
MR_PORT_EXIT,
MR_PORT_REDO,
MR_PORT_FAIL,
MR_PORT_TAILREC_CALL,
MR_PORT_EXCEPTION,
MR_PORT_COND,
MR_PORT_THEN,
MR_PORT_ELSE,
MR_PORT_NEG_ENTER,
MR_PORT_NEG_SUCCESS, // negated goal failed; negation succeeds
MR_PORT_NEG_FAILURE, // negated goal succeeded; negation fails
MR_PORT_DISJ_FIRST,
MR_PORT_DISJ_LATER,
MR_PORT_SWITCH,
MR_PORT_USER,
MR_PORT_NONE
} MR_TracePort;
#define MR_PORT_NUM_PORTS ((int) MR_PORT_NONE + 1)
#define MR_TRACE_PORT_ACTUAL_NAMES \
"CALL", \
"EXIT", \
"REDO", \
"FAIL", \
"TAIL", \
"EXCP", \
"COND", \
"THEN", \
"ELSE", \
"NEGE", \
"NEGS", \
"NEGF", \
"DSJF", \
"DSJL", \
"SWTC", \
"USER", \
"NONE"
#define MR_TRACE_PORT_SIMPLIFIED_NAMES \
"CALL", \
"EXIT", \
"REDO", \
"FAIL", \
"TAIL", \
"EXCP", \
"COND", \
"THEN", \
"ELSE", \
"NEGE", \
"NEGS", \
"NEGF", \
"DISJ", \
"DISJ", \
"SWTC", \
"USER", \
"NONE"
extern const char *MR_actual_port_names[];
extern const char *MR_simplified_port_names[];
// The following array says if a label inside a procedure is
// uniquely identifiable by its goal path only, its port only or
// whether both the port and goal path are necessary.
typedef enum {
PATH_ONLY, PORT_ONLY, PORT_AND_PATH
} MR_PathPort;
extern MR_PathPort MR_named_count_port[MR_PORT_NUM_PORTS];
extern void MR_trace_name_count_port_ensure_init(void);
#define MR_trace_incr_seq() ((MR_Word) ++MR_trace_call_seqno)
#define MR_trace_incr_depth() ((MR_Word) ++MR_trace_call_depth)
#define MR_trace_fill_std_slots(s1, s2, s3) \
(((s1) = MR_trace_event_number), \
((s2) = MR_trace_incr_seq()), \
((s3) = MR_trace_incr_depth()))
#define MR_trace_tailrec_std_slots(s1, s2, s3) \
(((s1) = MR_trace_event_number), \
((s2) = MR_trace_incr_seq()), \
((s3) = (s3) + 1))
#define MR_trace_reset_depth(d) \
(MR_trace_call_depth = (MR_Unsigned) (d))
#define MR_trace_reset_depth_from_full(d) \
((MR_trace_call_depth = (MR_Unsigned) (d)), \
(MR_trace_from_full = MR_TRUE))
#define MR_trace_reset_depth_from_shallow(d) \
((MR_trace_call_depth = (MR_Unsigned) (d)), \
(MR_trace_from_full = MR_FALSE))
// MR_trace is called from Mercury modules compiled with tracing.
// If the event is supposed to be traced, it performs an indirect call
// through MR_trace_func_ptr, which will point either to MR_trace_real,
// which is defined in the trace library, or to MR_trace_fake, defined here,
// which just prints an error message and aborts, or to MR_trace_count, also
// defined here, which counts the execution of the event.
//
// The return value, if not NULL, says where execution should continue
// after the event. (NULL means it should continue as usual.)
extern MR_Code *MR_trace(const MR_LabelLayout *);
extern MR_Code *MR_trace_fake(const MR_LabelLayout *);
extern MR_Code *MR_trace_count(const MR_LabelLayout *);
extern MR_Code *MR_user_trace(const MR_LabelLayout *);
// These three variables implement a table of module layout structures,
// sorted on module name, maintained by the macros in mercury_array_macros.h.
//
// Insertions are handled by MR_insert_module_info_into_module_table.
extern const MR_ModuleLayout **MR_module_infos;
extern unsigned MR_module_info_next;
extern unsigned MR_module_info_max;
extern void MR_insert_module_info_into_module_table(
const MR_ModuleLayout *module_layout);
// For every label reachable from the module table, write the id of the label
// and the number of times it has been executed to the specified file. For
// labels that haven't been executed, write them out only if the coverage_test
// argument is true. The return value is the number of labels whose trace count
// information was actually written out.
//
// The file can be recognized as a Mercury trace counts file as its first
// line matches MR_TRACE_COUNT_FILE_ID. The value of that macro should be
// kept in sync with trace_count_file_id in mdbcomp/trace_counts.m.
// One of the later lines gives the name of the program that the trace counts
// were derived from; this should be supplied by the caller as the progname
// argument.
#define MR_TRACE_COUNT_FILE_ID "Mercury trace counts file\n"
extern unsigned int MR_trace_write_label_exec_counts(FILE *fp,
const char *progname,
MR_bool coverage_test);
// Figure out where (to which file) to write out the label execution counts,
// and then invoke MR_trace_write_label_exec_counts to write it out there.
// If the user has asked for this data to be summarized, do that too.
//
// The dummy argument allows this function to be registered with
// MR_register_exception_cleanup.
extern void MR_trace_record_label_exec_counts(void *dummy);
// MR_trace_init() is called from mercury_runtime_init()
// when the debuggee programs begins, to perform any initialization
// that must be done before any traced Mercury code is executed.
// This includes the initialization code written in Mercury as well as main.
//
// MR_trace_start(enabled) is called from mercury_runtime_init()
// after the initialization code written in Mercury is executed,
// when we are about to start executing main. The argument says
// whether tracing is enabled for main (it is never enabled for
// initialization and finalization routines).
//
// MR_trace_end() is called from mercury_runtime_terminate() just
// after main has terminated and just before we call the finalization
// code written in Mercury.
//
// MR_trace_final() is called from mercury_runtime_terminate()
// after all Mercury code, including finalization code, has terminated.
extern void MR_trace_init(void);
extern void MR_trace_start(MR_bool enabled);
extern void MR_trace_end(void);
extern void MR_trace_final(void);
// Kill any windows created by mdb.
extern void (*MR_trace_shutdown)(void);
// The globals that define the interface between the tracing subsystem
// and compiled code, and which must be initialized in the permanent part
// of the runtime.
//
// XXX They should probably be in MercuryEngine.
// MR_debug_enabled says whether debugging of the program is enabled.
// It should keep the same value throughout the execution of the entire
// program after being set in mercury_wrapper.c, with two exceptions.
// First, the Mercury routines called as part of the functionality
// of the tracer itself (e.g. the term browser) should always be executed
// with MR_debug_enabled set to MR_FALSE. Second, when a procedure has
// the tabled_for_io_unitize annotation, which means that it can both do I/O
// and call Mercury code, then we turn the procedure and its descendants
// into a single unit by turning off tracing within the descendants.
// This is required to prevent the I/O tabling problems that could otherwise
// arise if we got retries from within the descendants.
extern MR_bool MR_debug_enabled;
// MR_debug_ever_enabled will keep the same value throughout the execution of
// the entire program after being set in mercury_wrapper.c to the same value
// as MR_debug_enabled. Unlike MR_debug_enabled, it is never reset, so one can
// use its value to test whether tracing was ever enabled.
extern MR_bool MR_debug_ever_enabled;
// When writing out trace counts at the end of execution, we will write out
// the identities of labels with zero execution counts if and only if
// MR_coverage_test_enabled is true.
extern MR_bool MR_coverage_test_enabled;
// If MR_trace_count_summary_file is not NULL, then its value gives the
// name of the file in which to accumulate trace count information. If this
// file doesn't exist, the trace counts data will be printed to it. If this
// file does exist, the trace counts data will be printed to files with this
// name as a base name plus a suffix .1, .2, .3 etc until the number reaches
// MR_trace_count_summary_max, at which point the program will invoke
// the command named by MR_trace_count_summary_cmd to consolidate all those
// trace counts in one file (the one named by MR_trace_count_summary_file).
extern const char *MR_trace_count_summary_file;
extern const char *MR_trace_count_summary_cmd;
extern unsigned int MR_trace_count_summary_max;
// MR_trace_count_enabled will keep the same value throughout the execution of
// the entire program after being set in mercury_wrapper.c to the same value
// as MR_debug_enabled. Unlike MR_debug_enabled, it is never reset, so one can
// use its value to test whether tracing was ever enabled.
extern MR_bool MR_trace_count_enabled;
// MR_trace_counts_file records the filename to use when dumping trace counts.
// It may be NULL, in which case a unique file name will be generated.
extern char *MR_trace_counts_file;
// MR_trace checks whether MR_trace_func_enabled is true, and return
// immediately if it is not.
//
// MR_trace_func_enabled should be updated whenever either of the variables
// it depends on is updated. The reason why we require this is that
// MR_trace_func_enabled is read many times, but MR_debug_enabled and
// MR_trace_count_enabled are updated only infrequently.
//
// Usually, MR_trace_func_enabled should be updated with the macro below.
// However, MR_debug_enabled and MR_trace_count_enabled can never be set
// simultaneously, and in places where performance is important, the update
// of MR_trace_func_enabled can exploit this.
extern MR_bool MR_trace_func_enabled;
#define MR_update_trace_func_enabled() \
do { \
MR_trace_func_enabled = \
MR_debug_enabled || MR_trace_count_enabled; \
} while (0)
// MR_selected_trace_func_ptr contains the address of the function to call
// in MR_trace if MR_trace_func_enabled is true.
//
// Since it is set from a signal handler (MR_trace_interrupt_handler),
// it must be declared `volatile'.
extern MR_Code *(*volatile MR_selected_trace_func_ptr)(
const MR_LabelLayout *);
// MR_trace_call_seqno counts distinct calls. The prologue of every procedure
// assigns the current value of this counter as the sequence number of that
// invocation and increments the counter. This and retry are the only ways
// that MR_trace_call_seqno is modified.
//
// MR_trace_call_depth records the current depth of the call tree. The prologue
// of every procedure assigns the current value of this variable plus one
// as the depth of that invocation. Just before making a call, the caller
// will set MR_trace_call_depth to its own remembered depth value.
// These and retry are the only ways in which MR_trace_call_depth is modified.
//
// Although neither MR_trace_call_seqno nor MR_trace_call_depth are used
// directly in this module, the seqno and depth arguments of MR_trace
// always derive their values from the saved values of these two global
// variables.
extern MR_Unsigned MR_trace_call_seqno;
extern MR_Unsigned MR_trace_call_depth;
// MR_trace_event_number is a simple counter of events. This is used in
// two places: in the debugger for display to the user and for skipping
// a given number of events, and when printing an abort message, so that
// the programmer can zero in on the source of the problem more quickly.
extern MR_Unsigned MR_trace_event_number;
// MR_trace_from_full is a boolean that is set before every call;
// it states whether the caller is being deep traced, or only shallow
// traced. If the called code is shallow traced, it will generate
// interface trace events only if MR_trace_from_full is true.
// (It will never generate internal events.) If the called code is deep
// traced, it will always generate all trace events, external and internal,
// regardless of the setting of this variable on entry.
//
// The initial value is set to MR_TRUE to allow the programmer to gain
// control in the debugger when main/2 is called.
extern MR_bool MR_trace_from_full;
// If set to true, MR_standardize_event_details modifies how functions that
// print event numbers and call sequence numbers operate, making them
// standardize these numbers. The Nth event number to be printed will be
// printed as E<N> and the Nth call sequence number will be printed as C<N>
// regardless of their actual values. This is intended to avoid hardcoding
// concrete event and call numbers in the expected outputs of the debugger
// test cases.
//
// The functions MR_standardize_event_num and MR_standardize_call_num implement
// the standardization itself.
extern MR_bool MR_standardize_event_details;
extern MR_Unsigned MR_standardize_event_num(MR_Unsigned event_num);
extern MR_Unsigned MR_standardize_call_num(MR_Unsigned call_num);
// Do we want to use the debugger within this process, or do want to use
// the Opium-style trace analyzer debugger implemented by an external process.
// This variable is set in mercury_wrapper.c and never modified afterwards.
typedef enum {
MR_TRACE_INTERNAL,
#ifdef MR_USE_EXTERNAL_DEBUGGER
MR_TRACE_EXTERNAL
#endif
} MR_Trace_Type;
extern MR_Trace_Type MR_trace_handler;
// MR_trace_unhide_events is a boolean. Normally, it is set to false, which
// means that events that the compiler designates as hidden are really hidden
// from the procedural debugger, being visible only when building the annotated
// trace. When an mdb command intended for implementors only sets it to true,
// hidden events will be visible to the procedural debugger too, i.e. the
// hidden annotation on events will cease to be effective.
//
// The MR_trace_have_unhid_events is a boolean that is set to true whenever
// MR_trace_unhide_events is set to true, and it is never reset to false.
// MR_trace_have_unhid_events will therefore be true if the user has ever
// unhidden events. The declarative debugger checks this flag and refuses
// to perform if it is set, because if this flag has ever been set, then the
// numbering of events may not be the same after a retry, which makes it
// impossible to *reliably* find the event at which the "dd" command was issued
// while building the annotated trace.
extern MR_bool MR_trace_unhide_events;
extern MR_bool MR_trace_have_unhid_events;
// When executing a retry on a call that has reused the stack frame of some
// of its ancestors, we start executing the code of the procedure from the very
// beginning. This code sets the stack slot that contains the count of
// how many times that stack frame was reused to zero. This is the right thing
// to do for normal execution, but doing it after a retry screws up the
// debugger's picture of the stack.
//
// These two variables are part of the fix for this problem. The boolean
// MR_trace_tailrec_have_reused_frames is almost always false. However,
// when we are executing a retry of a procedure with TAIL events, we set it
// momentarily to true, and set MR_trace_tailrec_num_reused_frames to the
// original value of the frame reuse counter. We make the procedure prologue
// for procedures with TAIL events check MR_trace_tailrec_have_reused_frames,
// and it is set, we initialize the slot not to zero but to the value in
// MR_trace_tailrec_num_reused_frames (we also reset the boolean to false).
//
// Note that the contents of MR_trace_tailrec_num_reused_frames are valid
// only when MR_trace_tailrec_have_reused_frames is true.
extern MR_bool MR_trace_tailrec_have_reused_frames;
extern MR_Unsigned MR_trace_tailrec_num_reused_frames;
// The details of I/O tabling are documented in library/table_builtin.m.
typedef enum {
// from program start to first debugger event
MR_IO_TABLING_UNINIT,
// from first debugger event to "table_io start" command
MR_IO_TABLING_BEFORE,
// from "table_io start" command to "table_io end" command
MR_IO_TABLING_DURING,
// from "table_io end" command to program exit
MR_IO_TABLING_AFTER
} MR_IoTablingPhase;
typedef MR_Unsigned MR_IoActionNum;
#define MR_IO_ACTION_MAX ((MR_IoActionNum) -1)
extern MR_IoTablingPhase MR_io_tabling_phase;
// True iff I/O tabling is enabled.
extern MR_bool MR_io_tabling_enabled;
// The root of the trie that we use for tabling I/O.
extern MR_TableNode MR_io_tabling_pointer;
// The I/O action number of the last I/O action.
extern MR_IoActionNum MR_io_tabling_counter;
// The highest I/O action number ever reached ("hwm" = "high water mark").
extern MR_IoActionNum MR_io_tabling_counter_hwm;
// The highest I/O action number which is too early to be tabled.
extern MR_IoActionNum MR_io_tabling_start;
// The highest I/O action number which is to be tabled.
extern MR_IoActionNum MR_io_tabling_end;
// The event number at which I/O tabling was started; zero before start.
extern MR_Unsigned MR_io_tabling_start_event_num;
// The event number at which I/O tabling was stopped; zero before stop.
extern MR_Unsigned MR_io_tabling_stop_event_num;
// The flag that controls whether we should generate diagnostics.
extern MR_bool MR_io_tabling_debug;
// The flag that controls whether I/O tabling is allowed at all.
extern MR_bool MR_io_tabling_allowed;
// These functions will report the number of the last event,
// if there have been some events, and will do nothing otherwise.
extern void MR_trace_report(FILE *fp);
extern void MR_trace_report_raw(int fd);
// If MR_trace_report_msg is not NULL, it will be included in messages
// from MR_trace_report.
extern char *MR_trace_report_msg;
// This function prints an error message and aborts. It should be called
// in situations where tracing is required, but `--trace' was not passed
// to c2init.
extern void MR_tracing_not_enabled(void);
// Return the details of I/O action <action_number> in three pieces:
// the name of the I/O action procedure in *proc_name_ptr, a boolean that is
// true iff procedure is a function in *is_func_ptr, and (if available)
// a Mercury representation of the argument list (minus the IO state arguments)
// in *arg_list_ptr.
// This function uses the heap pointer, so calls to it must be wrapped
// with MR_save_transient_hp() and MR_restore_transient_hp().
//
// If the procedure that performed the specified I/O action does not
// have the information we need to construct a univ for each argument,
// this function will set *have_arg_infos_ptr to false, and *arg_list_ptr
// will NOT be meaningful. If the function does have the information it needs,
// it will set *have_arg_infos_ptr to true, and will return a list of univs,
// one univ per non-I/O-state argument, in *arg_list_ptr.
//
// This function is called from the Mercury code in the debugger, in the
// browser directory. It is here, not in the trace directory, because code
// in the browser directory cannot call functions in the trace directory.
//
// All of the above is for the case where the io action <action_number>
// has been tabled, which this function indicates by returning MR_TRUE.
// If it has NOT been tabled, then this function will not return MR_FALSE,
// and *proc_name_ptr, *is_func_ptr, *have_arg_infos_ptr, and *arg_list_ptr
// will NOT be meaningful.
extern MR_bool MR_trace_get_action(MR_IoActionNum action_number,
MR_ConstString *proc_name_ptr, MR_Word *is_func_ptr,
MR_bool *have_arg_infos_ptr, MR_Word *arg_list_ptr);
// MR_turn_off_debug saves the current values of the variables controlling
// debugging (execution tracing and diagnostics) in the structure provided by
// the caller, and then turns them off. MR_turn_debug_back_on restores the
// saved values from the structure. If include_counter_vars is set, they also
// save and restore the global variables containing the event and sequence
// number counters and the call depth.
typedef struct {
MR_bool MR_sds_debug_enabled;
MR_bool MR_sds_io_tabling_enabled;
MR_bool MR_sds_debugflags[MR_MAXFLAG];
MR_bool MR_sds_include_counter_vars;
MR_Unsigned MR_sds_trace_call_seqno;
MR_Unsigned MR_sds_trace_call_depth;
MR_Unsigned MR_sds_trace_event_number;
} MR_SavedDebugState;
extern void MR_turn_off_debug(MR_SavedDebugState *saved_state,
MR_bool include_counter_vars);
extern void MR_turn_debug_back_on(
const MR_SavedDebugState *saved_state);
// These functions allow library/exceptions.m to tell the debuggers
// which exception has been thrown.
extern void MR_trace_set_exception_value(MR_Word exception);
extern MR_Word MR_trace_get_exception_value(void);
// Return a pointer to the execution count of a particular label.
extern MR_Unsigned *MR_trace_lookup_trace_count(
const MR_LabelLayout *label_layout);
// If MR_TRACE_HISTOGRAM is defined, MR_trace maintains two arrays of integers,
// MR_trace_histogram_all and MR_trace_histogram_exp, in which the element
// with subscript d is incremented when a trace event occurs at depth d.
// The intention is that the MR_trace_histogram_all records all events
// and is never reset, which means that it records information about a whole
// execution of the program. MR_trace_histogram_exp on the other hand can be
// zeroed by a command from the debugger at e.g a call port, and examined at
// e.g. an exit port, which means that it can record information about the
// execution of a call.
//
// Both arrays are allocated via malloc, and resized on demand. They are
// always the same size, and this size is stored in MR_trace_histogram_max.
// MR_trace_histogram_hwm stores the high water mark, i.e. the biggest
// depth number that has been encountered so far in the execution of the
// program.
#ifdef MR_TRACE_HISTOGRAM
extern int *MR_trace_histogram_all;
extern int *MR_trace_histogram_exp;
extern int MR_trace_histogram_max;
extern int MR_trace_histogram_hwm;
extern void MR_trace_print_histogram(FILE *fp, const char *which,
int *histogram, int max);
#endif // MR_TRACE_HISTOGRAM
extern void MR_io_tabling_stats(FILE *fp);
// These two functions work on a table that maps proc layout structures
// to the Mercury terms representing the bodies of those procedures.
// The Mercury term representation of the procedure body is constructed
// on demand from the bytecode in the procedure layout structure, but since
// this construction process allocates a significant amount of memory and
// takes a nontrivial amount of time, we cache the results in this table.
//
// MR_insert_proc_defn_rep adds the result of a conversion to the cache.
//
// MR_lookup_proc_defn_rep checks whether a previous call to
// MR_insert_proc_defn_rep has already cached the procedure body
// representation of a given procedure; a zero return value means that
// the answer is "no".
extern void MR_insert_proc_defn_rep(const MR_ProcLayout *proc_layout,
MR_Word proc_defn_rep);
extern MR_Word MR_lookup_proc_defn_rep(const MR_ProcLayout *proc_layout);
#ifndef MR_HIGHLEVEL_CODE
MR_declare_entry(MR_do_trace_redo_fail_shallow);
MR_declare_entry(MR_do_trace_redo_fail_deep);
#endif // !MR_HIGHLEVEL_CODE
// The compiler emits the following macro at each system-defined trace event.
#define MR_EVENT_SYS \
{ \
MR_Code *MR_jumpaddr; \
MR_save_transient_registers(); \
MR_jumpaddr = MR_trace((const MR_LabelLayout *) \
MR_HASH_DEF_LABEL_LAYOUT); \
MR_restore_transient_registers(); \
if (MR_jumpaddr != NULL) MR_GOTO(MR_jumpaddr); \
}
// The compiler emits the following macro at each user-defined trace event.
#define MR_EVENT_USER \
{ \
MR_Code *MR_jumpaddr; \
MR_save_transient_registers(); \
MR_jumpaddr = MR_user_trace((const MR_LabelLayout *) \
MR_HASH_DEF_LABEL_LAYOUT); \
MR_restore_transient_registers(); \
if (MR_jumpaddr != NULL) MR_GOTO(MR_jumpaddr); \
}
// The compiler used to emit the following macros instead of those above.
#define MR_EVENT(label_layout) \
{ \
MR_Code *MR_jumpaddr; \
MR_save_transient_registers(); \
MR_jumpaddr = MR_trace((const MR_LabelLayout *) \
&MR_LABEL_LAYOUT_NAME(MR_add_prefix(label))); \
MR_restore_transient_registers(); \
if (MR_jumpaddr != NULL) MR_GOTO(MR_jumpaddr); \
}
#define MR_USER_EVENT(label_layout) \
{ \
MR_Code *MR_jumpaddr; \
MR_save_transient_registers(); \
MR_jumpaddr = MR_user_trace((const MR_LabelLayout *) \
&MR_LABEL_LAYOUT_NAME(MR_add_prefix(label))); \
MR_restore_transient_registers(); \
if (MR_jumpaddr != NULL) MR_GOTO(MR_jumpaddr); \
}
// When using the heap pointer, we need to restore it, in case it is
// transient.
#define MR_TRACE_USE_HP(STATEMENTS) \
do { \
MR_restore_transient_registers(); \
STATEMENTS; \
MR_save_transient_registers(); \
} while (0)
// When calling Mercury code defined using `pragma export', we need
// to call save_registers() and restore_registers() around it.
// That in turn needs to be preceded/followed by
// restore/save_transient_registers() if it is in a C function.
//
// We also need to ensure that Mercury code called from the debugger
// doesn't screw up the data structures belonging to the program being
// debugged.
//
// XXX The code here is very similar to MR_turn_off_debug/MR_turn_debug_back_on
// in the source file. Look into merging the two pieces of code.
#if defined(MR_DEEP_PROFILING) && defined(MR_EXEC_TRACE)
#define MR_TRACE_CALL_MERCURY_DEEP_BEGIN \
do { \
MR_disable_deep_profiling_in_debugger = MR_TRUE; \
} while (0)
#define MR_TRACE_CALL_MERCURY_DEEP_END \
do { \
MR_disable_deep_profiling_in_debugger = MR_FALSE; \
} while (0)
#else
#define MR_TRACE_CALL_MERCURY_DEEP_BEGIN ((void) 0)
#define MR_TRACE_CALL_MERCURY_DEEP_END ((void) 0)
#endif
#define MR_TRACE_CALL_MERCURY(STATEMENTS) \
do { \
MR_bool saved_debug_enabled; \
MR_bool saved_io_enabled; \
MR_Unsigned saved_trace_call_seqno; \
MR_Unsigned saved_trace_call_depth; \
MR_Unsigned saved_trace_event_number; \
\
MR_TRACE_CALL_MERCURY_DEEP_BEGIN; \
saved_debug_enabled = MR_debug_enabled; \
saved_io_enabled = MR_io_tabling_enabled; \
saved_trace_call_seqno = MR_trace_call_seqno; \
saved_trace_call_depth = MR_trace_call_depth; \
saved_trace_event_number = MR_trace_event_number; \
MR_debug_enabled = MR_FALSE; \
MR_update_trace_func_enabled(); \
MR_io_tabling_enabled = MR_FALSE; \
MR_restore_transient_registers(); \
MR_save_registers(); \
STATEMENTS; \
MR_restore_registers(); \
MR_save_transient_registers(); \
MR_debug_enabled = saved_debug_enabled; \
MR_update_trace_func_enabled(); \
MR_io_tabling_enabled = saved_io_enabled; \
MR_trace_call_seqno = saved_trace_call_seqno; \
MR_trace_call_depth = saved_trace_call_depth; \
MR_trace_event_number = saved_trace_event_number; \
MR_TRACE_CALL_MERCURY_DEEP_END; \
} while (0)
#endif // MERCURY_TRACE_BASE_H