mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-24 05:43:53 +00:00
Estimated hours taken: 0.25 runtime/mercury_stack_trace.h: runtime/mercury_stack_trace.c: Fix a bug: MR_stack_trace_bottom and MR_nondet_stack_trace_bottom were being _defined_ in the header file. I changed it so that they were only _declared_ in the header file, and defined in the .c file.
502 lines
13 KiB
C
502 lines
13 KiB
C
/*
|
|
** Copyright (C) 1998-1999 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.
|
|
*/
|
|
|
|
/*
|
|
** mercury_stack_trace.c - implements stack traces.
|
|
**
|
|
** Main author: Tyson Dowd (trd).
|
|
*/
|
|
|
|
#include "mercury_imp.h"
|
|
#include "mercury_stack_trace.h"
|
|
#include <stdio.h>
|
|
|
|
static void MR_dump_stack_record_init(void);
|
|
static void MR_dump_stack_record_frame(FILE *fp,
|
|
const MR_Stack_Layout_Entry *,
|
|
Word *base_sp, Word *base_curfr,
|
|
void (*print_stack_record)(
|
|
FILE *, const MR_Stack_Layout_Entry *,
|
|
int, int, Word *, Word *));
|
|
static void MR_dump_stack_record_flush(FILE *fp,
|
|
void (*print_stack_record)(
|
|
FILE *, const MR_Stack_Layout_Entry *,
|
|
int, int, Word *, Word *));
|
|
|
|
/* see comments in mercury_stack_trace.h */
|
|
Code *MR_stack_trace_bottom;
|
|
Word *MR_nondet_stack_trace_bottom;
|
|
|
|
void
|
|
MR_dump_stack(Code *success_pointer, Word *det_stack_pointer,
|
|
Word *current_frame, bool include_trace_data)
|
|
{
|
|
#ifndef MR_STACK_TRACE
|
|
fprintf(stderr, "Stack dump not available in this grade.\n");
|
|
#else
|
|
const MR_Internal *label;
|
|
const MR_Stack_Layout_Label *layout;
|
|
const MR_Stack_Layout_Entry *entry_layout;
|
|
const char *result;
|
|
|
|
fprintf(stderr, "Stack dump follows:\n");
|
|
|
|
do_init_modules();
|
|
label = MR_lookup_internal_by_addr(success_pointer);
|
|
if (label == NULL) {
|
|
fprintf(stderr, "internal label not found\n");
|
|
} else {
|
|
layout = label->i_layout;
|
|
entry_layout = layout->MR_sll_entry;
|
|
result = MR_dump_stack_from_layout(stderr, entry_layout,
|
|
det_stack_pointer, current_frame, include_trace_data,
|
|
&MR_dump_stack_record_print);
|
|
|
|
if (result != NULL) {
|
|
fprintf(stderr, "%s\n", result);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
const char *
|
|
MR_dump_stack_from_layout(FILE *fp, const MR_Stack_Layout_Entry *entry_layout,
|
|
Word *det_stack_pointer, Word *current_frame, bool include_trace_data,
|
|
void (*print_stack_record)(FILE *, const MR_Stack_Layout_Entry *,
|
|
int, int, Word *, Word *))
|
|
{
|
|
MR_Stack_Walk_Step_Result result;
|
|
const MR_Stack_Layout_Label *return_label_layout;
|
|
const char *problem;
|
|
Word *stack_trace_sp;
|
|
Word *stack_trace_curfr;
|
|
Word *old_trace_sp;
|
|
Word *old_trace_curfr;
|
|
|
|
do_init_modules();
|
|
MR_dump_stack_record_init();
|
|
|
|
stack_trace_sp = det_stack_pointer;
|
|
stack_trace_curfr = current_frame;
|
|
|
|
do {
|
|
old_trace_sp = stack_trace_sp;
|
|
old_trace_curfr = stack_trace_curfr;
|
|
|
|
result = MR_stack_walk_step(entry_layout, &return_label_layout,
|
|
&stack_trace_sp, &stack_trace_curfr, &problem);
|
|
if (result == STEP_ERROR_BEFORE) {
|
|
MR_dump_stack_record_flush(fp, print_stack_record);
|
|
return problem;
|
|
} else if (result == STEP_ERROR_AFTER) {
|
|
if (include_trace_data) {
|
|
MR_dump_stack_record_frame(fp, entry_layout,
|
|
old_trace_sp, old_trace_curfr,
|
|
print_stack_record);
|
|
} else {
|
|
MR_dump_stack_record_frame(fp, entry_layout,
|
|
NULL, NULL, print_stack_record);
|
|
}
|
|
|
|
MR_dump_stack_record_flush(fp, print_stack_record);
|
|
return problem;
|
|
} else {
|
|
if (include_trace_data) {
|
|
MR_dump_stack_record_frame(fp, entry_layout,
|
|
old_trace_sp, old_trace_curfr,
|
|
print_stack_record);
|
|
} else {
|
|
MR_dump_stack_record_frame(fp, entry_layout,
|
|
NULL, NULL, print_stack_record);
|
|
}
|
|
}
|
|
|
|
if (return_label_layout == NULL) {
|
|
break;
|
|
}
|
|
|
|
entry_layout = return_label_layout->MR_sll_entry;
|
|
} while (TRUE);
|
|
|
|
MR_dump_stack_record_flush(fp, print_stack_record);
|
|
return NULL;
|
|
}
|
|
|
|
const MR_Stack_Layout_Label *
|
|
MR_find_nth_ancestor(const MR_Stack_Layout_Label *label_layout,
|
|
int ancestor_level, Word **stack_trace_sp, Word **stack_trace_curfr,
|
|
const char **problem)
|
|
{
|
|
MR_Stack_Walk_Step_Result result;
|
|
const MR_Stack_Layout_Label *return_label_layout;
|
|
int i;
|
|
|
|
if (ancestor_level < 0) {
|
|
*problem = "no such stack frame";
|
|
return NULL;
|
|
}
|
|
|
|
do_init_modules();
|
|
*problem = NULL;
|
|
for (i = 0; i < ancestor_level && label_layout != NULL; i++) {
|
|
(void) MR_stack_walk_step(label_layout->MR_sll_entry,
|
|
&return_label_layout,
|
|
stack_trace_sp, stack_trace_curfr, problem);
|
|
label_layout = return_label_layout;
|
|
}
|
|
|
|
if (label_layout == NULL && *problem == NULL) {
|
|
*problem = "not that many ancestors";
|
|
}
|
|
|
|
return label_layout;
|
|
}
|
|
|
|
|
|
MR_Stack_Walk_Step_Result
|
|
MR_stack_walk_step(const MR_Stack_Layout_Entry *entry_layout,
|
|
const MR_Stack_Layout_Label **return_label_layout,
|
|
Word **stack_trace_sp_ptr, Word **stack_trace_curfr_ptr,
|
|
const char **problem_ptr)
|
|
{
|
|
MR_Internal *label;
|
|
MR_Long_Lval location;
|
|
MR_Long_Lval_Type type;
|
|
int number;
|
|
int determinism;
|
|
Code *success;
|
|
|
|
*return_label_layout = NULL;
|
|
|
|
determinism = entry_layout->MR_sle_detism;
|
|
if (determinism < 0) {
|
|
/*
|
|
** This means we have reached some handwritten code that has
|
|
** no further information about the stack frame.
|
|
*/
|
|
|
|
*problem_ptr = "reached procedure with no stack trace info";
|
|
return STEP_ERROR_BEFORE;
|
|
}
|
|
|
|
if (MR_DETISM_DET_STACK(determinism)) {
|
|
location = entry_layout->MR_sle_succip_locn;
|
|
type = MR_LONG_LVAL_TYPE(location);
|
|
number = MR_LONG_LVAL_NUMBER(location);
|
|
|
|
if (type != MR_LONG_LVAL_TYPE_STACKVAR) {
|
|
*problem_ptr = "can only handle stackvars";
|
|
return STEP_ERROR_AFTER;
|
|
}
|
|
|
|
success = (Code *) MR_based_stackvar(*stack_trace_sp_ptr,
|
|
number);
|
|
*stack_trace_sp_ptr = *stack_trace_sp_ptr -
|
|
entry_layout->MR_sle_stack_slots;
|
|
} else {
|
|
success = MR_succip_slot(*stack_trace_curfr_ptr);
|
|
*stack_trace_curfr_ptr = MR_succfr_slot(*stack_trace_curfr_ptr);
|
|
}
|
|
|
|
if (success == MR_stack_trace_bottom) {
|
|
return STEP_OK;
|
|
}
|
|
|
|
label = MR_lookup_internal_by_addr(success);
|
|
if (label == NULL) {
|
|
*problem_ptr = "reached unknown label";
|
|
return STEP_ERROR_AFTER;
|
|
}
|
|
|
|
if (label->i_layout == NULL) {
|
|
*problem_ptr = "reached label with no stack layout info";
|
|
return STEP_ERROR_AFTER;
|
|
}
|
|
|
|
*return_label_layout = label->i_layout;
|
|
return STEP_OK;
|
|
}
|
|
|
|
void
|
|
MR_dump_nondet_stack_from_layout(FILE *fp, Word *base_maxfr)
|
|
{
|
|
int frame_size;
|
|
|
|
do_init_modules();
|
|
|
|
/*
|
|
** The comparison operator in the condition of the while loop
|
|
** should be >= if you want the trace to include the bottom frame
|
|
** created by mercury_wrapper.c (whose redoip/redofr field can be
|
|
** hijacked by other code), and > if you don't want the bottom
|
|
** frame to be included.
|
|
*/
|
|
|
|
while (base_maxfr >= MR_nondet_stack_trace_bottom) {
|
|
frame_size = base_maxfr - MR_prevfr_slot(base_maxfr);
|
|
if (frame_size == MR_NONDET_TEMP_SIZE) {
|
|
MR_print_nondstackptr(fp, base_maxfr);
|
|
fprintf(fp, ": temp\n");
|
|
fprintf(fp, " redoip: ");
|
|
printlabel(MR_redoip_slot(base_maxfr));
|
|
fprintf(fp, " redofr: ");
|
|
MR_print_nondstackptr(fp, MR_redofr_slot(base_maxfr));
|
|
fprintf(fp, " \n");
|
|
} else if (frame_size == MR_DET_TEMP_SIZE) {
|
|
MR_print_nondstackptr(fp, base_maxfr);
|
|
fprintf(fp, ": temp\n");
|
|
fprintf(fp, " redoip: ");
|
|
printlabel(MR_redoip_slot(base_maxfr));
|
|
fprintf(fp, " redofr: ");
|
|
MR_print_nondstackptr(fp, MR_redofr_slot(base_maxfr));
|
|
fprintf(fp, " \n");
|
|
fprintf(fp, " detfr: ");
|
|
MR_print_detstackptr(fp, MR_detfr_slot(base_maxfr));
|
|
fprintf(fp, " \n");
|
|
} else {
|
|
MR_print_nondstackptr(fp, base_maxfr);
|
|
fprintf(fp, ": ordinary, %d words\n",
|
|
frame_size);
|
|
fprintf(fp, " redoip: ");
|
|
printlabel(MR_redoip_slot(base_maxfr));
|
|
fprintf(fp, " redofr: ");
|
|
MR_print_nondstackptr(fp, MR_redofr_slot(base_maxfr));
|
|
fprintf(fp, " \n");
|
|
fprintf(fp, " succip: ");
|
|
printlabel(MR_succip_slot(base_maxfr));
|
|
fprintf(fp, " succfr: ");
|
|
MR_print_nondstackptr(fp, MR_succfr_slot(base_maxfr));
|
|
fprintf(fp, " \n");
|
|
}
|
|
|
|
base_maxfr = MR_prevfr_slot(base_maxfr);
|
|
}
|
|
}
|
|
|
|
static const MR_Stack_Layout_Entry *prev_entry_layout;
|
|
static int prev_entry_layout_count;
|
|
static int prev_entry_start_level;
|
|
static Word *prev_entry_base_sp;
|
|
static Word *prev_entry_base_curfr;
|
|
static int current_level;
|
|
|
|
static void
|
|
MR_dump_stack_record_init(void)
|
|
{
|
|
prev_entry_layout = NULL;
|
|
prev_entry_layout_count = 0;
|
|
prev_entry_start_level = 0;
|
|
current_level = 0;
|
|
}
|
|
|
|
static void
|
|
MR_dump_stack_record_frame(FILE *fp, const MR_Stack_Layout_Entry *entry_layout,
|
|
Word *base_sp, Word *base_curfr, void (*print_stack_record)(
|
|
FILE *, const MR_Stack_Layout_Entry *, int, int, Word *, Word *))
|
|
{
|
|
bool must_flush;
|
|
|
|
must_flush =
|
|
/*
|
|
** We cannot merge two calls if they are to different
|
|
** procedures.
|
|
*/
|
|
|
|
(entry_layout != prev_entry_layout) ||
|
|
|
|
/*
|
|
** We cannot merge two calls even to the same procedure.
|
|
** if we are printing trace data, since this will differ
|
|
** between the calls.
|
|
**
|
|
** Note that it is not possible for two calls to the same
|
|
** procedure to differ on whether the procedure has trace
|
|
** layout data or not; this is why we don't have to test
|
|
** prev_entry_base_sp and prev_entry_base_curfr.
|
|
*/
|
|
|
|
((base_sp != NULL) || (base_curfr != NULL));
|
|
|
|
if (must_flush) {
|
|
MR_dump_stack_record_flush(fp, print_stack_record);
|
|
|
|
prev_entry_layout = entry_layout;
|
|
prev_entry_layout_count = 1;
|
|
prev_entry_start_level = current_level;
|
|
prev_entry_base_sp = base_sp;
|
|
prev_entry_base_curfr = base_curfr;
|
|
} else {
|
|
prev_entry_layout_count++;
|
|
}
|
|
|
|
current_level++;
|
|
}
|
|
|
|
static void
|
|
MR_dump_stack_record_flush(FILE *fp, void (*print_stack_record)(
|
|
FILE *, const MR_Stack_Layout_Entry *, int, int, Word *, Word *))
|
|
{
|
|
if (prev_entry_layout != NULL) {
|
|
print_stack_record(fp, prev_entry_layout,
|
|
prev_entry_layout_count, prev_entry_start_level,
|
|
prev_entry_base_sp, prev_entry_base_curfr);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
MR_dump_stack_record_print(FILE *fp, const MR_Stack_Layout_Entry *entry_layout,
|
|
int count, int start_level, Word *base_sp, Word *base_curfr)
|
|
{
|
|
fprintf(fp, "%4d ", start_level);
|
|
|
|
if (count > 1) {
|
|
fprintf(fp, " %3d* ", count);
|
|
} else if ((base_sp == NULL) && (base_curfr == NULL)) {
|
|
fprintf(fp, "%5s ", "");
|
|
} else {
|
|
/*
|
|
** If we are printing trace data, we need all the horizonal
|
|
** room we can get, and there will not be any repeated lines,
|
|
** so we don't reserve space for the repeat counts.
|
|
*/
|
|
}
|
|
|
|
MR_print_proc_id(fp, entry_layout, NULL, base_sp, base_curfr);
|
|
}
|
|
|
|
void
|
|
MR_print_proc_id(FILE *fp, const MR_Stack_Layout_Entry *entry,
|
|
const char *extra, Word *base_sp, Word *base_curfr)
|
|
{
|
|
if (! MR_ENTRY_LAYOUT_HAS_PROC_ID(entry)) {
|
|
fatal_error("cannot print procedure id without layout");
|
|
}
|
|
|
|
if (base_sp != NULL && base_curfr != NULL) {
|
|
bool print_details = FALSE;
|
|
if (MR_ENTRY_LAYOUT_HAS_EXEC_TRACE(entry)) {
|
|
Integer maybe_from_full =
|
|
entry->MR_sle_maybe_from_full;
|
|
if (maybe_from_full > 0) {
|
|
/*
|
|
** for procedures compiled with shallow
|
|
** tracing, the details will be valid only
|
|
** if the value of MR_from_full saved in
|
|
** the appropriate stack slot was TRUE.
|
|
*/
|
|
if (MR_DETISM_DET_STACK(entry->MR_sle_detism)) {
|
|
print_details = MR_based_stackvar(
|
|
base_sp, maybe_from_full);
|
|
} else {
|
|
print_details = MR_based_framevar(
|
|
base_curfr, maybe_from_full);
|
|
}
|
|
} else {
|
|
/*
|
|
** for procedures compiled with full tracing,
|
|
** always print out the details
|
|
*/
|
|
print_details = TRUE;
|
|
}
|
|
}
|
|
if (print_details) {
|
|
if (MR_DETISM_DET_STACK(entry->MR_sle_detism)) {
|
|
fprintf(fp, "%7lu %7lu %4lu ",
|
|
(unsigned long)
|
|
MR_event_num_stackvar(base_sp) + 1,
|
|
(unsigned long)
|
|
MR_call_num_stackvar(base_sp),
|
|
(unsigned long)
|
|
MR_call_depth_stackvar(base_sp));
|
|
} else {
|
|
fprintf(fp, "%7lu %7lu %4lu ",
|
|
(unsigned long)
|
|
MR_event_num_framevar(base_curfr) + 1,
|
|
(unsigned long)
|
|
MR_call_num_framevar(base_curfr),
|
|
(unsigned long)
|
|
MR_call_depth_framevar(base_curfr));
|
|
}
|
|
} else {
|
|
/* ensure that the remaining columns line up */
|
|
fprintf(fp, "%21s", "");
|
|
}
|
|
}
|
|
|
|
if (MR_ENTRY_LAYOUT_COMPILER_GENERATED(entry)) {
|
|
fprintf(fp, "%s for %s:%s/%ld-%ld",
|
|
entry->MR_sle_comp.MR_comp_pred_name,
|
|
entry->MR_sle_comp.MR_comp_type_module,
|
|
entry->MR_sle_comp.MR_comp_type_name,
|
|
(long) entry->MR_sle_comp.MR_comp_arity,
|
|
(long) entry->MR_sle_comp.MR_comp_mode);
|
|
|
|
if (strcmp(entry->MR_sle_comp.MR_comp_type_module,
|
|
entry->MR_sle_comp.MR_comp_def_module) != 0)
|
|
{
|
|
fprintf(fp, " {%s}",
|
|
entry->MR_sle_comp.MR_comp_def_module);
|
|
}
|
|
} else {
|
|
if (entry->MR_sle_user.MR_user_pred_or_func == MR_PREDICATE) {
|
|
fprintf(fp, "pred");
|
|
} else if (entry->MR_sle_user.MR_user_pred_or_func ==
|
|
MR_FUNCTION)
|
|
{
|
|
fprintf(fp, "func");
|
|
} else {
|
|
fatal_error("procedure is not pred or func");
|
|
}
|
|
|
|
fprintf(fp, " %s:%s/%ld-%ld",
|
|
entry->MR_sle_user.MR_user_decl_module,
|
|
entry->MR_sle_user.MR_user_name,
|
|
(long) entry->MR_sle_user.MR_user_arity,
|
|
(long) entry->MR_sle_user.MR_user_mode);
|
|
|
|
if (strcmp(entry->MR_sle_user.MR_user_decl_module,
|
|
entry->MR_sle_user.MR_user_def_module) != 0)
|
|
{
|
|
fprintf(fp, " {%s}",
|
|
entry->MR_sle_user.MR_user_def_module);
|
|
}
|
|
}
|
|
|
|
fprintf(fp, " (%s)", MR_detism_names[entry->MR_sle_detism]);
|
|
|
|
if (extra != NULL) {
|
|
fprintf(fp, " %s\n", extra);
|
|
} else {
|
|
fprintf(fp, "\n");
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** 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.
|
|
*/
|
|
|
|
const char * MR_detism_names[] = {
|
|
"failure", /* 0 */
|
|
"", /* 1 */
|
|
"semidet", /* 2 */
|
|
"nondet", /* 3 */
|
|
"erroneous", /* 4 */
|
|
"", /* 5 */
|
|
"det", /* 6 */
|
|
"multi", /* 7 */
|
|
"", /* 8 */
|
|
"", /* 9 */
|
|
"cc_nondet", /* 10 */
|
|
"", /* 11 */
|
|
"", /* 12 */
|
|
"", /* 13 */
|
|
"cc_multi" /* 14 */
|
|
};
|