From c25d479cb59fae2751dc83a1ba3e47b08ab6755d Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 11 Mar 2026 12:33:03 +0300 Subject: [PATCH] cgen: unable to debug using GDB or LLDB (fixes #14904) --- vlib/builtin/builtin_d_gcboehm.c.v | 30 +++++++++ vlib/builtin/gc_debugger_linux.h | 55 +++++++++++++++++ vlib/v/gen/c/cmain.v | 61 ++++++++----------- .../c/testdata/gc_debugger_linux.c.must_have | 2 + vlib/v/gen/c/testdata/gc_debugger_linux.vv | 4 ++ 5 files changed, 116 insertions(+), 36 deletions(-) create mode 100644 vlib/builtin/gc_debugger_linux.h create mode 100644 vlib/v/gen/c/testdata/gc_debugger_linux.c.must_have create mode 100644 vlib/v/gen/c/testdata/gc_debugger_linux.vv diff --git a/vlib/builtin/builtin_d_gcboehm.c.v b/vlib/builtin/builtin_d_gcboehm.c.v index 731a4bf75..ef3de3baa 100644 --- a/vlib/builtin/builtin_d_gcboehm.c.v +++ b/vlib/builtin/builtin_d_gcboehm.c.v @@ -135,6 +135,7 @@ $if gcboehm_leak ? { } #include +#include "@VEXEROOT/vlib/builtin/gc_debugger_linux.h" // #include @@ -166,6 +167,8 @@ fn C.GC_enable() // returns non-zero if GC is disabled fn C.GC_is_disabled() i32 +fn C.GC_set_no_dls(i32) + // protect memory block from being freed before this call fn C.GC_reachable_here(voidptr) @@ -227,6 +230,10 @@ fn C.GC_set_stackbottom(voidptr, voidptr) fn C.GC_add_roots(voidptr, voidptr) fn C.GC_remove_roots(voidptr, voidptr) +fn C.v__gc_can_register_main_data_roots_linux() i32 +fn C.v__gc_debugger_present_linux() i32 +fn C.v__gc_register_main_data_roots_linux() + // fn C.GC_get_push_other_roots() fn() // fn C.GC_set_push_other_roots(fn()) @@ -252,3 +259,26 @@ pub fn gc_set_warn_proc(cb FnGC_WarnCB) { // used by builtin_init: fn internal_gc_warn_proc_none(msg &char, arg usize) {} + +@[markused] +fn gc_prepare_for_debugger_init() bool { + $if linux { + if C.v__gc_debugger_present_linux() != 0 + && C.v__gc_can_register_main_data_roots_linux() != 0 { + C.GC_set_no_dls(1) + return true + } + } + return false +} + +@[markused] +fn gc_restore_roots_after_debugger_init(use_manual_roots bool) { + if !use_manual_roots { + return + } + $if linux { + C.v__gc_register_main_data_roots_linux() + C.GC_set_no_dls(0) + } +} diff --git a/vlib/builtin/gc_debugger_linux.h b/vlib/builtin/gc_debugger_linux.h new file mode 100644 index 000000000..49b7c81bd --- /dev/null +++ b/vlib/builtin/gc_debugger_linux.h @@ -0,0 +1,55 @@ +#ifndef V_BUILTIN_GC_DEBUGGER_LINUX_H +#define V_BUILTIN_GC_DEBUGGER_LINUX_H + +#if defined(__linux__) +#include +#include +#include + +#pragma weak __data_start +#pragma weak data_start +#pragma weak _end + +extern int __data_start[]; +extern int data_start[]; +extern int _end[]; + +static inline int v__gc_can_register_main_data_roots_linux(void) { + void* data_start_ptr = __data_start != NULL ? (void*)__data_start : (void*)data_start; + void* data_end_ptr = (void*)_end; + return data_start_ptr != NULL && data_end_ptr != NULL && data_start_ptr < data_end_ptr; +} + +static inline int v__gc_debugger_present_linux(void) { + int fd = open("/proc/self/status", O_RDONLY); + if (fd < 0) { + return 0; + } + char buf[4096]; + ssize_t n = read(fd, buf, sizeof(buf) - 1); + (void)close(fd); + if (n <= 0) { + return 0; + } + buf[n] = '\0'; + char* tracer_pid = strstr(buf, "TracerPid:"); + if (tracer_pid == NULL) { + return 0; + } + tracer_pid += sizeof("TracerPid:") - 1; + while (*tracer_pid == ' ' || *tracer_pid == '\t') { + ++tracer_pid; + } + return *tracer_pid != '0' && *tracer_pid != '\n' && *tracer_pid != '\0'; +} + +static inline void v__gc_register_main_data_roots_linux(void) { + if (!v__gc_can_register_main_data_roots_linux()) { + return; + } + void* data_start_ptr = __data_start != NULL ? (void*)__data_start : (void*)data_start; + GC_add_roots(data_start_ptr, (void*)_end); +} +#endif + +#endif diff --git a/vlib/v/gen/c/cmain.v b/vlib/v/gen/c/cmain.v index c3325b75b..f162d84c6 100644 --- a/vlib/v/gen/c/cmain.v +++ b/vlib/v/gen/c/cmain.v @@ -148,23 +148,32 @@ fn (mut g Gen) gen_c_main_function_header() { } } +fn (mut g Gen) gen_boehm_gc_init() { + g.writeln('#if defined(_VGCBOEHM)') + if g.pref.gc_mode == .boehm_leak { + g.writeln('\tGC_set_find_leak(1);') + } + if g.pref.os == .linux && !g.pref.no_builtin { + g.writeln('\tbool __v_gc_debugger_workaround = builtin__gc_prepare_for_debugger_init();') + } + g.writeln('\tGC_set_pages_executable(0);') + if g.pref.use_coroutines { + g.writeln('\tGC_allow_register_threads();') + } + g.writeln('\tGC_INIT();') + if g.pref.os == .linux && !g.pref.no_builtin { + g.writeln('\tbuiltin__gc_restore_roots_after_debugger_init(__v_gc_debugger_workaround);') + } + if g.pref.gc_mode in [.boehm_incr, .boehm_incr_opt] { + g.writeln('\tGC_enable_incremental();') + } + g.writeln('#endif') +} + fn (mut g Gen) gen_c_main_header() { g.gen_c_main_function_header() if g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt, .boehm_leak] { - g.writeln('#if defined(_VGCBOEHM)') - if g.pref.gc_mode == .boehm_leak { - g.writeln('\tGC_set_find_leak(1);') - } - g.writeln('\tGC_set_pages_executable(0);') - if g.pref.use_coroutines { - g.writeln('\tGC_allow_register_threads();') - } - g.writeln('\tGC_INIT();') - - if g.pref.gc_mode in [.boehm_incr, .boehm_incr_opt] { - g.writeln('\tGC_enable_incremental();') - } - g.writeln('#endif') + g.gen_boehm_gc_init() } if g.pref.gc_mode == .vgc { g.writeln('\tbuiltin__vgc_init();') @@ -213,15 +222,7 @@ sapp_desc sokol_main(int argc, char* argv[]) { g.gen_c_main_trace_calls_hook() if g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt, .boehm_leak] { - g.writeln('#if defined(_VGCBOEHM)') - if g.pref.gc_mode == .boehm_leak { - g.writeln('\tGC_set_find_leak(1);') - } - g.writeln2('\tGC_set_pages_executable(0);', '\tGC_INIT();') - if g.pref.gc_mode in [.boehm_incr, .boehm_incr_opt] { - g.writeln('\tGC_enable_incremental();') - } - g.writeln('#endif') + g.gen_boehm_gc_init() } if g.pref.gc_mode == .vgc { g.writeln('\tbuiltin__vgc_init();') @@ -298,19 +299,7 @@ pub fn (mut g Gen) gen_c_main_for_tests() { g.writeln('') g.gen_c_main_function_header() if g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt, .boehm_leak] { - g.writeln('#if defined(_VGCBOEHM)') - if g.pref.gc_mode == .boehm_leak { - g.writeln('\tGC_set_find_leak(1);') - } - g.writeln('\tGC_set_pages_executable(0);') - if g.pref.use_coroutines { - g.writeln('\tGC_allow_register_threads();') - } - g.writeln('\tGC_INIT();') - if g.pref.gc_mode in [.boehm_incr, .boehm_incr_opt] { - g.writeln('\tGC_enable_incremental();') - } - g.writeln('#endif') + g.gen_boehm_gc_init() } if g.pref.gc_mode == .vgc { g.writeln('\tbuiltin__vgc_init();') diff --git a/vlib/v/gen/c/testdata/gc_debugger_linux.c.must_have b/vlib/v/gen/c/testdata/gc_debugger_linux.c.must_have new file mode 100644 index 000000000..86b63b8e0 --- /dev/null +++ b/vlib/v/gen/c/testdata/gc_debugger_linux.c.must_have @@ -0,0 +1,2 @@ +bool __v_gc_debugger_workaround = builtin__gc_prepare_for_debugger_init(); +builtin__gc_restore_roots_after_debugger_init(__v_gc_debugger_workaround); diff --git a/vlib/v/gen/c/testdata/gc_debugger_linux.vv b/vlib/v/gen/c/testdata/gc_debugger_linux.vv new file mode 100644 index 000000000..de42a552d --- /dev/null +++ b/vlib/v/gen/c/testdata/gc_debugger_linux.vv @@ -0,0 +1,4 @@ +// vtest vflags: -os linux +module main + +fn main() {} -- 2.39.5