mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-28 07:44:43 +00:00
Estimated hours taken: 20 Fergus's recent change to the handling of some builtins broke the tracing of those builtins. The following changes are a fix for this. compiler/polymorphism.m: Export the predicate that checks whether a predicate is a builtin that lacks the usually necessary typeinfos. Comment out a misleading and in any case not very useful progress message. compiler/liveness.m: Turn off type_info liveness for builtins without typeinfos. Since these builtins establish no gc points and shouldn't be execution traced, this is OK. Make type_info liveness part of live_info, since it can now be incorrect to look up the value of the option. (This may yield a speedup.) compiler/live_vars.m: compiler/store_alloc.m: Pass the pred_id to initial_liveness to liveness.m can do the test. compiler/passes_aux.m: Add a new traversal type that passes along the pred_id. compiler/mercury_compile.m: Turn off execution tracing for the modules builtin.m and private_builtin.m. The latter contains the interface predicates for the builtins without typeinfos. Since the interface predicates also lack the typeinfos, the compiler would get an internal abort if we left execution tracing on. In any case, these two modules contain stuff that users should consider language builtins, which means they should not be execution traced (they can still be stack traced in the right grade). Use the new traversal type for the modules that now need the pred_id. compiler/globals.m: Allow the trace level to be set from outside, in this case mercury_compile.m. The next batch of changes have to do with adding a stack dump command to the debugger. Since debugging is possible even in non-debug grades, this in turn requires allowing stack tracing to work in non-debug grades, on programs in which only some modules are compiled with execution (and hence stack) tracing. compiler/llds_out.m: compiler/mercury_compile.m: runtime/mercury_conf_param.h: Llds_out used to output "#include <mercury_imp.h>" as the first substantive thing in the generated C file. The set of #define parameters in effect when mercury_imp.h is processed determines whether the macros that optionally register stack layouts for label actually do so or not. The values of these parameters are derived from the grade, which means that with this setup it is not possible for a non-debug grade program to register its stack layouts in the label table. The new version of llds_out looks up the option that says whether this module is compiled with execution tracing or not, and if it is, it generates a #define MR_STACK_TRACE_THIS_MODULE *before* the #include of mercury_imp.h. This causes mercury_conf_param.h, included from mercury_imp.h, to define the macros MR_USE_STACK_LAYOUTS and and MR_INSERT_LABELS, which in turn cause stack layouts for labels in this module to be generated and to be inserted into the label table, *without* changing the grade string (this last part is why we do not simply define MR_STACK_TRACE). Use the same mechanism to #include mercury_trace.h when doing execution tracing, since it is simpler than the mechanism we used to use (mercury_compile.m including the #include in a list of C header file fragments). compiler/mercury_compile.m: runtime/mercury_conf_param.h: Split the MR_NEED_INITIALIZATION_CODE macro into two parts. The first, MR_MAY_NEED_INITIALIZATION, now controls whether initialization code makes it into the object file of a module. The second, MR_NEED_INITIALIZATION_AT_START, determines whether the initialization code is called before main/2. When a module is compiled with execution tracing, the macro MR_INSERT_LABELS turns on MR_MAY_NEED_INITIALIZATION but not MR_NEED_INITIALIZATION_AT_START. The debugger will make sure that the initialization code has been called before it tries to do a stack dump (which needs the initialization code to have been executed, because it needs labels to have been put into the label table so that from a return address it can find the layout of the proc to which it belongs). Define MR_NEED_INITIALIZATION_AT_START if PROFILE_TIME is defined, since if PROFILE_TIME is defined mercury_wrapper.c calls init_modules. The fact that MR_NEED_INITIALIZATION_CODE didn't used to be defined when PROFILE_TIME was defined was, I believe, a bug, which was not detected because we do not turn on PROFILE_TIME without also turning on PROFILE_CALLS. runtime/mercury_stack_trace.[ch]: Change the way stack dumps are done, to make it possible to print stack dumps from the debugger and to use trivial run-length encoding on the output (so that 100 consecutive calls to p yield the line "p * 100", rather than 100 lines of "p"). The stack routine now returns an indication of whether the stack dump was fully successful, and if not, a description of the reason why not. This requires knowing when we have found the end of the stack dump, so we provide a global variable, MR_stack_trace_bottom, which mercury_wrapper.c will set to global_success, the address main/2 goes to on success. s/multidet/multi/ runtime/mercury_wrapper.c: Set MR_stack_trace_bottom to the address of globals_success. Use MR_NEED_INITIALIZATION_AT_START to decide whether to call do_init_modules. runtime/mercury_stacks.h: Provide variants of detstackvar(n) and framevar(n) that look up sp and curfr in an array of saved regs, for use by the debugger. runtime/mercury_trace_util.c: Use the new variants of detstackvar(n) and framevar(n). This fixes an old bug on SPARCs. runtime/mercury_trace_internal.c: Completely reorganize the way debugger commands are handled. Centralize reading in command lines, and the breaking up of command lines into words. The command names are the same as they were, but command syntax is now much easier to change. Add a new command "d" to dump as much of the stack as the available information will allow. runtime/mercury_goto.h: Cosmetic changes to avoid the use of two different conditional compilation layout styles. util/mkinit.c: Since we cannot know when we generate the _init.c file whether any modules will be compiled with execution tracing and will thus need stack tracing, we must now include in the generated _init.c file the code to call the initialization functions in all the modules, even if MR_NEED_INITIALIZATION_AT_START is not set, since init_modules can be called later, from the debugger. (We should be able to use the same approach with the accurate collector.)
241 lines
5.9 KiB
C
241 lines
5.9 KiB
C
/*
|
|
** Copyright (C) 1998 The University of Melbourne.
|
|
** This file may only be copied under the terms of the GNU Library General
|
|
** Public License - see the file COPYING.LIB in the Mercury distribution.
|
|
*/
|
|
|
|
/*
|
|
** This file implements utilities that can be useful
|
|
** for both the internal and external debuggers.
|
|
**
|
|
** Main authors: Zoltan Somogyi and Fergus Henderson.
|
|
*/
|
|
|
|
#include "mercury_imp.h"
|
|
#include "mercury_stack_layout.h"
|
|
#include "mercury_trace_util.h"
|
|
|
|
Word MR_saved_regs[MAX_FAKE_REG];
|
|
|
|
void
|
|
MR_copy_regs_to_saved_regs(int max_mr_num)
|
|
{
|
|
/*
|
|
** In the process of browsing, we call Mercury code,
|
|
** which may clobber the contents of the virtual machine registers,
|
|
** both control and general purpose, and both real and virtual
|
|
** registers. We must therefore save and restore these.
|
|
** We store them in the MR_saved_regs array.
|
|
**
|
|
** The call to MR_trace will clobber the transient registers
|
|
** on architectures that have them. The compiler generated code
|
|
** will therefore call save_transient_registers to save the transient
|
|
** registers in the fake_reg array. We here restore them to the
|
|
** real registers, save them with the other registers back in
|
|
** fake_reg, and then copy all fake_reg entries to MR_saved_regs.
|
|
**
|
|
** If any code invoked by MR_trace is itself traced,
|
|
** MR_saved_regs will be overwritten, leading to a crash later on.
|
|
** This is one reason (but not the only one) why we turn off
|
|
** tracing when we call back Mercury code from this file.
|
|
*/
|
|
|
|
int i;
|
|
|
|
restore_transient_registers();
|
|
save_registers();
|
|
|
|
for (i = 0; i <= max_mr_num; i++) {
|
|
MR_saved_regs[i] = fake_reg[i];
|
|
}
|
|
}
|
|
|
|
void
|
|
MR_copy_saved_regs_to_regs(int max_mr_num)
|
|
{
|
|
/*
|
|
** We execute the converse procedure to MR_copy_regs_to_saved_regs.
|
|
** The save_transient_registers is there so that a call to the
|
|
** restore_transient_registers macro after MR_trace will do the
|
|
** right thing.
|
|
*/
|
|
|
|
int i;
|
|
|
|
for (i = 0; i <= max_mr_num; i++) {
|
|
fake_reg[i] = MR_saved_regs[i];
|
|
}
|
|
|
|
restore_registers();
|
|
save_transient_registers();
|
|
}
|
|
|
|
Word
|
|
MR_trace_make_var_list(const MR_Stack_Layout_Label *layout)
|
|
{
|
|
int var_count;
|
|
const MR_Stack_Layout_Vars *vars;
|
|
int i;
|
|
const char *name;
|
|
|
|
Word univ_list;
|
|
MR_Stack_Layout_Var* var;
|
|
Word univ, value;
|
|
MR_Live_Type live_type;
|
|
Word type_info;
|
|
|
|
var_count = layout->MR_sll_var_count;
|
|
vars = &layout->MR_sll_var_info;
|
|
|
|
/* build up the live variable list, starting from the end */
|
|
restore_transient_registers();
|
|
univ_list = list_empty();
|
|
save_transient_registers();
|
|
for (i = var_count - 1; i >= 0; i--) {
|
|
/*
|
|
** Look up the name, the type and value
|
|
** (XXX we don't include the name or the inst
|
|
** in the list that we return)
|
|
*/
|
|
|
|
name = MR_name_if_present(vars, i);
|
|
var = &vars->MR_slvs_pairs[i];
|
|
|
|
/*
|
|
** XXX The printing of type_infos is buggy at the moment
|
|
** due to the fake arity of private_builtin:typeinfo/1.
|
|
**
|
|
** XXX The printing of large data structures is painful
|
|
** at the moment due to the lack of a true browser.
|
|
**
|
|
** "variables" representing the saved values of succip, hp etc,
|
|
** which are the "variables" for which get_type_and_value
|
|
** fails, are not of interest to the trace analyzer.
|
|
*/
|
|
|
|
if ((strncmp(name, "TypeInfo", 8) == 0)
|
|
|| (strncmp(name, "ModuleInfo", 10) == 0)
|
|
|| (strncmp(name, "HLDS", 4) == 0)
|
|
|| !MR_trace_get_type_and_value(var, NULL, &type_info, &value))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
** Create a term of type `univ' to hold the type & value,
|
|
** and cons it onto the list.
|
|
** Note that the calls to save/restore transient registers
|
|
** can't be hoisted out of the loop, because
|
|
** MR_trace_get_type_and_value() calls MR_create_type_info()
|
|
** which may allocate memory using incr_saved_hp.
|
|
*/
|
|
|
|
restore_transient_registers();
|
|
incr_hp(univ, 2);
|
|
field(mktag(0), univ, UNIV_OFFSET_FOR_TYPEINFO) = type_info;
|
|
field(mktag(0), univ, UNIV_OFFSET_FOR_DATA) = value;
|
|
|
|
univ_list = list_cons(univ, univ_list);
|
|
save_transient_registers();
|
|
}
|
|
|
|
return univ_list;
|
|
}
|
|
|
|
/* if you want to debug this code, you may want to set this var to TRUE */
|
|
static bool MR_trace_print_locn = FALSE;
|
|
|
|
Word
|
|
MR_trace_lookup_live_lval(MR_Live_Lval locn, bool *succeeded)
|
|
{
|
|
int locn_num;
|
|
Word value;
|
|
|
|
*succeeded = FALSE;
|
|
value = 0;
|
|
|
|
locn_num = (int) MR_LIVE_LVAL_NUMBER(locn);
|
|
switch (MR_LIVE_LVAL_TYPE(locn)) {
|
|
case MR_LVAL_TYPE_R:
|
|
if (MR_trace_print_locn)
|
|
printf("r%d", locn_num);
|
|
value = saved_reg(MR_saved_regs, locn_num);
|
|
*succeeded = TRUE;
|
|
break;
|
|
|
|
case MR_LVAL_TYPE_F:
|
|
if (MR_trace_print_locn)
|
|
printf("f%d", locn_num);
|
|
break;
|
|
|
|
case MR_LVAL_TYPE_STACKVAR:
|
|
if (MR_trace_print_locn)
|
|
printf("stackvar%d", locn_num);
|
|
value = saved_detstackvar(MR_saved_regs, locn_num);
|
|
*succeeded = TRUE;
|
|
break;
|
|
|
|
case MR_LVAL_TYPE_FRAMEVAR:
|
|
if (MR_trace_print_locn)
|
|
printf("framevar%d", locn_num);
|
|
value = saved_framevar(MR_saved_regs, locn_num);
|
|
*succeeded = TRUE;
|
|
break;
|
|
|
|
case MR_LVAL_TYPE_SUCCIP:
|
|
if (MR_trace_print_locn)
|
|
printf("succip");
|
|
break;
|
|
|
|
case MR_LVAL_TYPE_MAXFR:
|
|
if (MR_trace_print_locn)
|
|
printf("maxfr");
|
|
break;
|
|
|
|
case MR_LVAL_TYPE_CURFR:
|
|
if (MR_trace_print_locn)
|
|
printf("curfr");
|
|
break;
|
|
|
|
case MR_LVAL_TYPE_HP:
|
|
if (MR_trace_print_locn)
|
|
printf("hp");
|
|
break;
|
|
|
|
case MR_LVAL_TYPE_SP:
|
|
if (MR_trace_print_locn)
|
|
printf("sp");
|
|
break;
|
|
|
|
case MR_LVAL_TYPE_UNKNOWN:
|
|
if (MR_trace_print_locn)
|
|
printf("unknown");
|
|
break;
|
|
|
|
default:
|
|
if (MR_trace_print_locn)
|
|
printf("DEFAULT");
|
|
break;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
bool
|
|
MR_trace_get_type_and_value(const MR_Stack_Layout_Var *var,
|
|
Word *type_params, Word *type_info, Word *value)
|
|
{
|
|
bool succeeded;
|
|
Word *pseudo_type_info;
|
|
int i;
|
|
|
|
if (!MR_LIVE_TYPE_IS_VAR(var->MR_slv_live_type)) {
|
|
return FALSE;
|
|
}
|
|
|
|
pseudo_type_info = MR_LIVE_TYPE_GET_VAR_TYPE(var->MR_slv_live_type);
|
|
*type_info = (Word) MR_create_type_info(type_params, pseudo_type_info);
|
|
*value = MR_trace_lookup_live_lval(var->MR_slv_locn, &succeeded);
|
|
return succeeded;
|
|
}
|