mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-06 07:49:02 +00:00
trace/mercury_trace_cmd_browsing.c:
Change the dump command to allow the name of the variable
whose value is to be dumped to be followed by a path,
specifying the *part* of the variable's value to be printed.
trace/mercury_trace_vars.[ch]:
To make that possible, provide a version of an existing function
that returns an extra value that the new code in
mercury_trace_cmd_browsing.c needs, and another extra value
that conceptually belongs with it.
Mark strange code with an XXX.
NEWS.md:
Announce both this new capability and a just-now contributes predicate.
library/pqueue.m:
Make the just-now contributed code more readable.
tests/debugger/browser_test.{m,inp,exp3}:
Extend this test case to test the new functionality.
1662 lines
53 KiB
C
1662 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, 2025 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;
|
|
// If bad_path is true, then value_problem contains the problematic part
|
|
// of the path.
|
|
MR_bool bad_path = MR_FALSE;
|
|
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_TypeInfo type_info;
|
|
MR_Word value;
|
|
const char *name;
|
|
char *path;
|
|
|
|
value_problem = MR_trace_parse_lookup_and_return_var_path(words[1],
|
|
&type_info, &value, &name, &path, &bad_path);
|
|
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%s.\n",
|
|
(bad_path ? "there is no path " : ""), 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;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|