Files
mercury/runtime/mercury_agc_debug.c
Zoltan Somogyi b61ea9de44 Implement a large chunk of the code that was previously missing for .mmos
Estimated hours taken: 20
Branches: main

Implement a large chunk of the code that was previously missing for .mmos
grades. The system now correctly computes several answers for the tc_minimal
test case, before going into an infinite loop (since the code for recognizing
the absence of further solutions is not yet there).

Significantly improve the infrastructure for debugging such changes.

compiler/table_gen.m:
	Complete the mmos transformation.

compiler/proc_gen.m:
	Handle the special return requirements of mmos generators, which must
	return not to a caller (since each generator is the root of its own
	SLD tree), but to a consumer in another SLD tree that is waiting for an
	answer.

compiler/hlds_pred.m:
	Provide a mechanism whereby table_gen.m can communicate to proc_gen.m
	the requirement for this special return.

compiler/trace_gen.m:
	When generating events, include the port and the goal path in a
	comment. This makes the generated C code significantly easier to
	understand.

compiler/layout_out.m:
	Export a function for trace_gen.m to use.

compiler/hlds_goal.m:
	Change goal_path_to_string to a function to make it easier to use.

compiler/*.m:
	Conform to the change to goal_path_to_string.

runtime/mercury_context.[ch]:
	In .mmos grades, include the current debugger call sequence number,
	depth, and event number in contexts, to be saved and loaded with
	the contexts. This allows each context to have its own separate
	sequence of events.

	This capability depends not directly on the grade, but on the macro
	MR_EXEC_TRACE_INFO_IN_CONTEXT. For now, this is defined only in .mmos
	grades, but in future, it may be useful in other grades as well.

runtime/mercury_conf_param.h:
	Define and document MR_EXEC_TRACE_INFO_IN_CONTEXT.

runtime/mercury_mm_own_stacks.[ch]:
runtime/mercury_tabling_preds.h:
	Implement some predicates needed by the own stack transformation.
	Implement the code for generators returning answers to consumers,
	and the code for consumers scheduling generators when they need
	more answers. At the moment, the code for detecting when generators
	depend on each other is not yet written.

	Provide better facilities for debugging own stack minimal model grades.

	Fix a cut-and-paste bug (wrong macro name guarding the handwritten
	C module).

runtime/Mmakefile:
	Rebuild only what needs to be rebuilt when mercury_tabling_preds.h
	changes.

runtime/mercury_label.[ch]:
	Add a utility function for returning the name of an arbitrary label
	(internal or entry).

	Rename some fields to give them MR_ prefixes.

	Always define the functions for recording both entry and internal
	labels, even if they are not called from most modules, since they
	may be called from a few handwritten modules in the runtime.

	Rename a function to avoid a clash with the name of a macro,
	and thus allow the change to mercury_goto.h.

runtime/mercury_goto.h:
	Fix a bug with MR_init_entry_an. This macro was supposed to always
	insert the entry label that is its argument into the entry table,
	but instead of calling the function it was meant to call, it called
	a macro that could be (and usually way) defined to expand to nothing.

	The fix is to call the function a different name than the macro,
	and to call the function, not the macro.

runtime/mercury_wrapper.c:
	In own stack minimal model grades, create a main context separate
	from the current context, since the current context may be needed
	to hold a generator's state. Make MR_eng_this_context point to
	this context.

	Register all labels in the debugging variants of minimal model grades.

runtime/mercury_accurate_gc.c:
runtime/mercury_agc_debug.c:
runtime/mercury_debug.c:
library/exception.m:
	Conform to the change to runtime/mercury_label.h.

runtime/mercury_stack_trace.c:
	Conform to the change to runtime/mercury_label.h.

	Document the link to trace/mercury_trace_internal.c.

trace/mercury_trace.[ch]:
trace/mercury_trace_cmd_forward.c:
	Split the GOTO command into two: STEP and GOTO. STEP always stops
	at the next event (without any test), even if it is in a different
	context (and possibly with a lower event number than the immediately
	previous event, since the event numbers in different contexts are
	not related). As before, GOTO always goes to the specified event
	number, but in .dmmos grades it can now be told that this event number
	should be matched only in a specified context. The specification is
	done by an extra argument specifying the short name of the context's
	generator; the ansence of such an argument means the main context.

trace/mercury_trace_cmd_internal.c:
	In own stack grades, when the current context is that of a generator,
	print the subgoal the generator is working on before the event number,
	call depth, call sequence number and the rest of the event report.

	Document the link to runtime/mercury_stack_trace.c, which has similar
	code.

trace/mercury_trace_cmd_external.c:
trace/mercury_trace_cmd_declararive.c:
	Use the STEP command where GOTO was used for this simpler job,
	since this is (very slightly) faster.

trace/mercury_trace_cmd_developer.c:
	Fix some bugs with handling own stack tables.

doc/user_guide.texi:
	Document the new functionality of the goto mdb command. The
	documentation is commented out, since .mmos grades are for developers
	only at the moment.

tools/lmc.in:
	Turn off C optimizations when C debugging is enabled. For some reason,
	the default value of --cflags-for-debug does not include -O0.
2007-01-03 05:17:21 +00:00

496 lines
16 KiB
C

/*
** vim: ts=4 sw=4 expandtab
*/
/*
** Copyright (C) 1998-2007 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.
*/
/*
** Debugging support for the accurate garbage collector.
*/
#include "mercury_imp.h"
#include "mercury_layout_util.h"
#include "mercury_deep_copy.h"
#include "mercury_agc_debug.h"
#ifdef MR_NATIVE_GC
/*
** Function prototypes.
*/
static void MR_dump_long_value(MR_LongLval locn, MR_MemoryZone *heap_zone,
MR_Word * stack_pointer, MR_Word *current_frame,
MR_bool do_regs);
static void MR_dump_short_value(MR_ShortLval locn, MR_MemoryZone *heap_zone,
MR_Word * stack_pointer, MR_Word *current_frame,
MR_bool do_regs);
static void MR_dump_live_variables(const MR_LabelLayout *layout,
MR_MemoryZone *heap_zone, MR_bool top_frame,
MR_Word *stack_pointer, MR_Word *current_frame);
/*---------------------------------------------------------------------------*/
/*
** Currently this variable is never modified by the Mercury runtime code,
** but it can be modified manually in gdb.
** XXX It would be nicer to set it based on a runtime option setting.
*/
#ifdef MR_DEBUG_AGC_PRINT_VARS
static MR_bool MR_debug_agc_print_vars = MR_TRUE;
#else
static MR_bool MR_debug_agc_print_vars = MR_FALSE;
#endif
void
MR_agc_dump_roots(MR_RootList roots)
{
#ifndef MR_HIGHLEVEL_CODE
MR_Word saved_regs[MR_MAX_FAKE_REG];
#endif
fflush(NULL);
fprintf(stderr, "Dumping roots\n");
if (MR_debug_agc_print_vars) {
while (roots != NULL) {
#ifndef MR_HIGHLEVEL_CODE
/*
** Restore the registers, because we need to save them to a more
** permanent backing store (we are going to call Mercury soon,
** and we don't want it messing with the saved registers).
*/
MR_restore_registers();
MR_copy_regs_to_saved_regs(MR_MAX_FAKE_REG - 1, saved_regs);
MR_hp = MR_ENGINE(MR_eng_debug_heap_zone->MR_zone_min);
MR_virtual_hp = MR_ENGINE(MR_eng_debug_heap_zone->MR_zone_min);
#endif /* !MR_HIGHLEVEL_CODE */
fflush(NULL);
MR_write_variable(roots->type_info, *roots->root);
fflush(NULL);
fprintf(stderr, "\n");
#ifndef MR_HIGHLEVEL_CODE
MR_copy_saved_regs_to_regs(MR_MAX_FAKE_REG - 1, saved_regs);
MR_save_registers();
#endif /* !MR_HIGHLEVEL_CODE */
roots = roots->next;
}
}
}
#ifndef MR_HIGHLEVEL_CODE
void
MR_agc_dump_nondet_stack_frames(MR_Internal *label, MR_MemoryZone *heap_zone,
MR_Word *stack_pointer, MR_Word *current_frame, MR_Word *max_frame)
{
MR_Code *success_ip;
int frame_size;
MR_bool registers_valid;
while (max_frame > MR_nondet_stack_trace_bottom) {
registers_valid = (max_frame == current_frame);
frame_size = max_frame - MR_prevfr_slot(max_frame);
if (frame_size == MR_NONDET_TEMP_SIZE) {
fprintf(stderr, "%p: nondet temp\n", max_frame);
fprintf(stderr, " redoip: ");
fflush(NULL);
MR_printlabel(stderr, MR_redoip_slot(max_frame));
fflush(NULL);
fprintf(stderr, " redofr: %p\n", MR_redofr_slot(max_frame));
label = MR_lookup_internal_by_addr(MR_redoip_slot(max_frame));
if (label && label->MR_internal_layout) {
MR_dump_live_variables(label->MR_internal_layout, heap_zone,
registers_valid, stack_pointer, MR_redofr_slot(max_frame));
}
} else if (frame_size == MR_DET_TEMP_SIZE) {
fprintf(stderr, "%p: nondet temp\n", max_frame);
fprintf(stderr, " redoip: ");
fflush(NULL);
MR_printlabel(stderr, MR_redoip_slot(max_frame));
fflush(NULL);
fprintf(stderr, " redofr: %p\n", MR_redofr_slot(max_frame));
fprintf(stderr, " detfr: %p\n", MR_tmp_detfr_slot(max_frame));
label = MR_lookup_internal_by_addr(MR_redoip_slot(max_frame));
if (label && label->MR_internal_layout) {
MR_dump_live_variables(label->MR_internal_layout, heap_zone,
registers_valid, MR_tmp_detfr_slot(max_frame), max_frame);
/*
** XXX should max_frame above be
** MR_redoip_slot(max_frame) instead?
*/
}
} else {
fprintf(stderr, "%p: nondet ordinary\n", max_frame);
fprintf(stderr, " redoip: ");
fflush(NULL);
MR_printlabel(stderr, MR_redoip_slot(max_frame));
fflush(NULL);
fprintf(stderr, " redofr: %p\n", MR_redofr_slot(max_frame));
fprintf(stderr, " succip: ");
fflush(NULL);
MR_printlabel(stderr, MR_succip_slot(max_frame));
fflush(NULL);
fprintf(stderr, " succfr: %p\n", MR_succfr_slot(max_frame));
/* XXX ??? */
label = MR_lookup_internal_by_addr(MR_redoip_slot(max_frame));
if (label != NULL && label->MR_internal_layout) {
MR_dump_live_variables(label->MR_internal_layout, heap_zone,
registers_valid, stack_pointer, MR_redofr_slot(max_frame));
fprintf(stderr, " this label: %s\n", label->MR_internal_name);
}
}
max_frame = MR_prevfr_slot(max_frame);
}
/* XXX Lookup the address (redoip?) and dump the variables */
fflush(NULL);
}
void
MR_agc_dump_stack_frames(MR_Internal *label, MR_MemoryZone *heap_zone,
MR_Word *stack_pointer, MR_Word *current_frame)
{
MR_Word saved_regs[MR_MAX_FAKE_REG];
int i;
int short_var_count;
int long_var_count;
MR_Word *type_params;
MR_TypeInfo type_info;
MR_Word value;
const MR_ProcLayout *entry_layout;
const MR_LabelLayout *layout;
const MR_Code *success_ip;
MR_bool top_frame;
layout = label->MR_internal_layout;
success_ip = label->MR_internal_addr;
entry_layout = layout->MR_sll_entry;
/*
** For each stack frame...
*/
top_frame = MR_TRUE;
while (MR_DETISM_DET_STACK(entry_layout->MR_sle_detism)) {
if (label->MR_internal_name != NULL) {
fprintf(stderr, " label: %s\n", label->MR_internal_name);
} else {
fprintf(stderr, " label: %p\n", label->MR_internal_addr);
}
if (success_ip == MR_stack_trace_bottom) {
break;
}
MR_dump_live_variables(layout, heap_zone, top_frame,
stack_pointer, current_frame);
/*
** Move to the next stack frame.
*/
{
MR_LongLval location;
MR_LongLvalType type;
int number;
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) {
MR_fatal_error("can only handle stackvars");
}
success_ip = (MR_Code *) MR_based_stackvar(stack_pointer, number);
stack_pointer = stack_pointer - entry_layout->MR_sle_stack_slots;
label = MR_lookup_internal_by_addr(success_ip);
}
top_frame = MR_FALSE;
layout = label->MR_internal_layout;
if (layout != NULL) {
entry_layout = layout->MR_sll_entry;
}
}
}
static void
MR_dump_live_variables(const MR_LabelLayout *label_layout,
MR_MemoryZone *heap_zone, MR_bool top_frame,
MR_Word *stack_pointer, MR_Word *current_frame)
{
int short_var_count;
int long_var_count;
int i;
MR_TypeInfo type_info;
MR_Word value;
MR_TypeInfoParams type_params;
MR_Word saved_regs[MR_MAX_FAKE_REG];
MR_Word *current_regs;
short_var_count = MR_short_desc_var_count(label_layout);
long_var_count = MR_long_desc_var_count(label_layout);
/*
** For the top stack frame, we should pass a pointer to a filled-in saved_regs
** instead of NULL. For other stack frames, passing NULL is fine, since output
** arguments are not live yet for any call except the top one.
*/
MR_restore_registers();
MR_copy_regs_to_saved_regs(MR_MAX_FAKE_REG - 1, saved_regs);
if (top_frame) {
current_regs = saved_regs;
} else {
current_regs = NULL;
}
type_params = MR_materialize_type_params_base(label_layout,
current_regs, stack_pointer, current_frame);
for (i = 0; i < long_var_count; i++) {
fprintf(stderr, "%-12s\t", "");
if (MR_PROC_LAYOUT_HAS_PROC_ID(label_layout->MR_sll_entry)) {
MR_print_proc_id(stderr, label_layout->MR_sll_entry);
}
dump_long_value(MR_long_desc_var_locn(label_layout, i),
heap_zone, stack_pointer, current_frame, top_frame);
fprintf(stderr, "\n");
fflush(NULL);
if (MR_debug_agc_print_vars) {
/*
** Call Mercury but use the debugging heap.
*/
MR_hp = MR_ENGINE(MR_eng_debug_heap_zone->MR_zone_min);
MR_virtual_hp = MR_ENGINE(MR_eng_debug_heap_zone->MR_zone_min);
if (MR_get_type_and_value_base(label_layout, i,
current_regs, stack_pointer, current_frame, type_params,
&type_info, &value)) {
printf("\t");
MR_write_variable(type_info, value);
printf("\n");
}
fflush(NULL);
}
}
for (; i < short_var_count; i++) {
fprintf(stderr, "%-12s\t", "");
if (MR_PROC_LAYOUT_HAS_PROC_ID(label_layout->MR_sll_entry)) {
MR_print_proc_id(stderr, label_layout->MR_sll_entry);
}
dump_short_value(MR_short_desc_var_locn(label_layout, i),
heap_zone, stack_pointer, current_frame, top_frame);
fprintf(stderr, "\n");
fflush(NULL);
if (MR_debug_agc_print_vars) {
/*
** Call Mercury but use the debugging heap.
*/
MR_hp = MR_ENGINE(MR_eng_debug_heap_zone->MR_zone_min);
MR_virtual_hp = MR_ENGINE(MR_eng_debug_heap_zone->MR_zone_min);
if (MR_get_type_and_value_base(label_layout, i,
current_regs, stack_pointer, current_frame, type_params,
&type_info, &value)) {
printf("\t");
MR_write_variable(type_info, value);
printf("\n");
}
fflush(NULL);
}
}
MR_copy_saved_regs_to_regs(MR_MAX_FAKE_REG - 1, saved_regs);
MR_save_registers();
MR_free(type_params);
}
static void
MR_dump_long_value(MR_LongLval locn, MR_MemoryZone *heap_zone,
MR_Word *stack_pointer, MR_Word *current_frame, MR_bool do_regs)
{
intw locn_num;
MR_Word value;
int difference;
MR_bool have_value;
value = 0;
have_value = MR_FALSE;
locn_num = MR_LONG_LVAL_NUMBER(locn);
switch (MR_LONG_LVAL_TYPE(locn)) {
case MR_LONG_LVAL_TYPE_R:
if (do_regs) {
value = MR_virtual_reg_value(locn_num);
have_value = MR_TRUE;
fprintf(stderr, "r%d\t", locn_num);
} else {
fprintf(stderr, "r%d (invalid)\t", locn_num);
}
break;
case MR_LONG_LVAL_TYPE_F:
fprintf(stderr, "f%d\t", locn_num);
break;
case MR_LONG_LVAL_TYPE_STACKVAR:
value = MR_based_stackvar(stack_pointer, locn_num);
have_value = MR_TRUE;
fprintf(stderr, "stackvar%d", locn_num);
break;
case MR_LONG_LVAL_TYPE_FRAMEVAR:
value = MR_based_framevar(current_frame, locn_num);
have_value = MR_TRUE;
fprintf(stderr, "framevar%d", locn_num);
break;
case MR_LONG_LVAL_TYPE_SUCCIP:
fprintf(stderr, "succip");
break;
case MR_LONG_LVAL_TYPE_MAXFR:
fprintf(stderr, "maxfr");
break;
case MR_LONG_LVAL_TYPE_CURFR:
fprintf(stderr, "curfr");
break;
case MR_LONG_LVAL_TYPE_HP:
fprintf(stderr, "hp");
break;
case MR_LONG_LVAL_TYPE_SP:
fprintf(stderr, "sp");
break;
case MR_LONG_LVAL_TYPE_INDIRECT:
fprintf(stderr, "offset %d from ",
MR_LONG_LVAL_INDIRECT_OFFSET(locn_num));
/* XXX Tyson will have to complete this */
/* based on what he wants this function to do */
case MR_LONG_LVAL_TYPE_UNKNOWN:
fprintf(stderr, "unknown");
break;
default:
fprintf(stderr, "LONG DEFAULT");
break;
}
if (have_value) {
if (value >= (MR_Word) heap_zone->MR_zone_min &&
value < (MR_Word) heap_zone->MR_zone_hardmax)
{
difference = (MR_Word *) value - (MR_Word *) heap_zone->MR_zone_min;
fprintf(stderr, "\thp[%d]\t(%lx)", difference, (long) value);
} else {
fprintf(stderr, "\t \t(%lx)", (long) value);
}
}
}
static void
MR_dump_short_value(MR_ShortLval locn, MR_MemoryZone *heap_zone,
MR_Word *stack_pointer, MR_Word *current_frame, MR_bool do_regs)
{
int locn_num;
MR_Word value;
int difference;
MR_bool have_value;
value = 0;
have_value = MR_FALSE;
locn_num = (int) locn >> MR_SHORT_LVAL_TAGBITS;
switch (MR_SHORT_LVAL_TYPE(locn)) {
case MR_SHORT_LVAL_TYPE_R:
if (do_regs) {
value = MR_virtual_reg_value(locn_num);
have_value = MR_TRUE;
fprintf(stderr, "r%d\t", locn_num);
} else {
fprintf(stderr, "r%d (invalid)\t", locn_num);
}
break;
case MR_SHORT_LVAL_TYPE_STACKVAR:
value = MR_based_stackvar(stack_pointer, locn_num);
have_value = MR_TRUE;
fprintf(stderr, "stackvar%d", locn_num);
break;
case MR_SHORT_LVAL_TYPE_FRAMEVAR:
value = MR_based_framevar(current_frame, locn_num);
have_value = MR_TRUE;
fprintf(stderr, "framevar%d", locn_num);
break;
case MR_SHORT_LVAL_TYPE_SPECIAL:
switch (locn_num) {
case MR_LONG_LVAL_TYPE_SUCCIP:
fprintf(stderr, "succip");
break;
case MR_LONG_LVAL_TYPE_MAXFR:
fprintf(stderr, "succip");
break;
case MR_LONG_LVAL_TYPE_CURFR:
fprintf(stderr, "curfr");
break;
case MR_LONG_LVAL_TYPE_HP:
fprintf(stderr, "hp");
break;
case MR_LONG_LVAL_TYPE_SP:
fprintf(stderr, "sp");
break;
default:
fprintf(stderr, "SPECIAL DEFAULT");
break;
}
break;
default:
fprintf(stderr, "SHORT DEFAULT");
break;
}
if (have_value) {
if (value >= (MR_Word) heap_zone->MR_zone_min &&
value < (MR_Word) heap_zone->MR_zone_hardmax)
{
difference = (MR_Word *) value - (MR_Word *) heap_zone->MR_zone_min;
fprintf(stderr, "\thp[%d]\t(%lx)", difference, (long) value);
} else {
fprintf(stderr, "\t \t(%lx)", (long) value);
}
}
}
#endif /* !MR_HIGHLEVEL_CODE */
#endif /* MR_NATIVE_GC */