Skip to content

Commit 3c6b8ae

Browse files
committed
Move into prof extension
1 parent 6988d5a commit 3c6b8ae

File tree

12 files changed

+52
-489
lines changed

12 files changed

+52
-489
lines changed

Rakefile

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ namespace :test do
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
@@ -232,7 +232,7 @@ namespace :spec do
232232
Rake::Task[t.name].enhance(
233233
[
234234
"compile:libdatadog_api.#{RUBY_VERSION[/\d+.\d+/]}_#{RUBY_PLATFORM}",
235-
"compile:datadog_runtime_stacks.#{RUBY_VERSION[/\d+.\d+/]}_#{RUBY_PLATFORM}"
235+
"compile:datadog_profiling_native_extension.#{RUBY_VERSION}_#{RUBY_PLATFORM}"
236236
]
237237
)
238238
end
@@ -467,11 +467,11 @@ namespace :coverage do
467467
task :report do
468468
require 'simplecov'
469469

470-
resultset_files = Dir["#{ENV.fetch("COVERAGE_DIR", "coverage")}/.resultset.json"] +
471-
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"]
472472

473473
SimpleCov.collate resultset_files do
474-
coverage_dir "#{ENV.fetch("COVERAGE_DIR", "coverage")}/report"
474+
coverage_dir "#{Datadog::DATADOG_ENV.fetch("COVERAGE_DIR", "coverage")}/report"
475475
formatter SimpleCov::Formatter::HTMLFormatter
476476
end
477477
end
@@ -481,11 +481,11 @@ namespace :coverage do
481481
require 'simplecov'
482482
require_relative 'spec/support/simplecov_fix'
483483

484-
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) }
485485
versions.map do |version|
486486
puts "Generating report for: #{version}"
487-
SimpleCov.collate Dir["#{ENV.fetch("COVERAGE_DIR", "coverage")}/versions/#{version}/**/.resultset.json"] do
488-
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}"
489489
formatter SimpleCov::Formatter::HTMLFormatter
490490
end
491491
end
@@ -505,10 +505,6 @@ NATIVE_EXTS = [
505505
ext.ext_dir = 'ext/libdatadog_api'
506506
end,
507507

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-
512508
Rake::ExtensionTask.new("datadog_profiling_native_extension.#{RUBY_VERSION}_#{RUBY_PLATFORM}") do |ext|
513509
ext.ext_dir = 'ext/datadog_profiling_native_extension'
514510
end,

datadog.gemspec

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

8484
spec.extensions = [
8585
'ext/datadog_profiling_native_extension/extconf.rb',
86-
'ext/datadog_runtime_stacks/extconf.rb',
8786
'ext/libdatadog_api/extconf.rb'
8887
]
8988
end

ext/datadog_profiling_native_extension/native_extension_helpers.rb

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@ module Profiling
66
module NativeExtensionHelpers
77
# Can be set when customers want to skip compiling the native extension entirely
88
ENV_NO_EXTENSION = "DD_PROFILING_NO_EXTENSION"
9+
LEGACY_ENV_NO_EXTENSION = "DD_NO_EXTENSION"
910
# Can be set to force rubygems to fail gem installation when profiling extension could not be built
1011
ENV_FAIL_INSTALL_IF_MISSING_EXTENSION = "DD_PROFILING_FAIL_INSTALL_IF_MISSING_EXTENSION"
12+
LEGACY_ENV_FAIL_INSTALL_IF_MISSING_EXTENSION = "DD_FAIL_INSTALL_IF_MISSING_EXTENSION"
1113

1214
# The MJIT header was introduced on 2.6 and removed on 3.3; for other Rubies we rely on datadog-ruby_core_source
1315
CAN_USE_MJIT_HEADER = RUBY_VERSION.start_with?("2.6", "2.7", "3.0.", "3.1.", "3.2.")
1416

1517
def self.fail_install_if_missing_extension?
16-
ENV[ENV_FAIL_INSTALL_IF_MISSING_EXTENSION].to_s.strip.downcase == "true"
18+
[
19+
ENV[ENV_FAIL_INSTALL_IF_MISSING_EXTENSION],
20+
ENV[LEGACY_ENV_FAIL_INSTALL_IF_MISSING_EXTENSION],
21+
].any? { |value| value.to_s.strip.downcase == "true" }
1722
end
1823

1924
# Used to check if profiler is supported, including user-visible clear messages explaining why their
@@ -131,15 +136,19 @@ def self.render_skipped_reason_file(reason:, suggested:)
131136
"<https://github.com/DataDog/dd-trace-rb/issues/new> so we can fix it :)",
132137
].freeze
133138

134-
disabled_via_env = explain_issue(
135-
"the `DD_PROFILING_NO_EXTENSION` environment variable is/was set to",
139+
env_var =
140+
[
141+
ENV_NO_EXTENSION,
142+
LEGACY_ENV_NO_EXTENSION,
143+
].find { |var| ENV[var].to_s.strip.downcase == "true" }
144+
145+
return unless env_var
146+
147+
explain_issue(
148+
"the `#{env_var}` environment variable is/was set to",
136149
"`true` during installation.",
137150
suggested: report_disabled,
138151
)
139-
140-
return unless ENV[ENV_NO_EXTENSION].to_s.strip.downcase == "true"
141-
142-
disabled_via_env
143152
end
144153

145154
private_class_method def self.on_jruby?

ext/datadog_profiling_native_extension/profiling.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "helpers.h"
1010
#include "private_vm_api_access.h"
1111
#include "ruby_helpers.h"
12+
#include "runtime_stacks.h"
1213
#include "setup_signal_handler.h"
1314
#include "time_helpers.h"
1415
#include "unsafe_api_calls_check.h"
@@ -65,6 +66,7 @@ void DDTRACE_EXPORT Init_datadog_profiling_native_extension(void) {
6566
encoded_profile_init(profiling_module);
6667
http_transport_init(profiling_module);
6768
stack_recorder_init(profiling_module);
69+
runtime_stacks_init(datadog_module);
6870
unsafe_api_calls_check_init();
6971

7072
// Hosts methods used for testing the native code using RSpec

ext/datadog_runtime_stacks/runtime_stacks.c renamed to ext/datadog_profiling_native_extension/runtime_stacks.c

Lines changed: 17 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@
3232
#endif
3333
#endif
3434

35-
#include <datadog/crashtracker.h>
3635
#include "runtime_stacks.h"
36+
#include <datadog/crashtracker.h>
3737
#include "datadog_ruby_common.h"
38+
#include "private_vm_api_access.h"
3839
#include <sys/mman.h>
3940
#include <unistd.h>
4041
#include <errno.h>
@@ -310,58 +311,7 @@ static void ruby_runtime_stack_callback(
310311
const char *function_name = "<C method>";
311312
const char *file_name = "<C extension>";
312313

313-
#ifdef RUBY_MJIT_HEADER
314-
// Only attempt method entry resolution on Ruby versions with MJIT header
315-
// where rb_vm_frame_method_entry is guaranteed to be available
316-
const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
317-
if (me && is_pointer_readable(me, sizeof(rb_callable_method_entry_t))) {
318-
if (me->def && is_pointer_readable(me->def, sizeof(*me->def))) {
319-
if (me->def->original_id) {
320-
const char *method_name = rb_id2name(me->def->original_id);
321-
if (method_name && is_pointer_readable(method_name, strlen(method_name))) {
322-
size_t method_name_len = strlen(method_name);
323-
if (method_name_len > 0 && method_name_len < 256) {
324-
function_name = method_name;
325-
}
326-
}
327-
}
328-
329-
if (me->def->type == VM_METHOD_TYPE_CFUNC && me->owner) {
330-
// Try to get the full class/module path
331-
VALUE owner_name = Qnil;
332-
VALUE actual_owner = me->owner;
333-
334-
// If this is a singleton class (like Fiddle's singleton class for module methods),
335-
// try to get the attached object which should be the actual module or else we will
336-
// just get `Module` which is not that useful to us
337-
if (RB_TYPE_P(me->owner, T_CLASS) && FL_TEST(me->owner, FL_SINGLETON)) {
338-
VALUE attached = rb_ivar_get(me->owner, rb_intern("__attached__"));
339-
if (attached != Qnil) {
340-
actual_owner = attached;
341-
}
342-
}
343-
344-
// Get the class/module path
345-
if (RB_TYPE_P(actual_owner, T_CLASS) || RB_TYPE_P(actual_owner, T_MODULE)) {
346-
owner_name = rb_class_path(actual_owner);
347-
}
348-
349-
// Fallback to rb_class_name if rb_class_path fails
350-
if (owner_name == Qnil) {
351-
owner_name = rb_class_name(actual_owner);
352-
}
353-
354-
if (owner_name != Qnil) {
355-
const char *owner_str = safe_string_ptr(owner_name);
356-
static char file_buffer[256];
357-
snprintf(file_buffer, sizeof(file_buffer), "<%s (C extension)>", owner_str);
358-
file_name = file_buffer;
359-
}
360-
}
361-
}
362-
}
363-
#else
364-
// For Ruby versions without MJIT header, use our own rb_vm_frame_method_entry implementation
314+
// Resolve Ruby C frames via rb_vm_frame_method_entry (Ruby or our fallback depending on version)
365315
const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
366316
if (me && is_pointer_readable(me, sizeof(rb_callable_method_entry_t))) {
367317
if (me->def && is_pointer_readable(me->def, sizeof(*me->def))) {
@@ -408,7 +358,6 @@ static void ruby_runtime_stack_callback(
408358
}
409359
}
410360
}
411-
#endif
412361

413362
ddog_crasht_RuntimeStackFrame frame = {
414363
.type_name = char_slice_from_cstr(NULL),
@@ -424,58 +373,6 @@ static void ruby_runtime_stack_callback(
424373
}
425374
}
426375

427-
// Support code for Ruby versions without MJIT header (copied from private_vm_api_access.c)
428-
#ifndef RUBY_MJIT_HEADER
429-
430-
#define MJIT_STATIC // No-op on older Rubies
431-
432-
#ifndef FALSE
433-
# define FALSE false
434-
#elif FALSE
435-
# error FALSE must be false
436-
#endif
437-
438-
#ifndef TRUE
439-
# define TRUE true
440-
#elif ! TRUE
441-
# error TRUE must be true
442-
#endif
443-
444-
static rb_callable_method_entry_t *
445-
check_method_entry(VALUE obj, int can_be_svar)
446-
{
447-
if (obj == Qfalse) return NULL;
448-
449-
switch (imemo_type(obj)) {
450-
case imemo_ment:
451-
return (rb_callable_method_entry_t *)obj;
452-
case imemo_cref:
453-
return NULL;
454-
case imemo_svar:
455-
if (can_be_svar) {
456-
return check_method_entry(((struct vm_svar *)obj)->cref_or_me, FALSE);
457-
}
458-
// fallthrough
459-
default:
460-
return NULL;
461-
}
462-
}
463-
464-
MJIT_STATIC const rb_callable_method_entry_t *
465-
rb_vm_frame_method_entry(const rb_control_frame_t *cfp)
466-
{
467-
const VALUE *ep = cfp->ep;
468-
rb_callable_method_entry_t *me;
469-
470-
while (!VM_ENV_LOCAL_P(ep)) {
471-
if ((me = check_method_entry(ep[VM_ENV_DATA_INDEX_ME_CREF], FALSE)) != NULL) return me;
472-
ep = VM_ENV_PREV_EP(ep);
473-
}
474-
475-
return check_method_entry(ep[VM_ENV_DATA_INDEX_ME_CREF], TRUE);
476-
}
477-
#endif // RUBY_MJIT_HEADER
478-
479376
static VALUE _native_register_runtime_stack_callback(DDTRACE_UNUSED VALUE _self) {
480377
enum ddog_crasht_CallbackResult result = ddog_crasht_register_runtime_frame_callback(
481378
ruby_runtime_stack_callback
@@ -495,10 +392,20 @@ static VALUE _native_is_runtime_callback_registered(DDTRACE_UNUSED VALUE _self)
495392
return ddog_crasht_is_runtime_callback_registered() ? Qtrue : Qfalse;
496393
}
497394

498-
void DDTRACE_EXPORT Init_datadog_runtime_stacks(void) {
499-
VALUE datadog_module = rb_define_module("Datadog");
395+
void runtime_stacks_init(VALUE datadog_module) {
500396
VALUE runtime_stacks_class = rb_define_class_under(datadog_module, "RuntimeStacks", rb_cObject);
501397

502-
rb_define_singleton_method(runtime_stacks_class, "_native_register_runtime_stack_callback", _native_register_runtime_stack_callback, 0);
503-
rb_define_singleton_method(runtime_stacks_class, "_native_is_runtime_callback_registered", _native_is_runtime_callback_registered, 0);
398+
rb_define_singleton_method(
399+
runtime_stacks_class,
400+
"_native_register_runtime_stack_callback",
401+
_native_register_runtime_stack_callback,
402+
0
403+
);
404+
rb_define_singleton_method(
405+
runtime_stacks_class,
406+
"_native_is_runtime_callback_registered",
407+
_native_is_runtime_callback_registered,
408+
0
409+
);
504410
}
411+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#pragma once
2+
3+
void runtime_stacks_init(VALUE datadog_module);
4+

ext/datadog_runtime_stacks/datadog_ruby_common.c

Lines changed: 0 additions & 80 deletions
This file was deleted.

0 commit comments

Comments
 (0)