From: "dalehamel (Dale Hamel) via ruby-core" Date: 2026-10-22T12:35:40+00:00 Subject: [ruby-core:127211] [Ruby Feature#21539] Export symbols for external profiling of stripped shared Ruby builds Issue #21539 has been updated by dalehamel (Dale Hamel). File debug-export.patch added > The profiler has to access it during the VM is locked? No. The profiler only reads `ruby_global_symbols`, and it doesn't need the VM lock (which an external process couldn't take anyway). It is used to name methods implemented in C. A Ruby method's frame has its name as strings in the iseq, but a C method's frame only has the method entry's `def->original_id`, and `ruby_global_symbols` is the only place to turn that ID into a name. Without it, C methods such as `Array#each` and `Kernel#require`, and the methods of C extensions, lose their names (they appear as e.g. `Array#`). The C function pointer doesn't help: over 90% of the functions behind core methods have no symbol in a stripped libruby, and their C names aren't the Ruby method names anyway. The lookup happens later, in the profiler's own process. At sample time it only records the method entry pointer. Afterwards it reads the ID and its name from the Ruby process's memory while Ruby keeps running, following the same path as `rb_id2str()`: `next_id`, `ids`, the bucket, then the entry. Ruby itself already reads the table this way without the VM lock: only the writers, `set_id_entry()` and `id_entry_dir_grow()`, hold it, and they publish with release stores that `get_id_serial_entry()` pairs with acquire loads. An entry never changes once published, so a name read once can be cached. If a read races with a resize of the directory, that one lookup can fail; the Ruby process is never affected. > it doesn't look nice that repeating the complex conditions and revealing aliasing. Good callout and agreed. I've attached a revised version of debug-aliases.patch, debug-export.patch, that defines the condition once. A `RUBY_DEBUG_EXPORT()` macro in `internal/compilers.h` wraps the alias and visibility attributes, and reuses configure's existing `HAVE_ATTRIBUTE_FUNCTION_ALIAS` check. `RUBY_ALIAS_FUNCTION` itself doesn't fit here: without alias support it falls back to a wrapper function, and it has no form for variables. Each site is now one line: ```c RUBY_DEBUG_EXPORT_TLS(ruby_debug_current_ec, ruby_current_ec) RUBY_DEBUG_EXPORT(ruby_debug_vm_exec, vm_exec) RUBY_DEBUG_EXPORT(ruby_debug_global_symbols, ruby_global_symbols) ``` It is enabled only on Linux, where these profilers run. The `ruby_debug_` prefix follows the debugger helpers libruby already exports, such as `ruby_debug_print_value`. The machine output should be nearly identical to Nayef's patch and performance should be the same, just the code is cleaner in this one. I tested it on `b709bdb3` with GCC 13.3 on x86-64 and arm64, and with Clang 18 on x86-64; it also applies cleanly to current master. - `make test-basic` passes, with no compiler warnings in the changed files. - After `strip --strip-all`, `.dynsym` gains only the three `ruby_debug_*` names, each with the same value, size and type as its original. - `vm.o` and `symbol.o` compile to the same instructions as the unmodified sources, except two assertion line numbers. With GCC on x86-64, no function in the linked libruby moves either. If you'd prefer the check in configure, or other names, I can change that. ---------------------------------------- Feature #21539: Export symbols for external profiling of stripped shared Ruby builds https://bugs.ruby-lang.org/issues/21539#change-119628 * Author: dalehamel (Dale Hamel) * Status: Open ---------------------------------------- ## Motivation External profilers such as [OpenTelemetry's eBPF profiler](https://github.com/open-telemetry/opentelemetry-ebpf-profiler) collect Ruby stacks from outside the Ruby process. Distribution packages commonly ship shared libruby but strip the full symbol table (`.symtab`) and debug information. Some of the symbols needed to discover Ruby's execution state are currently local symbols, so they disappear when stripped. Public dynamic symbols in `.dynsym` survive normal stripping. I'd like to narrow this proposal to making the necessary discovery symbols available in these stripped shared builds. ## Proposed exports Please export these three existing symbols from shared libruby: | Symbol | Purpose | | --- | --- | | `ruby_current_ec` | Locate the sampled native thread's execution context through TLS, providing access to its Ruby stack. | | `rb_vm_exec` | Identify the function's address range, where native stack unwinding should hand off to Ruby stack unwinding. The profiler does not call this function. | | `ruby_global_symbols` | Resolve Ruby method IDs to names for methods implemented in C. Stack walking can work without this, but those method names become ``. | The profiler also needs `ruby_version`, which is already exported and requires no change. The requested outcome is that these symbols are available through `.dynsym` in installed shared builds, without requiring distributions to retain the full symbol table or users to install debug information just to locate them. ## Scope This is a symbol-visibility request, not a request for a general out-of-process profiling API: - No requirement to stabilize internal struct layouts or make these internals a supported C-extension API. - No additional profiling stack, per-frame bookkeeping, or in-process profiling machinery. - The profiler remains responsible for supporting version-specific layouts and interpreter/JIT behavior. This would address the symbol-discovery obstacle for stripped distribution builds without taking on the broader API design discussed in #19119. Related profiler discussion: https://github.com/open-telemetry/opentelemetry-ebpf-profiler/issues/202 ---Files-------------------------------- protected.patch (1.55 KB) exported.patch (1.21 KB) debug-aliases.patch (1.88 KB) debug-export.patch (1.83 KB) -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/