-
Notifications
You must be signed in to change notification settings - Fork 372
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[PROF-8917] Add support for the libdatadog crash tracker
**What does this PR do?** This PR adds support for the libdatadog crash tracker feature (off by default). The crash tracker works by detecting when the Ruby VM reaches a segmentation fault, reporting the crash information as a last profile before the VM dies. All of the interesting work is in <DataDog/libdatadog#282>, this PR basically just wires things up. **Motivation:** This will be a useful tool when debugging VM crashes. **Additional Notes:** I'm opening this PR as a draft as the libdatadog support has not yet landed/been released. Also, there's a few open questions on: * fork handling * when to shut down **How to test the change?** (You'll need to build <<DataDog/libdatadog#282> until the crash tracker gets included in a libdatadog release) To test the crash tracker with an actual crash, try running the following on Ruby 2.6: ```bash $ DD_PROFILING_ENABLED=true DD_PROFILING_EXPERIMENTAL_CRASH_TRACKING_ENABLED=true DD_TRACE_DEBUG=true bundle exec ddtracerb exec ruby -e 'Process.detach(fork { exit! }).instance_variable_get(:@foo)' ``` This should also work in the future but right now it doesn't work correctly; still looking into why: ```bash $ DD_PROFILING_ENABLED=true DD_PROFILING_EXPERIMENTAL_CRASH_TRACKING_ENABLED=true DD_TRACE_DEBUG=true bundle exec ddtracerb exec ruby -e 'Process.kill("SEGV", Process.pid)' ```
- Loading branch information
Showing
14 changed files
with
319 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -648,4 +648,5 @@ target :ddtrace do | |
|
||
# TODO: gem 'libddwaf' | ||
library 'libddwaf' | ||
library 'libdatadog' | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
#include <ruby.h> | ||
#include <datadog/common.h> | ||
#include <libdatadog_helpers.h> | ||
|
||
static VALUE _native_start_crashtracker(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self); | ||
|
||
// Used to report Ruby VM crashes. | ||
// Once initialized, segfaults will be reported automatically using libdatadog. | ||
|
||
void crash_tracker_init(VALUE profiling_module) { | ||
VALUE crash_tracker_class = rb_define_class_under(profiling_module, "CrashTracker", rb_cObject); | ||
|
||
rb_define_singleton_method(crash_tracker_class, "_native_start_crashtracker", _native_start_crashtracker, -1); | ||
} | ||
|
||
static VALUE _native_start_crashtracker(int argc, VALUE *argv, DDTRACE_UNUSED VALUE _self) { | ||
VALUE options; | ||
rb_scan_args(argc, argv, "0:", &options); | ||
|
||
VALUE exporter_configuration = rb_hash_fetch(options, ID2SYM(rb_intern("exporter_configuration"))); | ||
VALUE path_to_crashtracking_receiver_binary = rb_hash_fetch(options, ID2SYM(rb_intern("path_to_crashtracking_receiver_binary"))); | ||
VALUE tags_as_array = rb_hash_fetch(options, ID2SYM(rb_intern("tags_as_array"))); | ||
|
||
ENFORCE_TYPE(exporter_configuration, T_ARRAY); | ||
ENFORCE_TYPE(tags_as_array, T_ARRAY); | ||
ENFORCE_TYPE(path_to_crashtracking_receiver_binary, T_STRING); | ||
|
||
VALUE version = ddtrace_version(); | ||
ddog_Endpoint endpoint = endpoint_from(exporter_configuration); | ||
|
||
// This needs to come last, after all things that can raise exceptions, as otherwise it can leak | ||
ddog_Vec_Tag tags = convert_tags(tags_as_array); | ||
|
||
ddog_prof_Configuration config = { | ||
.create_alt_stack = false, // This breaks the Ruby VM's stack overflow detection | ||
.endpoint = endpoint, | ||
.path_to_receiver_binary = char_slice_from_ruby_string(path_to_crashtracking_receiver_binary), | ||
}; | ||
|
||
ddog_prof_Metadata metadata = { | ||
.profiling_library_name = DDOG_CHARSLICE_C("dd-trace-rb"), | ||
.profiling_library_version = char_slice_from_ruby_string(version), | ||
.family = DDOG_CHARSLICE_C("ruby"), | ||
.tags = &tags, | ||
}; | ||
|
||
ddog_prof_Profile_Result result = ddog_prof_crashtracker_init(config, metadata); | ||
|
||
// Clean up before potentially raising any exceptions | ||
ddog_Vec_Tag_drop(tags); | ||
|
||
if (result.tag == DDOG_PROF_PROFILE_RESULT_ERR) { | ||
rb_raise(rb_eRuntimeError, "Failed to initialize the crash tracker: %"PRIsVALUE, get_error_details_and_drop(&result.err)); | ||
} | ||
|
||
return Qtrue; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'libdatadog' | ||
|
||
# FIXME: Move this to libdatadog -- this is only here to facilitate testing | ||
module ::Libdatadog | ||
def self.path_to_crashtracking_receiver_binary | ||
# TODO: Error handling when pkgconfig_folder is not detected correctly | ||
File.absolute_path("#{::Libdatadog.pkgconfig_folder}/../../bin/ddog-crashtracking-receiver") | ||
end | ||
end | ||
|
||
module Datadog | ||
module Profiling | ||
# Used to report Ruby VM crashes. | ||
# The interesting bits are implemented as native code and using libdatadog. | ||
# | ||
# Methods prefixed with _native_ are implemented in `crash_tracker.c` | ||
class CrashTracker | ||
def self.build_crash_tracker( | ||
exporter_configuration:, | ||
tags:, | ||
path_to_crashtracking_receiver_binary: Libdatadog.path_to_crashtracking_receiver_binary | ||
) | ||
unless path_to_crashtracking_receiver_binary | ||
Datadog.logger.warn( | ||
'Cannot enable profiling crash tracking as no path_to_crashtracking_receiver_binary was found' | ||
) | ||
return | ||
end | ||
|
||
begin | ||
new( | ||
exporter_configuration: exporter_configuration, | ||
path_to_crashtracking_receiver_binary: path_to_crashtracking_receiver_binary, | ||
tags_as_array: tags.to_a, | ||
).tap { | ||
Datadog.logger.debug('Crash tracker enabled') | ||
} | ||
rescue => e | ||
Datadog.logger.error("Failed to initialize crash tracking: #{e.message}") | ||
nil | ||
end | ||
end | ||
|
||
private | ||
|
||
def initialize(exporter_configuration:, path_to_crashtracking_receiver_binary:, tags_as_array:) | ||
self.class._native_start_crashtracker( | ||
exporter_configuration: exporter_configuration, | ||
path_to_crashtracking_receiver_binary: path_to_crashtracking_receiver_binary, | ||
tags_as_array: tags_as_array, | ||
) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
module Datadog | ||
module Profiling | ||
class CrashTracker | ||
def self.build_crash_tracker: ( | ||
exporter_configuration: [:agentless | :agent, untyped], | ||
tags: ::Hash[::String, ::String], | ||
?path_to_crashtracking_receiver_binary: ::String, | ||
) -> CrashTracker? | ||
|
||
private | ||
|
||
def initialize: ( | ||
exporter_configuration: [:agentless | :agent, untyped], | ||
path_to_crashtracking_receiver_binary: ::String, | ||
tags_as_array: ::Array[[::String, ::String]], | ||
) -> void | ||
|
||
def self._native_start_crashtracker: ( | ||
exporter_configuration: [:agentless | :agent, untyped], | ||
path_to_crashtracking_receiver_binary: ::String, | ||
tags_as_array: ::Array[[::String, ::String]], | ||
) -> void | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.