mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-16 18:03:36 +00:00
The usual mdb "dump" command puts every function symbol on its own line.
This guarantees that we generate any line that is too long to be displayed
on terminals, but it also generates output that is too stretched out
vertically for its structure to be readily apparent. Dumping the term
as a doc allows pretty_printer.m to put as many function symbols on a line
as would fit, without exceeding the maximum line length.
browser/browse.m:
Add save_term_to_file_doc, a way to save a (possibly synthetic)
browser term in a file, using an interface that works the same way
the predicate that saves browser terms as XML.
Inline a predicate at its only call site. Improve variable names.
trace/mercury_trace_browse.[ch]:
Add MR_trace_save_term_as_doc, as an interface function between
save_term_to_file_doc and the code of mercury_trace_cmd_browsing.c.
trace/mercury_trace_cmd_browsing.c:
Add support for a new -p/--prettyprint flag to the mdb "dump" command,
which asks for the given term to be dumped as a pretty_printer doc.
doc/user_guide.texi:
NEWS:
Document the new option.
library/pretty_printer.m:
NEWS:
Rename write_as_doc to write_doc_formatted, and fix its argument type.
tests/debugger/browser_test.inp:
Dump a term that we already dumped with "dump" with "dump -x" and
"dump -p" as well.
tests/debugger/browser_test.m:
Put the code to remove the files we are going to dump to
and then later to print the files we have dumped to into separate
predicates. This keeps most (but not all) line numbers unchanged
even though we now dump to more files.
tests/debugger/browser_test.exp3:
Update this file to account both for the extra output from the just-added
dump commands, and for the changes in line numbers.
1659 lines
53 KiB
C
1659 lines
53 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,
|
|
MR_bool *prettyprint, 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 *value_problem = NULL;
|
|
MR_bool quiet = MR_FALSE;
|
|
MR_bool xml = MR_FALSE;
|
|
MR_bool prettyprint = MR_FALSE;
|
|
|
|
// Set this to NULL to avoid uninitialization warnings.
|
|
|
|
browser_term = (MR_Word) NULL;
|
|
|
|
if (! MR_trace_options_dump(&quiet, &xml, &prettyprint,
|
|
&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;
|
|
|
|
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) {
|
|
value_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) {
|
|
value_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);
|
|
value_problem = MR_lookup_unambiguous_var_spec(var_spec,
|
|
&type_info, &value, &name);
|
|
if (value_problem == NULL) {
|
|
browser_term = MR_type_value_to_browser_term(type_info, value);
|
|
}
|
|
}
|
|
|
|
if (value_problem != NULL) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err, "mdb: %s.\n", value_problem);
|
|
} else {
|
|
if (xml && prettyprint) {
|
|
fflush(MR_mdb_out);
|
|
fprintf(MR_mdb_err,
|
|
"mdb: the -p and -x options of the \"dump\" command "
|
|
"are mutually exclusive; ignoring -p.\n");
|
|
}
|
|
|
|
if (xml) {
|
|
MR_trace_save_term_xml(words[2], browser_term);
|
|
} else if (prettyprint) {
|
|
MR_trace_save_term_doc(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' },
|
|
{ "prettyprint", MR_no_argument, NULL, 'p' },
|
|
{ NULL, MR_no_argument, NULL, 0 }
|
|
};
|
|
|
|
static MR_bool
|
|
MR_trace_options_dump(MR_bool *quiet, MR_bool *xml, MR_bool *prettyprint,
|
|
char ***words, int *word_count)
|
|
{
|
|
int c;
|
|
|
|
MR_optind = 0;
|
|
while ((c = MR_getopt_long(*word_count, *words, "pqx",
|
|
MR_trace_dump_opts, NULL)) != EOF)
|
|
{
|
|
switch (c) {
|
|
|
|
case 'p':
|
|
*prettyprint = MR_TRUE;
|
|
break;
|
|
|
|
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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|