Skip to content

Commit ffc9958

Browse files
authored
Merge pull request #5091 from DataDog/ivoanjo/prof-13115-ruby-4_0_0-preview2-profiler
[PROF-13115] Fix profiler support for Ruby 4.0.0-preview2
2 parents c823ca8 + 49e66f9 commit ffc9958

File tree

4 files changed

+40
-10
lines changed

4 files changed

+40
-10
lines changed

ext/datadog_profiling_native_extension/extconf.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,10 @@ def skip_building_extension!(reason)
141141
# On Ruby 3.5, we can't ask the object_id from IMEMOs (https://github.com/ruby/ruby/pull/13347)
142142
$defs << "-DNO_IMEMO_OBJECT_ID" unless RUBY_VERSION < "3.5"
143143

144-
# On Ruby 2.5 and 3.3, this symbol was not visible. It is on 2.6 to 3.2, as well as 3.4+
145-
$defs << "-DNO_RB_OBJ_INFO" if RUBY_VERSION.start_with?("2.5", "3.3")
144+
# This symbol is exclusively visible on certain Ruby versions: 2.6 to 3.2, as well as 3.4 and 3.5-preview1 (but not 4.0)
145+
# It's only used to get extra information about an object when a failure happens, so it's a "very nice to have" but not
146+
# actually required for correct behavior of the profiler.
147+
$defs << "-DNO_RB_OBJ_INFO" if RUBY_VERSION.start_with?("2.5", "3.3", "4.0")
146148

147149
# On older Rubies, rb_postponed_job_preregister/rb_postponed_job_trigger did not exist
148150
$defs << "-DNO_POSTPONED_TRIGGER" if RUBY_VERSION < "3.3"

spec/datadog/profiling/collectors/cpu_and_wall_time_worker_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -683,8 +683,8 @@
683683
end
684684

685685
it "records allocated objects" do
686-
# TODO: Ruby 3.5 - Remove this skip after investigation.
687-
pending('Allocation profiling call not working correctly on Ruby 3.5.0-preview1') if RUBY_DESCRIPTION.include?('preview')
686+
# TODO: Remove this when Ruby 3.5.0-preview1 is removed from CI
687+
pending('Allocation profiling call not working correctly on Ruby 3.5.0-preview1') if RUBY_DESCRIPTION.include?('3.5.0preview1')
688688

689689
stub_const("CpuAndWallTimeWorkerSpec::TestStruct", Struct.new(:foo))
690690

spec/datadog/profiling/collectors/stack_spec.rb

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
let(:metric_values) { {"cpu-time" => 123, "cpu-samples" => 456, "wall-time" => 789} }
1616
let(:labels) { {"label_a" => "value_a", "label_b" => "value_b", "state" => "unknown"}.to_a }
1717

18-
let(:raw_reference_stack) { stacks.fetch(:reference) }
19-
let(:reference_stack) { convert_reference_stack(raw_reference_stack) }
20-
let(:gathered_stack) { stacks.fetch(:gathered) }
18+
let(:raw_reference_stack) { stacks.fetch(:reference).freeze }
19+
let(:reference_stack) { convert_reference_stack(raw_reference_stack).freeze }
20+
let(:gathered_stack) { stacks.fetch(:gathered).freeze }
2121
let(:native_filenames_enabled) { false }
2222

2323
def sample(thread, recorder_instance, metric_values_hash, labels_array, **options)
@@ -220,7 +220,35 @@ def call_sleep
220220

221221
# I opted to join these two expects to avoid running the `load` above more than once
222222
it "matches the Ruby backtrace API AND has a sleeping frame at the top of the stack" do
223-
expect(gathered_stack).to eq reference_stack
223+
if RUBY_VERSION.start_with?("4.")
224+
# In Ruby 4, due to https://bugs.ruby-lang.org/issues/20968 while internally Integer#times has the path
225+
# `<internal:numeric>` (and this is what the profiler observes), Ruby actually hides this and "blames" it
226+
# on the last ruby file/line that was on the stack.
227+
#
228+
# @ivoanjo: At this point I'm not sure we want to match that behavior as we don't match it either when
229+
# using the "native filenames" feature. So for now we adjust the assertions to account for that
230+
unmatched_indexes =
231+
reference_stack.each_with_index.select { |frame, index| frame.base_label == "times" }.map(&:last)
232+
expect(unmatched_indexes).to_not be_empty
233+
234+
gathered_stack_without_unmatched = gathered_stack.dup
235+
reference_stack_without_unmatched = reference_stack.dup
236+
237+
# Check the expected frames are different -- and remove them from the match
238+
unmatched_indexes.sort.reverse_each do |index|
239+
expect(gathered_stack[index].path).to eq "<internal:numeric>"
240+
expect(reference_stack[index].path).to end_with "/interesting_backtrace_helper.rb"
241+
242+
gathered_stack_without_unmatched.delete_at(index)
243+
reference_stack_without_unmatched.delete_at(index)
244+
end
245+
246+
# ...match the rest of the frames
247+
expect(gathered_stack_without_unmatched).to eq reference_stack_without_unmatched
248+
else
249+
expect(gathered_stack).to eq reference_stack
250+
end
251+
224252
expect(reference_stack.first.base_label).to eq "sleep"
225253
end
226254
end

spec/datadog/profiling/native_extension_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,15 +190,15 @@
190190
subject(:safe_object_info) { described_class::Testing._native_safe_object_info(object_to_inspect) }
191191

192192
context "on a Ruby with rb_obj_info" do
193-
before { skip "Behavior does not apply to current Ruby version" if RUBY_VERSION.start_with?("2.5", "3.3") }
193+
before { skip "Behavior does not apply to current Ruby version" if RUBY_VERSION.start_with?("2.5", "3.3", "4.0") }
194194

195195
it "returns a string with information about the object" do
196196
expect(safe_object_info).to include("T_STRING")
197197
end
198198
end
199199

200200
context "on a Ruby without rb_obj_info" do
201-
before { skip "Behavior does not apply to current Ruby version" unless RUBY_VERSION.start_with?("2.5", "3.3") }
201+
before { skip "Behavior does not apply to current Ruby version" unless RUBY_VERSION.start_with?("2.5", "3.3", "4.0") }
202202

203203
it "returns a placeholder string and does not otherwise fail" do
204204
expect(safe_object_info).to eq "(No rb_obj_info for current Ruby)"

0 commit comments

Comments
 (0)