mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-10 19:33:11 +00:00
1527 lines
46 KiB
C
1527 lines
46 KiB
C
/*
|
|
** vim: ts=4 sw=4 expandtab
|
|
*/
|
|
/*
|
|
INIT mercury_sys_init_trace
|
|
ENDINIT
|
|
*/
|
|
/*
|
|
** Copyright (C) 1997-2008, 2011 The University of Melbourne.
|
|
** This file may only be copied under the terms of the GNU Library General
|
|
** Public License - see the file COPYING.LIB in the Mercury distribution.
|
|
*/
|
|
|
|
/*
|
|
** mercury_trace_base.c implements the interface between the main part
|
|
** of the runtime system (mainly mercury_wrapper.c) and the part of the
|
|
** tracing subsystem that has to be present even if no module in the program
|
|
** is compiled with execution tracing.
|
|
**
|
|
** Main author: Zoltan Somogyi.
|
|
*/
|
|
|
|
#include "mercury_imp.h"
|
|
#include "mercury_trace_base.h"
|
|
#include "mercury_engine.h"
|
|
#include "mercury_wrapper.h"
|
|
#include "mercury_misc.h"
|
|
#include "mercury_hash_table.h"
|
|
#include "mercury_layout_util.h" /* for MR_generate_proc_name_from_layout */
|
|
#include "mercury_runtime_util.h" /* for strerror() on some systems */
|
|
#include "mercury_signal.h" /* for MR_setup_signal() */
|
|
#include "mercury_builtin_types.h" /* for type_ctor_infos */
|
|
#include "mercury_array_macros.h" /* for type_ctor_infos */
|
|
#include <signal.h> /* for SIGINT */
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#ifdef MR_HAVE_UNISTD_H
|
|
#include <unistd.h> /* for the write system call */
|
|
#endif
|
|
|
|
#ifdef MR_HAVE_SYS_WAIT_H
|
|
#include <sys/wait.h> /* for the wait system call */
|
|
#endif
|
|
|
|
#if defined(MR_HAVE__SNPRINTF) && ! defined(MR_HAVE_SNPRINTF)
|
|
#define snprintf _snprintf
|
|
#endif
|
|
|
|
#define MR_TRACE_COUNT_SUMMARY_MAX_DEFAULT 20
|
|
|
|
void (*MR_trace_shutdown)(void) = NULL;
|
|
|
|
MR_bool MR_trace_count_enabled = MR_FALSE;
|
|
MR_bool MR_coverage_test_enabled = MR_FALSE;
|
|
const char *MR_trace_count_summary_file = NULL;
|
|
const char *MR_trace_count_summary_cmd = "mtc_union";
|
|
unsigned int MR_trace_count_summary_max =
|
|
MR_TRACE_COUNT_SUMMARY_MAX_DEFAULT;
|
|
char *MR_trace_counts_file = NULL;
|
|
|
|
MR_bool MR_debug_ever_enabled = MR_FALSE;
|
|
MR_bool MR_debug_enabled = MR_FALSE;
|
|
MR_bool MR_trace_func_enabled = MR_FALSE;
|
|
MR_Code *(*volatile MR_selected_trace_func_ptr)(
|
|
const MR_LabelLayout *);
|
|
MR_Unsigned MR_trace_call_seqno = 0;
|
|
MR_Unsigned MR_trace_call_depth = 0;
|
|
MR_Unsigned MR_trace_event_number = 0;
|
|
MR_bool MR_trace_from_full = MR_TRUE;
|
|
MR_bool MR_standardize_event_details = MR_FALSE;
|
|
MR_Trace_Type MR_trace_handler = MR_TRACE_INTERNAL;
|
|
|
|
MR_bool MR_trace_unhide_events = MR_FALSE;
|
|
MR_bool MR_trace_have_unhid_events = MR_FALSE;
|
|
|
|
MR_bool MR_trace_tailrec_have_reused_frames = MR_FALSE;
|
|
MR_Unsigned MR_trace_tailrec_num_reused_frames = 0;
|
|
|
|
/*
|
|
** I/O tabling is documented in library/table_builtin.m
|
|
*/
|
|
|
|
MR_IoTablingPhase MR_io_tabling_phase = MR_IO_TABLING_UNINIT;
|
|
MR_bool MR_io_tabling_enabled = MR_FALSE;
|
|
MR_TableNode MR_io_tabling_pointer = { 0 };
|
|
MR_IoActionNum MR_io_tabling_counter = 0;
|
|
MR_IoActionNum MR_io_tabling_counter_hwm = 0;
|
|
MR_IoActionNum MR_io_tabling_start = 0;
|
|
MR_IoActionNum MR_io_tabling_end = 0;
|
|
MR_Unsigned MR_io_tabling_start_event_num = 0;
|
|
MR_Unsigned MR_io_tabling_stop_event_num = 0;
|
|
MR_bool MR_io_tabling_debug = MR_FALSE;
|
|
|
|
#ifdef MR_EXEC_TRACE
|
|
MR_bool MR_io_tabling_allowed = MR_TRUE;
|
|
#else
|
|
MR_bool MR_io_tabling_allowed = MR_FALSE;
|
|
#endif
|
|
|
|
#ifdef MR_TRACE_HISTOGRAM
|
|
|
|
int *MR_trace_histogram_all = NULL;
|
|
int *MR_trace_histogram_exp = NULL;
|
|
int MR_trace_histogram_max = 0;
|
|
int MR_trace_histogram_hwm = 0;
|
|
|
|
#define MR_TRACE_HISTOGRAM_FILENAME ".mercury_histogram"
|
|
|
|
#endif
|
|
|
|
const char *MR_actual_port_names[] = { MR_TRACE_PORT_ACTUAL_NAMES };
|
|
const char *MR_simplified_port_names[] = { MR_TRACE_PORT_SIMPLIFIED_NAMES };
|
|
|
|
static const void *MR_get_orig_number(const void *record);
|
|
static int MR_hash_orig_number(const void *orig_number);
|
|
static MR_bool MR_equal_orig_number(const void *orig_number1,
|
|
const void *orig_number2);
|
|
static MR_Unsigned MR_standardize_num(MR_Unsigned num,
|
|
MR_Hash_Table *table_ptr, MR_bool *init_ptr,
|
|
int *next_ptr);
|
|
|
|
/**************************************************************************/
|
|
/*
|
|
** This section of this file deals with the actions executed at trace events.
|
|
*/
|
|
|
|
MR_Code *
|
|
MR_trace(const MR_LabelLayout *layout)
|
|
{
|
|
if (! MR_trace_func_enabled) {
|
|
return NULL;
|
|
}
|
|
|
|
return (*MR_selected_trace_func_ptr)(layout);
|
|
}
|
|
|
|
MR_Code *
|
|
MR_user_trace(const MR_LabelLayout *layout)
|
|
{
|
|
if (! MR_trace_func_enabled) {
|
|
return NULL;
|
|
}
|
|
|
|
return (*MR_selected_trace_func_ptr)(layout);
|
|
}
|
|
|
|
void
|
|
MR_tracing_not_enabled(void)
|
|
{
|
|
MR_fatal_error("This executable is not set up for debugging.\n"
|
|
"Rebuild the <main>_init.c file, "
|
|
"and give the `-t' (or `--trace')\n"
|
|
"option to c2init when you do so. "
|
|
"If you are using mmake, you\n"
|
|
"can do this by including "
|
|
"`-t' (or `--trace') in C2INITFLAGS.\n"
|
|
"For further details, please see the \"Debugging\" chapter "
|
|
"of the\n"
|
|
"Mercury User's Guide.\n");
|
|
}
|
|
|
|
MR_Code *
|
|
MR_trace_fake(const MR_LabelLayout *layout)
|
|
{
|
|
MR_tracing_not_enabled();
|
|
/*NOTREACHED*/
|
|
return NULL;
|
|
}
|
|
|
|
MR_Code *
|
|
MR_trace_count(const MR_LabelLayout *label_layout)
|
|
{
|
|
MR_Unsigned *exec_count;
|
|
|
|
exec_count = MR_trace_lookup_trace_count(label_layout);
|
|
|
|
#ifdef MR_TRACE_COUNT_DEBUG
|
|
{
|
|
const MR_LabelLayout *call_label_layout;
|
|
MR_uint_least16_t call_label_number;
|
|
const MR_ModuleLayout *module_layout;
|
|
const MR_ProcLayout *proc_layout;
|
|
|
|
proc_layout = label_layout->MR_sll_entry;
|
|
module_layout = proc_layout->MR_sle_module_layout;
|
|
call_label_layout = proc_layout->MR_sle_call_label;
|
|
if (label_layout != call_label_layout) {
|
|
/*
|
|
** We should only get here if we have executed the call label,
|
|
** which means its count should be nonzero.
|
|
*/
|
|
|
|
call_label_number = call_label_layout->MR_sll_label_num_in_module;
|
|
if (call_label_number >=
|
|
module_layout->MR_ml_num_label_exec_counts)
|
|
{
|
|
MR_fatal_error("MR_trace_count: invalid call label number");
|
|
}
|
|
|
|
if (module_layout->MR_ml_label_exec_count[call_label_number] == 0)
|
|
{
|
|
MR_fatal_error("MR_trace_count: call label count is zero");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
*exec_count += 1;
|
|
return NULL;
|
|
}
|
|
|
|
MR_Unsigned *
|
|
MR_trace_lookup_trace_count(const MR_LabelLayout *label_layout)
|
|
{
|
|
const MR_ModuleLayout *module_layout;
|
|
const MR_ProcLayout *proc_layout;
|
|
MR_uint_least16_t label_number;
|
|
|
|
proc_layout = label_layout->MR_sll_entry;
|
|
if (! MR_PROC_LAYOUT_HAS_EXEC_TRACE(proc_layout)) {
|
|
MR_fatal_error("MR_trace_lookup_trace_count: no exec trace");
|
|
}
|
|
|
|
module_layout = proc_layout->MR_sle_module_layout;
|
|
label_number = label_layout->MR_sll_label_num_in_module;
|
|
if (label_number >= module_layout->MR_ml_num_label_exec_counts) {
|
|
MR_fatal_error("MR_trace_lookup_trace_count: invalid label number");
|
|
}
|
|
|
|
return &(module_layout->MR_ml_label_exec_count[label_number]);
|
|
}
|
|
|
|
#define INIT_MODULE_TABLE_SIZE 10
|
|
|
|
const MR_ModuleLayout **MR_module_infos;
|
|
unsigned MR_module_info_next = 0;
|
|
unsigned MR_module_info_max = 0;
|
|
|
|
void
|
|
MR_insert_module_info_into_module_table(const MR_ModuleLayout *module)
|
|
{
|
|
int slot;
|
|
|
|
MR_GC_ensure_room_for_next(MR_module_info, const MR_ModuleLayout *,
|
|
INIT_MODULE_TABLE_SIZE, MR_ALLOC_SITE_NONE);
|
|
MR_prepare_insert_into_sorted(MR_module_infos, MR_module_info_next, slot,
|
|
strcmp(MR_module_infos[slot]->MR_ml_name, module->MR_ml_name));
|
|
|
|
MR_module_infos[slot] = module;
|
|
}
|
|
|
|
static void MR_trace_write_quoted_atom(FILE *fp, const char *atom);
|
|
static void MR_trace_write_string(FILE *fp, const char *atom);
|
|
static unsigned MR_trace_write_label_exec_counts_for_file(FILE *fp,
|
|
const MR_ModuleLayout *module,
|
|
const MR_ModuleFileLayout *file,
|
|
const char *module_name,
|
|
MR_bool coverage_test);
|
|
|
|
MR_PathPort MR_named_count_port[MR_PORT_NONE + 1];
|
|
|
|
#define MERCURY_TRACE_COUNTS_PREFIX "mercury_trace_counts"
|
|
#define TEMP_SUFFIX ".tmp"
|
|
|
|
#if defined(F_OK)
|
|
/* unistd.h version */
|
|
#define MR_PERMISSIONS F_OK
|
|
#else
|
|
/* win32 version */
|
|
#define MR_PERMISSIONS 0
|
|
#endif
|
|
|
|
#define MR_FILE_EXISTS(filename) (access(filename, MR_PERMISSIONS) == 0)
|
|
|
|
void
|
|
MR_trace_record_label_exec_counts(void *dummy)
|
|
{
|
|
FILE *fp;
|
|
char *name;
|
|
unsigned name_len;
|
|
MR_bool summarize;
|
|
MR_bool keep;
|
|
char *slash;
|
|
const char *program_name;
|
|
|
|
program_name = MR_copy_string(MR_progname);
|
|
slash = strrchr(program_name, '/');
|
|
if (slash != NULL) {
|
|
program_name = slash + 1;
|
|
}
|
|
|
|
summarize = MR_FALSE;
|
|
keep = MR_FALSE;
|
|
if (MR_trace_count_summary_file != NULL) {
|
|
if (MR_FILE_EXISTS(MR_trace_count_summary_file)) {
|
|
int i;
|
|
|
|
/* 30 bytes must be enough for the dot, the value of i, and '\0' */
|
|
name_len = strlen(MR_trace_count_summary_file) + 30;
|
|
name = MR_malloc(name_len);
|
|
|
|
fp = NULL;
|
|
/* search for a suffix that doesn't exist yet */
|
|
for (i = 1; i <= MR_trace_count_summary_max; i++) {
|
|
snprintf(name, name_len, "%s.%d",
|
|
MR_trace_count_summary_file, i);
|
|
if (! MR_FILE_EXISTS(name)) {
|
|
/* file doesn't exist, commit to this one */
|
|
if (i == MR_trace_count_summary_max) {
|
|
summarize = MR_TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
/*
|
|
** The summary file doesn't yet exist, create it.
|
|
*/
|
|
name = MR_copy_string(MR_trace_count_summary_file);
|
|
}
|
|
} else if (MR_trace_counts_file) {
|
|
name = MR_copy_string(MR_trace_counts_file);
|
|
keep = MR_TRUE;
|
|
} else {
|
|
char *s;
|
|
|
|
/*
|
|
** If no trace counts file name is provided, then we generate
|
|
** a file name.
|
|
*/
|
|
|
|
/* 100 bytes must be enough for the process id, dots and '\0' */
|
|
name_len = strlen(MERCURY_TRACE_COUNTS_PREFIX) + strlen(program_name)
|
|
+ 100;
|
|
name = MR_malloc(name_len);
|
|
snprintf(name, name_len, ".%s.%s.%d", MERCURY_TRACE_COUNTS_PREFIX,
|
|
program_name, getpid());
|
|
|
|
/* make sure name is an acceptable filename */
|
|
for (s = name; *s != '\0'; s++) {
|
|
if (*s == '/') {
|
|
*s = '_';
|
|
}
|
|
}
|
|
}
|
|
|
|
fp = fopen(name, "w");
|
|
if (fp != NULL) {
|
|
unsigned num_written;
|
|
|
|
num_written = MR_trace_write_label_exec_counts(fp,
|
|
program_name, MR_coverage_test_enabled);
|
|
(void) fclose(fp);
|
|
|
|
if (num_written == 0 && !keep) {
|
|
/*
|
|
** We did not write out any trace counts, so there is nothing
|
|
** to gather.
|
|
*/
|
|
|
|
(void) unlink(name);
|
|
summarize = MR_FALSE;
|
|
}
|
|
} else {
|
|
fprintf(stderr, "%s: %s\n", name, strerror(errno));
|
|
/*
|
|
** You can't summarize a file list if you can't create
|
|
** one of its files.
|
|
*/
|
|
summarize = MR_FALSE;
|
|
}
|
|
|
|
free(name);
|
|
|
|
if (summarize) {
|
|
char *cmd;
|
|
unsigned cmd_len;
|
|
int summary_status;
|
|
int mv_status;
|
|
int unlink_status;
|
|
int i;
|
|
const char *old_options;
|
|
|
|
/* 30 bytes must be enough for the dot, the value of i, and space */
|
|
name_len = strlen(MR_trace_count_summary_file) + 30;
|
|
name = MR_malloc(name_len);
|
|
|
|
cmd_len = strlen(MR_trace_count_summary_cmd) + 4;
|
|
cmd_len += strlen(MR_trace_count_summary_file)
|
|
+ strlen(TEMP_SUFFIX) + 1;
|
|
cmd_len += (MR_trace_count_summary_max + 1) * name_len;
|
|
cmd_len += 100;
|
|
|
|
cmd = MR_malloc(cmd_len);
|
|
|
|
strcpy(cmd, MR_trace_count_summary_cmd);
|
|
strcat(cmd, " -o ");
|
|
strcat(cmd, MR_trace_count_summary_file);
|
|
strcat(cmd, TEMP_SUFFIX);
|
|
strcat(cmd, " ");
|
|
strcat(cmd, MR_trace_count_summary_file);
|
|
|
|
for (i = 1; i <= MR_trace_count_summary_max; i++) {
|
|
snprintf(name, name_len, "%s.%d", MR_trace_count_summary_file, i);
|
|
strcat(cmd, " ");
|
|
strcat(cmd, name);
|
|
}
|
|
|
|
strcat(cmd, " > /dev/null 2>&1");
|
|
|
|
old_options = getenv("MERCURY_OPTIONS");
|
|
if (old_options != NULL) {
|
|
(void) MR_setenv("MERCURY_OPTIONS", "", MR_TRUE);
|
|
summary_status = system(cmd);
|
|
(void) MR_setenv("MERCURY_OPTIONS", old_options, MR_TRUE);
|
|
} else {
|
|
summary_status = system(cmd);
|
|
}
|
|
|
|
if (summary_status == 0) {
|
|
strcpy(cmd, "mv ");
|
|
strcat(cmd, MR_trace_count_summary_file);
|
|
strcat(cmd, TEMP_SUFFIX);
|
|
strcat(cmd, " ");
|
|
strcat(cmd, MR_trace_count_summary_file);
|
|
mv_status = system(cmd);
|
|
|
|
if (mv_status == 0) {
|
|
/* delete all files whose data is now in the summary file */
|
|
for (i = 1; i <= MR_trace_count_summary_max; i++) {
|
|
snprintf(name, name_len, "%s.%d",
|
|
MR_trace_count_summary_file, i);
|
|
unlink_status = unlink(name);
|
|
if (unlink_status != 0) {
|
|
MR_fatal_error(
|
|
"couldn't create summary of trace data");
|
|
}
|
|
}
|
|
} else {
|
|
MR_fatal_error("couldn't create summary of trace data");
|
|
}
|
|
} else {
|
|
MR_fatal_error("couldn't create summary of trace data");
|
|
}
|
|
|
|
free(name);
|
|
free(cmd);
|
|
}
|
|
}
|
|
|
|
unsigned
|
|
MR_trace_write_label_exec_counts(FILE *fp, const char *progname,
|
|
MR_bool coverage_test)
|
|
{
|
|
const MR_ModuleLayout *module;
|
|
const MR_ModuleFileLayout *file;
|
|
int num_modules;
|
|
int num_files;
|
|
int module_num;
|
|
int file_num;
|
|
unsigned num_written;
|
|
char *s;
|
|
|
|
MR_trace_name_count_port_ensure_init();
|
|
|
|
fprintf(fp, "%s", MR_TRACE_COUNT_FILE_ID);
|
|
if (coverage_test) {
|
|
fputs("single_file(base_count_file_type(user_all, ", fp);
|
|
} else {
|
|
fputs("single_file(base_count_file_type(user_nonzero, ", fp);
|
|
}
|
|
|
|
MR_trace_write_string(fp, progname);
|
|
fputs(")).\n", fp);
|
|
|
|
num_modules = MR_module_info_next;
|
|
num_written = 0;
|
|
for (module_num = 0; module_num < num_modules; module_num++) {
|
|
module = MR_module_infos[module_num];
|
|
num_files = module->MR_ml_filename_count;
|
|
|
|
fputs("module ", fp);
|
|
MR_trace_write_quoted_atom(fp, module->MR_ml_name);
|
|
fputc('\n', fp);
|
|
|
|
for (file_num = 0; file_num < num_files; file_num++) {
|
|
file = module->MR_ml_module_file_layout[file_num];
|
|
num_written += MR_trace_write_label_exec_counts_for_file(fp,
|
|
module, file, module->MR_ml_name, coverage_test);
|
|
}
|
|
}
|
|
|
|
return num_written;
|
|
}
|
|
|
|
static unsigned
|
|
MR_trace_write_label_exec_counts_for_file(FILE *fp,
|
|
const MR_ModuleLayout *module, const MR_ModuleFileLayout *file,
|
|
const char *module_name, MR_bool coverage_test)
|
|
{
|
|
const MR_LabelLayout *label;
|
|
const MR_ProcLayout *prev_proc;
|
|
const MR_ProcLayout *proc;
|
|
const MR_UserProcId *id;
|
|
MR_TracePort port;
|
|
int num_labels;
|
|
int label_num;
|
|
int label_index;
|
|
unsigned num_written;
|
|
MR_Unsigned exec_count;
|
|
MR_PathPort path_port;
|
|
|
|
fputs("file ", fp);
|
|
MR_trace_write_quoted_atom(fp, file->MR_mfl_filename);
|
|
fputc('\n', fp);
|
|
|
|
prev_proc = NULL;
|
|
num_labels = file->MR_mfl_label_count;
|
|
num_written = 0;
|
|
for (label_num = 0; label_num < num_labels; label_num++) {
|
|
label = file->MR_mfl_label_layout[label_num];
|
|
proc = label->MR_sll_entry;
|
|
label_index = label->MR_sll_label_num_in_module;
|
|
exec_count = module->MR_ml_label_exec_count[label_index];
|
|
if (! MR_PROC_LAYOUT_IS_UCI(proc) && label_index > 0 &&
|
|
(exec_count > 0 || coverage_test))
|
|
{
|
|
num_written++;
|
|
|
|
id = &proc->MR_sle_user;
|
|
if (proc != prev_proc) {
|
|
if (MR_strdiff(module_name, id->MR_user_def_module)) {
|
|
MR_fatal_error(
|
|
"MR_trace_write_label_exec_counts_for_file: "
|
|
"module name mismatch");
|
|
}
|
|
|
|
if (id->MR_user_pred_or_func == MR_PREDICATE) {
|
|
fputs("pproc", fp);
|
|
} else {
|
|
fputs("fproc", fp);
|
|
}
|
|
|
|
if (MR_strdiff(module_name, id->MR_user_decl_module)) {
|
|
/* turn pproc/fproc into pprocdecl/fprocdecl */
|
|
fputs("decl ", fp);
|
|
MR_trace_write_quoted_atom(fp, id->MR_user_decl_module);
|
|
}
|
|
|
|
fputc(' ', fp);
|
|
MR_trace_write_quoted_atom(fp, id->MR_user_name);
|
|
fprintf(fp, " %d %d\n", id->MR_user_arity, id->MR_user_mode);
|
|
}
|
|
|
|
port = label->MR_sll_port;
|
|
path_port = MR_named_count_port[port];
|
|
|
|
switch (path_port) {
|
|
|
|
case PORT_ONLY:
|
|
fputs(MR_actual_port_names[port], fp);
|
|
break;
|
|
|
|
case PATH_ONLY:
|
|
putc('<', fp);
|
|
fputs(MR_label_goal_path(label), fp);
|
|
putc('>', fp);
|
|
break;
|
|
|
|
case PORT_AND_PATH:
|
|
fputs(MR_actual_port_names[port], fp);
|
|
putc(' ', fp);
|
|
putc('<', fp);
|
|
fputs(MR_label_goal_path(label), fp);
|
|
putc('>', fp);
|
|
break;
|
|
|
|
default:
|
|
MR_fatal_error(
|
|
"MR_trace_write_label_exec_counts_for_file: "
|
|
"bad path_port");
|
|
break;
|
|
}
|
|
|
|
putc(' ', fp);
|
|
fprintf(fp, "%d", file->MR_mfl_label_lineno[label_num]);
|
|
|
|
if (exec_count > 0) {
|
|
putc(' ', fp);
|
|
fprintf(fp, "%" MR_INTEGER_LENGTH_MODIFIER "u", exec_count);
|
|
}
|
|
|
|
putc('\n', fp);
|
|
|
|
prev_proc = proc;
|
|
}
|
|
}
|
|
|
|
return num_written;
|
|
}
|
|
|
|
void
|
|
MR_trace_name_count_port_ensure_init()
|
|
{
|
|
static MR_bool done = MR_FALSE;
|
|
|
|
MR_do_init_modules_debugger();
|
|
|
|
if (! done) {
|
|
MR_TracePort port;
|
|
|
|
for (port = MR_PORT_CALL; port <= MR_PORT_NONE; port++) {
|
|
MR_named_count_port[port] = PATH_ONLY;
|
|
}
|
|
|
|
MR_named_count_port[MR_PORT_CALL] = PORT_ONLY;
|
|
MR_named_count_port[MR_PORT_EXIT] = PORT_ONLY;
|
|
MR_named_count_port[MR_PORT_REDO] = PORT_ONLY;
|
|
MR_named_count_port[MR_PORT_FAIL] = PORT_ONLY;
|
|
|
|
MR_named_count_port[MR_PORT_NEG_ENTER] = PORT_AND_PATH;
|
|
MR_named_count_port[MR_PORT_NEG_SUCCESS] = PORT_AND_PATH;
|
|
MR_named_count_port[MR_PORT_NEG_FAILURE] = PORT_AND_PATH;
|
|
|
|
done = MR_TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** The output of this is supposed to be equivalent to term_io__quote_atom
|
|
** except that it always uses quotes, even if not strictly necessary.
|
|
*/
|
|
static void
|
|
MR_trace_write_quoted_atom(FILE *fp, const char *atom)
|
|
{
|
|
const char *c;
|
|
|
|
fputc('\'', fp);
|
|
for (c = atom; *c != '\0'; c++) {
|
|
switch (*c) {
|
|
case '\'':
|
|
fputs("\\'", fp);
|
|
break;
|
|
case '"':
|
|
fputs("\\\"", fp);
|
|
break;
|
|
case '\\':
|
|
fputs("\\\\", fp);
|
|
break;
|
|
case '\n':
|
|
fputs("\\n", fp);
|
|
break;
|
|
case '\t':
|
|
fputs("\\t", fp);
|
|
break;
|
|
case '\b':
|
|
fputs("\\b", fp);
|
|
break;
|
|
default:
|
|
/* This assumes isalnum is the same as char__isalnum.
|
|
** The line noise is the equivalent of
|
|
** is_mercury_punctuation_char in library/term_io.m
|
|
** and compiler/mercury_to_mercury.m; any changes here
|
|
** may require similar changes there.
|
|
*/
|
|
if (MR_isalnum(*c) ||
|
|
strchr(" !@#$%^&*()-_+=`~{}[];:'\"<>.,/?\\|", *c))
|
|
{
|
|
fputc(*c, fp);
|
|
} else {
|
|
fprintf(fp, "\\%03o\\", *c);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
fputc('\'', fp);
|
|
}
|
|
|
|
/*
|
|
** The output of this is supposed to be equivalent to writing out a string.
|
|
*/
|
|
|
|
static void
|
|
MR_trace_write_string(FILE *fp, const char *atom)
|
|
{
|
|
const char *c;
|
|
|
|
fputc('\"', fp);
|
|
for (c = atom; *c != '\0'; c++) {
|
|
switch (*c) {
|
|
case '"':
|
|
fputs("\\\"", fp);
|
|
break;
|
|
case '\\':
|
|
fputs("\\\\", fp);
|
|
break;
|
|
case '\n':
|
|
fputs("\\n", fp);
|
|
break;
|
|
case '\t':
|
|
fputs("\\t", fp);
|
|
break;
|
|
case '\b':
|
|
fputs("\\b", fp);
|
|
break;
|
|
default:
|
|
fputc(*c, fp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
fputc('\"', fp);
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*
|
|
** This section of this file deals with the actions executed at the start
|
|
** and end of execution.
|
|
*/
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
MR_bool MR_saved_tabledebug;
|
|
#endif
|
|
|
|
void
|
|
MR_trace_init(void)
|
|
{
|
|
#ifdef MR_TABLE_DEBUG
|
|
/*
|
|
** We don't want to see any tabling debugging messages from
|
|
** initialization code about entering and leaving commit goals.
|
|
*/
|
|
|
|
MR_saved_tabledebug = MR_tabledebug;
|
|
MR_tabledebug = MR_FALSE;
|
|
#endif
|
|
|
|
#ifdef MR_USE_EXTERNAL_DEBUGGER
|
|
if (MR_trace_handler == MR_TRACE_EXTERNAL) {
|
|
if (MR_address_of_trace_init_external != NULL) {
|
|
MR_address_of_trace_init_external();
|
|
} else {
|
|
MR_tracing_not_enabled();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
MR_trace_final(void)
|
|
{
|
|
#ifdef MR_USE_EXTERNAL_DEBUGGER
|
|
if (MR_trace_handler == MR_TRACE_EXTERNAL) {
|
|
if (MR_address_of_trace_final_external != NULL) {
|
|
MR_address_of_trace_final_external();
|
|
} else {
|
|
MR_tracing_not_enabled();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
** If mdb started a window, make sure it dies now.
|
|
*/
|
|
if (MR_trace_shutdown != NULL) {
|
|
(*MR_trace_shutdown)();
|
|
}
|
|
}
|
|
|
|
void
|
|
MR_trace_start(MR_bool enabled)
|
|
{
|
|
MR_trace_event_number = 0;
|
|
MR_trace_call_seqno = 0;
|
|
MR_trace_call_depth = 0;
|
|
MR_trace_from_full = MR_TRUE;
|
|
MR_debug_enabled = enabled;
|
|
MR_update_trace_func_enabled();
|
|
|
|
#ifdef MR_TABLE_DEBUG
|
|
/*
|
|
** Restore the value saved by MR_trace_init.
|
|
*/
|
|
|
|
MR_tabledebug = MR_saved_tabledebug;
|
|
#endif
|
|
|
|
/*
|
|
** Install the SIGINT signal handler.
|
|
** We only do this if tracing is enabled, and only
|
|
** for the internal debugger. (This is a bit conservative:
|
|
** it might work fine for the external debugger too,
|
|
** but I'm just not certain of that.)
|
|
*/
|
|
if (enabled &&
|
|
MR_address_of_trace_interrupt_handler != NULL &&
|
|
MR_trace_handler == MR_TRACE_INTERNAL)
|
|
{
|
|
MR_setup_signal(SIGINT,
|
|
(MR_Code *) MR_address_of_trace_interrupt_handler,
|
|
MR_FALSE, "mdb: cannot install SIGINT signal handler");
|
|
}
|
|
}
|
|
|
|
void
|
|
MR_trace_end(void)
|
|
{
|
|
MR_debug_enabled = MR_FALSE;
|
|
MR_update_trace_func_enabled();
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*
|
|
** This section of this file deals with the standardization of event and
|
|
** call sequence numbers. We use standardized event and call numbers to
|
|
** reduce the number of .exp files we need to create for debugger test cases.
|
|
*/
|
|
|
|
#define MR_STANDARD_HASH_TABLE_SIZE 1024
|
|
|
|
typedef struct {
|
|
MR_Unsigned MR_std_orig_number;
|
|
MR_Unsigned MR_std_std_number;
|
|
} MR_Standard_Hash_Record;
|
|
|
|
static const void *
|
|
MR_get_orig_number(const void *record)
|
|
{
|
|
return (const void *)
|
|
((MR_Standard_Hash_Record *) record)->MR_std_orig_number;
|
|
}
|
|
|
|
static int
|
|
MR_hash_orig_number(const void *orig_number)
|
|
{
|
|
return (int) (((MR_Unsigned) orig_number) % MR_STANDARD_HASH_TABLE_SIZE);
|
|
}
|
|
|
|
static MR_bool
|
|
MR_equal_orig_number(const void *orig_number1, const void *orig_number2)
|
|
{
|
|
return (MR_Unsigned) orig_number1 == (MR_Unsigned) orig_number2;
|
|
}
|
|
|
|
static MR_Hash_Table MR_standard_event_num_table = {
|
|
MR_STANDARD_HASH_TABLE_SIZE, NULL,
|
|
MR_get_orig_number, MR_hash_orig_number, MR_equal_orig_number
|
|
};
|
|
|
|
static MR_Hash_Table MR_standard_call_num_table = {
|
|
MR_STANDARD_HASH_TABLE_SIZE, NULL,
|
|
MR_get_orig_number, MR_hash_orig_number, MR_equal_orig_number
|
|
};
|
|
|
|
static MR_bool MR_init_event_num_hash = MR_FALSE;
|
|
static MR_bool MR_init_call_num_hash = MR_FALSE;
|
|
|
|
static int MR_next_std_event_num = 1;
|
|
static int MR_next_std_call_num = 1;
|
|
|
|
static MR_Unsigned
|
|
MR_standardize_num(MR_Unsigned num, MR_Hash_Table *table_ptr,
|
|
MR_bool *init_ptr, int *next_ptr)
|
|
{
|
|
const MR_Standard_Hash_Record *record;
|
|
MR_Standard_Hash_Record *new_record;
|
|
int std_num;
|
|
|
|
if (! *init_ptr) {
|
|
*init_ptr = MR_TRUE;
|
|
MR_init_hash_table(*table_ptr);
|
|
}
|
|
|
|
record = MR_lookup_hash_table(*table_ptr, num);
|
|
if (record != NULL) {
|
|
return record->MR_std_std_number;
|
|
}
|
|
|
|
std_num = *next_ptr;
|
|
(*next_ptr)++;
|
|
|
|
new_record = MR_GC_NEW(MR_Standard_Hash_Record);
|
|
new_record->MR_std_orig_number = num;
|
|
new_record->MR_std_std_number = std_num;
|
|
(void) MR_insert_hash_table(*table_ptr, new_record);
|
|
return std_num;
|
|
}
|
|
|
|
MR_Unsigned
|
|
MR_standardize_event_num(MR_Unsigned event_num)
|
|
{
|
|
return MR_standardize_num(event_num, &MR_standard_event_num_table,
|
|
&MR_init_event_num_hash, &MR_next_std_event_num);
|
|
}
|
|
|
|
MR_Unsigned
|
|
MR_standardize_call_num(MR_Unsigned call_num)
|
|
{
|
|
return MR_standardize_num(call_num, &MR_standard_call_num_table,
|
|
&MR_init_call_num_hash, &MR_next_std_call_num);
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*
|
|
** This section of this file reports on trace events so far, for use
|
|
** in messages about abnormal program termination.
|
|
*/
|
|
|
|
char *MR_trace_report_msg = NULL;
|
|
|
|
void
|
|
MR_trace_report(FILE *fp)
|
|
{
|
|
if (MR_trace_event_number > 0) {
|
|
/*
|
|
** This means that the executable was compiled with tracing,
|
|
** which implies that the user wants trace info on abort.
|
|
*/
|
|
|
|
if (MR_trace_report_msg != NULL) {
|
|
fprintf(fp, "%s\n", MR_trace_report_msg);
|
|
}
|
|
|
|
if (MR_standardize_event_details) {
|
|
fprintf(fp, "Last trace event was event #E%ld.\n",
|
|
(long) MR_standardize_event_num(MR_trace_event_number));
|
|
} else {
|
|
fprintf(fp, "Last trace event was event #%ld.\n",
|
|
(long) MR_trace_event_number);
|
|
}
|
|
|
|
#ifdef MR_TRACE_HISTOGRAM
|
|
{
|
|
FILE *hfp;
|
|
|
|
hfp = fopen(MR_TRACE_HISTOGRAM_FILENAME, "w");
|
|
if (hfp != NULL) {
|
|
MR_trace_print_histogram(hfp, "All-inclusive",
|
|
MR_trace_histogram_all, MR_trace_histogram_hwm);
|
|
if (fclose(hfp) == 0) {
|
|
fprintf(fp, "Event histogram put into file `%s'.\n",
|
|
MR_TRACE_HISTOGRAM_FILENAME);
|
|
} else {
|
|
fprintf(fp, "Cannot put event histogram into `%s': %s."
|
|
MR_TRACE_HISTOGRAM_FILENAME, strerror(errno));
|
|
}
|
|
} else {
|
|
fprintf(fp, "Cannot open `%s': %s.\n"
|
|
MR_TRACE_HISTOGRAM_FILENAME, strerror(errno));
|
|
}
|
|
}
|
|
#endif /* MR_TRACE_HISTOGRAM */
|
|
}
|
|
}
|
|
|
|
void
|
|
MR_trace_report_raw(int fd)
|
|
{
|
|
char buf[80]; /* that ought to be more than long enough */
|
|
int ret;
|
|
|
|
if (MR_trace_event_number > 0) {
|
|
/*
|
|
** This means that the executable was compiled with tracing,
|
|
** which implies that the user wants trace info on abort.
|
|
*/
|
|
|
|
if (MR_trace_report_msg != NULL) {
|
|
do {
|
|
/* XXX we don't handle successful but partial writes */
|
|
ret = write(fd, MR_trace_report_msg,
|
|
strlen(MR_trace_report_msg));
|
|
} while (ret == -1 && MR_is_eintr(errno));
|
|
}
|
|
|
|
if (MR_standardize_event_details) {
|
|
sprintf(buf, "Last trace event was event #E%ld.\n",
|
|
(long) MR_standardize_event_num(MR_trace_event_number));
|
|
} else {
|
|
sprintf(buf, "Last trace event was event #%ld.\n",
|
|
(long) MR_trace_event_number);
|
|
}
|
|
do {
|
|
/* XXX we don't handle successful but partial writes */
|
|
ret = write(fd, buf, strlen(buf));
|
|
} while (ret == -1 && MR_is_eintr(errno));
|
|
}
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*
|
|
** This section of this file deals with I/O actions.
|
|
*/
|
|
|
|
MR_bool
|
|
MR_trace_get_action(MR_IoActionNum action_number, MR_ConstString *proc_name_ptr,
|
|
MR_Word *is_func_ptr, MR_Word *arg_list_ptr)
|
|
{
|
|
const MR_TableIoDecl *table_io_decl;
|
|
const MR_ProcLayout *proc_layout;
|
|
MR_ConstString proc_name;
|
|
MR_Word is_func;
|
|
MR_Word arg_list;
|
|
MR_Word arg;
|
|
int filtered_arity;
|
|
int arity;
|
|
int hv;
|
|
MR_TrieNode answer_block_trie;
|
|
MR_Word *answer_block;
|
|
MR_TypeInfo *type_params;
|
|
MR_TypeInfo type_info;
|
|
|
|
if (! (MR_io_tabling_start <= action_number
|
|
&& action_number < MR_io_tabling_counter_hwm))
|
|
{
|
|
return MR_FALSE;
|
|
}
|
|
|
|
MR_TABLE_START_INT(NULL, MR_tabledebug, MR_FALSE,
|
|
answer_block_trie, (MR_TrieNode) &MR_io_tabling_pointer,
|
|
MR_io_tabling_start, action_number);
|
|
answer_block = answer_block_trie->MR_answerblock;
|
|
|
|
if (answer_block == NULL) {
|
|
return MR_FALSE;
|
|
}
|
|
|
|
table_io_decl = (const MR_TableIoDecl *) answer_block[0];
|
|
proc_layout = table_io_decl->MR_table_io_decl_proc;
|
|
filtered_arity = table_io_decl->MR_table_io_decl_filtered_arity;
|
|
|
|
MR_generate_proc_name_from_layout(proc_layout, &proc_name, &arity,
|
|
&is_func);
|
|
|
|
type_params = MR_materialize_answer_block_type_params(
|
|
table_io_decl->MR_table_io_decl_type_params, answer_block,
|
|
filtered_arity);
|
|
|
|
MR_restore_transient_hp();
|
|
arg_list = MR_list_empty();
|
|
MR_save_transient_hp();
|
|
for (hv = filtered_arity; hv >= 1; hv--) {
|
|
type_info = MR_create_type_info(type_params,
|
|
table_io_decl->MR_table_io_decl_ptis[hv - 1]);
|
|
MR_restore_transient_hp();
|
|
MR_new_univ_on_hp(arg, type_info, answer_block[hv]);
|
|
arg_list = MR_univ_list_cons(arg, arg_list);
|
|
MR_save_transient_hp();
|
|
}
|
|
|
|
MR_free(type_params);
|
|
|
|
*proc_name_ptr = proc_name;
|
|
*is_func_ptr = is_func;
|
|
*arg_list_ptr = arg_list;
|
|
return MR_TRUE;
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*
|
|
** This section of this file deals with switching debugging on and off.
|
|
**
|
|
** XXX The code here is very similar to MR_TRACE_CALL_MERCURY in the header
|
|
** file. Look into merging the two pieces of code.
|
|
*/
|
|
|
|
void
|
|
MR_turn_off_debug(MR_SavedDebugState *saved_state,
|
|
MR_bool include_counter_vars)
|
|
{
|
|
int i;
|
|
|
|
saved_state->MR_sds_debug_enabled = MR_debug_enabled;
|
|
saved_state->MR_sds_io_tabling_enabled = MR_io_tabling_enabled;
|
|
MR_debug_enabled = MR_FALSE;
|
|
MR_update_trace_func_enabled();
|
|
MR_io_tabling_enabled = MR_FALSE;
|
|
|
|
for (i = 0; i < MR_MAXFLAG ; i++) {
|
|
saved_state->MR_sds_debugflags[i] = MR_debugflag[i];
|
|
MR_debugflag[i] = MR_FALSE;
|
|
}
|
|
|
|
saved_state->MR_sds_include_counter_vars = include_counter_vars;
|
|
saved_state->MR_sds_trace_call_seqno = MR_trace_call_seqno;
|
|
saved_state->MR_sds_trace_call_depth = MR_trace_call_depth;
|
|
saved_state->MR_sds_trace_event_number = MR_trace_event_number;
|
|
|
|
#if defined(MR_DEEP_PROFILING) && defined(MR_EXEC_TRACE)
|
|
MR_disable_deep_profiling_in_debugger = MR_TRUE;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
MR_turn_debug_back_on(const MR_SavedDebugState *saved_state)
|
|
{
|
|
int i;
|
|
|
|
MR_debug_enabled = saved_state->MR_sds_debug_enabled;
|
|
MR_update_trace_func_enabled();
|
|
MR_io_tabling_enabled = saved_state->MR_sds_io_tabling_enabled;
|
|
|
|
for (i = 0; i < MR_MAXFLAG; i++) {
|
|
MR_debugflag[i] = saved_state->MR_sds_debugflags[i];
|
|
}
|
|
|
|
if (saved_state->MR_sds_include_counter_vars) {
|
|
MR_trace_call_seqno = saved_state->MR_sds_trace_call_seqno;
|
|
MR_trace_call_depth = saved_state->MR_sds_trace_call_depth;
|
|
MR_trace_event_number = saved_state->MR_sds_trace_event_number;
|
|
}
|
|
|
|
#if defined(MR_DEEP_PROFILING) && defined(MR_EXEC_TRACE)
|
|
MR_disable_deep_profiling_in_debugger = MR_FALSE;
|
|
#endif
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*
|
|
** This section of this file deals with recording the value of an exception.
|
|
*/
|
|
|
|
static MR_Word MR_trace_exception_value = (MR_Word) NULL;
|
|
|
|
void
|
|
MR_trace_set_exception_value(MR_Word exception)
|
|
{
|
|
MR_trace_exception_value = exception;
|
|
}
|
|
|
|
MR_Word
|
|
MR_trace_get_exception_value(void)
|
|
{
|
|
return MR_trace_exception_value;
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*
|
|
** This section of this file deals with measuring the distribution of events
|
|
** across depths.
|
|
*/
|
|
|
|
#ifdef MR_TRACE_HISTOGRAM
|
|
|
|
void
|
|
MR_trace_print_histogram(FILE *fp, const char *which, int *histogram, int max)
|
|
{
|
|
int i;
|
|
|
|
fprintf(fp, "%s histogram\n", which);
|
|
for (i = 1; i <= max; i++) {
|
|
fprintf(fp, "depth %4d: %10d", i, histogram[i]);
|
|
if (i + 1 <= max && histogram[i] != 0) {
|
|
fprintf(fp, ", branching factor %7.2f\n",
|
|
(float) histogram[i+1] / (float) histogram[i]);
|
|
} else {
|
|
fprintf(fp, "\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif /* MR_TRACE_HISTOGRAM */
|
|
|
|
/**************************************************************************/
|
|
/*
|
|
** This section of this file deals with statistics about which procedures
|
|
** are respondible for what fraction of I/O table entries.
|
|
*/
|
|
|
|
#define MR_IO_TABLE_STATS_HASH_TABLE_SIZE 1024
|
|
|
|
typedef struct {
|
|
const MR_ProcLayout *MR_io_tabling_stats_proc;
|
|
MR_Unsigned MR_io_tabling_stats_count;
|
|
} MR_IO_Table_Stats_Hash_Record;
|
|
|
|
static const void *
|
|
MR_get_proc_layout(const void *record)
|
|
{
|
|
return (const void *)
|
|
((MR_IO_Table_Stats_Hash_Record *) record)->MR_io_tabling_stats_proc;
|
|
}
|
|
|
|
static int
|
|
MR_hash_proc_layout(const void *proc)
|
|
{
|
|
return (((MR_Unsigned) proc) >> 5) % MR_STANDARD_HASH_TABLE_SIZE;
|
|
}
|
|
|
|
static MR_bool
|
|
MR_equal_proc_layout(const void *proc1, const void *proc2)
|
|
{
|
|
return (const MR_ProcLayout *) proc1 == (const MR_ProcLayout *) proc2;
|
|
}
|
|
|
|
static MR_Hash_Table MR_io_tabling_stats_table = {
|
|
MR_IO_TABLE_STATS_HASH_TABLE_SIZE, NULL,
|
|
MR_get_proc_layout, MR_hash_proc_layout, MR_equal_proc_layout
|
|
};
|
|
|
|
static int MR_io_tabling_stats_sort_arena_next;
|
|
static MR_IO_Table_Stats_Hash_Record *MR_io_tabling_stats_sort_arena;
|
|
|
|
static void MR_add_to_sort_arena(const void *addr);
|
|
static int MR_compare_in_sort_arena(const void *addr1, const void *addr2);
|
|
|
|
static void
|
|
MR_add_to_sort_arena(const void *addr)
|
|
{
|
|
const MR_IO_Table_Stats_Hash_Record *record;
|
|
int next;
|
|
|
|
record = (const MR_IO_Table_Stats_Hash_Record *) addr;
|
|
next = MR_io_tabling_stats_sort_arena_next;
|
|
MR_io_tabling_stats_sort_arena[next].MR_io_tabling_stats_proc =
|
|
record->MR_io_tabling_stats_proc;
|
|
MR_io_tabling_stats_sort_arena[next].MR_io_tabling_stats_count =
|
|
record->MR_io_tabling_stats_count;
|
|
MR_io_tabling_stats_sort_arena_next++;
|
|
}
|
|
|
|
static int
|
|
MR_compare_in_sort_arena(const void *addr1, const void *addr2)
|
|
{
|
|
const MR_IO_Table_Stats_Hash_Record *record1;
|
|
const MR_IO_Table_Stats_Hash_Record *record2;
|
|
|
|
record1 = (const MR_IO_Table_Stats_Hash_Record *) addr1;
|
|
record2 = (const MR_IO_Table_Stats_Hash_Record *) addr2;
|
|
return record2->MR_io_tabling_stats_count -
|
|
record1->MR_io_tabling_stats_count;
|
|
}
|
|
|
|
void
|
|
MR_io_tabling_stats(FILE *fp)
|
|
{
|
|
const MR_TableIoDecl *table_io_decl;
|
|
const MR_ProcLayout *proc_layout;
|
|
MR_ConstString proc_name;
|
|
int arity;
|
|
MR_Word is_func;
|
|
int hv;
|
|
MR_TrieNode answer_block_trie;
|
|
MR_Word *answer_block;
|
|
MR_Hash_Table hash_table;
|
|
MR_IO_Table_Stats_Hash_Record *hash_record;
|
|
MR_IO_Table_Stats_Hash_Record *record;
|
|
int num_entries;
|
|
int count;
|
|
int i;
|
|
|
|
/*
|
|
** Create a fresh new hash table, separate the table created by
|
|
** any previous call to this function. We can't use structure assignment,
|
|
** as that causes gcc 3.2 to throw a fit.
|
|
*/
|
|
hash_table.MR_ht_size = MR_io_tabling_stats_table.MR_ht_size;
|
|
hash_table.MR_ht_store = NULL;
|
|
hash_table.MR_ht_key = MR_io_tabling_stats_table.MR_ht_key;
|
|
hash_table.MR_ht_hash = MR_io_tabling_stats_table.MR_ht_hash;
|
|
hash_table.MR_ht_equal = MR_io_tabling_stats_table.MR_ht_equal;
|
|
MR_init_hash_table(hash_table);
|
|
num_entries = 0;
|
|
|
|
for (i = MR_io_tabling_start; i < MR_io_tabling_counter_hwm; i++) {
|
|
MR_TABLE_START_INT(NULL, MR_FALSE, MR_FALSE, answer_block_trie,
|
|
(MR_TrieNode) &MR_io_tabling_pointer, MR_io_tabling_start, i);
|
|
answer_block = answer_block_trie->MR_answerblock;
|
|
|
|
if (answer_block == NULL) {
|
|
continue;
|
|
}
|
|
|
|
table_io_decl = (const MR_TableIoDecl *) answer_block[0];
|
|
proc_layout = table_io_decl->MR_table_io_decl_proc;
|
|
|
|
hash_record = MR_lookup_hash_table(hash_table, proc_layout);
|
|
if (hash_record == NULL) {
|
|
hash_record = MR_GC_NEW(MR_IO_Table_Stats_Hash_Record);
|
|
hash_record->MR_io_tabling_stats_proc = proc_layout;
|
|
hash_record->MR_io_tabling_stats_count = 1;
|
|
(void) MR_insert_hash_table(hash_table, hash_record);
|
|
num_entries++;
|
|
} else {
|
|
hash_record->MR_io_tabling_stats_count++;
|
|
}
|
|
}
|
|
|
|
MR_io_tabling_stats_sort_arena =
|
|
MR_GC_NEW_ARRAY(MR_IO_Table_Stats_Hash_Record, num_entries);
|
|
MR_io_tabling_stats_sort_arena_next = 0;
|
|
MR_process_all_entries(hash_table, MR_add_to_sort_arena);
|
|
|
|
if (MR_io_tabling_stats_sort_arena_next != num_entries) {
|
|
MR_fatal_error("MR_io_tabling_stats: num_entries mismatch");
|
|
}
|
|
|
|
qsort(MR_io_tabling_stats_sort_arena, num_entries,
|
|
sizeof(MR_IO_Table_Stats_Hash_Record), MR_compare_in_sort_arena);
|
|
|
|
for (i = 0; i < num_entries; i++) {
|
|
record = &MR_io_tabling_stats_sort_arena[i];
|
|
proc_layout = record->MR_io_tabling_stats_proc;
|
|
count = record->MR_io_tabling_stats_count;
|
|
MR_generate_proc_name_from_layout(proc_layout, &proc_name, &arity,
|
|
&is_func);
|
|
|
|
fprintf(fp, "%8d %4s %s/%d\n", count, (is_func ? "func" : "pred"),
|
|
proc_name, arity);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*
|
|
** This section of this file maps proc layouts to materialized procedure
|
|
** body representations.
|
|
**
|
|
** We record information about procedure representations in a hash table
|
|
** that is indexed by the proc layout address.
|
|
**
|
|
** This table is used by the declarative debugger. Since the declarative
|
|
** debugger can be required in any grade, we always include this table, but
|
|
** it is initialized (and the bulk of its memory allocated) only if the
|
|
** declarative debugger is in fact invoked.
|
|
*/
|
|
|
|
#define PROC_DEFN_REP_TABLE_SIZE (1 << 16) /* 64k */
|
|
|
|
typedef struct {
|
|
const MR_ProcLayout *plr_layout;
|
|
MR_Word plr_rep;
|
|
} MR_ProcLayoutRep;
|
|
|
|
static void MR_do_init_proc_defn_rep_table(void);
|
|
static const void *MR_proc_layout_rep_key(const void *proc_layout);
|
|
static int MR_hash_proc_layout_addr(const void *addr);
|
|
static MR_bool MR_equal_proc_layouts(const void *addr1,
|
|
const void *addr2);
|
|
|
|
static MR_Hash_Table proc_defn_rep_table = {
|
|
PROC_DEFN_REP_TABLE_SIZE,
|
|
NULL,
|
|
MR_proc_layout_rep_key,
|
|
MR_hash_proc_layout_addr,
|
|
MR_equal_proc_layouts
|
|
};
|
|
|
|
static void
|
|
MR_do_init_proc_defn_rep_table(void)
|
|
{
|
|
static MR_bool done = MR_FALSE;
|
|
|
|
if (!done) {
|
|
MR_init_hash_table(proc_defn_rep_table);
|
|
done = MR_TRUE;
|
|
}
|
|
}
|
|
|
|
void
|
|
MR_insert_proc_defn_rep(const MR_ProcLayout *proc_layout,
|
|
MR_Word proc_defn_rep)
|
|
{
|
|
MR_ProcLayoutRep *layout_rep;
|
|
|
|
MR_do_init_proc_defn_rep_table();
|
|
|
|
layout_rep = MR_GC_NEW(MR_ProcLayoutRep);
|
|
layout_rep->plr_layout = proc_layout;
|
|
layout_rep->plr_rep = proc_defn_rep;
|
|
|
|
(void) MR_insert_hash_table(proc_defn_rep_table, layout_rep);
|
|
|
|
#ifdef MR_DEBUG_PROC_REP
|
|
if (MR_progdebug) {
|
|
printf("insert: layout %p, rep %x, pair %p\n",
|
|
proc_layout, proc_defn_rep, layout_rep);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MR_Word
|
|
MR_lookup_proc_defn_rep(const MR_ProcLayout *proc_layout)
|
|
{
|
|
const MR_ProcLayoutRep *layout_rep;
|
|
|
|
MR_do_init_proc_defn_rep_table();
|
|
|
|
layout_rep = MR_lookup_hash_table(proc_defn_rep_table, proc_layout);
|
|
if (layout_rep == NULL) {
|
|
#ifdef MR_DEBUG_PROC_REP
|
|
if (MR_progdebug) {
|
|
printf("search for layout %p: not found\n", proc_layout);
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef MR_DEBUG_PROC_REP
|
|
if (MR_progdebug) {
|
|
printf("search for layout %p: found pair %p, rep %x\n",
|
|
proc_layout, layout_rep, layout_rep->plr_rep);
|
|
}
|
|
#endif
|
|
|
|
return layout_rep->plr_rep;
|
|
}
|
|
|
|
static const void *
|
|
MR_proc_layout_rep_key(const void *pair)
|
|
{
|
|
MR_ProcLayoutRep *proc_layout_rep;
|
|
|
|
proc_layout_rep = (MR_ProcLayoutRep *) pair;
|
|
if (proc_layout_rep == NULL) {
|
|
return NULL;
|
|
} else {
|
|
return (const void *) proc_layout_rep->plr_layout;
|
|
}
|
|
}
|
|
|
|
static int
|
|
MR_hash_proc_layout_addr(const void *addr)
|
|
{
|
|
return (((MR_Unsigned) addr) >> 5) % PROC_DEFN_REP_TABLE_SIZE;
|
|
}
|
|
|
|
static MR_bool
|
|
MR_equal_proc_layouts(const void *addr1, const void *addr2)
|
|
{
|
|
return ((const MR_ProcLayout *) addr1) == ((const MR_ProcLayout *) addr2);
|
|
}
|
|
|
|
/**************************************************************************/
|
|
/*
|
|
** This section of this file provides the code that generated redo events.
|
|
** Its labels are pointed to by the temp frames pushed onto the nondet stack
|
|
** by model_non procedures when they exit.
|
|
*/
|
|
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
|
|
MR_define_extern_entry(MR_do_trace_redo_fail_shallow);
|
|
MR_define_extern_entry(MR_do_trace_redo_fail_deep);
|
|
|
|
MR_BEGIN_MODULE(MR_trace_labels_module)
|
|
MR_init_entry_an(MR_do_trace_redo_fail_shallow);
|
|
MR_init_entry_an(MR_do_trace_redo_fail_deep);
|
|
MR_BEGIN_CODE
|
|
|
|
MR_define_entry(MR_do_trace_redo_fail_shallow);
|
|
/*
|
|
** If this code ever needs changing, you may also need to change
|
|
** the code in extras/exceptions/exception.m similarly.
|
|
*/
|
|
if (MR_redo_fromfull_framevar(MR_redofr_slot(MR_curfr))) {
|
|
MR_Code *MR_jumpaddr;
|
|
MR_save_transient_registers();
|
|
MR_jumpaddr = MR_trace((const MR_LabelLayout *)
|
|
MR_redo_layout_framevar(MR_redofr_slot(MR_curfr)));
|
|
MR_restore_transient_registers();
|
|
if (MR_jumpaddr != NULL) {
|
|
MR_GOTO(MR_jumpaddr);
|
|
}
|
|
}
|
|
MR_fail();
|
|
|
|
MR_define_entry(MR_do_trace_redo_fail_deep);
|
|
#if 0
|
|
/* For use in case this ever needs to be debugged again. */
|
|
printf("MR_curfr = %p\n", MR_curfr);
|
|
printf("MR_redofr_slot(MR_curfr) = %p\n", MR_redofr_slot(MR_curfr));
|
|
printf("&MR_redo_layout_framevar(MR_redofr_slot(MR_curfr) = %p\n",
|
|
&MR_redo_layout_framevar(MR_redofr_slot(MR_curfr)));
|
|
printf("MR_redo_layout_framevar(MR_redofr_slot(MR_curfr) = %p\n",
|
|
MR_redo_layout_framevar(MR_redofr_slot(MR_curfr)));
|
|
#endif
|
|
/*
|
|
** If this code ever needs changing, you may also need to change
|
|
** the code in library/exception.m similarly.
|
|
*/
|
|
{
|
|
MR_Code *MR_jumpaddr;
|
|
|
|
MR_save_transient_registers();
|
|
MR_jumpaddr = MR_trace((const MR_LabelLayout *)
|
|
MR_redo_layout_framevar(MR_redofr_slot(MR_curfr)));
|
|
MR_restore_transient_registers();
|
|
if (MR_jumpaddr != NULL) {
|
|
MR_GOTO(MR_jumpaddr);
|
|
}
|
|
}
|
|
MR_fail();
|
|
|
|
MR_END_MODULE
|
|
|
|
#endif /* !MR_HIGHLEVEL_CODE */
|
|
|
|
/* forward decls to suppress gcc warnings */
|
|
void mercury_sys_init_trace_init(void);
|
|
void mercury_sys_init_trace_init_type_tables(void);
|
|
#ifdef MR_DEEP_PROFILING
|
|
void mercury_sys_init_trace_write_out_proc_statics(FILE *fp);
|
|
#endif
|
|
|
|
void mercury_sys_init_trace_init(void)
|
|
{
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
MR_trace_labels_module();
|
|
#endif
|
|
}
|
|
|
|
void mercury_sys_init_trace_init_type_tables(void)
|
|
{
|
|
/* no types to register */
|
|
}
|
|
|
|
#ifdef MR_DEEP_PROFILING
|
|
void mercury_sys_init_trace_write_out_proc_statics(FILE *fp)
|
|
{
|
|
/* no proc_statics to write out */
|
|
}
|
|
#endif
|