Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PROF-10241] Extract libdatadog crashtracker telemetry into separate extension #3824

Merged

Conversation

ivoanjo
Copy link
Member

@ivoanjo ivoanjo commented Aug 2, 2024

What does this PR do?

In #3384 we added support for the libdatadog crashtracker telemetry, and in #3816 we enabled it by default.

But so far the crashtracker telemetry support has been tied to the profiler:

  1. It interfaces with libdatadog via the profiler native extension
  2. It's only turned on when the profiler is turned on

This PR is the first step into allowing the crashtracker to be used completely independently of the profiler. Specifically, it addresses only 1) above, and does not yet address 2. (See additional notes for more details).

This PR introduces a new, separate native extension (libdatadog_api) that links to libdatadog separate from the profiler native extension, and moves the crashtracker.c bindings for the crashtracker to it.

This new extension borrows the same strategy as the profiler native extension for building and linking to libdatadog. This led to a bunch of code being extracted (and a bit being copy-pasted) from the profiler native extension.

The below diff looks "scarier" than it is, because most of the diff is things being moved around. After opening the PR, I plan to do a quick pass of comments to facilitate review and indicate more clearly when things just moved around.

Note that the crashtracker is working with this PR -- it's just still depending on profiling being enabled as well.

Motivation:

A big motivation for this work is to enable the crashtracker to work for customers not using profiling (or even in setups where the profiling doesn't work).

Additional Notes:

There's some copy-pasta 🍝:

I chose to copy-paste the datadog_ruby_common.h/datadog_ruby_common.c as it's kinda hard (and I did spend some time on it) to get extconf.rb to read .c and .h files from other directories, and to add them as correct dependencies to the generated Makefile. If you do figure out how to do it, please share!

What's missing for later PRs:

  1. The crashtracker is still only being turned on when the profiler is turned on. We'll need to extract it to be a top-level component, outside Datadog::Profiling.

  2. The native bits still place the crashtracker in Datadog::Profiling::Crashtracker, it should be moved as well.

  3. The crashtracker extension is built in development only when running the profiler test suite. This does not affect an actual regular installation.

  4. The crashtracker is controlled by the DD_PROFILING_EXPERIMENTAL_CRASH_TRACKING_ENABLED / c.profiling.advanced.experimental_crash_tracking_enabled setting. We need to deprecate this setting and rename it to something more appropriate.

  5. Configuration of the endpoint is currently reusing some helper code in profiling/http_transport.rb that was not yet extracted.

Things to keep in mind about libdatadog/this integration:

  1. Libdatadog only has binary builds for linux. This means, like the profiler native extension, this is a "soft" integration that is built to gracefully work with this:

    1. If, during installation, the extension detects that it cannot compile, it will just skip that step and still allow a successful installation.
    2. You can force the extension to not be built with DD_NO_EXTENSION=true.
    3. For testing, you can set DD_FAIL_INSTALL_IF_MISSING_EXTENSION=true to force installation to fail immediately. This is useful when testing/debugging if the installation is working or not.
    4. We wrap extension loading with a begin/rescue, and the code gracefully handles situations where the extension is not present.
  2. I've added a document showing how you can develop natively on macOS with libdatadog.

How to test the change?

This snippet will enable profiling and crash Ruby:

DD_TRACE_DEBUG=true DD_PROFILING_ENABLED=true bundle exec ddprofrb exec ruby -e 'Process.kill("SEGV", Process.pid)'

You should see logging indicating that the crashtracker has been turned on, and if you have an active Datadog agent running, you'll see a crash report being submitted.

This extension will be used to call libdatadog APIs without needing
to go through profiling.
There's a lot of copy-pasta and I'm not entirely happy. Let's go with
it for now, to get it moving.
…nsions

This cleans up the wad of copy-pasted code I did for my first attempt.
These new files just moved things around from where they were in the
profiler, and other than minimal cleanups, they are the same stuff.
We need to use these features in extconf, so let's tell rubocop to stop
annoying us about it.
This also included adding a `skip_building_extension!` to
`libdatadog_api/extconf.rb` so we can properly handle pkg-config
failures like the profiler extension does.
This was the last bit that had been copy-pasta'd from the profiler
native extension into the `libdatadog_api/extconf_helpers` and thus we
are now sharing most of the extconf helpers.
This makes it easy to use the same prebuilt extension across multiple
Ruby patch versions.
One less file, and we can always extract it again later.
@ivoanjo ivoanjo requested review from a team as code owners August 2, 2024 16:11
@github-actions github-actions bot added the profiling Involves Datadog profiling label Aug 2, 2024
require 'rubygems'
require_relative '../libdatadog_extconf_helpers'

def skip_building_extension!(reason)

Choose a reason for hiding this comment

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

🔵 Code Quality Violation

Avoid top-level methods definition. Organize methods in modules/classes. (...read more)

This rule emphasizes the importance of organizing methods within modules or classes in Ruby. In Ruby, it's considered a best practice to wrap methods within classes or modules. This is because it helps in grouping related methods together, which in turn makes the code easier to understand, maintain, and reuse.

Not adhering to this rule can lead to a disorganized codebase, making it hard for other developers to understand and maintain the code. It can also lead to potential name clashes if a method is defined in the global scope.

To avoid violating this rule, always define your methods within a class or a module. For example, instead of writing def some_method; end, you should write class SomeClass def some_method; end end. This not only adheres to the rule but also improves the readability and maintainability of your code.

View in Datadog  Leave us feedback  Documentation

require 'rubygems'
require_relative '../libdatadog_extconf_helpers'

def skip_building_extension!(reason)

Choose a reason for hiding this comment

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

🔵 Code Quality Violation

Avoid top-level methods definition. Organize methods in modules/classes. (...read more)

This rule emphasizes the importance of organizing methods within modules or classes in Ruby. In Ruby, it's considered a best practice to wrap methods within classes or modules. This is because it helps in grouping related methods together, which in turn makes the code easier to understand, maintain, and reuse.

Not adhering to this rule can lead to a disorganized codebase, making it hard for other developers to understand and maintain the code. It can also lead to potential name clashes if a method is defined in the global scope.

To avoid violating this rule, always define your methods within a class or a module. For example, instead of writing def some_method; end, you should write class SomeClass def some_method; end end. This not only adheres to the rule but also improves the readability and maintainability of your code.

View in Datadog  Leave us feedback  Documentation

require 'rubygems'
require_relative '../libdatadog_extconf_helpers'

def skip_building_extension!(reason)

Choose a reason for hiding this comment

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

🔵 Code Quality Violation

Avoid top-level methods definition. Organize methods in modules/classes. (...read more)

This rule emphasizes the importance of organizing methods within modules or classes in Ruby. In Ruby, it's considered a best practice to wrap methods within classes or modules. This is because it helps in grouping related methods together, which in turn makes the code easier to understand, maintain, and reuse.

Not adhering to this rule can lead to a disorganized codebase, making it hard for other developers to understand and maintain the code. It can also lead to potential name clashes if a method is defined in the global scope.

To avoid violating this rule, always define your methods within a class or a module. For example, instead of writing def some_method; end, you should write class SomeClass def some_method; end end. This not only adheres to the rule but also improves the readability and maintainability of your code.

View in Datadog  Leave us feedback  Documentation

Copy link
Member Author

@ivoanjo ivoanjo Aug 2, 2024

Choose a reason for hiding this comment

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

These things got moved over from other helper files and put together here, they're mostly unchanged.

Copy link
Member Author

Choose a reason for hiding this comment

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

These things got moved over from other helper files and put together here, they're mostly unchanged.

Comment on lines -5 to -15
// Used to mark symbols to be exported to the outside of the extension.
// Consider very carefully before tagging a function with this.
#define DDTRACE_EXPORT __attribute__ ((visibility ("default")))

// Used to mark function arguments that are deliberately left unused
#ifdef __GNUC__
#define DDTRACE_UNUSED __attribute__((unused))
#else
#define DDTRACE_UNUSED
#endif

Copy link
Member Author

Choose a reason for hiding this comment

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

Moved to datadog_ruby_common.h

Copy link
Member Author

Choose a reason for hiding this comment

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

Moved to datadog_ruby_common.c

Copy link
Member Author

Choose a reason for hiding this comment

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

Moved to datadog_ruby_common.h

Copy link
Member Author

Choose a reason for hiding this comment

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

Moved to libdatadog_extconf_helpers.rb

Copy link
Member Author

Choose a reason for hiding this comment

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

Moved to datadog_ruby_common.c

Copy link
Member Author

Choose a reason for hiding this comment

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

Moved to datadog_ruby_common.h

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy/pasta of file from profiling native extension. See PR description for why.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy/pasta of file from profiling native extension. See PR description for why.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is a simplified version of datadog_profiling_native_extension/extconf.rb

Copy link
Member Author

Choose a reason for hiding this comment

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

Extracted from datadog_profiling_native_extension/native_extension_helpers.rb with minimal changes

Comment on lines 15 to +22
class Crashtracker
LIBDATADOG_API_FAILURE =
begin
require "libdatadog_api.#{RUBY_VERSION[/\d+.\d+/]}_#{RUBY_PLATFORM}"
nil
rescue LoadError => e
e.message
end
Copy link
Member Author

Choose a reason for hiding this comment

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

This is needed for gracefully handling the extension being missing. Before the extraction, we were relying on the profiler loading code taking care of the graceful loading (which it does)

@codecov-commenter
Copy link

Codecov Report

Attention: Patch coverage is 96.15385% with 3 lines in your changes missing coverage. Please review.

Project coverage is 97.87%. Comparing base (d3125b9) to head (5f6580d).

Files Patch % Lines
ext/libdatadog_extconf_helpers.rb 90.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3824      +/-   ##
==========================================
- Coverage   97.88%   97.87%   -0.01%     
==========================================
  Files        1261     1262       +1     
  Lines       75624    75653      +29     
  Branches     3706     3710       +4     
==========================================
+ Hits        74025    74048      +23     
- Misses       1599     1605       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@pr-commenter
Copy link

pr-commenter bot commented Aug 2, 2024

Benchmarks

Benchmark execution time: 2024-08-02 16:49:37

Comparing candidate commit 5f6580d in PR branch ivoanjo/prof-10241-libdatadog-crashtracker-without-profiling-v2 with baseline commit d3125b9 in branch master.

Found 0 performance improvements and 0 performance regressions! Performance is the same for 10 metrics, 2 unstable metrics.

Copy link
Contributor

@AlexJF AlexJF left a comment

Choose a reason for hiding this comment

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

LGTM!

@ivoanjo ivoanjo merged commit 4fbe269 into master Aug 5, 2024
182 checks passed
@ivoanjo ivoanjo deleted the ivoanjo/prof-10241-libdatadog-crashtracker-without-profiling-v2 branch August 5, 2024 14:14
@github-actions github-actions bot added this to the 2.3.0 milestone Aug 5, 2024
@ivoanjo ivoanjo added the core Involves Datadog core libraries label Aug 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Involves Datadog core libraries profiling Involves Datadog profiling
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants