Files
mercury/runtime/mercury_stack_trace.h
Zoltan Somogyi 53b573692a Convert C code to use // style comments.
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.
2016-07-14 13:57:35 +02:00

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