Files
mercury/runtime/mercury_label.c
Zoltan Somogyi 05ef8e01fb Rename the .ll_debug grade component to .c_debug.
Rename mmc and mgnuc options that set this grade component to --c-debug-grade.
Let the options named --c-debug of both mmc and mgnuc enable C level debugging
of only the module being compiled.

runtime/mercury_grade.h:
    Rename the .ll_debug grade component to .c_debug. Also rename the C macro
    that controls the presence or absence of this grade component
    from MR_LL_DEBUG to MR_C_DEBUG_GRADE.

runtime/mercury_conf_param.h:
runtime/mercury_debug.c:
runtime/mercury_debug.h:
runtime/mercury_engine.c:
runtime/mercury_label.c:
runtime/mercury_memory_zones.c:
runtime/mercury_memory_zones.h:
runtime/mercury_overflow.c:
runtime/mercury_std.h:
runtime/mercury_wrapper.c:
    Rename the MR_LOWLEVEL_DEBUG macro to MR_DEBUG_THE_RUNTIME.
    Previously, the name of this macro wrongly implied that it had
    something to do with the old .ll_debug grade component, even though

    - the MR_LOWLEVEL_DEBUG macro was designed to debug LLDS grades,
      since only these existed when it was created, while

    - the .ll_debug grade component (now .c_debug) is useful only for
      MLDS grades targeting C.

compiler/options.m:
    Rename the old confusingly named low_level_debug option to c_debug_grade.
    Move it to the list of grade options, and fix its documentation, which
    was completely wrong:

    - code in compile_target_code.m treated it as being a synonym of
      the .ll_debug (now .c_debug) grade component, while
    - its (commented out) documentation here in options.m said it called for
      the enabling of what is now MR_DEBUG_THE_RUNTIME.

compiler/compile_target_code.m:
    Conform to the rename just above.

    Define MR_C_DEBUG_GRADE instead of MR_LL_DEBUG if c_debug_grade is enabled.

    Pass -g to the C compiler if either c_debug_grade or target_debug
    is enabled.

    Add an XXX about a missing safety check for an obsolete experimental
    feature.

compiler/compute_grade.m:
    When given a grade with a .c_debug grade component, set only the
    c_debug_grade option; don't set the target_debug option, which is NOT
    a grade option. The change to compile_target_code.m above handles the
    only situation in which this implication was formerly required.

scripts/canonical_grade.sh-subr:
scripts/init_grade_options.sh-subr:
scripts/parse_grade_options.sh-subr:
    Look for and process the .c_debug grade component instead of .ll_debug.
    Use a sh variable named c_debug_grade to record its absence/presence.

    Look for and process the --c-debug-grade grade-component option,
    setting the same sh variable, c_debug_grade. (All grade components
    can be set piecemeal using sh options to the scripts using these
    subroutines.) This replaces the old, confusingly named option
    --low-level-debug.

scripts/mgnuc.in:
scripts/mgnuc_file_opts.sh-subr:
    Consistently use the sh variable c_debug to record the presence of
    the (non-grade) --c-debug option to mgnuc, and the sh variable
    c_debug_grade to record the presence of the .c_debug grade component.

    Stop looking for and handling the --low-level-debug option, which
    mgnuc used to document, even though this duplicated the same documentation
    in init_grade_options.sh-subr, which mgnuc includes. The difference was
    that init_grade_options.sh-subr meant it to represent the old .ll_debug
    MLDS grade component, while mgnuc treated it as specifying what is now
    MR_DEBUG_THE_RUNTIME for LLDS grades. It didn't help that two sh variables
    with quite different semantics had names that differed only in an
    underscore: LLDEBUG_OPTS vs LL_DEBUG_OPTS.

scripts/Mmakefile:
    Add a missing dependency to force the rebuild of mgnuc after each update
    of its sh subroutine mgnuc_file_ops.sh-subr.

doc/user_guide.texi:
    Document the --c-debug-grade option of mmc. This option was not publicly
    documented under its original misleading name (--low-level-debug), but
    its documentation is now possible without contorted dancing around the
    name.

    Clarify the documentation of mgnuc's --c-debug option.

README.sanitizers:
configure.ac:
    Conform to the rename of the grade component.

grade_lib/grade_spec.m:
grade_lib/grade_string.m:
grade_lib/grade_structure.m:
grade_lib/try_all_grade_structs.m:
    Conform to the rename of the grade component .ll_debug to .c_debug.

    Don't allow the .c_debug grade component in LLDS grades.

    In grade_string.m, add some obvious implications of some grade components.

grade_lib/choose_grade.m:
grade_lib/grade_lib.m:
grade_lib/test_grades.m:
grade_lib/var_value_names.m:
    Fix white space.

scripts/ml.in:
tools/lmc.in:
tools/test_mercury:
    Conform to the change in compile_target_code.m to the naming of
    Boehm gc library variants.
2022-12-29 20:33:08 +11:00

348 lines
9.8 KiB
C

// vim: ts=4 sw=4 expandtab ft=c
// Copyright (C) 1994-2001, 2003-2004, 2006-2007 The University of Melbourne.
// Copyright (C) 2014, 2016, 2018 The Mercury team.
// This file is distributed under the terms specified in COPYING.LIB.
// label.c defines the label table, which is a pair of hash tables
// that map from procedure names to addresses and vice versa.
#include "mercury_imp.h" // we need libmer_dll.h for Windows DLLs
#include <stdio.h>
#include <string.h>
#include "mercury_label.h"
#include "mercury_stack_layout.h" // for MR_ProcLayout
#include "mercury_hash_table.h" // for MR_Hash_Table and its ops
#include "mercury_prof.h" // for prof_output_addr_decl()
#include "mercury_engine.h" // for MR_progdebug
#include "mercury_wrapper.h" // for MR_do_init_modules()
#if defined(MR_MINIMAL_MODEL_DEBUG) && !defined(MR_TABLE_DEBUG)
// MR_MINIMAL_MODEL_DEBUG implies MR_TABLE_DEBUG in some other files, since
// if we want to debug minimal model tabling we need to enable all the
// debugging facilities of those files. However, since MR_TABLE_DEBUG
// increases object file sizes and link times significantly (by implying
// MR_DEBUG_LABEL_NAMES), we don't necessarily want this implication
// to hold globally. MR_TABLE_DEBUG therefore may not be defined globally.
// If it isn't defined for this file, MR_NEED_ENTRY_LABEL_INFO and
// MR_NEED_ENTRY_LABEL_ARRAY probably won't be defined either.
#define MR_NEED_ENTRY_LABEL_INFO
#define MR_NEED_ENTRY_LABEL_ARRAY
#endif
// We record information about entry labels in an array that we sort
// by code address once all the entry labels have been inserted.
// Space for the array is provided by malloc, and it is expanded when needed.
//
// This array is needed only by accurate garbage collection and when
// doing low-level debugging.
#ifdef MR_NEED_ENTRY_LABEL_ARRAY
// the number of entries in the initial array
#define INIT_ENTRY_SIZE (1 << 8)
static MR_Entry *entry_array;
static int entry_array_size; // # of entries allocated
static int entry_array_next; // # of entries used
static MR_bool entry_array_sorted;
#endif // MR_NEED_ENTRY_LABEL_ARRAY
// We record information about internal labels in a hash table
// that is indexed by the code address of the label.
//
// This table is used by stack tracing and execution tracing.
// Since execution tracing and hence stack tracing can be required
// in any grade, we always include this table.
#define INTERNAL_SIZE (1 << 16) // 64k
static const void *internal_addr(const void *internal);
static MR_bool equal_addr(const void *addr1, const void *addr2);
static int hash_addr(const void *addr);
static MR_Hash_Table internal_addr_table = {INTERNAL_SIZE, NULL,
internal_addr, hash_addr, equal_addr};
void
MR_do_init_label_tables(void)
{
static MR_bool done = MR_FALSE;
if (!done) {
#ifdef MR_NEED_ENTRY_LABEL_ARRAY
entry_array_next = 0;
entry_array_size = INIT_ENTRY_SIZE;
entry_array_sorted = MR_TRUE;
entry_array = MR_NEW_ARRAY(MR_Entry, entry_array_size);
#endif
MR_init_hash_table(internal_addr_table);
done = MR_TRUE;
}
}
#ifdef MR_NEED_ENTRY_LABEL_INFO
void
MR_do_insert_entry_label(const char *name, MR_Code *addr,
const MR_ProcLayout *entry_layout)
{
MR_do_init_label_tables();
#ifdef MR_MPROF_PROFILE_CALLS
if (MR_profiling) {
MR_prof_output_addr_decl(name, addr);
}
#endif // MR_MPROF_PROFILE_CALLS
#ifdef MR_DEBUG_THE_RUNTIME
if (MR_progdebug) {
// We can't assume that MR_DEBUG_THE_RUNTIME was turned on in the code
// that generated the call to this function just because
// MR_DEBUG_THE_RUNTIME is turned on here.
if (name != NULL) {
printf("recording entry label %s at %p\n", name, addr);
} else {
printf("recording entry label at %p\n", addr);
}
}
#endif // MR_DEBUG_THE_RUNTIME
#ifdef MR_NEED_ENTRY_LABEL_ARRAY
if (entry_array_next >= entry_array_size) {
entry_array_size *= 2;
entry_array = realloc(entry_array,
entry_array_size * sizeof(MR_Entry));
if (entry_array == NULL) {
MR_fatal_error("run out of memory for entry label array");
}
}
entry_array[entry_array_next].MR_entry_addr = addr;
entry_array[entry_array_next].MR_entry_name = name;
entry_array[entry_array_next].MR_entry_layout = entry_layout;
entry_array_next++;
entry_array_sorted = MR_FALSE;
#endif // MR_NEED_ENTRY_LABEL_ARRAY
}
#else // MR_NEED_ENTRY_LABEL_INFO
void
MR_do_insert_entry_label(const char *name, MR_Code *addr,
const MR_ProcLayout *entry_layout)
{
// Do nothing, but the function must still exist, since entry labels
// defined with MR_init_entry_an will generate calls to it.
}
#endif // MR_NEED_ENTRY_LABEL_INFO
#ifdef MR_NEED_ENTRY_LABEL_ARRAY
static int compare_entry_addr(const void *e1, const void *e2);
static int
compare_entry_addr(const void *e1, const void *e2)
{
const MR_Entry *entry1;
const MR_Entry *entry2;
entry1 = (const MR_Entry *) e1;
entry2 = (const MR_Entry *) e2;
if (entry1->MR_entry_addr > entry2->MR_entry_addr) {
return 1;
} else if (entry1->MR_entry_addr < entry2->MR_entry_addr) {
return -1;
} else {
return 0;
}
}
MR_Entry *
MR_prev_entry_by_addr(const MR_Code *addr)
{
int lo;
int hi;
int mid;
MR_do_init_label_tables();
MR_do_init_modules();
if (!entry_array_sorted) {
qsort(entry_array, entry_array_next, sizeof(MR_Entry),
compare_entry_addr);
entry_array_sorted = MR_TRUE;
}
lo = 0;
hi = entry_array_next-1;
if (lo > hi || addr < entry_array[lo].MR_entry_addr) {
return NULL;
}
while (lo <= hi) {
mid = (lo + hi) / 2;
if (entry_array[mid].MR_entry_addr == addr) {
return &entry_array[mid];
} else if (entry_array[mid].MR_entry_addr < addr) {
lo = mid + 1;
} else {
hi = mid - 1;
}
}
if (lo < entry_array_next && entry_array[lo].MR_entry_addr < addr) {
return &entry_array[lo];
} else {
return &entry_array[lo - 1];
}
}
#else // MR_NEED_ENTRY_LABEL_ARRAY
MR_Entry *
MR_prev_entry_by_addr(const MR_Code *addr)
{
return NULL;
}
#endif // MR_NEED_ENTRY_LABEL_ARRAY
void
MR_insert_internal_label(const char *name, MR_Code *addr,
const MR_LabelLayout *label_layout)
{
MR_Internal *internal;
MR_Internal *prev_internal;
MR_do_init_label_tables();
internal = MR_GC_NEW_ATTRIB(MR_Internal, MR_ALLOC_SITE_RUNTIME);
internal->MR_internal_addr = addr;
internal->MR_internal_layout = label_layout;
internal->MR_internal_name = name;
#ifdef MR_DEBUG_THE_RUNTIME
if (MR_progdebug) {
// We can't assume that MR_DEBUG_THE_RUNTIME was turned on in the code
// that generated the call to this function just because
// MR_DEBUG_THE_RUNTIME is turned on here.
if (name != NULL) {
printf("inserting internal label %s at %p\n", name, addr);
} else {
printf("inserting internal label at %p\n", addr);
}
}
#endif
prev_internal = (MR_Internal *)
MR_insert_hash_table(internal_addr_table, internal);
if (prev_internal != NULL) {
// Two labels at same location will happen quite often, when the code
// generated between them turns out to be empty. In this case,
// MR_insert_hash_table will not have inserted internal into the table.
//
// If only one of internal and prev_internal have a layout structure,
// make sure that we associate the layout structure with the label
// address.
//
// If both internal and prev_internal have a layout structure,
// we rely on the compiler to make sure that it is ok to use
// either of their layout structures.
if (prev_internal->MR_internal_layout == NULL) {
prev_internal->MR_internal_layout = label_layout;
}
}
}
MR_Internal *
MR_lookup_internal_by_addr(const MR_Code *addr)
{
MR_do_init_label_tables();
MR_do_init_modules();
#ifdef MR_DEBUG_THE_RUNTIME
if (MR_progdebug) {
printf("looking for internal label at %p\n", addr);
}
#endif
return (MR_Internal *) MR_lookup_hash_table(internal_addr_table, addr);
}
static const void *
internal_addr(const void *internal)
{
if (internal == NULL) {
return NULL;
} else {
return (const void *)
(((const MR_Internal *) internal)->MR_internal_addr);
}
}
static MR_bool
equal_addr(const void *addr1, const void *addr2)
{
return ((const MR_Code *) addr1) == ((const MR_Code *) addr2);
}
static int
hash_addr(const void *addr)
{
return (((MR_Unsigned) addr) >> 3) % INTERNAL_SIZE;
}
void
MR_process_all_internal_labels(void f(const void *))
{
MR_do_init_label_tables();
MR_process_all_entries(internal_addr_table, f);
}
// The code of MR_lookup_entry_or_internal is similar to, but significantly
// simpler than, MR_print_label in mercury_debug.c.
const char *
MR_lookup_entry_or_internal(const MR_Code *addr)
{
MR_Internal *internal;
MR_Entry *entry;
internal = MR_lookup_internal_by_addr(addr);
if (internal != NULL) {
if (internal->MR_internal_name != NULL) {
return internal->MR_internal_name;
} else {
return "unnamed internal label";
}
}
entry = MR_prev_entry_by_addr(addr);
if (entry != NULL && entry->MR_entry_addr == addr) {
if (entry->MR_entry_name != NULL) {
return entry->MR_entry_name;
} else {
return "unnamed entry label";
}
}
return "unknown";
}