Project

General

Profile

Feature #21539

Updated by dalehamel (Dale Hamel) 4 days ago

## Motivation 

 External While ruby does have a great API for getting stack traces within the ruby processes, as used by profilers such as [OpenTelemetry's eBPF profiler](https://github.com/open-telemetry/opentelemetry-ebpf-profiler) collect Ruby stacks like vernier and stackprof, there are some projects which aim to profile ruby from outside of the Ruby process. 

 Distribution packages commonly ship shared libruby but strip Some examples include: 

 - rbspy (https://github.com/rbspy/rbspy/tree/main/ruby-structs/src) 
 - rbperf (https://github.com/javierhonduco/rbperf) 
 - open telemetry (https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/main/support/ebpf/ruby_tracer.ebpf.c, https://github.com/open-telemetry/opentelemetry-ebpf-profiler/blob/main/interpreter/ruby/ruby.go) 


 These first two take the full symbol table (`.symtab`) and debug information. Some approach of embedding the symbols needed ruby headers within the profiler to discover Ruby's execution state are currently local symbols, so they 
 disappear when stripped. Public dynamic symbols in `.dynsym` survive normal stripping. be able to walk the stack. 

 I'd like otel's bpf exporter was relying on access to narrow this proposal to making symbols (https://github.com/open-telemetry/opentelemetry-ebpf-profiler/issues/202), which were removed some time ago https://github.com/ruby/ruby/pull/7459. As a result, it cannot profile newer rubies as it cannot unwind the necessary discovery symbols available in these stripped shared builds. stacks. 

 ## Proposed exports 

 Please export All of these three existing symbols from shared libruby: 

 | Symbol | Purpose | 
 | --- | --- | 
 | `ruby_current_ec` | Locate solutions kind of take the sampled native thread's execution context through TLS, providing access approach of trying to its Ruby stack. | 
 | `rb_vm_exec` | Identify reverse engineer the function's address range, where native stack unwinding should hand off to Ruby stack unwinding. The profiler does not call ruby process and rely on somewhat hacky approaches that touch internal things that might frequently move around between ruby versions, and all feel a bit brittle. I think the root of 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 `<unknown cfunc>`. | 

 The profiler also needs `ruby_version`, which is already exported and requires that there is no change. public, stable api for this sort of external profiling. 

 The requested outcome is that these symbols are available through `.dynsym` in installed shared builds, without requiring distributions to retain I'm not exactly sure what the full symbol table or users solution should be, but it would be great for ruby to install debug information just to locate       them. 

 ## Scope 

 This is offer a symbol-visibility request, not a request recommended, supported, and stable way for a general out-of-process profiling API: external profilers (whether they are using perf api's or ptrace) to be able to: 

 - No requirement Obtain the ruby native stack starting point, and walk it while being able to stabilize internal struct layouts or make these internals a supported C-extension API. resolve the native symbols 
 - No additional profiling stack, per-frame bookkeeping, or in-process profiling machinery. Obtain a reference to the ruby execution context and walk the ruby interpreter stack 
 - The profiler remains responsible for supporting version-specific layouts Have all of this work in a stable and interpreter/JIT behavior. reasonable way regardless of if / which ruby jit is enabled 

 This It would address the symbol-discovery obstacle be awesome for stripped distribution builds without taking some ruby maintainers / experts to weigh-in on the broader what such an external API design discussed in #19119. 

 Related profiler discussion: https://github.com/open-telemetry/opentelemetry-ebpf-profiler/issues/202 
 could look like. Perhaps a public header containing stable (or at least perhaps, versioned?) public symbols and perhaps structs as a starting point? This might necessitate that some fields be refactored out of existing structs, and could slow down work related to them (eg, members need to be added or removed).

Back