mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-15 05:44:58 +00:00
Estimated hours taken: 25 Branches: main Add --resume option to `dd' command. This resumes the previous declarative debugging session and allows the user to switch between the procedural and declarative debuggers freely. browser/declarative_analyser.m Add analysis_type type which is used to tell the analyser whether it must start a new session or resume a previous session. browser/declarative_debugger.m Add two versions of the exported diagnosis predicate: one to resume a previous session and one to start a new session. browser/declarative_user.m Print usage message to the correct output stream. doc/user_guide.texi Document the new option. runtime/mercury_stack_trace.c runtime/mercury_stack_trace.h Add a function to find the first call on the stack whose event number is less than or equal to a given event number or whose call sequence number is less than or equal to a given call sequence number. Since this new function uses some code very similar to existing code in the function that prints the stack, separate this code into a new function called MR_call_details_are_valid. trace/mercury_trace_declarative.c trace/mercury_trace_declarative.h Previously it could be safely assumed that the current event would be somewhere inside the materialized portion of the annotated trace, so it was sufficient to record the topmost node of the annotated trace and retry to there whenever we needed to build a new subtree (and to the topmost node plus some extra for a supertree). Now, however, the user may go to any event in the program before resuming the previous dd session. We could just retry to the call event for main/2, but this would be far from optimal, especially if the user is debugging code deep down in the program's call tree. Instead we retry to the first call on the stack whose event number is less than or equal to the call event number of the node we want to build a subtree for and then start forward execution from there. When building a supertree we retry to the first call on the stack whose event number is less than or equal to the event number of the call at the top of the currently materialized portion of the annotated trace. Then when we get to the call at the top of the currently materialized portion of the annotated trace through forward execution, we do a retry to the desired depth and start building the new supertree. Desribe the function of some of the global variables in more detail. Remove the global MR_edt_topmost_call_depth since it is no longer needed. Fix an inconsistency where the depth limit was being set to MR_edt_depth_step_size when starting a dd session, but to MR_edt_depth_step_size + 1 when building an additional portion of the annotated trace. Don't update the saved event details from the global event number/seqno/depth variables at the start of MR_decl_diagnosis. The globals could have been updated by a previous call to Mercury code and could have incorrect values. tests/debugger/declarative/Mmakefile tests/debugger/declarative/resume.exp tests/debugger/declarative/resume.inp tests/debugger/declarative/resume.m Test the --resume option. Specifically test the creation of a supertree and subtree from a resumed session where the user has gone to an event before and after the materialized portion of the annotated trace. trace/mercury_trace_internal.c Handle the --resume option.
322 lines
11 KiB
C
322 lines
11 KiB
C
/*
|
|
** Copyright (C) 1998-2001, 2003-2005 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.
|
|
*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
** 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
|
|
** pragma c_code, 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 Mercury pragma c_code (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 from Mercury code,
|
|
** it would probably be best to make 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 void (*MR_Print_Stack_Record)(FILE *fp,
|
|
const MR_Proc_Layout *proc_layout,
|
|
int count, int level,
|
|
MR_Word *base_sp, MR_Word * base_curfr,
|
|
const char *filename, int linenumber,
|
|
const char *goal_path,
|
|
MR_bool context_mismatch);
|
|
|
|
extern const char *MR_dump_stack_from_layout(FILE *fp,
|
|
const MR_Label_Layout *label_layout,
|
|
MR_Word *det_stack_pointer,
|
|
MR_Word *current_frame,
|
|
MR_bool include_trace_data,
|
|
MR_bool include_contexts,
|
|
int frame_limit, int line_limit,
|
|
MR_Print_Stack_Record 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,
|
|
int frame_limit, int 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, int frame_limit, int line_limit,
|
|
MR_Word *maxfr, const MR_Label_Layout *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_Traverse_Nondet_Frame_Func(void *user_data,
|
|
const MR_Label_Layout *layout, MR_Word *base_sp,
|
|
MR_Word *base_curfr);
|
|
|
|
extern void MR_traverse_nondet_stack_from_layout(
|
|
MR_Word *maxfr, const MR_Label_Layout *label_layout,
|
|
MR_Word *base_sp, MR_Word *base_curfr,
|
|
MR_Traverse_Nondet_Frame_Func *traverse_frame_func,
|
|
void *traverse_frame_func_data);
|
|
|
|
|
|
/*
|
|
** 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_Label_Layout *MR_find_nth_ancestor(
|
|
const MR_Label_Layout *label_layout,
|
|
int ancestor_level, MR_Word **stack_trace_sp,
|
|
MR_Word **stack_trace_curfr, 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.
|
|
**
|
|
** return_label_layout 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_Stack_Walk_Step_Result;
|
|
|
|
extern MR_Stack_Walk_Step_Result
|
|
MR_stack_walk_step(const MR_Proc_Layout *entry_layout,
|
|
const MR_Label_Layout **return_label_layout,
|
|
MR_Word **stack_trace_sp_ptr, MR_Word **stack_trace_curfr_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_Label_Layout *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_Proc_Layout *entry,
|
|
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_Proc_Layout *entry,
|
|
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_Proc_Layout *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_Proc_Layout *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_Proc_Layout *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_Proc_Layout *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 position argument says where (if anywhere) the contexts should appear.
|
|
*/
|
|
|
|
typedef enum {
|
|
MR_CONTEXT_NOWHERE,
|
|
MR_CONTEXT_BEFORE,
|
|
MR_CONTEXT_AFTER,
|
|
MR_CONTEXT_PREVLINE,
|
|
MR_CONTEXT_NEXTLINE
|
|
} MR_Context_Position;
|
|
|
|
extern void MR_print_proc_id_trace_and_context(FILE *fp,
|
|
MR_bool include_trace_data, MR_Context_Position pos,
|
|
const MR_Proc_Layout *entry,
|
|
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,
|
|
const MR_Proc_Layout *entry_layout, int count,
|
|
int start_level, MR_Word *base_sp, MR_Word *base_curfr,
|
|
const char *filename, int linenumber,
|
|
const char *goal_path, MR_bool context_mismatch);
|
|
|
|
/*
|
|
** 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_find_first_call_seq_or_event;
|
|
|
|
extern int MR_find_first_call_less_eq_seq_or_event(
|
|
MR_find_first_call_seq_or_event seq_or_event,
|
|
MR_Unsigned seq_no_or_event_no,
|
|
const MR_Label_Layout *label_layout,
|
|
MR_Word *det_stack_pointer, MR_Word *current_frame,
|
|
const char **problem);
|
|
|
|
#endif /* MERCURY_STACK_TRACE_H */
|