/*
** vim: ts=4 sw=4 expandtab
*/
/*
** Copyright (C) 1997-2003, 2005-2007, 2011 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.
*/

/*
** mercury_trace.h defines the interface by which the internal and external
** debuggers can control how the tracing subsystem treats events.
**
** The macros, functions and variables of this module are intended to be
** referred to only from code generated by the Mercury compiler, and from
** hand-written code in the Mercury runtime or the Mercury standard library,
** and even then only if at least some part of the program was compiled
** with some form of execution tracing.
**
** The parts of the tracing system that need to be present even when tracing
** is not enabled are in the module runtime/mercury_trace_base.
*/

#ifndef MERCURY_TRACE_H
#define MERCURY_TRACE_H

#include "mercury_memory_zones.h"   /* for MR_MAX_FAKE_REG */
#include "mercury_types.h"          /* for MR_Unsigned etc */
#include "mercury_stack_trace.h"    /* for MR_Level etc    */
#include "mercury_trace_base.h"     /* for MR_TracePort    */
#include "mercury_std.h"            /* for MR_bool         */


typedef MR_Unsigned MR_IgnoreCount;

/*
** MR_EventInfo is used to hold the information for a trace event.  One
** of these is built by MR_trace_event and is passed (by reference)
** throughout the tracing system.
*/

typedef struct MR_EventInfo_Struct {
    MR_Unsigned             MR_event_number;
    MR_Unsigned             MR_call_seqno;
    MR_Unsigned             MR_call_depth;
    MR_TracePort            MR_trace_port;
    const MR_LabelLayout    *MR_event_sll;
    const char              *MR_event_path;
    MR_Word                 MR_saved_regs[MR_MAX_FAKE_REG];
    int                     MR_max_mr_num;
    MR_Float                MR_saved_f_regs[MR_MAX_VIRTUAL_F_REG];
    int                     MR_max_f_num;
} MR_EventInfo;

/*
** The above declarations are part of the interface between MR_trace_real
** and the internal and external debuggers. Even though MR_trace_real is
** defined in mercury_trace.c, its prototype is not here. Instead, it is
** in runtime/mercury_init.h. This is necessary because the address of
** MR_trace_real may be taken in automatically generated <main>_init.c files,
** and we do not want to include mercury_trace.h in such files; we don't want
** them to refer to the trace directory at all unless debugging is enabled.
**
** MR_trace_real_decl is the version of MR_trace_real we use when gathering
** events for the annotated trace.
**
** MR_trace_real_decl_implicit_subtree is a specialized version of 
** MR_trace_real_decl for events which are in implicit subtrees.
*/

extern  MR_Code *MR_trace_real_decl(const MR_LabelLayout *);

extern  MR_Code *MR_trace_real_decl_implicit_subtree(const MR_LabelLayout *);

/*
** Ideally, MR_trace_retry works by resetting the state of the stacks and
** registers to the state appropriate for the call to the selected ancestor,
** setting *jumpaddr to point to the start of the code for the selected
** ancestor, and returning MR_RETRY_OK_DIRECT.
**
** If resetting the stacks requires discarding the stack frame of a procedure
** whose evaluation method is memo or loopcheck, we must also reset the call
** table entry for that particular call to uninitialized. There are two reasons
** for this. The first is that the call table entry was uninitialized at the
** time of the first call, so if the retried call is to do what the original
** call did, it must find the call table entry in the same state. The second
** reason is that if we did not reset the call table entry, then the retried
** call would find the "call active" marker left by the original call, and
** since this normally indicates an infinite loop, it will generate a runtime
** abort.
**
** Unfortunately, resetting the call table entry to uninitialized does not work
** in general for procedures whose evaluation method is minimal model tabling.
** In such procedures, a subgoal can be a consumer as well as a generator,
** and control passes between consumers and generators in a complex fashion.
** There is no safe way to reset the state of such a system, except to wait
** for normal forward execution to execute the completion operation on an
** SCC of mutually dependent subgoals.
**
** If the stack segments between the current call and the call to be retried
** contain one or more such complete SCCs, then MR_trace_retry will return
** either MR_RETRY_OK_FINISH_FIRST or MR_RETRY_OK_FAIL_FIRST. The first
** indicates that the `retry' command should be executed only after a `finish'
** command on the selected call has made the state of the SCC quiescent.
** However, if the selected call is itself a generator, then reaching one of
** its exit ports is not enough to make its SCC quiescent; for that, one must
** wait for its failure. This is why in such cases, MR_trace_retry will ask
** for the `retry' command to be executed only after a `fail' command.
** 
** If the fail command reaches an exception port on the selected call instead
** of the fail port, then the SCC cannot be made quiescent, and MR_trace_retry
** will return MR_RETRY_ERROR, putting a description of the error into
** *problem. It will also do this for other, more prosaic problems, such as
** when it finds that some of the stack frames it looks at lack debugging
** information.
**
** Retry across I/O is unsafe in general. It is therefore allowed only if one
** of the following is true.
**
** - If the retry is in a tabled region, and we believe that all I/O actions
**   are tabled, either because we are in a grade in which all I/O actions
**   really are tabled or because the caller tells us to assume so.
** - If across_io is MR_RETRY_IO_FORCE.
** - If across_io is MR_RETRY_IO_INTERACTIVE (in which case in_fp and out_fp
**   must both be non-NULL), and the user, when asked whether he/she wants
**   to perform the retry anyway, says yes.
**
** If across_io is set to MR_RETRY_IO_INTERACTIVE then the string pointed to by
** the retry_interactive_message argument will be used to ask the user 
** whether they want to perform an unsafe retry or not.
**
** If an unsafe retry across IO is performed then the unsafe_retry argument
** will be set to MR_TRUE, otherwise it will be set to MR_FALSE.  
*/

typedef enum {
    MR_RETRY_IO_FORCE,
    MR_RETRY_IO_INTERACTIVE,
    MR_RETRY_IO_ONLY_IF_SAFE
} MR_RetryAcrossIo;

typedef enum {
    MR_RETRY_OK_DIRECT,
    MR_RETRY_OK_FINISH_FIRST,
    MR_RETRY_OK_FAIL_FIRST,
    MR_RETRY_ERROR
} MR_RetryResult;

extern  MR_RetryResult  MR_trace_retry(MR_EventInfo *event_info,
                            MR_Level ancestor_level,
                            MR_RetryAcrossIo across_io,
                            MR_bool assume_all_io_is_tabled,
                            const char *retry_interactive_message,
                            MR_bool *unsafe_retry, const char **problem,
                            FILE *in_fp, FILE *out_fp, MR_Code **jumpaddr);

/*
** MR_trace_cmd says what mode the tracer is in, i.e. how events should be
** treated.
**
** If MR_trace_cmd == MR_CMD_COLLECT, the event handler calls
** MR_COLLECT_filter() until the end of the execution is reached or until
** the `stop_collecting' variable is set to MR_TRUE. It is the tracer mode
** after a `collect' request.
**
** If MR_trace_cmd == MR_CMD_STEP, the event handler will stop at the next
** event.
**
** If MR_trace_cmd == MR_CMD_GOTO, the event handler will stop at the next
** event whose event number is equal to or greater than MR_trace_stop_event.
** In own stack minimal model grades, the MR_trace_stop_generator has to match
** the current context as well, the matching being defined by the
** MR_cur_generator_is_named() macro
**
** If MR_trace_cmd == MR_CMD_NEXT, the event handler will stop at the next
** event at depth MR_trace_stop_depth.
**
** If MR_trace_cmd == MR_CMD_FINISH, the event handler will stop at the next
** event at depth MR_trace_stop_depth and whose port is EXIT or FAIL or
** EXCEPTION.
**
** If MR_trace_cmd == MR_CMD_FAIL, the event handler will stop at the next
** event at depth MR_trace_stop_depth and whose port is FAIL or EXCEPTION.
**
** If MR_trace_cmd == MR_CMD_RESUME_FORWARD, the event handler will stop at
** the next event of any call whose port is *not* REDO or FAIL or EXCEPTION.
**
** If MR_trace_cmd == MR_CMD_RETURN, the event handler will stop at
** the next event of any call whose port is *not* EXIT.
**
** If MR_trace_cmd == MR_CMD_USER, the event handler will stop at the
** next user-defined event.
**
** If MR_trace_cmd == MR_CMD_MIN_DEPTH, the event handler will stop at
** the next event of any call whose depth is at least MR_trace_stop_depth.
**
** If MR_trace_cmd == MR_CMD_MAX_DEPTH, the event handler will stop at
** the next event of any call whose depth is at most MR_trace_stop_depth.
**
** If MR_trace_cmd == MR_CMD_TO_END, the event handler will not stop
** until the end of the program.
**
** If the event handler does not stop at an event, it will print the
** summary line for the event if MR_trace_print_intermediate is true.
*/

typedef enum {
    MR_CMD_COLLECT,
    MR_CMD_STEP,
    MR_CMD_GOTO,
    MR_CMD_NEXT,
    MR_CMD_FINISH,
    MR_CMD_FAIL,
    MR_CMD_RESUME_FORWARD,
    MR_CMD_EXCP,
    MR_CMD_RETURN,
    MR_CMD_USER,
    MR_CMD_MIN_DEPTH,
    MR_CMD_MAX_DEPTH,
    MR_CMD_TO_END
} MR_TraceCmdType;

typedef enum {
    MR_PRINT_LEVEL_NONE,    /* no events at all                        */
    MR_PRINT_LEVEL_SOME,    /* events matching an active spy point     */
    MR_PRINT_LEVEL_ALL      /* all events                              */
} MR_TracePrintLevel;

/*
** Type of pointers to filter/4 procedures for collect request
*/

typedef void    (*MR_FilterFuncPtr)(MR_Integer, MR_Integer, MR_Integer,
                    MR_Word, MR_Word, MR_String, MR_String, MR_String,
                    MR_Integer, MR_Integer, MR_Word, MR_Integer, MR_String,
                    MR_Integer, MR_Word, MR_Word *, MR_Char *);

typedef struct {
    MR_TraceCmdType         MR_trace_cmd;   
                            /*
                            ** The MR_trace_stop_depth field is meaningful
                            ** if MR_trace_cmd is MR_CMD_NEXT or MR_CMD_FINISH.
                            */
    MR_Unsigned             MR_trace_stop_depth;
                            /*
                            ** The MR_trace_stop_event field is meaningful
                            ** if MR_trace_cmd is MR_CMD_GOTO.
                            **
                            ** The MR_trace_stop_generator field is meaningful
                            ** if MR_trace_cmd is MR_CMD_GOTO and the grade
                            ** is own stack minimal model.
                            */
    MR_Unsigned             MR_trace_stop_event;
    const char              *MR_trace_stop_generator;
    MR_TracePrintLevel      MR_trace_print_level;
    MR_bool                 MR_trace_strict;
#ifdef  MR_TRACE_CHECK_INTEGRITY
    MR_bool                 MR_trace_check_integrity;
#endif

                            /*
                            ** The next field is an optimization;
                            ** it must be set to !MR_trace_strict ||
                            ** MR_trace_print_level != MR_PRINT_LEVEL_NONE
                            ** || MR_trace_check_integrity (the last only
                            ** if defined, of course).
                            */
    MR_bool                 MR_trace_must_check;

                            /*
                            ** The MR_filter_ptr field points to the filter/4
                            ** procedure during a collect request.
                            */
    MR_FilterFuncPtr        MR_filter_ptr;
} MR_TraceCmdInfo;

/*
** The data structure that tells MR_trace_real and MR_trace_real_decl
** what to do. Exported only for use by mercury_trace_declarative.c.
*/

extern  MR_TraceCmdInfo     MR_trace_ctrl;

/*
** MR_cur_generator_is_named(genname) succeeds if the current context
** belongs to a generator named `genname' (using either MR_gen_addr_short_name
** or MR_gen_subgoal), or genname is NULL or the empty string and the current
** context does not belong to a generator.
*/

#ifdef  MR_USE_MINIMAL_MODEL_OWN_STACKS
  #define   MR_cur_generator_is_named(genname)                              \
    (                                                                       \
        (MR_ENGINE(MR_eng_this_context) != NULL)                            \
    &&                                                                      \
        (                                                                   \
            (                                                               \
                (MR_ENGINE(MR_eng_this_context)->MR_ctxt_owner_generator    \
                    == NULL)                                                \
            &&                                                              \
                ((genname) == NULL || MR_streq((genname), ""))              \
            )                                                               \
        ||                                                                  \
            (                                                               \
                (MR_ENGINE(MR_eng_this_context)->MR_ctxt_owner_generator    \
                    != NULL)                                                \
            &&                                                              \
                ((genname) != NULL)                                         \
            &&                                                              \
                (                                                           \
                    MR_streq((genname),                                     \
                        MR_gen_addr_short_name(                             \
                            MR_ENGINE(MR_eng_this_context)->                \
                            MR_ctxt_owner_generator))                       \
                ||                                                          \
                    MR_streq((genname),                                     \
                        MR_gen_subgoal(                                     \
                            MR_ENGINE(MR_eng_this_context)->                \
                            MR_ctxt_owner_generator))                       \
                )                                                           \
            )                                                               \
        )                                                                   \
    )
#else   /* MR_USE_MINIMAL_MODEL_OWN_STACKS */
  #define   MR_cur_generator_is_named(genname)      MR_TRUE
#endif   /* MR_USE_MINIMAL_MODEL_OWN_STACKS */

#ifdef  MR_TRACE_CHECK_INTEGRITY
  #define MR_init_trace_check_integrity(cmd)                            \
    do { (cmd)->MR_trace_check_integrity = MR_FALSE; } while (0)
#else
  #define MR_init_trace_check_integrity(cmd)    ((void) 0)
#endif

#define MR_port_is_final(port)      ((port) == MR_PORT_EXIT || \
                                     (port) == MR_PORT_FAIL || \
                                     (port) == MR_PORT_EXCEPTION)

#define MR_port_is_interface(port)  ((port) <= MR_PORT_EXCEPTION)

#define MR_port_is_entry(port)      ((port) == MR_PORT_CALL)

extern  void    MR_trace_init_modules(void);

#endif /* MERCURY_TRACE_H */
