mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
runtime/*.[ch]:
trace/*.[chyl]:
As above. In some places, improve comments, e.g. by expanding contractions
such as "we've". Add #ifndef guards against double inclusion around
the trace/*.h files that did not already have them.
tools/*:
Make the corresponding changes in shell scripts that generate .[ch] files
in the runtime.
tests/*:
Conform to a slight change in the text of a message.
405 lines
18 KiB
C
405 lines
18 KiB
C
// vim: ts=4 sw=4 expandtab ft=c
|
|
|
|
// 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 frame_limit is nonzero, dumps at most limit frames.
|
|
// If line_limit is nonzero, dumps at most limit lines.
|
|
// The output format is not meant to be intelligible to non-implementors.
|
|
|
|
extern void MR_dump_nondet_stack(FILE *fp,
|
|
MR_FrameLimit frame_limit, MR_SpecLineLimit line_limit,
|
|
MR_Word *maxfr);
|
|
|
|
// MR_dump_nondet_stack_from_layout
|
|
//
|
|
// This function dumps the nondet stack.
|
|
// If frame_limit is nonzero, dumps at most limit frames.
|
|
// If line_limit is nonzero, dumps at most limit lines.
|
|
// The output format is not meant to be intelligible to non-implementors.
|
|
|
|
extern void MR_dump_nondet_stack_from_layout(FILE *fp,
|
|
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 nondeterministic 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_ip 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_ip;
|
|
|
|
// The bottom nondet stack frame is created by the runtime system before
|
|
// it calls "main/2". MR_nondet_stack_trace_bottom_fr holds the address
|
|
// of this frame. Nondet stack dumps continue only as long as the address
|
|
// of the frame being dumped is above this address.
|
|
//
|
|
// Since address comparisons with MR_nondet_stack_trace_bottom_fr make sense
|
|
// only if the other pointer is also in the same stack segment (if the grade
|
|
// allows stack segments), we record the identity of the nondet stack segment
|
|
// that contains MR_nondet_stack_trace_bottom_fr in
|
|
// MR_nondet_stack_trace_bottom_zone.
|
|
//
|
|
// MR_above_bottom_nondet_frame(fr) returns true for all nondet stack frames
|
|
// that are conceptually above the bottom frame created by the runtime.
|
|
// MR_at_or_above_bottom_nondet_frame(fr) returns true for this bottom
|
|
// frame as well. We use these macros to prevent stack traces descending
|
|
// into nonexistent stack frames *below* the bottom frame.
|
|
|
|
extern MR_Word *MR_nondet_stack_trace_bottom_fr;
|
|
#ifdef MR_STACK_SEGMENTS
|
|
extern MR_MemoryZone *MR_nondet_stack_trace_bottom_zone;
|
|
#endif
|
|
|
|
#ifndef MR_STACK_SEGMENTS
|
|
#define MR_above_bottom_nondet_frame(fr) \
|
|
((fr) > MR_nondet_stack_trace_bottom_fr)
|
|
#define MR_at_or_above_bottom_nondet_frame(fr) \
|
|
((fr) >= MR_nondet_stack_trace_bottom_fr)
|
|
#else
|
|
#define MR_above_bottom_nondet_frame(fr) \
|
|
(!MR_in_zone((fr), MR_nondet_stack_trace_bottom_zone) \
|
|
|| ((fr) > MR_nondet_stack_trace_bottom_fr))
|
|
#define MR_at_or_above_bottom_nondet_frame(fr) \
|
|
(!MR_in_zone((fr), MR_nondet_stack_trace_bottom_zone) \
|
|
|| ((fr) >= MR_nondet_stack_trace_bottom_fr))
|
|
#endif
|
|
|
|
// The different Mercury determinisms are internally represented by integers.
|
|
// This array gives the correspondence 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
|