mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
browser/browse.m:
browser/browser_info.m:
browser/collect_lib.m:
browser/declarative_debugger.m:
browser/declarative_oracle.m:
browser/declarative_user.m:
browser/diff.m:
browser/help.m:
browser/interactive_query.m:
browser/parse.m:
browser/util.m:
Replace implicit streams with explicit streams.
Shorten lines longer than 79 chars.
In some places, simplify some code, often using constructs such as
string.format that either did not exist or were too expensive to use
when the original code was written.
In some places, change predicate names that were not meaningful
without module qualification by *including* the module qualification
in the name (e.g. init -> browser_info_init).
In some places, add XXXs.
In browser_info.m, make the output stream *part* of the debugger type,
because without this, having the debugger type belong to the stream
typeclass does NOT make sense. (The typeclass instance for debugger
used to always write to the current output stream, which this diff
is replacing with the use of explicitly specified streams.)
In browse.m, consistently put stream arguments before other arguments.
In browse.m, when exporting Mercury predicates to C, export them
under names with the standard ML_BROWSE_ prefix, NOT under the name
of a *different* predicate with that prefix.
In diff.m, eliminate an unnecessary difference between what we print
when the difference between two terms is at the root, vs what we print
when the difference between two terms is lower down.
In interactive_query.m, when trying to write a program out to a file,
do NOT write the program to the current output stream if we cannot open
the file, since that would accomplish nothing useful.
Also in interactive_query.m, cleanup .dylib instead of .so on MacOS.
In util.m, delete some unused predicates.
In collect_lib.m, document why some code is not worth updating.
In declarative_oracle.m, rename predicates with previously-ambiguous
names.
browser/MDBFLAGS.in:
Specify --warn-implicit-stream-calls for all Mercury modules
in the browser directory from now.
trace/mercury_trace_browse.c:
trace/mercury_trace_cmd_browsing.c:
ssdb/ssdb.m:
Conform to the changes in browser/*.m.
tests/debugger/queens.{exp,exp2}:
Expect the extra output from browser/diff.m.
1643 lines
52 KiB
C
1643 lines
52 KiB
C
// vim: ts=4 sw=4 expandtab ft=c
|
|
|
|
// Copyright (C) 1998-2008,2010,2012 The University of Melbourne.
|
|
// Copyright (C) 2017-2018, 2020 The Mercury team.
|
|
// This file is distributed under the terms specified in COPYING.LIB.
|
|
|
|
// This module implements the mdb commands in the "browsing" 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_string.h"
|
|
|
|
#include "mercury_trace_internal.h"
|
|
#include "mercury_trace_cmds.h"
|
|
#include "mercury_trace_cmd_browsing.h"
|
|
#include "mercury_trace_cmd_parameter.h"
|
|
#include "mercury_trace_vars.h"
|
|
#include "mercury_trace_hold_vars.h"
|
|
#include "mercury_trace_browse.h"
|
|
#include "mercury_trace_util.h"
|
|
|
|
#include "mdb.listing.mh"
|
|
#include "mdb.diff.mh"
|
|
#include "mdb.declarative_execution.mh"
|
|
#include "mdbcomp.program_representation.mh"
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void MR_trace_set_level_and_report(int ancestor_level,
|
|
MR_bool detailed, MR_bool print_optionals);
|
|
static const char *MR_trace_browse_exception(MR_EventInfo *event_info,
|
|
MR_Browser browser, MR_BrowseCallerType caller,
|
|
MR_BrowseFormat format);
|
|
static const char *MR_trace_browse_proc_body(MR_EventInfo *event_info,
|
|
MR_Browser browser, MR_BrowseCallerType caller,
|
|
MR_BrowseFormat format);
|
|
|
|
// Functions to invoke the user's web browser on terms or goals.
|
|
static void MR_trace_browse_web(MR_Word type_info, MR_Word value,
|
|
MR_BrowseCallerType caller, MR_BrowseFormat format);
|
|
static void MR_trace_browse_goal_web(MR_ConstString name,
|
|
MR_Word arg_list, MR_Word is_func,
|
|
MR_BrowseCallerType caller, MR_BrowseFormat format);
|
|
|
|
static void MR_trace_cmd_stack_2(MR_EventInfo *event_info,
|
|
MR_bool detailed, MR_FrameLimit frame_limit,
|
|
int line_limit);
|
|
|
|
static const char *MR_trace_new_source_window(const char *window_cmd,
|
|
const char *server_cmd, const char *server_name,
|
|
int timeout, MR_bool force, MR_bool verbose,
|
|
MR_bool split);
|
|
|
|
static MR_bool MR_trace_options_detailed(MR_bool *detailed,
|
|
char ***words, int *word_count);
|
|
static MR_bool MR_trace_options_stack_trace(MR_bool *print_all,
|
|
MR_bool *detailed, MR_SpecLineLimit *line_limit,
|
|
MR_SpecLineLimit *clique_line_limit,
|
|
MR_FrameLimit *frame_limit,
|
|
char ***words, int *word_count);
|
|
static MR_bool MR_trace_options_print(MR_BrowseFormat *format,
|
|
MR_Unsigned *max_printed_actions,
|
|
MR_bool *set_max_printed_actions,
|
|
char ***words, int *word_count);
|
|
static MR_bool MR_trace_options_browse(MR_BrowseFormat *format,
|
|
MR_bool *web,
|
|
char ***words, int *word_count);
|
|
static MR_bool MR_trace_options_view(const char **window_cmd,
|
|
const char **server_cmd, const char **server_name,
|
|
MR_Unsigned *timeout, MR_bool *force, MR_bool *verbose,
|
|
MR_bool *split, MR_bool *close_window,
|
|
char ***words, int *word_count);
|
|
static MR_bool MR_trace_options_diff(MR_Unsigned *start,
|
|
MR_Unsigned *max, char ***words, int *word_count);
|
|
static MR_bool MR_trace_options_dump(MR_bool *quiet, MR_bool *xml,
|
|
char ***words, int *word_count);
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
MR_Next
|
|
MR_trace_cmd_level(char **words, int word_count, MR_TraceCmdInfo *cmd,
|
|
MR_EventInfo *event_info, MR_Code **jumpaddr)
|
|
{
|
|
MR_Unsigned n;
|
|
MR_bool detailed;
|
|
MR_Level selected_level;
|
|
|
|
detailed = MR_FALSE;
|
|
if (! MR_trace_options_detailed(&detailed, &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,
|
|
&selected_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,
|
|
&selected_level))
|
|
{
|
|
// The error message has already been printed.
|
|
return KEEP_INTERACTING;
|
|
}
|
|
} else if (word_count == 2 && MR_trace_is_natural_number(words[1], &n)) {
|
|
selected_level = n;
|
|
} else if (word_count == 1) {
|
|
selected_level = 0;
|
|
} else {
|
|
MR_trace_usage_cur_cmd();
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
MR_trace_set_level_and_report(selected_level, detailed,
|
|
MR_print_optionals);
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
MR_Next
|
|
MR_trace_cmd_up(char **words, int word_count, MR_TraceCmdInfo *cmd,
|
|
MR_EventInfo *event_info, MR_Code **jumpaddr)
|
|
{
|
|
MR_Unsigned n;
|
|
MR_bool detailed;
|
|
|
|
detailed = MR_FALSE;
|
|
if (! MR_trace_options_detailed(&detailed, &words, &word_count)) {
|
|
// The usage message has already been printed.
|
|
;
|
|
} else if (word_count == 2 && MR_trace_is_natural_number(words[1], &n)) {
|
|
MR_trace_set_level_and_report(MR_trace_current_level() + n, detailed,
|
|
MR_print_optionals);
|
|
} else if (word_count == 1) {
|
|
MR_trace_set_level_and_report(MR_trace_current_level() + 1, detailed,
|
|
MR_print_optionals);
|
|
} else {
|
|
MR_trace_usage_cur_cmd();
|
|
}
|
|
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
MR_Next
|
|
MR_trace_cmd_down(char **words, int word_count, MR_TraceCmdInfo *cmd,
|
|
MR_EventInfo *event_info, MR_Code **jumpaddr)
|
|
{
|
|
MR_Unsigned n;
|
|
MR_bool detailed;
|
|
|
|
detailed = MR_FALSE;
|
|
if (! MR_trace_options_detailed(&detailed, &words, &word_count)) {
|
|
// The usage message has already been printed.
|
|
;
|
|
} else if (word_count == 2 && MR_trace_is_natural_number(words[1], &n)) {
|
|
MR_trace_set_level_and_report(MR_trace_current_level() - n, detailed,
|
|
MR_print_optionals);
|
|
} else if (word_count == 1) {
|
|
MR_trace_set_level_and_report(MR_trace_current_level() - 1, detailed,
|
|
MR_print_optionals);
|
|
} else {
|
|
MR_trace_usage_cur_cmd();
|
|
}
|
|
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
MR_Next
|
|
MR_trace_cmd_vars(char **words, int word_count, MR_TraceCmdInfo *cmd,
|
|
MR_EventInfo *event_info, MR_Code **jumpaddr)
|
|
{
|
|
if (word_count == 1) {
|
|
const char *problem;
|
|
|
|
problem = MR_trace_list_vars(MR_mdb_out);
|
|
if (problem != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
|
|
}
|
|
} else {
|
|
MR_trace_usage_cur_cmd();
|
|
}
|
|
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
MR_Next
|
|
MR_trace_cmd_held_vars(char **words, int word_count, MR_TraceCmdInfo *cmd,
|
|
MR_EventInfo *event_info, MR_Code **jumpaddr)
|
|
{
|
|
if (word_count == 1) {
|
|
MR_trace_list_held_vars(MR_mdb_out);
|
|
} else {
|
|
MR_trace_usage_cur_cmd();
|
|
}
|
|
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
#define MR_NEXT_NUM_IO_ACTIONS_TO_PRINT 20
|
|
#define MR_ALL_NUM_IO_ACTIONS_TO_PRINT 500
|
|
|
|
MR_Next
|
|
MR_trace_cmd_print(char **words, int word_count, MR_TraceCmdInfo *cmd,
|
|
MR_EventInfo *event_info, MR_Code **jumpaddr)
|
|
{
|
|
MR_BrowseFormat format;
|
|
MR_Unsigned max_printed_actions;
|
|
MR_bool set_max_printed_actions;
|
|
const char *problem;
|
|
MR_Unsigned action;
|
|
MR_Unsigned lo_action;
|
|
MR_Unsigned hi_action;
|
|
static MR_bool have_next_io_action = MR_FALSE;
|
|
static MR_Unsigned next_io_action = 0;
|
|
|
|
if (! MR_trace_options_print(&format,
|
|
&max_printed_actions, &set_max_printed_actions, &words, &word_count))
|
|
{
|
|
// The usage message has already been printed.
|
|
;
|
|
} else if (word_count == 1) {
|
|
problem = MR_trace_browse_one_goal(MR_mdb_out,
|
|
MR_trace_browse_goal_internal, MR_BROWSE_CALLER_PRINT, format);
|
|
|
|
if (problem != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
|
|
}
|
|
} else if (word_count == 2) {
|
|
if (MR_streq(words[1], "*")) {
|
|
problem = MR_trace_browse_all(MR_mdb_out,
|
|
MR_trace_browse_internal, format);
|
|
} else if (MR_streq(words[1], "goal")) {
|
|
problem = MR_trace_browse_one_goal(MR_mdb_out,
|
|
MR_trace_browse_goal_internal, MR_BROWSE_CALLER_PRINT, format);
|
|
} else if (MR_streq(words[1], "exception")) {
|
|
problem = MR_trace_browse_exception(event_info,
|
|
MR_trace_browse_internal, MR_BROWSE_CALLER_PRINT, format);
|
|
} else if (MR_streq(words[1], "proc_body")) {
|
|
problem = MR_trace_browse_proc_body(event_info,
|
|
MR_trace_browse_internal, MR_BROWSE_CALLER_PRINT, format);
|
|
} else if ((MR_streq(words[1], "io") || MR_streq(words[1], "action")))
|
|
{
|
|
MR_Unsigned num_printed_actions;
|
|
|
|
if (!set_max_printed_actions) {
|
|
max_printed_actions = MR_NEXT_NUM_IO_ACTIONS_TO_PRINT;
|
|
}
|
|
|
|
if (MR_io_tabling_phase == MR_IO_TABLING_BEFORE) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err,
|
|
"mdb: I/O tabling has not yet started.\n");
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
if (MR_io_tabling_counter_hwm == 0) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err,
|
|
"mdb: There are no tabled I/O actions yet.\n");
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
if (have_next_io_action && (!
|
|
(MR_io_tabling_start <= next_io_action
|
|
&& next_io_action < MR_io_tabling_counter_hwm)))
|
|
{
|
|
have_next_io_action = MR_FALSE;
|
|
}
|
|
|
|
if (have_next_io_action) {
|
|
lo_action = next_io_action;
|
|
} else {
|
|
lo_action = MR_io_tabling_start;
|
|
}
|
|
|
|
hi_action = lo_action + max_printed_actions;
|
|
if (hi_action >= MR_io_tabling_counter_hwm) {
|
|
hi_action = MR_io_tabling_counter_hwm - 1;
|
|
}
|
|
|
|
num_printed_actions = hi_action - lo_action + 1;
|
|
if (num_printed_actions <= 0) {
|
|
fprintf(MR_mdb_out, "There are no I/O actions to print\n");
|
|
have_next_io_action = MR_FALSE;
|
|
} else {
|
|
for (action = lo_action; action <= hi_action; action++) {
|
|
fprintf(MR_mdb_out,
|
|
"action %" MR_INTEGER_LENGTH_MODIFIER "u: ", action);
|
|
problem = MR_trace_browse_action(MR_mdb_out, action,
|
|
MR_trace_browse_goal_internal,
|
|
MR_BROWSE_CALLER_PRINT, format);
|
|
|
|
if (problem != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
|
|
return KEEP_INTERACTING;
|
|
}
|
|
}
|
|
|
|
if (hi_action == MR_io_tabling_counter_hwm - 1) {
|
|
fprintf(MR_mdb_out,
|
|
"there are no more actions (yet)\n");
|
|
} else {
|
|
fprintf(MR_mdb_out,
|
|
"there are more actions, up to action "
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "u\n",
|
|
MR_io_tabling_counter_hwm - 1);
|
|
}
|
|
|
|
next_io_action = hi_action + 1;
|
|
have_next_io_action = MR_TRUE;
|
|
}
|
|
} else {
|
|
problem = MR_trace_parse_browse_one(MR_mdb_out, MR_TRUE, words[1],
|
|
MR_trace_browse_internal, MR_BROWSE_CALLER_PRINT, format,
|
|
MR_FALSE);
|
|
}
|
|
|
|
if (problem != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
|
|
}
|
|
} else if (word_count == 3 &&
|
|
(MR_streq(words[1], "io") || MR_streq(words[1], "action")))
|
|
{
|
|
if (MR_io_tabling_phase == MR_IO_TABLING_BEFORE) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err,
|
|
"mdb: I/O tabling has not yet started.\n");
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
if (MR_io_tabling_counter_hwm == 0) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err,
|
|
"mdb: There are no tabled I/O actions yet.\n");
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
if (MR_streq(words[2], "limits")) {
|
|
fprintf(MR_mdb_out,
|
|
"I/O tabling has recorded actions "
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "u to "
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "u.\n",
|
|
MR_io_tabling_start, MR_io_tabling_counter_hwm - 1);
|
|
fflush(MR_mdb_out);
|
|
} else if (MR_trace_is_natural_number(words[2], &action)) {
|
|
if (! (MR_io_tabling_start <= action
|
|
&& action < MR_io_tabling_counter_hwm))
|
|
{
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err,
|
|
"I/O tabling has only recorded actions "
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "u to "
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "u.\n",
|
|
MR_io_tabling_start, MR_io_tabling_counter_hwm - 1);
|
|
have_next_io_action = MR_FALSE;
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
problem = MR_trace_browse_action(MR_mdb_out, action,
|
|
MR_trace_browse_goal_internal,
|
|
MR_BROWSE_CALLER_PRINT, format);
|
|
|
|
if (problem != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
|
|
have_next_io_action = MR_FALSE;
|
|
}
|
|
|
|
next_io_action = action + 1;
|
|
have_next_io_action = MR_TRUE;
|
|
} else if (MR_trace_is_natural_number_pair(words[2],
|
|
&lo_action, &hi_action))
|
|
{
|
|
if (lo_action >= hi_action) {
|
|
// Swap lo_action and hi_action.
|
|
MR_Unsigned tmp;
|
|
|
|
tmp = lo_action;
|
|
lo_action = hi_action;
|
|
hi_action = tmp;
|
|
}
|
|
|
|
if (! (MR_io_tabling_start <= lo_action
|
|
&& hi_action < MR_io_tabling_counter_hwm))
|
|
{
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err,
|
|
"I/O tabling has only recorded actions "
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "u to "
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "u.\n",
|
|
MR_io_tabling_start, MR_io_tabling_counter_hwm - 1);
|
|
have_next_io_action = MR_FALSE;
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
for (action = lo_action; action <= hi_action; action++) {
|
|
fprintf(MR_mdb_out,
|
|
"action %" MR_INTEGER_LENGTH_MODIFIER "u: ", action);
|
|
problem = MR_trace_browse_action(MR_mdb_out, action,
|
|
MR_trace_browse_goal_internal,
|
|
MR_BROWSE_CALLER_PRINT, format);
|
|
|
|
if (problem != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
|
|
return KEEP_INTERACTING;
|
|
}
|
|
}
|
|
|
|
next_io_action = hi_action + 1;
|
|
have_next_io_action = MR_TRUE;
|
|
} else if (MR_streq(words[2], "*")) {
|
|
if (!set_max_printed_actions) {
|
|
max_printed_actions = MR_ALL_NUM_IO_ACTIONS_TO_PRINT;
|
|
}
|
|
|
|
lo_action = MR_io_tabling_start;
|
|
hi_action = MR_io_tabling_counter_hwm - 1;
|
|
|
|
if (lo_action + max_printed_actions < hi_action) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err,
|
|
"I/O tabling has recorded "
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "d actions, "
|
|
"numbered %" MR_INTEGER_LENGTH_MODIFIER "u to "
|
|
"%" MR_INTEGER_LENGTH_MODIFIER "u.\n",
|
|
MR_io_tabling_counter_hwm + MR_io_tabling_start - 1,
|
|
MR_io_tabling_start, MR_io_tabling_counter_hwm - 1);
|
|
if (set_max_printed_actions) {
|
|
fprintf(MR_mdb_err,
|
|
"Following your request via the -m option, "
|
|
"only the first %" MR_INTEGER_LENGTH_MODIFIER "u "
|
|
"actions will be printed.\n",
|
|
max_printed_actions);
|
|
} else {
|
|
fprintf(MR_mdb_err,
|
|
"Without your explicit request via the -m option, "
|
|
"only the default maximum "
|
|
"of %" MR_INTEGER_LENGTH_MODIFIER "u "
|
|
"actions will be printed.\n",
|
|
max_printed_actions);
|
|
}
|
|
|
|
hi_action = lo_action + max_printed_actions - 1;
|
|
}
|
|
|
|
for (action = lo_action; action <= hi_action; action++) {
|
|
fprintf(MR_mdb_out,
|
|
"action %" MR_INTEGER_LENGTH_MODIFIER "u: ", action);
|
|
problem = MR_trace_browse_action(MR_mdb_out, action,
|
|
MR_trace_browse_goal_internal,
|
|
MR_BROWSE_CALLER_PRINT, format);
|
|
|
|
if (problem != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
|
|
return KEEP_INTERACTING;
|
|
}
|
|
}
|
|
|
|
next_io_action = hi_action + 1;
|
|
have_next_io_action = MR_TRUE;
|
|
} else {
|
|
MR_trace_usage_cur_cmd();
|
|
}
|
|
} else {
|
|
MR_trace_usage_cur_cmd();
|
|
}
|
|
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
MR_Next
|
|
MR_trace_cmd_browse(char **words, int word_count, MR_TraceCmdInfo *cmd,
|
|
MR_EventInfo *event_info, MR_Code **jumpaddr)
|
|
{
|
|
MR_BrowseFormat format;
|
|
MR_bool web;
|
|
MR_IoActionNum action;
|
|
MR_GoalBrowser goal_browser;
|
|
MR_Browser browser;
|
|
const char *problem;
|
|
|
|
if (! MR_trace_options_browse(&format, &web, &words, &word_count)) {
|
|
// The usage message has already been printed.
|
|
;
|
|
} else {
|
|
if (web) {
|
|
goal_browser = MR_trace_browse_goal_web;
|
|
browser = MR_trace_browse_web;
|
|
} else {
|
|
goal_browser = MR_trace_browse_goal_internal;
|
|
browser = MR_trace_browse_internal;
|
|
}
|
|
|
|
if (word_count == 1) {
|
|
problem = MR_trace_browse_one_goal(MR_mdb_out, goal_browser,
|
|
MR_BROWSE_CALLER_BROWSE, format);
|
|
|
|
if (problem != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
|
|
}
|
|
} else if (word_count == 2) {
|
|
if (MR_streq(words[1], "goal")) {
|
|
problem = MR_trace_browse_one_goal(MR_mdb_out, goal_browser,
|
|
MR_BROWSE_CALLER_BROWSE, format);
|
|
} else if (MR_streq(words[1], "exception")) {
|
|
problem = MR_trace_browse_exception(event_info, browser,
|
|
MR_BROWSE_CALLER_BROWSE, format);
|
|
} else if (MR_streq(words[1], "proc_body")) {
|
|
problem = MR_trace_browse_proc_body(event_info, browser,
|
|
MR_BROWSE_CALLER_BROWSE, format);
|
|
} else {
|
|
problem = MR_trace_parse_browse_one(MR_mdb_out, MR_FALSE,
|
|
words[1], browser, MR_BROWSE_CALLER_BROWSE, format,
|
|
MR_TRUE);
|
|
}
|
|
|
|
if (problem != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
|
|
}
|
|
} else if (word_count == 3 &&
|
|
(MR_streq(words[1], "io") || MR_streq(words[1], "action"))
|
|
&& MR_trace_is_natural_number(words[2], &action))
|
|
{
|
|
problem = MR_trace_browse_action(MR_mdb_out, action, goal_browser,
|
|
MR_BROWSE_CALLER_BROWSE, format);
|
|
|
|
if (problem != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
|
|
}
|
|
} else {
|
|
MR_trace_usage_cur_cmd();
|
|
}
|
|
}
|
|
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
MR_Next
|
|
MR_trace_cmd_stack(char **words, int word_count, MR_TraceCmdInfo *cmd,
|
|
MR_EventInfo *event_info, MR_Code **jumpaddr)
|
|
{
|
|
MR_bool print_all;
|
|
MR_bool detailed;
|
|
MR_FrameLimit frame_limit;
|
|
MR_SpecLineLimit clique_line_limit;
|
|
MR_SpecLineLimit line_limit;
|
|
MR_SpecLineLimit spec_line_limit;
|
|
const MR_LabelLayout *layout;
|
|
MR_Word *saved_regs;
|
|
const char *msg;
|
|
|
|
detailed = MR_FALSE;
|
|
print_all = MR_FALSE;
|
|
frame_limit = 0;
|
|
clique_line_limit = 10;
|
|
line_limit = 100;
|
|
if (! MR_trace_options_stack_trace(&print_all, &detailed,
|
|
&line_limit, &clique_line_limit, &frame_limit, &words, &word_count))
|
|
{
|
|
// The usage message has already been printed.
|
|
return KEEP_INTERACTING;
|
|
} else if (word_count == 1) {
|
|
line_limit = MR_stack_default_line_limit;
|
|
} else if (word_count == 2 &&
|
|
MR_trace_is_natural_number(words[1], &spec_line_limit))
|
|
{
|
|
line_limit = spec_line_limit;
|
|
} else {
|
|
MR_trace_usage_cur_cmd();
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
layout = event_info->MR_event_sll;
|
|
saved_regs = event_info->MR_saved_regs;
|
|
|
|
#ifdef MR_DEBUG_STACK_DUMP_CLIQUE
|
|
MR_trace_init_modules();
|
|
fprintf(MR_mdb_out, "OLD STACK DUMP:\n");
|
|
msg = MR_dump_stack_from_layout(MR_mdb_out, layout,
|
|
MR_saved_sp(saved_regs), MR_saved_curfr(saved_regs),
|
|
detailed, MR_context_position != MR_CONTEXT_NOWHERE,
|
|
frame_limit, line_limit,
|
|
&MR_dump_stack_record_print);
|
|
|
|
if (msg != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "%s.\n", msg);
|
|
}
|
|
|
|
fprintf(MR_mdb_out, "\nNEW STACK DUMP:\n");
|
|
#endif
|
|
msg = MR_dump_stack_from_layout_clique(MR_mdb_out, layout,
|
|
MR_saved_sp(saved_regs), MR_saved_curfr(saved_regs),
|
|
detailed, MR_context_position != MR_CONTEXT_NOWHERE,
|
|
!print_all, clique_line_limit, frame_limit, line_limit,
|
|
&MR_dump_stack_record_print);
|
|
|
|
if (msg != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "%s.\n", msg);
|
|
}
|
|
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
MR_Next
|
|
MR_trace_cmd_current(char **words, int word_count, MR_TraceCmdInfo *cmd,
|
|
MR_EventInfo *event_info, MR_Code **jumpaddr)
|
|
{
|
|
if (word_count == 1) {
|
|
MR_trace_event_print_internal_report(event_info);
|
|
} else {
|
|
MR_trace_usage_cur_cmd();
|
|
}
|
|
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
MR_Next
|
|
MR_trace_cmd_view(char **words, int word_count, MR_TraceCmdInfo *cmd,
|
|
MR_EventInfo *event_info, MR_Code **jumpaddr)
|
|
{
|
|
const char *window_cmd = NULL;
|
|
const char *server_cmd = NULL;
|
|
const char *server_name = NULL;
|
|
MR_Unsigned timeout = 8; // In seconds.
|
|
MR_bool force = MR_FALSE;
|
|
MR_bool verbose = MR_FALSE;
|
|
MR_bool split = MR_FALSE;
|
|
MR_bool close_window = MR_FALSE;
|
|
const char *msg;
|
|
|
|
if (! MR_trace_options_view(&window_cmd, &server_cmd, &server_name,
|
|
&timeout, &force, &verbose, &split, &close_window,
|
|
&words, &word_count))
|
|
{
|
|
// The usage message has already been printed.
|
|
;
|
|
} else if (word_count != 1) {
|
|
MR_trace_usage_cur_cmd();
|
|
} else if (close_window) {
|
|
MR_trace_maybe_close_source_window(verbose);
|
|
} else {
|
|
msg = MR_trace_new_source_window(window_cmd, server_cmd, server_name,
|
|
timeout, force, verbose, split);
|
|
if (msg != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s.\n", msg);
|
|
}
|
|
|
|
MR_trace_maybe_sync_source_window(event_info, verbose);
|
|
}
|
|
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
MR_Next
|
|
MR_trace_cmd_hold(char **words, int word_count, MR_TraceCmdInfo *cmd,
|
|
MR_EventInfo *event_info, MR_Code **jumpaddr)
|
|
{
|
|
char *event_var_name;
|
|
char *held_var_name;
|
|
MR_TypeInfo type_info;
|
|
MR_Word value;
|
|
const char *problem;
|
|
MR_bool bad_subterm;
|
|
|
|
if (word_count == 2) {
|
|
event_var_name = words[1];
|
|
held_var_name = words[1];
|
|
} else if (word_count == 3) {
|
|
event_var_name = words[1];
|
|
held_var_name = words[2];
|
|
} else {
|
|
MR_trace_usage_cur_cmd();
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
if (strpbrk(held_var_name, "^/") != NULL) {
|
|
// Don't allow path separators in variable names.
|
|
MR_trace_usage_cur_cmd();
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
if (held_var_name[0] == '$') {
|
|
// Ignore any unneeded initial $ signs.
|
|
held_var_name = &held_var_name[1];
|
|
}
|
|
|
|
problem = MR_trace_parse_lookup_var_path(event_var_name, &type_info,
|
|
&value, &bad_subterm);
|
|
if (problem != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s%s.\n",
|
|
(bad_subterm? "there is no path " : ""), problem);
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
if (! MR_add_hold_var(held_var_name, type_info, value)) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: there is already a held variable $%s\n",
|
|
held_var_name);
|
|
}
|
|
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
MR_Next
|
|
MR_trace_cmd_diff(char **words, int word_count, MR_TraceCmdInfo *cmd,
|
|
MR_EventInfo *event_info, MR_Code **jumpaddr)
|
|
{
|
|
MR_Unsigned start;
|
|
MR_Unsigned max;
|
|
char *name1;
|
|
char *name2;
|
|
MR_TypeInfo type_info1;
|
|
MR_TypeInfo type_info2;
|
|
MR_Word value1;
|
|
MR_Word value2;
|
|
MR_Word univ1;
|
|
MR_Word univ2;
|
|
const char *problem1;
|
|
const char *problem2;
|
|
MR_bool bad_subterm1;
|
|
MR_bool bad_subterm2;
|
|
MercuryFile mdb_out;
|
|
|
|
start = 0;
|
|
max = 20;
|
|
if (! MR_trace_options_diff(&start, &max, &words, &word_count)) {
|
|
// The usage message has already been printed.
|
|
return KEEP_INTERACTING;
|
|
} else if (word_count != 3) {
|
|
MR_trace_usage_cur_cmd();
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
name1 = words[1];
|
|
name2 = words[2];
|
|
problem1 = MR_trace_parse_lookup_var_path(name1, &type_info1, &value1,
|
|
&bad_subterm1);
|
|
problem2 = MR_trace_parse_lookup_var_path(name2, &type_info2, &value2,
|
|
&bad_subterm2);
|
|
if (problem1 != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s%s.\n",
|
|
(bad_subterm1? "arg1: there is no path " : ""), problem1);
|
|
return KEEP_INTERACTING;
|
|
}
|
|
if (problem2 != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s%s.\n",
|
|
(bad_subterm2? "arg2: there is no path " : ""), problem2);
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
MR_c_file_to_mercury_file(MR_mdb_out, &mdb_out);
|
|
MR_TRACE_CALL_MERCURY(
|
|
MR_new_univ_on_hp(univ1, type_info1, value1);
|
|
MR_new_univ_on_hp(univ2, type_info2, value2);
|
|
ML_report_diffs(MR_wrap_output_stream(&mdb_out), start, max,
|
|
univ1, univ2);
|
|
);
|
|
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
MR_Next
|
|
MR_trace_cmd_dump(char **words, int word_count, MR_TraceCmdInfo *cmd,
|
|
MR_EventInfo *event_info, MR_Code **jumpaddr)
|
|
{
|
|
MR_Word browser_term;
|
|
const char *problem = NULL;
|
|
MR_bool quiet = MR_FALSE;
|
|
MR_bool xml = MR_FALSE;
|
|
|
|
// Set this to NULL to avoid uninitialization warnings.
|
|
|
|
browser_term = (MR_Word) NULL;
|
|
|
|
if (! MR_trace_options_dump(&quiet, &xml, &words, &word_count)) {
|
|
// The usage message has already been printed.
|
|
;
|
|
} else if (word_count != 3) {
|
|
MR_trace_usage_cur_cmd();
|
|
} else {
|
|
if (MR_streq(words[1], "goal")) {
|
|
const char *name;
|
|
MR_Word arg_list;
|
|
MR_bool is_func;
|
|
|
|
problem = NULL;
|
|
MR_convert_goal_to_synthetic_term(&name, &arg_list, &is_func);
|
|
browser_term = MR_synthetic_to_browser_term(name, arg_list,
|
|
is_func);
|
|
} else if (MR_streq(words[1], "exception")) {
|
|
MR_Word exception;
|
|
|
|
exception = MR_trace_get_exception_value();
|
|
if (exception == (MR_Word) NULL) {
|
|
problem = "missing exception value";
|
|
} else {
|
|
browser_term = MR_univ_to_browser_term(exception);
|
|
}
|
|
} else if (MR_streq(words[1], "proc_body")) {
|
|
const MR_ProcLayout *entry;
|
|
MR_Word rep;
|
|
|
|
entry = event_info->MR_event_sll->MR_sll_entry;
|
|
|
|
if (entry->MR_sle_body_bytes == NULL) {
|
|
problem = "current procedure has no body bytecodes";
|
|
} else {
|
|
MR_TRACE_CALL_MERCURY(
|
|
MR_MDBCOMP_trace_read_proc_defn_rep(
|
|
entry->MR_sle_body_bytes,
|
|
event_info->MR_event_sll, &rep);
|
|
);
|
|
|
|
browser_term = MR_type_value_to_browser_term(
|
|
(MR_TypeInfo) ML_proc_defn_rep_type(), rep);
|
|
}
|
|
} else {
|
|
MR_VarSpec var_spec;
|
|
MR_TypeInfo type_info;
|
|
MR_Word value;
|
|
const char *name;
|
|
|
|
MR_convert_arg_to_var_spec(words[1], &var_spec);
|
|
problem = MR_lookup_unambiguous_var_spec(var_spec,
|
|
&type_info, &value, &name);
|
|
if (problem == NULL) {
|
|
browser_term = MR_type_value_to_browser_term(type_info, value);
|
|
}
|
|
}
|
|
|
|
if (problem != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
|
|
} else {
|
|
if (xml) {
|
|
MR_trace_save_term_xml(words[2], browser_term);
|
|
} else {
|
|
MR_trace_save_term(words[2], browser_term);
|
|
}
|
|
|
|
if (!quiet) {
|
|
fprintf(MR_mdb_out, "Dumped %s to %s\n", words[1], words[2]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
// list [num]
|
|
//
|
|
// List num lines of context around the line number of the context of the
|
|
// current point (i.e. level in the call stack). If num is not given,
|
|
// the number of context lines defaults to the value of the context_lines
|
|
// setting.
|
|
//
|
|
// TODO: add the following (use MR_parse_source_locn()):
|
|
// list filename:num[-num]
|
|
//
|
|
// List a range of lines from a given file. If only one number is given,
|
|
// the default number of lines of context is used.
|
|
|
|
MR_Next
|
|
MR_trace_cmd_list(char **words, int word_count,
|
|
MR_TraceCmdInfo *cmd, MR_EventInfo *event_info, MR_Code **jumpaddr)
|
|
{
|
|
const MR_ProcLayout *entry_ptr;
|
|
const char *filename;
|
|
int lineno;
|
|
int first_lineno;
|
|
int last_lineno;
|
|
MR_Word *base_sp_ptr;
|
|
MR_Word *base_curfr_ptr;
|
|
MR_Unsigned num = MR_num_context_lines;
|
|
MR_String aligned_filename;
|
|
|
|
if (word_count > 2) {
|
|
MR_trace_usage_cur_cmd();
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
if (word_count == 2 && !MR_trace_is_natural_number(words[1], &num)) {
|
|
MR_trace_usage_cur_cmd();
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
MR_trace_current_level_details(&entry_ptr, &filename, &lineno,
|
|
&base_sp_ptr, &base_curfr_ptr);
|
|
|
|
MR_TRACE_USE_HP(
|
|
MR_make_aligned_string(aligned_filename, (MR_String) filename);
|
|
);
|
|
|
|
first_lineno = lineno - num;
|
|
last_lineno = lineno + num;
|
|
if (first_lineno < 1) {
|
|
first_lineno = 1;
|
|
}
|
|
|
|
if (MR_listing_cmd != NULL && strlen(MR_listing_cmd) > 0) {
|
|
MR_TRACE_CALL_MERCURY(
|
|
ML_LISTING_list_file_with_command(MR_mdb_out, MR_mdb_err,
|
|
MR_listing_cmd, (char *) aligned_filename,
|
|
first_lineno, last_lineno, lineno, MR_listing_path);
|
|
);
|
|
} else {
|
|
MR_TRACE_CALL_MERCURY(
|
|
ML_LISTING_list_file(MR_mdb_out, MR_mdb_err,
|
|
(char *) aligned_filename,
|
|
first_lineno, last_lineno, lineno, MR_listing_path);
|
|
);
|
|
}
|
|
|
|
return KEEP_INTERACTING;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void
|
|
MR_trace_set_level_and_report(int ancestor_level, MR_bool detailed,
|
|
MR_bool print_optionals)
|
|
{
|
|
const char *problem;
|
|
const MR_ProcLayout *entry;
|
|
MR_Word *base_sp;
|
|
MR_Word *base_curfr;
|
|
const char *filename;
|
|
int lineno;
|
|
int indent;
|
|
|
|
problem = MR_trace_set_level(ancestor_level, print_optionals);
|
|
if (problem == NULL) {
|
|
fprintf(MR_mdb_out, "Ancestor level set to %d:\n",
|
|
ancestor_level);
|
|
MR_trace_current_level_details(&entry, &filename, &lineno,
|
|
&base_sp, &base_curfr);
|
|
fprintf(MR_mdb_out, "%4d ", ancestor_level);
|
|
if (detailed) {
|
|
// We want to print the trace info first regardless
|
|
// of the value of MR_context_position.
|
|
|
|
MR_print_call_trace_info(MR_mdb_out, entry, base_sp, base_curfr);
|
|
indent = 26;
|
|
} else {
|
|
indent = 5;
|
|
}
|
|
|
|
MR_print_proc_id_trace_and_context(MR_mdb_out, MR_FALSE,
|
|
MR_context_position, MR_user_event_context, entry, MR_FALSE,
|
|
base_sp, base_curfr, "", filename, lineno, MR_FALSE,
|
|
"", 0, indent);
|
|
} else {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "%s.\n", problem);
|
|
}
|
|
}
|
|
|
|
void
|
|
MR_trace_browse_internal(MR_Word type_info, MR_Word value,
|
|
MR_BrowseCallerType caller, MR_BrowseFormat format)
|
|
{
|
|
switch (caller) {
|
|
|
|
case MR_BROWSE_CALLER_BROWSE:
|
|
MR_trace_browse(type_info, value, format);
|
|
break;
|
|
|
|
case MR_BROWSE_CALLER_PRINT:
|
|
case MR_BROWSE_CALLER_PRINT_ALL:
|
|
fprintf(MR_mdb_out, "\t");
|
|
fflush(MR_mdb_out);
|
|
MR_trace_print(type_info, value, caller, format);
|
|
break;
|
|
|
|
default:
|
|
MR_fatal_error("MR_trace_browse_internal: unknown caller type");
|
|
}
|
|
}
|
|
|
|
void
|
|
MR_trace_browse_goal_internal(MR_ConstString name, MR_Word arg_list,
|
|
MR_Word is_func, MR_BrowseCallerType caller, MR_BrowseFormat format)
|
|
{
|
|
switch (caller) {
|
|
|
|
case MR_BROWSE_CALLER_BROWSE:
|
|
MR_trace_browse_goal(name, arg_list, is_func, format);
|
|
break;
|
|
|
|
case MR_BROWSE_CALLER_PRINT:
|
|
MR_trace_print_goal(name, arg_list, is_func, caller, format);
|
|
break;
|
|
|
|
case MR_BROWSE_CALLER_PRINT_ALL:
|
|
MR_fatal_error("MR_trace_browse_goal_internal: bad caller type");
|
|
|
|
default:
|
|
MR_fatal_error("MR_trace_browse_goal_internal:"
|
|
" unknown caller type");
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
MR_trace_browse_exception(MR_EventInfo *event_info, MR_Browser browser,
|
|
MR_BrowseCallerType caller, MR_BrowseFormat format)
|
|
{
|
|
MR_TypeInfo type_info;
|
|
MR_Word value;
|
|
MR_Word exception;
|
|
|
|
if (event_info->MR_trace_port != MR_PORT_EXCEPTION) {
|
|
return "command only available from EXCP ports";
|
|
}
|
|
|
|
exception = MR_trace_get_exception_value();
|
|
if (exception == (MR_Word) NULL) {
|
|
return "missing exception value";
|
|
}
|
|
|
|
MR_unravel_univ(exception, type_info, value);
|
|
|
|
(*browser)((MR_Word) type_info, value, caller, format);
|
|
|
|
return (const char *) NULL;
|
|
}
|
|
|
|
static const char *
|
|
MR_trace_browse_proc_body(MR_EventInfo *event_info, MR_Browser browser,
|
|
MR_BrowseCallerType caller, MR_BrowseFormat format)
|
|
{
|
|
const MR_ProcLayout *entry;
|
|
MR_Word rep;
|
|
|
|
entry = event_info->MR_event_sll->MR_sll_entry;
|
|
|
|
if (entry->MR_sle_body_bytes == NULL) {
|
|
return "current procedure has no body info";
|
|
}
|
|
|
|
MR_TRACE_CALL_MERCURY(
|
|
MR_MDBCOMP_trace_read_proc_defn_rep(entry->MR_sle_body_bytes,
|
|
event_info->MR_event_sll, &rep);
|
|
);
|
|
|
|
(*browser)(ML_proc_defn_rep_type(), rep, caller, format);
|
|
return (const char *) NULL;
|
|
}
|
|
|
|
static void
|
|
MR_trace_browse_web(MR_Word type_info, MR_Word value,
|
|
MR_BrowseCallerType caller, MR_BrowseFormat format)
|
|
{
|
|
MR_Word browser_term;
|
|
|
|
browser_term = MR_type_value_to_browser_term((MR_TypeInfo) type_info,
|
|
value);
|
|
|
|
MR_trace_save_and_invoke_web_browser(browser_term);
|
|
}
|
|
|
|
static void
|
|
MR_trace_browse_goal_web(MR_ConstString name, MR_Word arg_list,
|
|
MR_Word is_func, MR_BrowseCallerType caller, MR_BrowseFormat format)
|
|
{
|
|
MR_Word browser_term;
|
|
|
|
browser_term = MR_synthetic_to_browser_term(name, arg_list, is_func);
|
|
MR_trace_save_and_invoke_web_browser(browser_term);
|
|
}
|
|
|
|
// Implement the `view' command. First, check if there is a server attached.
|
|
// If so, either stop it or abort the command, depending on whether '-f'
|
|
// was given. Then, if a server name was not supplied, start a new server
|
|
// with a unique name (which has been MR_malloc'd), otherwise attach to the
|
|
// server with the supplied name (and make a MR_malloc'd copy of the name).
|
|
|
|
static const char *
|
|
MR_trace_new_source_window(const char *window_cmd, const char *server_cmd,
|
|
const char *server_name, int timeout, MR_bool force,
|
|
MR_bool verbose, MR_bool split)
|
|
{
|
|
const char *msg;
|
|
|
|
if (MR_trace_source_server.server_name != NULL) {
|
|
// We are already attached to a server.
|
|
|
|
if (force) {
|
|
MR_trace_maybe_close_source_window(verbose);
|
|
} else {
|
|
return "error: server already open (use '-f' to force)";
|
|
}
|
|
}
|
|
|
|
MR_trace_source_server.split = split;
|
|
if (server_cmd != NULL) {
|
|
MR_trace_source_server.server_cmd = MR_copy_string(server_cmd);
|
|
} else {
|
|
MR_trace_source_server.server_cmd = NULL;
|
|
}
|
|
|
|
if (server_name == NULL) {
|
|
msg = MR_trace_source_open_server(&MR_trace_source_server,
|
|
window_cmd, timeout, verbose);
|
|
} else {
|
|
MR_trace_source_server.server_name = MR_copy_string(server_name);
|
|
msg = MR_trace_source_attach(&MR_trace_source_server, timeout,
|
|
verbose);
|
|
if (msg != NULL) {
|
|
// Something went wrong, so we should free the
|
|
// strings we allocated just above.
|
|
|
|
MR_free(MR_trace_source_server.server_name);
|
|
MR_trace_source_server.server_name = NULL;
|
|
MR_free(MR_trace_source_server.server_cmd);
|
|
MR_trace_source_server.server_cmd = NULL;
|
|
}
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
void
|
|
MR_trace_maybe_sync_source_window(MR_EventInfo *event_info, MR_bool verbose)
|
|
{
|
|
const MR_LabelLayout *parent;
|
|
const char *filename;
|
|
int lineno;
|
|
const char *parent_filename;
|
|
int parent_lineno;
|
|
const char *problem; // Not used.
|
|
MR_Word *base_sp;
|
|
MR_Word *base_curfr;
|
|
const char *msg;
|
|
MR_Level actual_level;
|
|
|
|
if (MR_trace_source_server.server_name != NULL) {
|
|
lineno = 0;
|
|
filename = "";
|
|
parent_lineno = 0;
|
|
parent_filename = "";
|
|
|
|
if (filename[0] == '\0') {
|
|
(void) MR_find_context(event_info->MR_event_sll,
|
|
&filename, &lineno);
|
|
}
|
|
|
|
// At interface ports we send both the parent context and the
|
|
// current context. Otherwise, we just send the current context.
|
|
|
|
if (MR_port_is_interface(event_info->MR_trace_port)) {
|
|
base_sp = MR_saved_sp(event_info->MR_saved_regs);
|
|
base_curfr = MR_saved_curfr(event_info->MR_saved_regs);
|
|
parent = MR_find_nth_ancestor(event_info->MR_event_sll, 1,
|
|
&base_sp, &base_curfr, &actual_level, &problem);
|
|
if (actual_level != 1) {
|
|
parent_filename = filename;
|
|
parent_lineno = lineno;
|
|
} else if (parent != NULL) {
|
|
(void) MR_find_context(parent, &parent_filename,
|
|
&parent_lineno);
|
|
}
|
|
}
|
|
|
|
msg = MR_trace_source_sync(&MR_trace_source_server, filename, lineno,
|
|
parent_filename, parent_lineno, verbose);
|
|
if (msg != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s.\n", msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
MR_trace_maybe_close_source_window(MR_bool verbose)
|
|
{
|
|
const char *msg;
|
|
|
|
if (MR_trace_source_server.server_name != NULL) {
|
|
msg = MR_trace_source_close(&MR_trace_source_server, verbose);
|
|
if (msg != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s.\n", msg);
|
|
}
|
|
|
|
MR_free(MR_trace_source_server.server_name);
|
|
MR_trace_source_server.server_name = NULL;
|
|
MR_free(MR_trace_source_server.server_cmd);
|
|
MR_trace_source_server.server_cmd = NULL;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
const char *const MR_trace_print_cmd_args[] =
|
|
{ "-f", "-p", "-v", "--flat", "--pretty", "--verbose",
|
|
"exception", "goal", "*", NULL };
|
|
|
|
// It is better to have a single completion where possible,
|
|
// so don't include `-d' here.
|
|
|
|
const char *const MR_trace_stack_cmd_args[] =
|
|
{ "--detailed", NULL };
|
|
|
|
const char *const MR_trace_view_cmd_args[] =
|
|
{ "-c", "-f", "-n", "-s", "-t", "-v", "-w", "-2",
|
|
"--close", "--verbose", "--force", "--split-screen",
|
|
"--window-command", "--server-command", "--server-name",
|
|
"--timeout", NULL };
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
static struct MR_option MR_trace_detailed_opts[] =
|
|
{
|
|
{ "detailed", MR_no_argument, NULL, 'd' },
|
|
{ NULL, MR_no_argument, NULL, 0 }
|
|
};
|
|
|
|
static MR_bool
|
|
MR_trace_options_detailed(MR_bool *detailed, char ***words, int *word_count)
|
|
{
|
|
int c;
|
|
|
|
MR_optind = 0;
|
|
while ((c = MR_getopt_long(*word_count, *words, "d",
|
|
MR_trace_detailed_opts, NULL)) != EOF)
|
|
{
|
|
switch (c) {
|
|
|
|
case 'd':
|
|
*detailed = MR_TRUE;
|
|
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;
|
|
}
|
|
|
|
static MR_bool
|
|
MR_trace_options_stack_trace(MR_bool *print_all, MR_bool *detailed,
|
|
MR_SpecLineLimit *line_limit, MR_SpecLineLimit *clique_line_limit,
|
|
MR_FrameLimit *frame_limit, char ***words, int *word_count)
|
|
{
|
|
int c;
|
|
|
|
MR_optind = 0;
|
|
while ((c = MR_getopt_long(*word_count, *words, "ac:df:",
|
|
MR_trace_detailed_opts, NULL)) != EOF)
|
|
{
|
|
switch (c) {
|
|
case 'a':
|
|
*print_all = MR_TRUE;
|
|
*line_limit = 0;
|
|
break;
|
|
|
|
case 'c':
|
|
if (! MR_trace_is_natural_number(MR_optarg, clique_line_limit))
|
|
{
|
|
MR_trace_usage_cur_cmd();
|
|
return MR_FALSE;
|
|
}
|
|
*print_all = MR_FALSE;
|
|
break;
|
|
|
|
case 'd':
|
|
*detailed = MR_TRUE;
|
|
break;
|
|
|
|
case 'f':
|
|
if (! MR_trace_is_natural_number(MR_optarg, frame_limit)) {
|
|
MR_trace_usage_cur_cmd();
|
|
return MR_FALSE;
|
|
}
|
|
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;
|
|
}
|
|
|
|
static struct MR_option MR_trace_print_opts[] =
|
|
{
|
|
// Please keep the formatting options in sync with MR_trace_browse_opts.
|
|
{ "flat", MR_no_argument, NULL, 'f' },
|
|
{ "raw_pretty", MR_no_argument, NULL, 'r' },
|
|
{ "verbose", MR_no_argument, NULL, 'v' },
|
|
{ "pretty", MR_no_argument, NULL, 'p' },
|
|
{ "max", MR_required_argument, NULL, 'm' },
|
|
{ NULL, MR_no_argument, NULL, 0 }
|
|
};
|
|
|
|
static MR_bool
|
|
MR_trace_options_print(MR_BrowseFormat *format,
|
|
MR_Unsigned *max_printed_actions, MR_bool *set_max_printed_actions,
|
|
char ***words, int *word_count)
|
|
{
|
|
int c;
|
|
|
|
*format = MR_BROWSE_DEFAULT_FORMAT;
|
|
*max_printed_actions = -1;
|
|
*set_max_printed_actions = MR_FALSE;
|
|
MR_optind = 0;
|
|
while ((c = MR_getopt_long(*word_count, *words, "frvpm:",
|
|
MR_trace_print_opts, NULL)) != EOF)
|
|
{
|
|
switch (c) {
|
|
|
|
case 'f':
|
|
*format = MR_BROWSE_FORMAT_FLAT;
|
|
break;
|
|
|
|
case 'r':
|
|
*format = MR_BROWSE_FORMAT_RAW_PRETTY;
|
|
break;
|
|
|
|
case 'v':
|
|
*format = MR_BROWSE_FORMAT_VERBOSE;
|
|
break;
|
|
|
|
case 'p':
|
|
*format = MR_BROWSE_FORMAT_PRETTY;
|
|
break;
|
|
|
|
case 'm':
|
|
if (! MR_trace_is_natural_number(MR_optarg,
|
|
max_printed_actions))
|
|
{
|
|
MR_trace_usage_cur_cmd();
|
|
return MR_FALSE;
|
|
}
|
|
if (*max_printed_actions == 0) {
|
|
// Printing a maximum of zero I/O actions
|
|
// does not make any sense.
|
|
MR_trace_usage_cur_cmd();
|
|
return MR_FALSE;
|
|
}
|
|
*set_max_printed_actions = MR_TRUE;
|
|
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;
|
|
}
|
|
|
|
static struct MR_option MR_trace_browse_opts[] =
|
|
{
|
|
// Please keep the formatting options in sync with MR_trace_print_opts.
|
|
{ "flat", MR_no_argument, NULL, 'f' },
|
|
{ "raw_pretty", MR_no_argument, NULL, 'r' },
|
|
{ "verbose", MR_no_argument, NULL, 'v' },
|
|
{ "pretty", MR_no_argument, NULL, 'p' },
|
|
{ "web", MR_no_argument, NULL, 'w' },
|
|
{ NULL, MR_no_argument, NULL, 0 }
|
|
};
|
|
|
|
static MR_bool
|
|
MR_trace_options_browse(MR_BrowseFormat *format, MR_bool *web,
|
|
char ***words, int *word_count)
|
|
{
|
|
int c;
|
|
|
|
*format = MR_BROWSE_DEFAULT_FORMAT;
|
|
*web = MR_FALSE;
|
|
MR_optind = 0;
|
|
while ((c = MR_getopt_long(*word_count, *words, "frvpw",
|
|
MR_trace_browse_opts, NULL)) != EOF)
|
|
{
|
|
switch (c) {
|
|
|
|
case 'f':
|
|
*format = MR_BROWSE_FORMAT_FLAT;
|
|
break;
|
|
|
|
case 'r':
|
|
*format = MR_BROWSE_FORMAT_RAW_PRETTY;
|
|
break;
|
|
|
|
case 'v':
|
|
*format = MR_BROWSE_FORMAT_VERBOSE;
|
|
break;
|
|
|
|
case 'p':
|
|
*format = MR_BROWSE_FORMAT_PRETTY;
|
|
break;
|
|
|
|
case 'w':
|
|
*web = MR_TRUE;
|
|
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;
|
|
}
|
|
|
|
static struct MR_option MR_trace_view_opts[] =
|
|
{
|
|
{ "close", MR_no_argument, NULL, 'c' },
|
|
{ "window-command", MR_required_argument, NULL, 'w' },
|
|
{ "server-command", MR_required_argument, NULL, 's' },
|
|
{ "server-name", MR_required_argument, NULL, 'n' },
|
|
{ "timeout", MR_required_argument, NULL, 't' },
|
|
{ "force", MR_no_argument, NULL, 'f' },
|
|
{ "verbose", MR_no_argument, NULL, 'v' },
|
|
{ "split-screen", MR_no_argument, NULL, '2' },
|
|
{ NULL, MR_no_argument, NULL, 0 }
|
|
};
|
|
|
|
static MR_bool
|
|
MR_trace_options_view(const char **window_cmd, const char **server_cmd,
|
|
const char **server_name, MR_Unsigned *timeout, MR_bool *force,
|
|
MR_bool *verbose, MR_bool *split, MR_bool *close_window,
|
|
char ***words, int *word_count)
|
|
{
|
|
int c;
|
|
MR_bool no_close = MR_FALSE;
|
|
|
|
MR_optind = 0;
|
|
while ((c = MR_getopt_long(*word_count, *words, "cw:s:n:t:fv2",
|
|
MR_trace_view_opts, NULL)) != EOF)
|
|
{
|
|
// Option '-c' is mutually incompatible with '-f', '-t',
|
|
// '-s', '-n', '-w' and '-2'.
|
|
|
|
switch (c) {
|
|
|
|
case 'c':
|
|
if (no_close) {
|
|
MR_trace_usage_cur_cmd();
|
|
return MR_FALSE;
|
|
}
|
|
|
|
*close_window = MR_TRUE;
|
|
break;
|
|
|
|
case 'w':
|
|
if (*close_window) {
|
|
MR_trace_usage_cur_cmd();
|
|
return MR_FALSE;
|
|
}
|
|
|
|
*window_cmd = MR_optarg;
|
|
no_close = MR_TRUE;
|
|
break;
|
|
|
|
case 's':
|
|
if (*close_window) {
|
|
MR_trace_usage_cur_cmd();
|
|
return MR_FALSE;
|
|
}
|
|
|
|
*server_cmd = MR_optarg;
|
|
no_close = MR_TRUE;
|
|
break;
|
|
|
|
case 'n':
|
|
if (*close_window) {
|
|
MR_trace_usage_cur_cmd();
|
|
return MR_FALSE;
|
|
}
|
|
|
|
*server_name = MR_optarg;
|
|
no_close = MR_TRUE;
|
|
break;
|
|
|
|
case 't':
|
|
if (*close_window ||
|
|
! MR_trace_is_natural_number(MR_optarg, timeout))
|
|
{
|
|
MR_trace_usage_cur_cmd();
|
|
return MR_FALSE;
|
|
}
|
|
|
|
no_close = MR_TRUE;
|
|
break;
|
|
|
|
case 'f':
|
|
if (*close_window) {
|
|
MR_trace_usage_cur_cmd();
|
|
return MR_FALSE;
|
|
}
|
|
|
|
*force = MR_TRUE;
|
|
no_close = MR_TRUE;
|
|
break;
|
|
|
|
case 'v':
|
|
*verbose = MR_TRUE;
|
|
break;
|
|
|
|
case '2':
|
|
if (*close_window) {
|
|
MR_trace_usage_cur_cmd();
|
|
return MR_FALSE;
|
|
}
|
|
|
|
*split = MR_TRUE;
|
|
no_close = MR_TRUE;
|
|
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;
|
|
}
|
|
|
|
static struct MR_option MR_trace_diff_opts[] =
|
|
{
|
|
{ "start", MR_required_argument, NULL, 's' },
|
|
{ "max", MR_required_argument, NULL, 'm' },
|
|
{ NULL, MR_no_argument, NULL, 0 }
|
|
};
|
|
|
|
static MR_bool
|
|
MR_trace_options_diff(MR_Unsigned *start, MR_Unsigned *max,
|
|
char ***words, int *word_count)
|
|
{
|
|
int c;
|
|
|
|
MR_optind = 0;
|
|
while ((c = MR_getopt_long(*word_count, *words, "m:s:",
|
|
MR_trace_diff_opts, NULL)) != EOF)
|
|
{
|
|
switch (c) {
|
|
|
|
case 'm':
|
|
if (! MR_trace_is_natural_number(MR_optarg, max)) {
|
|
MR_trace_usage_cur_cmd();
|
|
return MR_FALSE;
|
|
}
|
|
break;
|
|
|
|
case 's':
|
|
if (! MR_trace_is_natural_number(MR_optarg, start)) {
|
|
MR_trace_usage_cur_cmd();
|
|
return MR_FALSE;
|
|
}
|
|
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;
|
|
}
|
|
|
|
static struct MR_option MR_trace_dump_opts[] =
|
|
{
|
|
{ "quiet", MR_no_argument, NULL, 'q' },
|
|
{ "xml", MR_no_argument, NULL, 'x' },
|
|
{ NULL, MR_no_argument, NULL, 0 }
|
|
};
|
|
|
|
static MR_bool
|
|
MR_trace_options_dump(MR_bool *quiet, MR_bool *xml,
|
|
char ***words, int *word_count)
|
|
{
|
|
int c;
|
|
|
|
MR_optind = 0;
|
|
while ((c = MR_getopt_long(*word_count, *words, "qx",
|
|
MR_trace_dump_opts, NULL)) != EOF)
|
|
{
|
|
switch (c) {
|
|
|
|
case 'q':
|
|
*quiet = MR_TRUE;
|
|
break;
|
|
|
|
case 'x':
|
|
*xml = MR_TRUE;
|
|
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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|