1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-07-04 00:30:30 +02:00

Allow precise tracing of dynstacks

Gosh this was a slog

* libguile/dynstack.c (dynstack_ensure_space): Use malloc and free.
Threads have off-heap dynstacks, with manual marking.
(scm_trace_dynstack): Implement tracing.
(trace_pinned_trampoline, scm_trace_dynstack_roots): Implement tracing
for active threads.
(scm_dynstack_capture): Tag dynstacks.
* libguile/dynstack.h (scm_t_dynstack): Add a tag.
(scm_t_dynstack_winder_flags): Add SCM_F_DYNSTACK_WINDER_MANAGED.
* libguile/dynwind.h (scm_t_wind_flags): Add SCM_F_WIND_MANAGED.
* libguile/dynwind.c (scm_dynwind_unwind_handler_with_scm)
(scm_dynwind_rewind_handler_with_scm): These values need to be traced by
GC.
* libguile/scm.h (scm_tc16_dynstack_slice): New typecode.  No need for
equality etc because it shouldn't escape to Scheme (currently).
* libguile/trace.h: Add trace decls.
* libguile/threads.c (scm_trace_thread_roots): Trace dynstacks
explicitly here, as they are off-heap.
This commit is contained in:
Andy Wingo 2025-06-19 16:32:56 +02:00
parent 923bfdc7ed
commit 278ba99027
7 changed files with 155 additions and 43 deletions

View file

@ -1,4 +1,4 @@
/* Copyright 2012-2013,2018 /* Copyright 2012-2013,2018,2025
Free Software Foundation, Inc. Free Software Foundation, Inc.
This file is part of Guile. This file is part of Guile.
@ -37,6 +37,7 @@
#include "fluids.h" #include "fluids.h"
#include "variable.h" #include "variable.h"
#include "threads.h" #include "threads.h"
#include "trace.h"
#include "dynstack.h" #include "dynstack.h"
@ -44,7 +45,8 @@
#define PROMPT_WORDS 6 #define PROMPT_WORDS 6
#define PROMPT_KEY(top) (SCM_PACK ((top)[0])) #define PROMPT_KEY_LOC(top) ((SCM*)(top))
#define PROMPT_KEY(top) (*PROMPT_KEY_LOC(top))
#define PROMPT_FP(top) ((ptrdiff_t) ((top)[1])) #define PROMPT_FP(top) ((ptrdiff_t) ((top)[1]))
#define SET_PROMPT_FP(top, fp) do { top[1] = (scm_t_bits)(fp); } while (0) #define SET_PROMPT_FP(top, fp) do { top[1] = (scm_t_bits)(fp); } while (0)
#define PROMPT_SP(top) ((ptrdiff_t) ((top)[2])) #define PROMPT_SP(top) ((ptrdiff_t) ((top)[2]))
@ -55,18 +57,24 @@
#define WINDER_WORDS 2 #define WINDER_WORDS 2
#define WINDER_PROC(top) ((scm_t_guard) ((top)[0])) #define WINDER_PROC(top) ((scm_t_guard) ((top)[0]))
#define WINDER_DATA(top) ((void *) ((top)[1])) #define WINDER_DATA_LOC(top) ((void **) ((top) + 1))
#define WINDER_DATA(top) (*WINDER_DATA_LOC(top))
#define DYNWIND_WORDS 2 #define DYNWIND_WORDS 2
#define DYNWIND_ENTER(top) (SCM_PACK ((top)[0])) #define DYNWIND_ENTER_LOC(top) ((SCM*)(top))
#define DYNWIND_LEAVE(top) (SCM_PACK ((top)[1])) #define DYNWIND_LEAVE_LOC(top) ((SCM*)(top) + 1)
#define DYNWIND_ENTER(top) (*DYNWIND_ENTER_LOC(top))
#define DYNWIND_LEAVE(top) (*DYNWIND_LEAVE_LOC(top))
#define WITH_FLUID_WORDS 2 #define WITH_FLUID_WORDS 2
#define WITH_FLUID_FLUID(top) (SCM_PACK ((top)[0])) #define WITH_FLUID_FLUID_LOC(top) ((SCM*)(top))
#define WITH_FLUID_VALUE_BOX(top) (SCM_PACK ((top)[1])) #define WITH_FLUID_VALUE_BOX_LOC(top) ((SCM*)(top) + 1)
#define WITH_FLUID_FLUID(top) (*WITH_FLUID_FLUID_LOC(top))
#define WITH_FLUID_VALUE_BOX(top) (*WITH_FLUID_VALUE_BOX_LOC(top))
#define DYNAMIC_STATE_WORDS 1 #define DYNAMIC_STATE_WORDS 1
#define DYNAMIC_STATE_STATE_BOX(top) (SCM_PACK ((top)[0])) #define DYNAMIC_STATE_STATE_BOX_LOC(top) ((SCM*)(top))
#define DYNAMIC_STATE_STATE_BOX(top) (*DYNAMIC_STATE_STATE_BOX_LOC(top))
@ -100,22 +108,111 @@ dynstack_ensure_space (scm_t_dynstack *dynstack, size_t n)
if (capacity < height + n) if (capacity < height + n)
{ {
scm_t_bits *new_base; scm_t_bits *old_base = dynstack->base;
while (capacity < height + n) while (capacity < height + n)
capacity = (capacity < 4) ? 8 : (capacity * 2); capacity = (capacity < 4) ? 8 : (capacity * 2);
new_base = scm_gc_malloc (capacity * sizeof(scm_t_bits), "dynstack"); dynstack->base = scm_malloc (capacity * sizeof(scm_t_bits));
dynstack->top = dynstack->base + height;
dynstack->limit = dynstack->base + capacity;
copy_scm_t_bits (new_base, dynstack->base, height); copy_scm_t_bits (dynstack->base, old_base, height);
clear_scm_t_bits (dynstack->base, height); clear_scm_t_bits (dynstack->base + height, capacity - height);
free (old_base);
dynstack->base = new_base;
dynstack->top = new_base + height;
dynstack->limit = new_base + capacity;
} }
} }
void
scm_dynstack_init_for_thread (scm_t_dynstack *dynstack)
{
dynstack->tag = -1;
dynstack->base = NULL;
dynstack->limit = NULL;
dynstack->top = NULL;
dynstack_ensure_space (dynstack, 1000);
dynstack->top += SCM_DYNSTACK_HEADER_LEN;
}
void
scm_trace_dynstack (struct scm_dynstack *dynstack,
void (*trace) (struct gc_edge edge,
struct gc_heap *heap,
void *trace_data),
struct gc_heap *heap, void *trace_data)
{
scm_t_bits *walk;
for (walk = SCM_DYNSTACK_PREV (dynstack->top); walk;
walk = SCM_DYNSTACK_PREV (walk))
{
scm_t_bits tag = SCM_DYNSTACK_TAG (walk);
switch (SCM_DYNSTACK_TAG_TYPE (tag))
{
case SCM_DYNSTACK_TYPE_FRAME:
break;
case SCM_DYNSTACK_TYPE_UNWINDER:
case SCM_DYNSTACK_TYPE_REWINDER:
if (SCM_DYNSTACK_TAG_FLAGS (tag) & SCM_F_DYNSTACK_WINDER_MANAGED)
trace (gc_edge (WINDER_DATA_LOC (walk)), heap, trace_data);
break;
case SCM_DYNSTACK_TYPE_WITH_FLUID:
trace (gc_edge (WITH_FLUID_FLUID_LOC (walk)), heap, trace_data);
trace (gc_edge (WITH_FLUID_VALUE_BOX_LOC (walk)), heap, trace_data);
break;
case SCM_DYNSTACK_TYPE_PROMPT:
trace (gc_edge (PROMPT_KEY_LOC (walk)), heap, trace_data);
// No need to trace the jmpbuf; either:
// 1. the prompt is active and thus the jmpbuf is on the
// stack and traced conservatively already
// 2. the dynstack is part of a delimited continuation, in
// which case the jmpbuf is garbage and will be rewound
// if the dynstack is reinstated
// 2. the dynstack is part of an undelimited continuation, in
// which case the jmpbuf is conservatively marked as part
// of the associated continuation
break;
case SCM_DYNSTACK_TYPE_DYNWIND:
trace (gc_edge (DYNWIND_ENTER_LOC (walk)), heap, trace_data);
trace (gc_edge (DYNWIND_LEAVE_LOC (walk)), heap, trace_data);
break;
case SCM_DYNSTACK_TYPE_DYNAMIC_STATE:
trace (gc_edge (DYNAMIC_STATE_STATE_BOX_LOC (walk)), heap, trace_data);
break;
default:
abort ();
}
}
}
struct trace_pinned_trampoline
{
void (*trace_pinned) (struct gc_ref ref,
struct gc_heap *heap,
void *trace_data);
void *trace_data;
};
static void
trace_pinned_trampoline (struct gc_edge edge,
struct gc_heap *heap,
void *trace_data)
{
struct trace_pinned_trampoline *data = trace_data;
return data->trace_pinned (gc_edge_ref (edge), heap, data->trace_data);
}
void
scm_trace_dynstack_roots (struct scm_dynstack *dynstack,
void (*trace_pinned) (struct gc_ref ref,
struct gc_heap *heap,
void *trace_data),
struct gc_heap *heap, void *trace_data)
{
struct trace_pinned_trampoline data = { trace_pinned, trace_data };
return scm_trace_dynstack (dynstack, trace_pinned_trampoline, heap, &data);
}
static inline scm_t_bits * static inline scm_t_bits *
push_dynstack_entry_unchecked (scm_t_dynstack *dynstack, push_dynstack_entry_unchecked (scm_t_dynstack *dynstack,
scm_t_dynstack_item_type type, scm_t_dynstack_item_type type,
@ -280,7 +377,6 @@ scm_dynstack_capture_all (scm_t_dynstack *dynstack)
scm_t_dynstack * scm_t_dynstack *
scm_dynstack_capture (scm_t_dynstack *dynstack, scm_t_bits *item) scm_dynstack_capture (scm_t_dynstack *dynstack, scm_t_bits *item)
{ {
char *mem;
scm_t_dynstack *ret; scm_t_dynstack *ret;
size_t len; size_t len;
@ -288,9 +384,9 @@ scm_dynstack_capture (scm_t_dynstack *dynstack, scm_t_bits *item)
assert (item <= dynstack->top); assert (item <= dynstack->top);
len = dynstack->top - item + SCM_DYNSTACK_HEADER_LEN; len = dynstack->top - item + SCM_DYNSTACK_HEADER_LEN;
mem = scm_gc_malloc (sizeof (*ret) + len * sizeof(scm_t_bits), "dynstack"); ret = scm_gc_malloc (sizeof (*ret) + len * sizeof(scm_t_bits), "dynstack");
ret = (scm_t_dynstack *) mem; ret->tag = scm_tc16_dynstack_slice;
ret->base = (scm_t_bits *) (mem + sizeof (*ret)); ret->base = ret->inline_storage;
ret->limit = ret->base + len; ret->limit = ret->base + len;
ret->top = ret->base + len; ret->top = ret->base + len;

View file

@ -1,7 +1,7 @@
#ifndef SCM_DYNSTACK_H #ifndef SCM_DYNSTACK_H
#define SCM_DYNSTACK_H #define SCM_DYNSTACK_H
/* Copyright 2012-2013,2018 /* Copyright 2012-2013,2018,2025
Free Software Foundation, Inc. Free Software Foundation, Inc.
This file is part of Guile. This file is part of Guile.
@ -36,9 +36,11 @@
typedef struct scm_dynstack typedef struct scm_dynstack
{ {
scm_t_bits tag;
scm_t_bits *base; scm_t_bits *base;
scm_t_bits *top; scm_t_bits *top;
scm_t_bits *limit; scm_t_bits *limit;
scm_t_bits inline_storage[];
} scm_t_dynstack; } scm_t_dynstack;
@ -133,7 +135,8 @@ typedef enum {
} scm_t_dynstack_frame_flags; } scm_t_dynstack_frame_flags;
typedef enum { typedef enum {
SCM_F_DYNSTACK_WINDER_EXPLICIT = (1 << SCM_DYNSTACK_TAG_FLAGS_SHIFT) SCM_F_DYNSTACK_WINDER_EXPLICIT = (1 << SCM_DYNSTACK_TAG_FLAGS_SHIFT),
SCM_F_DYNSTACK_WINDER_MANAGED = (2 << SCM_DYNSTACK_TAG_FLAGS_SHIFT)
} scm_t_dynstack_winder_flags; } scm_t_dynstack_winder_flags;
typedef enum { typedef enum {
@ -145,6 +148,8 @@ typedef void (*scm_t_guard) (void *);
SCM_INTERNAL void scm_dynstack_init_for_thread (scm_t_dynstack *);
/* Pushing and popping entries on the dynamic stack. */ /* Pushing and popping entries on the dynamic stack. */
SCM_INTERNAL void scm_dynstack_push_frame (scm_t_dynstack *, SCM_INTERNAL void scm_dynstack_push_frame (scm_t_dynstack *,

View file

@ -1,4 +1,4 @@
/* Copyright 1995-1996,1998-2001,2003-2004,2006,2008,2010-2012,2018 /* Copyright 1995-1996,1998-2001,2003-2004,2006,2008,2010-2012,2018,2025
Free Software Foundation, Inc. Free Software Foundation, Inc.
This file is part of Guile. This file is part of Guile.
@ -99,7 +99,8 @@ scm_dynwind_unwind_handler_with_scm (void (*proc) (SCM), SCM data,
scm_t_wind_flags flags) scm_t_wind_flags flags)
{ {
/* FIXME: This is not a safe cast. */ /* FIXME: This is not a safe cast. */
scm_dynwind_unwind_handler ((scm_t_guard) proc, SCM2PTR (data), flags); scm_dynwind_unwind_handler ((scm_t_guard) proc, SCM2PTR (data),
flags | SCM_F_WIND_MANAGED);
} }
void void
@ -107,7 +108,8 @@ scm_dynwind_rewind_handler_with_scm (void (*proc) (SCM), SCM data,
scm_t_wind_flags flags) scm_t_wind_flags flags)
{ {
/* FIXME: This is not a safe cast. */ /* FIXME: This is not a safe cast. */
scm_dynwind_rewind_handler ((scm_t_guard) proc, SCM2PTR (data), flags); scm_dynwind_rewind_handler ((scm_t_guard) proc, SCM2PTR (data),
flags | SCM_F_WIND_MANAGED);
} }
void void

View file

@ -1,7 +1,7 @@
#ifndef SCM_DYNWIND_H #ifndef SCM_DYNWIND_H
#define SCM_DYNWIND_H #define SCM_DYNWIND_H
/* Copyright 1995-1996,1998-2000,2003-2004,2006,2008,2011-2012,2018 /* Copyright 1995-1996,1998-2000,2003-2004,2006,2008,2011-2012,2018,2025
Free Software Foundation, Inc. Free Software Foundation, Inc.
This file is part of Guile. This file is part of Guile.
@ -37,7 +37,8 @@ typedef enum {
} scm_t_dynwind_flags; } scm_t_dynwind_flags;
typedef enum { typedef enum {
SCM_F_WIND_EXPLICITLY = SCM_F_DYNSTACK_WINDER_EXPLICIT SCM_F_WIND_EXPLICITLY = SCM_F_DYNSTACK_WINDER_EXPLICIT,
SCM_F_WIND_MANAGED = SCM_F_DYNSTACK_WINDER_MANAGED
} scm_t_wind_flags; } scm_t_wind_flags;
SCM_API void scm_dynwind_begin (scm_t_dynwind_flags); SCM_API void scm_dynwind_begin (scm_t_dynwind_flags);

View file

@ -517,6 +517,7 @@ typedef uintptr_t scm_t_bits;
#define scm_tc16_random_state 0x067f #define scm_tc16_random_state 0x067f
#define scm_tc16_regexp 0x077f #define scm_tc16_regexp 0x077f
#define scm_tc16_locale 0x087f #define scm_tc16_locale 0x087f
#define scm_tc16_dynstack_slice 0x097f
/* Definitions for tc16: */ /* Definitions for tc16: */

View file

@ -87,18 +87,6 @@
/* FIXME: For the moment, the bodies of thread objects are traced /* FIXME: For the moment, the bodies of thread objects are traced
conservatively; only bdw, heap-conservative-mmc, and conservatively; only bdw, heap-conservative-mmc, and
heap-conservative-parallel-mmc are supported. */ heap-conservative-parallel-mmc are supported. */
static void
scm_trace_dynstack (scm_t_dynstack *dynstack,
void (*trace_edge) (struct gc_edge edge,
struct gc_heap *heap,
void *trace_data),
struct gc_heap *heap, void *trace_data)
{
/* FIXME: Untagged array. Perhaps this should be off-heap... or
interleaved on the main stack. */
trace_edge (gc_edge (&dynstack->base), heap, trace_data);
}
void void
scm_trace_thread (struct scm_thread *thread, scm_trace_thread (struct scm_thread *thread,
void (*trace_edge) (struct gc_edge edge, void (*trace_edge) (struct gc_edge edge,
@ -151,6 +139,10 @@ scm_trace_thread_roots (struct scm_thread *thread,
struct gc_heap *heap, void *trace_data) struct gc_heap *heap, void *trace_data)
{ {
trace_pinned (gc_ref_from_heap_object (thread), heap, trace_data); trace_pinned (gc_ref_from_heap_object (thread), heap, trace_data);
#if GC_CONSERVATIVE_TRACE
scm_trace_dynstack_roots (&thread->dynstack, trace_pinned, heap, trace_data);
#endif
/* FIXME: Trace is not a tagged allocation. */
scm_trace_vm_roots (&thread->vm, trace_pinned, trace_ambiguous, heap, scm_trace_vm_roots (&thread->vm, trace_pinned, trace_ambiguous, heap,
trace_data); trace_data);
} }
@ -480,9 +472,7 @@ guilify_self_2 (SCM dynamic_state)
t->dynamic_state->thread_local_values = scm_c_make_hash_table (0); t->dynamic_state->thread_local_values = scm_c_make_hash_table (0);
scm_set_current_dynamic_state (dynamic_state); scm_set_current_dynamic_state (dynamic_state);
t->dynstack.base = scm_gc_malloc (16 * sizeof (scm_t_bits), "dynstack"); scm_dynstack_init_for_thread (&t->dynstack);
t->dynstack.limit = t->dynstack.base + 16;
t->dynstack.top = t->dynstack.base + SCM_DYNSTACK_HEADER_LEN;
t->block_asyncs = 0; t->block_asyncs = 0;
} }

View file

@ -23,11 +23,13 @@
#include "libguile/scm.h" #include "libguile/scm.h"
#include "gc-ref.h" #include "gc-ref.h"
#include "gc-edge.h"
struct scm_thread; struct scm_thread;
struct scm_vm; struct scm_vm;
struct scm_dynstack;
struct gc_heap; struct gc_heap;
struct gc_heap_roots { int unused; }; struct gc_heap_roots { int unused; };
@ -78,5 +80,20 @@ scm_trace_loader_roots (void (*trace_ambiguous) (uintptr_t lo,
struct gc_heap *heap, struct gc_heap *heap,
void *trace_data); void *trace_data);
SCM_INTERNAL void
scm_trace_dynstack (struct scm_dynstack *dynstack,
void (*trace) (struct gc_edge edge,
struct gc_heap *heap,
void *trace_data),
struct gc_heap *heap,
void *trace_data);
SCM_INTERNAL void
scm_trace_dynstack_roots (struct scm_dynstack *dynstack,
void (*trace_pinned) (struct gc_ref ref,
struct gc_heap *heap,
void *trace_data),
struct gc_heap *heap,
void *trace_data);
#endif /* SCM_THREADS_INTERNAL_H */ #endif /* SCM_THREADS_INTERNAL_H */