mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-15 22:03:26 +00:00
Estimated hours taken: 20
Branches: main
Implement some of Mark's wish list for making user events more useful.
1. When executing "print *" in mdb, we used to print both the values of all
attributes and the values of all live variables. Since some attributes'
values were given directly by live variables, this lead to some things being
printed twice. This diff eliminates this duplication.
2. At user events, we now print the name of the event. Whether we print the
other stuff we also print at events (the predicate containing the event,
and its source location) is now controlled by a new mdb command,
"user_event_context".
3. We would like different solvers to be compilable independently of one
another. This means that neither solver's event set should depend on the
existence of the events needed by the other solvers. This diff therefore
eliminates the requirement that all modules of the program be compiled with
the same event set specification. Instead, a program may contain modules
that were compiled with different event sets. Each event set is named;
the new requirement is that different named event sets may coexist in the
program (each being used to compile some modules), but two event sets with
the same name must be identical in all other respects as well (we need this
requirement to prevent inconsistencies arising between different versions of
the same event set).
4. We now generate user events even from modules compiled with --trace shallow.
The problem here is that user events can occur in procedures that do not
get caller events and whose ancestors may not get caller events either.
Yet these procedures must still pass on debugger information such as call
sequence numbers and the call depth to the predicate with the user event.
This diff therefore decouples the generation of code for this basic debugger
infrastructure information from the generation of call events by inventing
two new trace levels, settable by the compiler only (i.e. not from the
command line). The trace level "basic_user" is for procedures containing a
user event whose trace level (in a shallow traced module) would otherwise be
"none". The trace level "basic" is for procedures not containing a user
event but which nevertheless may need to transmit information (e.g. depth)
to a user event. For the foreseeable future, this means that shallow traced
modules containing user events will have some debugging overhead compiled
into *all* their procedures.
runtime/mercury_stack_layout.h:
Add a new field to MR_UserEvent structures, giving the HLDS number of
the variable representing each attribute.
Add a new field to module layout structures, giving the name of the
event set (if any) the module was compiled with.
Add the new trace levels to the MR_TraceLevel type.
Update the current layout structure version number.
runtime/mercury_stack_trace.[ch]:
Allow the printing of the containing predicate's name and/or the
filename/linenumber context to be turned off when printing contexts.
Factor out some of the code involved in this printing.
Give a bunch of variables better names.
Rename a type to get rid of unnecessary underscores.
compiler/prog_data.m:
compiler/prog_event.m:
Include the event set name in the information we have about the event
set.
compiler/simplify.m:
Mark each procedure and each module that contains user events
as containing user events.
Use the same technique to mark each procedure that contains parallel
conjunctions as containing parallel conjunctions, instead of marking
the predicate containing the procedure. (Switch detection may eliminate
arbitrary goals, including parallel conjunctions, from switch arms
that are unreachable due to initial insts, and in any case we want to
handle the procedures of a predicate independently from each other
after mode analysis.)
Also, change the code handling generic calls to switch on the generic
call kind, and factor out some common code.
compiler/hlds_module.m:
compiler/hlds_pred.m:
Provide slots in the proc_info and the module_info for the information
gathered by simplify.
compiler/trace_params.m:
Implement the new trace levels described above. This required changing
the signature of some of the predicates of this module.
compiler/code_info.m:
Record whether the compiler generated any trace events. We need to know
this, because if it did, then we must generate a proc layout structure
for it.
compiler/proc_gen.m:
Act on the information recorded by code_info.m.
Factor out the code for generating the call event and its layout
structure, since the conditions for generating this event have changed.
compiler/continuation_info.m:
compiler/call_gen.m:
For each user event, record the id of the variables corresponding to
each argument of a user event.
compiler/layout.m:
compiler/layout_out.m:
compiler/stack_layout.m:
Generate the new field (giving the HLDS variable number of each
attribute) in user event structures, and the new field (event set name)
in module layout structures.
Allow the call event's layout structure to be missing. This is needed
for user events in shallow traced modules.
compiler/options.m:
compiler/handle_options.m:
compiler/mercury_compiler.m:
Rename the option for specifying event sets from --event-spec-file-name
to --event-set-file-name, since it specifies only one event set, not
all events.
compiler/jumpopt.m:
Give some predicates better names.
compiler/dep_par_conj.m:
compiler/deforest.m:
compiler/granularity.m:
compiler/hlds_out.m:
compiler/inlining.m:
compiler/intermod.m:
compiler/lambda.m:
compiler/liveness.m:
compiler/modes.m:
compiler/opt_debug.m:
compiler/optimize.m:
compiler/size_proc.m:
compiler/stack_alloc.m:
compiler/store_alloc.m:
compiler/table_gen.m:
compiler/trace_gen.m:
compiler/typecheck.m:
Conform to the changes above.
doc/mdb_categories:
Mention the new mdb command.
doc/user_guide.texi:
Update the documentation of user events to account for the changes
above.
trace/mercury_event_parser.y:
trace/mercury_event_scanner.l:
Modify the grammar for event set specifications to a name for the
event set.
trace/mercury_event_spec.[ch]:
Instead of recording information about event sets internally
in this module, return a representation of each event set read in
to the callers, for them to do with as they please.
Include the event set name when we print the Mercury term for
compiler/prog_event.m.
trace/mercury_trace.c:
Do not assume that every procedure that contains an event contains a
call event (and hence a call event layout structure), since that
is not true anymore.
trace/mercury_trace_cmd_parameter.[ch]:
Implement the new mdb command "user_event_context".
trace/mercury_trace_cmd_internal.[ch]:
Include "user_event_context" in the list of mdb commands.
Print the user event name at user events. Let the current setting
of the user_event_context mdb command determine what else to print
at such events.
Instead of reading in one event set on initialization, read in
all event sets that occur in the program.
trace/mercury_trace_tables.[ch]:
Allow the gathering of information for more than one event set
from the modules of the program.
trace/mercury_trace_vars.[ch]:
For each attribute value of a user event, record what the HLDS variable
number of the attribute is. When printing all variables at an event,
mark the variable numbers of printed attributes as being printed
already, causing the variable with the same number not to be printed.
Include the name of the variable (if it has one) in the description
of an attribute. Without this, users may wonder why the value of the
variable wasn't printed.
trace/mercury_trace_cmd_browsing.[ch]:
Pass the current setting of the user_event_context mdb command to
runtime/mercury_stack_trace.c when printing the context of an event.
tests/debugger/user_event_shallow.{m,inp,exp}:
New test case to test the new functionality. This test case is the same
as the user_event test case, but it is compiled with shallow tracing,
and its mdb input exercises the user_event_context mdb command.
tests/debugger/user_event_spec:
tests/invalid/invalid_event_spec:
Update these event set spec files by adding an event set name.
tests/debugger/Mmakefile:
tests/debugger/Mercury.options:
Enable the new test case.
tests/debugger/user_event.exp:
Update the expected output of the old user event test case, which now
prints event names, but doesn't print attribute values twice.
tests/debugger/completion.exp:
Expect the new "user_event_context" mdb command in the command list.
tests/debugger/mdb_command_test.inp:
Test the existence of the documentation for the new mdb command.
tests/invalid/Mercury.options:
Conform to the name change of the --event-spec-file-name option.
341 lines
12 KiB
C
341 lines
12 KiB
C
/*
|
|
** Copyright (C) 1998-2001, 2003-2006 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_PrintStackRecord)(FILE *fp,
|
|
const MR_ProcLayout *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_LabelLayout *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_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,
|
|
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_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_Traverse_Nondet_Frame_Func(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_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_LabelLayout *MR_find_nth_ancestor(
|
|
const MR_LabelLayout *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_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,
|
|
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,
|
|
const MR_ProcLayout *proc_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_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 */
|