Skip to content

Profiling: Skip class/module memsize on Ruby 4 to avoid heap profiling SIGSEGV#5938

Merged
ivoanjo merged 5 commits into
DataDog:masterfrom
navidemad:navidemad/profiling-skip-class-memsize-ruby4
Jul 1, 2026
Merged

Profiling: Skip class/module memsize on Ruby 4 to avoid heap profiling SIGSEGV#5938
ivoanjo merged 5 commits into
DataDog:masterfrom
navidemad:navidemad/profiling-skip-class-memsize-ruby4

Conversation

@navidemad

Copy link
Copy Markdown
Contributor

What does this PR do?

Skips computing rb_obj_memsize_of for T_CLASS / T_MODULE / T_ICLASS objects on Ruby 4.0+, where it can crash the VM with a SIGSEGV during heap profiling.

ruby_obj_memsize_of already keeps a denylist of rb_obj_memsize_of paths that crash the VM (e.g. T_NODE). This adds the class-like types to that denylist on Ruby 4.0+, gated by a new NO_SAFE_CLASS_MEMSIZE compile-time define. On older Rubies the define is absent, so the preprocessed code is byte-for-byte unchanged.

Motivation:

With experimental heap profiling enabled on Ruby 4.0, we saw recurring SIGSEGV crashes on production Sidekiq workers (#5936). During heap profile serialization, the recorder resurrects each tracked object via ObjectSpace._id2ref and calls rb_obj_memsize_of on it. For class objects, rb_obj_memsize_of walks Ruby 4.0's per-namespace class extensions (rb_class_classext_foreachclassext_memsizerb_id_table_memsize) and dereferences invalid classext memory, killing the process. Class objects contribute negligibly to a heap size profile, so skipping them removes the crash with no meaningful loss of signal. The full root-cause analysis is in #5936.

Change log entry

Yes. Profiling: Fix a SIGSEGV crash that could happen with experimental heap profiling enabled on Ruby 4.0.

Additional Notes:

The gate is a new NO_SAFE_CLASS_MEMSIZE define, set for RUBY_VERSION >= "4", following the existing extconf.rb convention for Ruby-4-specific behavior. A complete fix would also need Ruby-side hardening of classext_memsize / rb_class_classext_foreach, but skipping class memsize is enough to stop the crash.

How to test the change?

Added a regression test in stack_recorder_spec.rb: it tracks a Class as a live heap object and checks that the profiler reports it without crashing, with a heap-live-size of 0 on Ruby 4.0+ (and the real ObjectSpace.memsize_of on older Rubies). Verified on Linux + Ruby 4.0 (tracer-4.0): the full stack_recorder_spec passes (64 examples, 0 failures). On Ruby < 4 the change is a no-op at the preprocessor level.

…g SIGSEGV

On Ruby 4.0, computing the memsize of a class/module/iclass walks the
per-namespace class extensions (rb_class_classext_foreach ->
classext_memsize -> rb_id_table_memsize) and can crash the VM with a
SIGSEGV when called on objects resurrected via ObjectSpace._id2ref
during heap profiling.

Add T_CLASS/T_MODULE/T_ICLASS to the existing crash-path denylist in
ruby_obj_memsize_of, gated behind a new NO_SAFE_CLASS_MEMSIZE define for
Ruby >= 4. On older Rubies the preprocessed code is unchanged. Class
objects contribute negligibly to a heap size profile.

See DataDog#5936
@vpellan vpellan added the community Was opened by a community member label Jun 24, 2026

@ivoanjo ivoanjo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for submitting this! I think it makes sense to merge this in -- I've just submitted a few suggested tweaks to the comments :D

In particular, at this moment I believe this is an underlying bug on the Ruby VM -- just one that hasn't been spotted/fixed yet since for most Ruby apps rb_obj_memsize_of/ObjectSpace.memsize_of is very rarely called, whereas dd-trace-rb calls it all the time, so we're in a way "fuzzing" the Ruby VM and finding the one-in-a-million needle.

It's also not clear to me that the issue is related to the _id2ref -- we did have some issues regarding it on Ruby 4 earlier on, but the current approach we're using is expected to be safe and supported (although we have plans to replace it), so I think that may be a bit of a "red herring" in this situation.

Other than that, I think the workaround makes sense until we can figure out a way of reproducing this and then report it upstream.

Comment thread ext/datadog_profiling_native_extension/extconf.rb Outdated
Comment thread ext/datadog_profiling_native_extension/ruby_helpers.c Outdated
Comment thread spec/datadog/profiling/stack_recorder_spec.rb Outdated
@ivoanjo ivoanjo self-assigned this Jun 30, 2026
@ivoanjo ivoanjo added the profiling Involves Datadog profiling label Jun 30, 2026

@ivoanjo ivoanjo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Looks great, thank you!

I'll take care of getting this merged :D

@datadog-datadog-prod-us1

datadog-datadog-prod-us1 Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Pipelines

Fix all issues with BitsAI

⚠️ Warnings

🚦 5 Pipeline jobs failed

Comment typing stats on PR | compute-stats   View in Datadog   GitHub Actions

Pull Request Labeler | triage   View in Datadog   GitHub Actions

Static Analysis | zizmor   View in Datadog   GitHub Actions

View all 5 failed jobs.

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: adf281f | Docs | Datadog PR Page | Give us feedback!

@ivoanjo
ivoanjo merged commit 3ab60d6 into DataDog:master Jul 1, 2026
1327 of 1381 checks passed
@ivoanjo ivoanjo added this to the 2.37.0 milestone Jul 2, 2026
hayat01sh1da pushed a commit to hayat01sh1da/dd-trace-rb that referenced this pull request Jul 11, 2026
…e to incompatibility

**What does this PR do?**

The profiler supports two profile types related to heap profiling:
* Heap Live Objects
* Heap Live Size

This PR disables "Heap Live Size" on Ruby 4.0, leaving only "Heap Live
Objects" available.

**Motivation:**

We've had reports of issues (such as
DataDog#5936 when using heap live
size on Ruby 4.0).

We've previously added a workaround in
DataDog#5938 but we're now
seeing issues with many different kinds of objects, not just classes,
and thus we're disabling this feature on Ruby 4.0 until we can root
cause the issue and fix it.

**Additional Notes:**

This is how the message looks in practice:

```
I, [2026-07-10T11:24:56.368794 #2947286]  INFO -- datadog: [datadog]
Heap live size profiling is currently incompatible with Ruby 4 and has
been disabled. Heap live objects is not affected and remains enabled.
```

**How to test the change?**

I've included tests for this config change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community Was opened by a community member profiling Involves Datadog profiling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants