Files
mercury/trace/mercury_trace_cmd_backward.c
Zoltan Somogyi c733b0359b Give the Mercury debugger the ability to detect cliques of mutually recursive
Estimated hours taken: 30
Branches: main

Give the Mercury debugger the ability to detect cliques of mutually recursive
predicates on the stack. Exploit this ability to enhance the debugger's
level, retry, finish and stack commands.

runtime/mercury_stack_trace.[ch]:
	Add a function, MR_find_clique_entry, that detects the clique
	that contains the top stack frame. This is used to implement the new
	arguments "clentry" and "clparent" (short for clique entry and parent)
	options of the level, retry and finish commands. "clique" is a synonym
	for "clentry" in these commands.

	Add a function, MR_dump_stack_layout_clique, that implements the
	new capabilities of the stack command. It can detect more than one
	clique, anywhere on the stack.

	To make this possible, modify the existing functions for printing
	the lines of stack traces. These used to keep some information around
	between calls in global variables. Now that information is stored in
	two structures that the caller passes them. One contains the parameters
	that govern what is to be printed, the other contains information about
	what has been buffered up to be printed, but has not been flushed yet.
	(The old code was confused in its handling of parameters. Some parts
	of it looked up the global variables storing them, while other parts
	were given the parameter values by their callers, values that could
	have been -but weren't- inconsistent.)

	Change the buffer flushing code to be idempotent, since in the new
	code, sometimes it is hard to avoid flushing the buffer more than once,
	and we want only the first to print its contents.

	Make some type names conform to our standard style.

runtime/mercury_stack_layout.h:
	Add a new flag in MR_ProcLayouts: a flag that indicates that the
	procedure has one or more higher order arguments. The new code in
	mercury_stack_trace.c handles procedures with this flag specially:
	it does not consider two non-consecutive occurrences of such procedures
	on the stack to be necessarily part of the same clique. This is to
	avoid having two calls to e.g. list.map in different part of the
	program pulling all the procedures between those parts on the stack
	into a single clique. (The deep profiler has a very similar tweak.)

	Add a pointer to the corresponding part of the compiler.

compiler/hlds_pred.m:
	Add a predicate to test whether a predicate has any higher order args.

compiler/stack_layout.m:
	When computing the flag in proc layouts, call the new procedure in
	hlds_pred.m to help figure it out.

trace/mercury_trace_cmd_backward.c:
	Implement the new options of the "retry" command.

trace/mercury_trace_cmd_forward.c:
	Implement the new options of the "finish" command.

trace/mercury_trace_cmd_browsing.c:
	Implement the "new options of the "level" command.

	Implement the new functionality of the "stack" command.

trace/mercury_trace_util.[ch]:
	Add some code common to the implementations of the level, retry and
	finish commands.

trace/mercury_trace_external.c:
	Conform to the changes to the runtime.

doc/user_guide.texi:
	Document the debugger's new capabilities.

NEWS:
	Announce the debugger's new capabilities.

tests/debugger/mutrec.{m,inp,exp}:
	A new test case to test the handling of the stack command
	in the presence of cliques.

tests/debugger/mutrec_higher_order.{m,inp,exp}:
	A new test case to test the handling of the stack command
	in the presence of cliques and higher order predicates.

tests/debugger/Mmakefile:
	Enable both new test cases.
2012-06-05 18:19:33 +00:00

202 lines
6.3 KiB
C

/*
** vim: ts=4 sw=4 expandtab
*/
/*
** Copyright (C) 1998-2008,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.
*/
/*
** This module implements the mdb commands in the "backward" category.
**
** The structure of these files is:
**
** - all the #includes
** - local macros and declarations of local static functions
** - one function for each command in the category
** - any auxiliary functions
** - any command argument strings
** - option processing functions.
*/
#include "mercury_std.h"
#include "mercury_getopt.h"
#include "mercury_trace.h"
#include "mercury_trace_internal.h"
#include "mercury_trace_cmds.h"
#include "mercury_trace_cmd_backward.h"
#include "mercury_trace_cmd_parameter.h"
#include "mercury_trace_command_queue.h"
#include "mercury_trace_util.h"
/****************************************************************************/
/*
** The message to print for retries through un-io-tabled areas, when
** the MR_RETRY_IO_INTERACTIVE option is given.
*/
#define MR_UNTABLED_IO_RETRY_MESSAGE \
"Retry across I/O operations is not always safe.\n" \
"Are you sure you want to do it? "
static MR_bool MR_trace_options_retry(MR_RetryAcrossIo *across_io,
MR_bool *assume_all_io_is_tabled,
char ***words, int *word_count);
/****************************************************************************/
MR_Next
MR_trace_cmd_retry(char **words, int word_count, MR_TraceCmdInfo *cmd,
MR_EventInfo *event_info, MR_Code **jumpaddr)
{
MR_Level n;
MR_Level ancestor_level;
MR_RetryAcrossIo across_io;
const char *problem;
MR_RetryResult result;
MR_bool assume_all_io_is_tabled;
MR_bool unsafe_retry;
ancestor_level = 0;
across_io = MR_RETRY_IO_INTERACTIVE;
assume_all_io_is_tabled = MR_FALSE;
if (! MR_trace_options_retry(&across_io, &assume_all_io_is_tabled,
&words, &word_count))
{
; /* the usage message has already been printed */
} else if (word_count == 2 &&
( MR_streq(words[1], "clique") || MR_streq(words[1], "clentry")))
{
if (MR_find_clique_entry_mdb(event_info, MR_CLIQUE_ENTRY_FRAME,
&ancestor_level))
{
/* the error message has already been printed */
return KEEP_INTERACTING;
}
} else if (word_count == 2 && MR_streq(words[1], "clparent")) {
if (MR_find_clique_entry_mdb(event_info, MR_CLIQUE_ENTRY_PARENT_FRAME,
&ancestor_level))
{
/* the error message has already been printed */
return KEEP_INTERACTING;
}
} else if (word_count == 2 && MR_trace_is_natural_number(words[1], &n)) {
ancestor_level = n;
} else if (word_count == 1) {
ancestor_level = 0;
} else {
MR_trace_usage_cur_cmd();
return KEEP_INTERACTING;
}
if (ancestor_level == 0 && MR_port_is_entry(event_info->MR_trace_port)) {
MR_trace_do_noop();
return KEEP_INTERACTING;
}
result = MR_trace_retry(event_info, ancestor_level,
across_io, assume_all_io_is_tabled, MR_UNTABLED_IO_RETRY_MESSAGE,
&unsafe_retry, &problem, MR_mdb_in, MR_mdb_out, jumpaddr);
switch (result) {
case MR_RETRY_OK_DIRECT:
cmd->MR_trace_cmd = MR_CMD_GOTO;
cmd->MR_trace_stop_event = MR_trace_event_number + 1;
cmd->MR_trace_strict = MR_FALSE;
cmd->MR_trace_print_level = MR_default_print_level;
return STOP_INTERACTING;
case MR_RETRY_OK_FINISH_FIRST:
cmd->MR_trace_cmd = MR_CMD_FINISH;
cmd->MR_trace_stop_depth = event_info->MR_call_depth - ancestor_level;
cmd->MR_trace_strict = MR_TRUE;
cmd->MR_trace_print_level = MR_PRINT_LEVEL_NONE;
/* Arrange to retry the call once it is finished. */
/* XXX we should use the same options as the original retry */
MR_insert_command_line_at_head("retry -o");
return STOP_INTERACTING;
case MR_RETRY_OK_FAIL_FIRST:
cmd->MR_trace_cmd = MR_CMD_FAIL;
cmd->MR_trace_stop_depth = event_info->MR_call_depth - ancestor_level;
cmd->MR_trace_strict = MR_TRUE;
cmd->MR_trace_print_level = MR_PRINT_LEVEL_NONE;
/* Arrange to retry the call once it is finished. */
/* XXX we should use the same options as the original retry */
MR_insert_command_line_at_head("retry -o");
return STOP_INTERACTING;
case MR_RETRY_ERROR:
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "%s\n", problem);
return KEEP_INTERACTING;
}
MR_fatal_error("unrecognized retry result");
}
/****************************************************************************/
/*
** "retry --assume-all-io-is-tabled" is deliberately not documented as
** it is for developers only.
*/
const char *const MR_trace_retry_cmd_args[] =
{ "--force", "--interactive", "--only-if-safe", NULL };
/****************************************************************************/
static struct MR_option MR_trace_retry_opts[] =
{
{ "assume-all-io-is-tabled", MR_no_argument, NULL, 'a' },
{ "force", MR_no_argument, NULL, 'f' },
{ "interactive", MR_no_argument, NULL, 'i' },
{ "only-if-safe", MR_no_argument, NULL, 'o' },
{ NULL, MR_no_argument, NULL, 0 }
};
static MR_bool
MR_trace_options_retry(MR_RetryAcrossIo *across_io,
MR_bool *assume_all_io_is_tabled, char ***words, int *word_count)
{
int c;
MR_optind = 0;
while ((c = MR_getopt_long(*word_count, *words, "afio",
MR_trace_retry_opts, NULL)) != EOF)
{
switch (c) {
case 'a':
*assume_all_io_is_tabled = MR_TRUE;
break;
case 'f':
*across_io = MR_RETRY_IO_FORCE;
break;
case 'i':
*across_io = MR_RETRY_IO_INTERACTIVE;
break;
case 'o':
*across_io = MR_RETRY_IO_ONLY_IF_SAFE;
break;
default:
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
}
*words = *words + MR_optind - 1;
*word_count = *word_count - MR_optind + 1;
return MR_TRUE;
}