Skip to content

Commit

Permalink
[PROF-9226] Add test for Ruby profiler when extension dir is moved an…
Browse files Browse the repository at this point in the history
…d relative rpath is needed

**What does this PR do?**

This PR adds a new test case that validates that
DataDog/dd-trace-rb#3582 and
DataDog/dd-trace-rb#3683 keep working fine.

**Motivation:**

As described in DataDog/dd-trace-rb#3683, this
a somewhat annoying thing to test, but important to avoid regressing.

**Additional Notes:**

You can actually see the evolution of both of those fixes in
this test.

E.g. here's dd-trace-rb 1.21.0 (prior to
DataDog/dd-trace-rb#3582 ):

```
W, [2024-06-12T09:34:08.759519 #7]  WARN -- ddtrace: [ddtrace] (/app/vendor-moved/bundle/ruby/3.3.0/gems/ddtrace-1.21.1/lib/datadog/core/configuration/components.rb:115:in `startup!') Profiling was requested but is not supported, profiling disabled: There was an error loading the profiling native extension due to 'RuntimeError Failure to load datadog_profiling_native_extension.3.3.2_x86_64-linux due to /app/vendor-moved/bundle/ruby/3.3.0/gems/ddtrace-1.21.1/lib/datadog/profiling/../../datadog_profiling_native_extension.3.3.2_x86_64-linux.so: cannot open shared object file: No such file or directory' at '/app/vendor-moved/bundle/ruby/3.3.0/gems/ddtrace-1.21.1/lib/datadog/profiling/load_native_extension.rb:26:in `<top (required)>''
    --- FAIL: TestScenarios/scenarios/ruby_extension_dir_and_rpath (14.86s)
```

in this version, we failed because we couldn't load the native
extension.

Then here's dd-trace-rb 1.23.1 (without
DataDog/dd-trace-rb#3683 ) and if we
don't move the `vendor` folder (but still delete the so from the
lib folder):

```
    --- PASS: TestScenarios/scenarios/ruby_extension_dir_and_rpath (18.96s)
```

...but if we additionally move the vendor folder (aka what this PR
does in the Dockerfile):

```
W, [2024-06-12T09:37:33.517188 #6]  WARN -- ddtrace: [ddtrace] (/app/vendor-moved/bundle/ruby/3.3.0/gems/ddtrace-1.23.1/lib/datadog/core/configuration/components.rb:116:in `startup!') Profiling was requested but is not supported, profiling disabled: There was an error loading the profiling native extension due to 'RuntimeError Failure to load datadog_profiling_native_extension.3.3.2_x86_64-linux due to libdatadog_profiling.so: cannot open shared object file: No such file or directory' at '/app/vendor-moved/bundle/ruby/3.3.0/gems/ddtrace-1.23.1/lib/datadog/profiling/load_native_extension.rb:39:in `<top (required)>''
    --- FAIL: TestScenarios/scenarios/ruby_extension_dir_and_rpath (3.25s)
```

Notice it fails BUT the error is now different from the one above --
the error is relating to loading `libdatadog_profiling.so`, not
`datadog_profiling_native_extension.3.3.2_x86_64-linux.so`.

And with the change in DataDog/dd-trace-rb#3683
(which will be in 1.23.2):

```
    --- PASS: TestScenarios/scenarios/ruby_extension_dir_and_rpath (9.60s)
```

**NOTE**: For this test, unlike other Ruby tests we have, we're pulling
in the latest **released** gem version (e.g. with `gem 'datadog'` on the
`gems.rb` file), not the latest from git (as we do for other Ruby
tests).

This is because gems get installed in different paths when bundler
downloads them directly from git, and we want to validate the path when
a stable version is installed.

This also means that this PR will show up as failed until the latest
datadog release (which will be 2.2.0) gets released. (Or 1.23.2, but
I left the test setup to test the latest 2.x releases, not the 1.x ones,
although I used 1.x on my tests above to show the evolution of the
issue).
  • Loading branch information
ivoanjo committed Jun 12, 2024
1 parent a3abdf3 commit 4dd19bc
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 1 deletion.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#==============================================================================#
# Directories to ignore (do not add trailing '/'s, they skip symlinks).
#==============================================================================#
binaries/*
data/*

.idea
binaries/*
gems.locked
20 changes: 20 additions & 0 deletions scenarios/ruby_extension_dir_and_rpath/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM ruby:3.3

ENV DD_TRACE_DEBUG true
ENV DD_PROFILING_PPROF_PREFIX="/app/data/profiles_"

# Copy the Ruby program into the container
COPY ./scenarios/ruby_extension_dir_and_rpath/gems.rb ./scenarios/ruby_basic/main.rb /app/
RUN chmod 644 /app/*

# Set the working directory to the location of the program
WORKDIR /app

RUN bundle config set --local path 'vendor/bundle'
RUN bundle install
RUN rm -f vendor/bundle/ruby/*/gems/ddtrace-*/lib/*so vendor/bundle/ruby/*/bundler/gems/*/lib/datadog*so
RUN bundle config set --local path 'vendor-moved/bundle'
RUN mv vendor vendor-moved

# Run the program when the container starts
CMD bundle exec ddprofrb exec ruby main.rb
23 changes: 23 additions & 0 deletions scenarios/ruby_extension_dir_and_rpath/expected_profile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"test_name":"ruby_basic",
"stacks": [
{
"profile-type": "cpu-time",
"stack-content":
[
{
"regular_expression": "\u003cmain\u003e;a",
"value": 330000000,
"percent": 33,
"error_margin": 7
},
{
"regular_expression": "\u003cmain\u003e;b",
"value": 660000000,
"percent": 66,
"error_margin": 7
}
]
}
]
}
3 changes: 3 additions & 0 deletions scenarios/ruby_extension_dir_and_rpath/gems.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
source 'https://rubygems.org'

gem 'datadog'
57 changes: 57 additions & 0 deletions scenarios/ruby_extension_dir_and_rpath/main.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
require 'timeout'

# The Ruby profiler does not (yet) include a way of exporting a pprof to a file, so we implement it here:
class ExportToFile
PPROF_PREFIX = ENV.fetch('DD_PROFILING_PPROF_PREFIX')

def export(flush)
File.write("#{PPROF_PREFIX}#{flush.start.strftime('%Y%m%dT%H%M%SZ')}.pprof", flush.pprof_data)
true
end
end

Datadog.configure do |c|
c.profiling.enabled = true
c.profiling.exporter.transport = ExportToFile.new
end

Timeout.timeout(5) do
until Datadog::Profiling::Collectors::CpuAndWallTimeWorker::Testing._native_is_running?(
Datadog.send(:components).profiler.send(:worker)
)
sleep(0.5)
end
end

def a
x = 0
i = 0
while i < 10_000_000
x += i
i += 1
end
end

def b
x = 0
i = 0
while i < 20_000_000
x += i
i += 1
end
end

test_duration = 50
exec_time_env = ENV['EXECUTION_TIME_SEC']
if exec_time_env
test_duration = exec_time_env.to_i
exit(1) if test_duration == 0
end

puts "Executable #{__FILE__} starting for #{test_duration} seconds"
end_time = Time.now + test_duration
while Time.now < end_time
a
b
end
puts "Executable #{__FILE__} finished successfully"

0 comments on commit 4dd19bc

Please sign in to comment.