/*
** vim: ts=4 sw=4 expandtab
*/
/*
** Copyright (C) 1998-2001,2003-2006,2008,2011-2012 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.
*/

#ifndef MERCURY_STACK_TRACE_H
#define MERCURY_STACK_TRACE_H

#include "mercury_regs.h"
#include "mercury_stack_layout.h"
#include <stdio.h>

/*
** mercury_stack_trace.h:
**
** Definitions for use by the stack tracing.
*/

typedef MR_Unsigned MR_FrameLimit;
typedef MR_Unsigned MR_SpecLineLimit;
typedef MR_Unsigned MR_Level;

/*---------------------------------------------------------------------------*/

/*
** MR_dump_stack
**
** Given the succip, det stack pointer and current frame, generate a
** stack dump showing the name of each active procedure on the stack.
** If include_trace_data data is set, also print the call event number,
** call sequence number and depth for every traced procedure.
** NOTE: MR_dump_stack will assume that the succip is for the topmost
** stack frame. If you call MR_dump_stack from some foreign_proc,
** that may not be the case.
** Due to some optimizations (or lack thereof) the MR_dump_stack call
** may end up inside code that has a stack frame allocated, but
** that has a succip for the previous stack frame.
** Don't call MR_dump_stack from a Mercury procedure defined by a foreign_proc
** (calling from other C code in the runtime is probably ok, provided the
** succip corresponds to the topmost stack frame).
** (See library/require.m for a technique for calling MR_dump_stack
** from Mercury).
** If you need a more convenient way of calling this from Mercury code,
** it would probably be best to do it using an impure predicate defined
** using `:- external'.
*/

extern  void    MR_dump_stack(MR_Code *success_pointer,
                    MR_Word *det_stack_pointer, MR_Word *current_frame,
                    MR_bool include_trace_data);

/*
** MR_dump_stack_from_layout
**
** This function does the same job and makes the same assumptions
** as MR_dump_stack, but instead of the succip, it takes the label
** layout of the current point in the current procedure as input.
** It also takes a parameter that tells it where to put the stack dump
** and flags that say whether to include execution trace data and/or
** line numbers. If limit is nonzero, dumps at most limit frames.
**
** If the entire wanted part of the stack was printed successfully,
** the return value is NULL; otherwise, it is a string indicating
** why the dump was cut short.
*/

typedef struct {
    /*
    ** The min_level and max_level fields give the range of call levels
    ** covered by this dump record. The frame_count field gives the number
    ** of stack frames covered by this dump record.
    **
    ** Normally, each call has its own frame, which translates into
    ** max_level+1-min_level being equal to frame_count. However,
    ** frame_count can be less than this if the procedure has tail
    ** recursion events. However, frame_count does not have to be one
    ** for such procedures, since not all recursive calls are tail
    ** recursive calls.
    **
    ** If include_trace_data is TRUE, frame_count should be 1.
    */
    const MR_ProcLayout     *MR_sdi_proc_layout;
    MR_Level                MR_sdi_min_level;
    MR_Level                MR_sdi_max_level;
    MR_Unsigned             MR_sdi_num_frames;
    const char              *MR_sdi_filename;
    int                     MR_sdi_linenumber;
    MR_bool                 MR_sdi_context_mismatch;

    /* These fields are meaningful only if include_trace_data is TRUE. */
    MR_Word                 *MR_sdi_base_sp;
    MR_Word                 *MR_sdi_base_curfr;
    const char              *MR_sdi_goal_path;
} MR_StackFrameDumpInfo;

typedef void        (*MR_PrintStackRecord)(FILE *fp,
                        MR_bool include_trace_data,
                        const MR_StackFrameDumpInfo *frame_dump_info);

extern  const char  *MR_dump_stack_from_layout(FILE *fp,
                        const MR_LabelLayout *label_layout,
                        MR_Word *det_stack_pointer,
                        MR_Word *current_frame,
                        MR_bool include_trace_data,
                        MR_bool include_contexts,
                        MR_FrameLimit frame_limit,
                        MR_SpecLineLimit line_limit,
                        MR_PrintStackRecord print_stack_record);

extern  const char  *MR_dump_stack_from_layout_clique(FILE *fp,
                        const MR_LabelLayout *label_layout,
                        MR_Word *det_stack_pointer,
                        MR_Word *current_frame,
                        MR_bool include_trace_data,
                        MR_bool include_contexts,
                        MR_bool detect_cliques,
                        MR_SpecLineLimit clique_line_limit,
                        MR_FrameLimit frame_limit,
                        MR_SpecLineLimit line_limit,
                        MR_PrintStackRecord print_stack_record);

/*
** MR_dump_nondet_stack
**
** This function dumps the control slots of the nondet stack.
** If limit_addr is nonnull, dumps only frames above limit_addr.
** If limit is nonzero, dumps at most limit frames.
** The output format is not meant to be intelligible to non-implementors.
*/

extern  void        MR_dump_nondet_stack(FILE *fp, MR_Word *limit_addr,
                        MR_FrameLimit frame_limit, MR_SpecLineLimit line_limit,
                        MR_Word *maxfr);

/*
** MR_dump_nondet_stack_from_layout
**
** This function dumps the nondet stack.
** If limit_addr is nonnull, dumps only frames above limit_addr.
** If limit is nonzero, dumps at most limit frames.
** The output format is not meant to be intelligible to non-implementors.
*/

extern  void        MR_dump_nondet_stack_from_layout(FILE *fp,
                        MR_Word *limit_addr, MR_FrameLimit frame_limit,
                        MR_SpecLineLimit line_limit, MR_Word *maxfr,
                        const MR_LabelLayout *label_layout,
                        MR_Word *base_sp, MR_Word *base_curfr);

/*
** MR_traverse_nondet_stack_from_layout
**
** This function traverses the nondet stack, calling the specified
** function for each frame.
*/

typedef void        MR_TraverseNondetFrameFunc(void *user_data,
                        const MR_LabelLayout *layout, MR_Word *base_sp,
                        MR_Word *base_curfr);

extern  void        MR_traverse_nondet_stack_from_layout(
                        MR_Word *maxfr, const MR_LabelLayout *label_layout,
                        MR_Word *base_sp, MR_Word *base_curfr,
                        MR_TraverseNondetFrameFunc *traverse_frame_func,
                        void *traverse_frame_func_data);

/*
** MR_find_clique_entry
**
** Walk the stack from the current event to the stack frame of main.
** The initial part of this walk visits the stack frames of procedures
** that are mutually recursive with the current event's procedure; 
** the rest of the walk visits the frames of other procedures.
** This function find the boundary between these two parts.
**
** If we cannot walk all the way to main (e.g. because some stack frames
** have no layout information, or because the stack does not have the required
** depth), we return a pointer to an error message, and neither
** *clique_entry_level nor *first_outside_ancestor_level will be meaningful.
**
** If we can walk all the way to main, then we will set *clique_entry_level
** to be the level on the stack (in the sense of a number you can give to
** MR_find_nth_ancestor) of the stack frame that is in the initial mutually
** recursive group, but whose caller is not, and it will set
** *first_outside_ancestor_level to the level of the caller, unless there
** is no such caller, in which case we set *first_outside_ancestor_level
** to a negative number.
**
** Either clique_entry_level or first_outside_ancestor_level may be NULL,
** if the caller does not need one or other of these numbers.
*/

extern const char   *MR_find_clique_entry(
                        const MR_LabelLayout *label_layout,
                        MR_Word *det_stack_pointer, MR_Word *current_frame,
                        int *clique_entry_level,
                        int *first_outside_ancestor_level);

/*
** MR_find_nth_ancestor
**
** Return the layout structure of the return label of the call
** ancestor_level levels above the current call. Label_layout
** tells us how to decipher the stack of the current call, while
** *stack_trace_sp and *stack_trace_curfr tell us where it is.
** On return, *stack_trace_sp and *stack_trace_curfr will be
** set up to match the specified ancestor.
**
** If the required stack walk is not possible (e.g. because some
** stack frames have no layout information, or because the stack
** does not have the required depth), the return value will be NULL,
** and problem will point to an error message.
*/

extern  const MR_LabelLayout *MR_find_nth_ancestor(
                        const MR_LabelLayout *label_layout,
                        MR_Level ancestor_level, MR_Word **stack_trace_sp,
                        MR_Word **stack_trace_curfr,
                        MR_Level *actual_level_ptr, const char **problem);

/*
** MR_stack_walk_step
**
** This function takes the entry_layout for the current stack
** frame (which is the topmost stack frame from the two stack
** pointers given), and moves down one stack frame, i.e. to the
** caller's frame, setting the stack pointers to their new levels.
** The number of times that the topmost stack has been reused
** is returned in *reused_frames_ptr.
**
** *return_label_layout_ptr will be set to the stack_layout of the
** continuation label, or NULL if the bottom of the stack has
** been reached.
**
** The meanings of the possible return values from MR_stack_walk_step
** are as follows:
**
** MR_STEP_OK:              everything is fine.
** MR_STEP_ERROR_BEFORE:    entry_layout has no valid stack trace info.
** MR_STEP_ERROR_AFTER:     entry_layout has valid stack trace info,
**                          but its caller does not.
**
** If a MR_stack_walk_step encounters a problem, it will set problem_ptr
** to point to a string representation of the error.
**
** Note that for nondetermistic code, this function will only
** traverse the success continuations (via MR_succfr),
** not the frames which represent failure continuations
** (which would be accessible via MR_redofr).
*/

typedef enum {
    MR_STEP_ERROR_BEFORE,
    MR_STEP_ERROR_AFTER,
    MR_STEP_OK
} MR_StackWalkStepResult;

extern  MR_StackWalkStepResult
                    MR_stack_walk_step(const MR_ProcLayout *entry_layout,
                        const MR_LabelLayout **return_label_layout,
                        MR_Word **stack_trace_sp_ptr,
                        MR_Word **stack_trace_curfr_ptr,
                        MR_Unsigned *reused_frames_ptr,
                        const char **problem_ptr);

/*
** MR_stack_trace_bottom should be set to the address of global_success,
** the label main/2 goes to on success. Stack dumps terminate when they
** reach a stack frame whose saved succip slot contains this address.
*/

extern  MR_Code         *MR_stack_trace_bottom;

/*
** MR_nondet_stack_trace_bottom should be set to the address of the buffer
** nondet stack frame created before calling main. Nondet stack dumps terminate
** when they reach a stack frame whose redoip contains this address. Note that
** the redoip and redofr slots of this frame may be hijacked.
*/

extern  MR_Word         *MR_nondet_stack_trace_bottom;

/*
** The different Mercury determinisms are internally represented by integers.
** This array gives the correspondance with the internal representation and
** the names that are usually used to denote determinisms.
*/

extern  const char      *MR_detism_names[];

/*
** MR_find_context attempts to look up the file name and line number
** corresponding to a label identified by its layout structure. If successful,
** it fills in *fileptr and *lineptr accordingly, and returns MR_TRUE;
** otherwise, it returns MR_FALSE.
*/

extern  MR_bool         MR_find_context(const MR_LabelLayout *label,
                            const char **fileptr, int *lineptr);

/*
** MR_print_call_trace_info prints the call event number, call sequence number
** and call depth of the call stored in the stack frame of the procedure
** identified by the given proc layout. It requires the procedure to have
** trace layout information, and the relevant one of base_sp and base_curfr
** to be non-NULL, since these numbers are stored in stack slots.
**
** MR_maybe_print_call_trace_info calls MR_print_call_trace_info if
** include_trace_data is MR_TRUE and the other conditions required by
** MR_print_call_trace_info are satisfied.
*/

extern  void            MR_print_call_trace_info(FILE *fp,
                            const MR_ProcLayout *proc_layout,
                            MR_Word *base_sp, MR_Word *base_curfr);

extern  void            MR_maybe_print_call_trace_info(FILE *fp,
                            MR_bool include_trace_data,
                            const MR_ProcLayout *proc_layout,
                            MR_Word *base_sp, MR_Word *base_curfr);

/*
** MR_print_proc_id prints an identification of the given procedure,
** consisting of "pred" or "func", module name, pred or func name, arity,
** mode number and determinism. It does not output a newline, so that
** the caller can put something else after the procedure id on the same line.
*/

extern  void            MR_print_proc_id(FILE *fp, const MR_ProcLayout *entry);

/*
** MR_print_pred_id prints everything that MR_print_proc_id does, except
** the mode number and determinism.
*/

extern  void            MR_print_pred_id(FILE *fp, const MR_ProcLayout *entry);

/*
** MR_print_proc_spec prints a string that uniquely specifies the given
** procedure to the debugger.
*/

extern  void            MR_print_proc_spec(FILE *fp,
                            const MR_ProcLayout *entry);

/*
** MR_print_proc_separate prints a string that uniquely specifies the given
** procedure to the debugger, with each component of a name in a separate field
** to allow the output to be processed by tools (e.g. awk scripts).
*/

extern  void            MR_print_proc_separate(FILE *fp,
                            const MR_ProcLayout *entry);

/*
** MR_print_proc_id_trace_and_context prints an identification of the given
** procedure, together with call trace information (if available), a context
** within the procedure, and possibly a context identifying the caller.
** The pos argument says where (if anywhere) the contexts should appear;
** the user_event_context argument says what parts of the context (if any)
** to print for user defined events.
*/

typedef enum {
    MR_CONTEXT_NOWHERE,
    MR_CONTEXT_BEFORE,
    MR_CONTEXT_AFTER,
    MR_CONTEXT_PREVLINE,
    MR_CONTEXT_NEXTLINE
} MR_ContextPosition;

typedef enum {
    MR_USER_EVENT_CONTEXT_NONE,
    MR_USER_EVENT_CONTEXT_FILE,
    MR_USER_EVENT_CONTEXT_PROC,
    MR_USER_EVENT_CONTEXT_FULL
} MR_UserEventContext;

extern  void        MR_print_proc_id_trace_and_context(FILE *fp,
                        MR_bool include_trace_data, MR_ContextPosition pos,
                        MR_UserEventContext user_event_context,
                        const MR_ProcLayout *proc_layout,
                        const char *maybe_user_event_name,
                        MR_Word *base_sp, MR_Word *base_curfr,
                        const char *path, const char *filename, int lineno,
                        MR_bool print_parent,
                        const char *parent_filename, int parent_lineno,
                        int indent);

/*
** MR_dump_stack_record_print() prints one line of a stack dump.
*/

extern  void        MR_dump_stack_record_print(FILE *fp,
                        MR_bool include_trace_data,
                        const MR_StackFrameDumpInfo *frame_dump_info);

/*
** Find the first call event on the stack whose event number or sequence number
** is less than or equal to the given event number or sequence number.  The
** level of the call in the stack is returned.  This can then be passed to
** MR_trace_retry as the ancestor_level.  If no such call is found then -1 is
** returned and problem is set to the reason why the call could not be
** found.
*/

typedef enum {
    MR_FIND_FIRST_CALL_BEFORE_SEQ,
    MR_FIND_FIRST_CALL_BEFORE_EVENT
} MR_FindFirstCallSeqOrEvent;

extern  int         MR_find_first_call_less_eq_seq_or_event(
                        MR_FindFirstCallSeqOrEvent seq_or_event,
                        MR_Unsigned seq_no_or_event_no,
                        const MR_LabelLayout *label_layout,
                        MR_Word *det_stack_pointer, MR_Word *current_frame,
                        const char **problem);

#endif /* MERCURY_STACK_TRACE_H */
