Skip to content

Commit d056dfc

Browse files
committed
Move into own extension
1 parent 1a73d22 commit d056dfc

File tree

14 files changed

+434
-247
lines changed

14 files changed

+434
-247
lines changed

Rakefile

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,16 @@ namespace :test do
5656

5757
candidates.each_key do |group|
5858
env = if group.empty?
59-
{}
60-
else
61-
gemfile = AppraisalConversion.to_bundle_gemfile(group)
62-
{'BUNDLE_GEMFILE' => gemfile}
63-
end
59+
{}
60+
else
61+
gemfile = AppraisalConversion.to_bundle_gemfile(group)
62+
{ 'BUNDLE_GEMFILE' => gemfile }
63+
end
6464
command = "bundle check || bundle install && bundle exec rake #{spec_task}"
6565
command += "'[#{spec_arguments}]'" if spec_arguments
6666

67-
total_executors = ENV.key?('CIRCLE_NODE_TOTAL') ? ENV['CIRCLE_NODE_TOTAL'].to_i : nil
68-
current_executor = ENV.key?('CIRCLE_NODE_INDEX') ? ENV['CIRCLE_NODE_INDEX'].to_i : nil
67+
total_executors = Datadog::DATADOG_ENV.key?('CIRCLE_NODE_TOTAL') ? Datadog::DATADOG_ENV['CIRCLE_NODE_TOTAL'].to_i : nil
68+
current_executor = Datadog::DATADOG_ENV.key?('CIRCLE_NODE_INDEX') ? Datadog::DATADOG_ENV['CIRCLE_NODE_INDEX'].to_i : nil
6969

7070
if total_executors && current_executor && total_executors > 1
7171
@execution_count ||= 0
@@ -83,10 +83,10 @@ desc 'Run RSpec'
8383
namespace :spec do
8484
# REMINDER: If adding a new task here, make sure also add it to the `Matrixfile`
8585
task all: [:main, :benchmark, :custom_cop,
86-
:graphql, :graphql_unified_trace_patcher, :graphql_trace_patcher, :graphql_tracing_patcher,
87-
:rails, :railsredis, :railsredis_activesupport, :railsactivejob,
88-
:elasticsearch, :http, :redis, :sidekiq, :sinatra, :hanami, :hanami_autoinstrument,
89-
:profiling, :core_with_libdatadog_api, :error_tracking, :open_feature]
86+
:graphql, :graphql_unified_trace_patcher, :graphql_trace_patcher, :graphql_tracing_patcher,
87+
:rails, :railsredis, :railsredis_activesupport, :railsactivejob,
88+
:elasticsearch, :http, :redis, :sidekiq, :sinatra, :hanami, :hanami_autoinstrument,
89+
:profiling, :core_with_libdatadog_api, :error_tracking, :open_feature]
9090

9191
desc '' # "Explicitly hiding from `rake -T`"
9292
RSpec::Core::RakeTask.new(:main) do |t, args|
@@ -229,7 +229,12 @@ namespace :spec do
229229
t.pattern = CORE_WITH_LIBDATADOG_API.join(', ')
230230
t.rspec_opts = args.to_a.join(' ')
231231
end.tap do |t|
232-
Rake::Task[t.name].enhance(["compile:libdatadog_api.#{RUBY_VERSION[/\d+.\d+/]}_#{RUBY_PLATFORM}"])
232+
Rake::Task[t.name].enhance(
233+
[
234+
"compile:libdatadog_api.#{RUBY_VERSION[/\d+.\d+/]}_#{RUBY_PLATFORM}",
235+
"compile:datadog_runtime_stacks.#{RUBY_VERSION[/\d+.\d+/]}_#{RUBY_PLATFORM}"
236+
]
237+
)
233238
end
234239
# rubocop:enable Style/MultilineBlockChain
235240

@@ -319,7 +324,7 @@ namespace :spec do
319324
rescue => e
320325
# Compilation failed (likely unsupported Ruby version) - tests will skip gracefully
321326
puts "Warning: libdatadog_api compilation failed: #{e.class}: #{e}"
322-
puts "DSM tests will be skipped for this Ruby version"
327+
puts 'DSM tests will be skipped for this Ruby version'
323328
end
324329

325330
DSM_ENABLED_LIBRARIES.each do |task_name|
@@ -462,11 +467,11 @@ namespace :coverage do
462467
task :report do
463468
require 'simplecov'
464469

465-
resultset_files = Dir["#{ENV.fetch("COVERAGE_DIR", "coverage")}/.resultset.json"] +
466-
Dir["#{ENV.fetch("COVERAGE_DIR", "coverage")}/versions/**/.resultset.json"]
470+
resultset_files = Dir["#{Datadog::DATADOG_ENV.fetch('COVERAGE_DIR', 'coverage')}/.resultset.json"] +
471+
Dir["#{Datadog::DATADOG_ENV.fetch('COVERAGE_DIR', 'coverage')}/versions/**/.resultset.json"]
467472

468473
SimpleCov.collate resultset_files do
469-
coverage_dir "#{ENV.fetch("COVERAGE_DIR", "coverage")}/report"
474+
coverage_dir "#{Datadog::DATADOG_ENV.fetch('COVERAGE_DIR', 'coverage')}/report"
470475
formatter SimpleCov::Formatter::HTMLFormatter
471476
end
472477
end
@@ -476,11 +481,11 @@ namespace :coverage do
476481
require 'simplecov'
477482
require_relative 'spec/support/simplecov_fix'
478483

479-
versions = Dir["#{ENV.fetch("COVERAGE_DIR", "coverage")}/versions/*"].map { |f| File.basename(f) }
484+
versions = Dir["#{Datadog::DATADOG_ENV.fetch('COVERAGE_DIR', 'coverage')}/versions/*"].map { |f| File.basename(f) }
480485
versions.map do |version|
481486
puts "Generating report for: #{version}"
482-
SimpleCov.collate Dir["#{ENV.fetch("COVERAGE_DIR", "coverage")}/versions/#{version}/**/.resultset.json"] do
483-
coverage_dir "#{ENV.fetch("COVERAGE_DIR", "coverage")}/report/versions/#{version}"
487+
SimpleCov.collate Dir["#{Datadog::DATADOG_ENV.fetch('COVERAGE_DIR', 'coverage')}/versions/#{version}/**/.resultset.json"] do
488+
coverage_dir "#{Datadog::DATADOG_ENV.fetch('COVERAGE_DIR', 'coverage')}/report/versions/#{version}"
484489
formatter SimpleCov::Formatter::HTMLFormatter
485490
end
486491
end
@@ -500,6 +505,10 @@ NATIVE_EXTS = [
500505
ext.ext_dir = 'ext/libdatadog_api'
501506
end,
502507

508+
Rake::ExtensionTask.new("datadog_runtime_stacks.#{RUBY_VERSION[/\d+.\d+/]}_#{RUBY_PLATFORM}") do |ext|
509+
ext.ext_dir = 'ext/datadog_runtime_stacks'
510+
end,
511+
503512
Rake::ExtensionTask.new("datadog_profiling_native_extension.#{RUBY_VERSION}_#{RUBY_PLATFORM}") do |ext|
504513
ext.ext_dir = 'ext/datadog_profiling_native_extension'
505514
end,

datadog.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ Gem::Specification.new do |spec|
8383

8484
spec.extensions = [
8585
'ext/datadog_profiling_native_extension/extconf.rb',
86+
'ext/datadog_runtime_stacks/extconf.rb',
8687
'ext/libdatadog_api/extconf.rb'
8788
]
8889
end
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include "datadog_ruby_common.h"
2+
3+
// IMPORTANT: Currently this file is copy-pasted between extensions. Make sure to update all versions when doing any change!
4+
5+
void raise_unexpected_type(VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char* function_name) {
6+
rb_exc_raise(
7+
rb_exc_new_str(
8+
rb_eTypeError,
9+
rb_sprintf("wrong argument %"PRIsVALUE" for '%s' (expected a %s) at %s:%d:in `%s'",
10+
rb_inspect(value),
11+
value_name,
12+
type_name,
13+
file,
14+
line,
15+
function_name
16+
)
17+
)
18+
);
19+
}
20+
21+
VALUE datadog_gem_version(void) {
22+
VALUE ddtrace_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
23+
ENFORCE_TYPE(ddtrace_module, T_MODULE);
24+
VALUE version_module = rb_const_get(ddtrace_module, rb_intern("VERSION"));
25+
ENFORCE_TYPE(version_module, T_MODULE);
26+
VALUE version_string = rb_const_get(version_module, rb_intern("STRING"));
27+
ENFORCE_TYPE(version_string, T_STRING);
28+
return version_string;
29+
}
30+
31+
static VALUE log_failure_to_process_tag(VALUE err_details) {
32+
return log_warning(rb_sprintf("Failed to convert tag: %"PRIsVALUE, err_details));
33+
}
34+
35+
__attribute__((warn_unused_result))
36+
ddog_Vec_Tag convert_tags(VALUE tags_as_array) {
37+
ENFORCE_TYPE(tags_as_array, T_ARRAY);
38+
39+
long tags_count = RARRAY_LEN(tags_as_array);
40+
ddog_Vec_Tag tags = ddog_Vec_Tag_new();
41+
42+
for (long i = 0; i < tags_count; i++) {
43+
VALUE name_value_pair = rb_ary_entry(tags_as_array, i);
44+
45+
if (!RB_TYPE_P(name_value_pair, T_ARRAY)) {
46+
ddog_Vec_Tag_drop(tags);
47+
ENFORCE_TYPE(name_value_pair, T_ARRAY);
48+
}
49+
50+
// Note: We can index the array without checking its size first because rb_ary_entry returns Qnil if out of bounds
51+
VALUE tag_name = rb_ary_entry(name_value_pair, 0);
52+
VALUE tag_value = rb_ary_entry(name_value_pair, 1);
53+
54+
if (!(RB_TYPE_P(tag_name, T_STRING) && RB_TYPE_P(tag_value, T_STRING))) {
55+
ddog_Vec_Tag_drop(tags);
56+
ENFORCE_TYPE(tag_name, T_STRING);
57+
ENFORCE_TYPE(tag_value, T_STRING);
58+
}
59+
60+
ddog_Vec_Tag_PushResult push_result =
61+
ddog_Vec_Tag_push(&tags, char_slice_from_ruby_string(tag_name), char_slice_from_ruby_string(tag_value));
62+
63+
if (push_result.tag == DDOG_VEC_TAG_PUSH_RESULT_ERR) {
64+
// libdatadog validates tags and may catch invalid tags that ddtrace didn't actually catch.
65+
// We warn users about such tags, and then just ignore them.
66+
67+
int exception_state;
68+
rb_protect(log_failure_to_process_tag, get_error_details_and_drop(&push_result.err), &exception_state);
69+
70+
// Since we are calling into Ruby code, it may raise an exception. Ensure that dynamically-allocated tags
71+
// get cleaned before propagating the exception.
72+
if (exception_state) {
73+
ddog_Vec_Tag_drop(tags);
74+
rb_jump_tag(exception_state); // "Re-raise" exception
75+
}
76+
}
77+
}
78+
79+
return tags;
80+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#pragma once
2+
3+
// IMPORTANT: Currently this file is copy-pasted between extensions. Make sure to update all versions when doing any change!
4+
5+
#include <ruby.h>
6+
#include <datadog/common.h>
7+
8+
// Used to mark symbols to be exported to the outside of the extension.
9+
// Consider very carefully before tagging a function with this.
10+
#define DDTRACE_EXPORT __attribute__ ((visibility ("default")))
11+
12+
// Used to mark function arguments that are deliberately left unused
13+
#ifdef __GNUC__
14+
#define DDTRACE_UNUSED __attribute__((unused))
15+
#else
16+
#define DDTRACE_UNUSED
17+
#endif
18+
19+
#define ADD_QUOTES_HELPER(x) #x
20+
#define ADD_QUOTES(x) ADD_QUOTES_HELPER(x)
21+
22+
// Ruby has a Check_Type(value, type) that is roughly equivalent to this BUT Ruby's version is rather cryptic when it fails
23+
// e.g. "wrong argument type nil (expected String)". This is a replacement that prints more information to help debugging.
24+
#define ENFORCE_TYPE(value, type) \
25+
{ if (RB_UNLIKELY(!RB_TYPE_P(value, type))) raise_unexpected_type(value, ADD_QUOTES(value), ADD_QUOTES(type), __FILE__, __LINE__, __func__); }
26+
27+
#define ENFORCE_BOOLEAN(value) \
28+
{ if (RB_UNLIKELY(value != Qtrue && value != Qfalse)) raise_unexpected_type(value, ADD_QUOTES(value), "true or false", __FILE__, __LINE__, __func__); }
29+
30+
#define ENFORCE_TYPED_DATA(value, type) \
31+
{ if (RB_UNLIKELY(!rb_typeddata_is_kind_of(value, type))) raise_unexpected_type(value, ADD_QUOTES(value), "TypedData of type " ADD_QUOTES(type), __FILE__, __LINE__, __func__); }
32+
33+
NORETURN(void raise_unexpected_type(VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char* function_name));
34+
35+
// Helper to retrieve Datadog::VERSION::STRING
36+
VALUE datadog_gem_version(void);
37+
38+
static inline ddog_CharSlice char_slice_from_ruby_string(VALUE string) {
39+
ENFORCE_TYPE(string, T_STRING);
40+
ddog_CharSlice char_slice = {.ptr = RSTRING_PTR(string), .len = RSTRING_LEN(string)};
41+
return char_slice;
42+
}
43+
44+
static inline ddog_CharSlice char_slice_from_cstr(const char *cstr) {
45+
if (cstr == NULL) {
46+
return (ddog_CharSlice){.ptr = NULL, .len = 0};
47+
}
48+
return (ddog_CharSlice){.ptr = cstr, .len = strlen(cstr)};
49+
}
50+
51+
static inline VALUE log_warning(VALUE warning) {
52+
VALUE datadog_module = rb_const_get(rb_cObject, rb_intern("Datadog"));
53+
VALUE logger = rb_funcall(datadog_module, rb_intern("logger"), 0);
54+
55+
return rb_funcall(logger, rb_intern("warn"), 1, warning);
56+
}
57+
58+
__attribute__((warn_unused_result))
59+
ddog_Vec_Tag convert_tags(VALUE tags_as_array);
60+
61+
static inline VALUE ruby_string_from_error(const ddog_Error *error) {
62+
ddog_CharSlice char_slice = ddog_Error_message(error);
63+
return rb_str_new(char_slice.ptr, char_slice.len);
64+
}
65+
66+
static inline VALUE get_error_details_and_drop(ddog_Error *error) {
67+
VALUE result = ruby_string_from_error(error);
68+
ddog_Error_drop(error);
69+
return result;
70+
}

0 commit comments

Comments
 (0)