Skip to content

Commit

Permalink
add use_allocation_tracing parameter for DDCov tool
Browse files Browse the repository at this point in the history
  • Loading branch information
anmarchenko committed Jul 10, 2024
1 parent 59df766 commit ca32796
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 38 deletions.
16 changes: 10 additions & 6 deletions ext/datadog_cov/datadog_cov.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,9 @@ struct dd_cov_data
// contain any methods that could be covered by line tracepoint.
//
// Allocation tracing works only in multi threaded mode.
bool allocation_profiling_enabled;
bool allocation_tracing_enabled;
VALUE object_allocation_tracepoint;
VALUE classes_covered_by_allocation;
VALUE class_to_sourcefile_mapping;
};

static void dd_cov_mark(void *ptr)
Expand All @@ -97,7 +96,6 @@ static void dd_cov_mark(void *ptr)
rb_gc_mark_movable(dd_cov_data->th_covered);
rb_gc_mark_movable(dd_cov_data->object_allocation_tracepoint);
rb_gc_mark_movable(dd_cov_data->classes_covered_by_allocation);
rb_gc_mark_movable(dd_cov_data->class_to_sourcefile_mapping);
}

static void dd_cov_free(void *ptr)
Expand Down Expand Up @@ -139,7 +137,7 @@ static VALUE dd_cov_allocate(VALUE klass)
dd_cov_data->last_filename_ptr = 0;
dd_cov_data->threading_mode = MULTI_THREADED_COVERAGE_MODE;

dd_cov_data->allocation_profiling_enabled = true;
dd_cov_data->allocation_tracing_enabled = true;
dd_cov_data->object_allocation_tracepoint = rb_tracepoint_new(Qnil, RUBY_INTERNAL_EVENT_NEWOBJ, process_newobj_event, (void *)dd_cov);
dd_cov_data->classes_covered_by_allocation = rb_hash_new();

Expand Down Expand Up @@ -174,6 +172,12 @@ static VALUE dd_cov_initialize(int argc, VALUE *argv, VALUE self)
rb_raise(rb_eArgError, "threading mode is invalid");
}

VALUE rb_allocation_tracing_enabled = rb_hash_lookup(opt, ID2SYM(rb_intern("use_allocation_tracing")));
if (rb_allocation_tracing_enabled == Qtrue && threading_mode == SINGLE_THREADED_COVERAGE_MODE)
{
rb_raise(rb_eArgError, "allocation tracing is not supported in single threaded mode");
}

struct dd_cov_data *dd_cov_data;
TypedData_Get_Struct(self, struct dd_cov_data, &dd_cov_data_type, dd_cov_data);

Expand All @@ -187,7 +191,7 @@ static VALUE dd_cov_initialize(int argc, VALUE *argv, VALUE self)
dd_cov_data->ignored_path = ruby_strndup(RSTRING_PTR(rb_ignored_path), dd_cov_data->ignored_path_len);
}

dd_cov_data->allocation_profiling_enabled = (threading_mode == MULTI_THREADED_COVERAGE_MODE);
dd_cov_data->allocation_tracing_enabled = (rb_allocation_tracing_enabled == Qtrue);

return Qnil;
}
Expand Down Expand Up @@ -330,7 +334,7 @@ static VALUE dd_cov_start(VALUE self)
rb_add_event_hook(process_line_event, RUBY_EVENT_LINE, self);
}

if (dd_cov_data->allocation_profiling_enabled)
if (dd_cov_data->allocation_tracing_enabled)
{
dd_cov_data->classes_covered_by_allocation = rb_hash_new();
rb_tracepoint_enable(dd_cov_data->object_allocation_tracepoint);
Expand Down
3 changes: 2 additions & 1 deletion lib/datadog/ci/test_optimisation/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ def coverage_collector
Thread.current[:dd_coverage_collector] ||= Coverage::DDCov.new(
root: Git::LocalRepository.root,
ignored_path: @bundle_location,
threading_mode: code_coverage_mode
threading_mode: code_coverage_mode,
use_allocation_tracing: @use_allocation_tracing
)
end

Expand Down
2 changes: 1 addition & 1 deletion sig/datadog/ci/test_optimisation/coverage/ddcov.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Datadog
class DDCov
type threading_mode = :multi | :single

def initialize: (root: String, ignored_path: String?, threading_mode: threading_mode) -> void
def initialize: (root: String, ignored_path: String?, threading_mode: threading_mode, use_allocation_tracing: bool) -> void

def start: () -> void

Expand Down
2 changes: 2 additions & 0 deletions spec/datadog/ci/contrib/minitest/helpers/simple_model.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class SimpleModel
end
4 changes: 4 additions & 0 deletions spec/datadog/ci/contrib/minitest/instrumentation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -403,12 +403,15 @@ def test_foo
Minitest::Runnable.reset

require_relative "helpers/addition_helper"
require_relative "helpers/simple_model"
class SomeTest < Minitest::Test
def test_pass
assert true
end

def test_pass_other
# make sure that allocating objects is covered
SimpleModel.new
# add thread to test that code coverage is collected
t = Thread.new do
AdditionHelper.add(1, 2)
Expand Down Expand Up @@ -504,6 +507,7 @@ def test_pass_other
test_span = test_spans.find { |span| span.get_tag("test.name") == "test_pass_other" }
cov_event = find_coverage_for_test(test_span)
expect(cov_event.coverage.keys).to include(absolute_path("helpers/addition_helper.rb"))
expect(cov_event.coverage.keys).to include(absolute_path("helpers/simple_model.rb"))
end

context "when test optimisation skips tests" do
Expand Down
100 changes: 70 additions & 30 deletions spec/ddcov/ddcov_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,16 @@
RSpec.describe Datadog::CI::TestOptimisation::Coverage::DDCov do
let(:ignored_path) { nil }
let(:threading_mode) { :multi }
subject { described_class.new(root: root, ignored_path: ignored_path, threading_mode: threading_mode) }
let(:use_allocation_tracing) { true }

subject do
described_class.new(
root: root,
ignored_path: ignored_path,
threading_mode: threading_mode,
use_allocation_tracing: use_allocation_tracing
)
end

describe "code coverage collection" do
let!(:calculator) { Calculator.new }
Expand Down Expand Up @@ -156,11 +165,16 @@

context "multi threaded execution" do
def thread_local_cov
Thread.current[:datadog_ci_cov] ||= described_class.new(root: root, threading_mode: threading_mode)
Thread.current[:datadog_ci_cov] ||= described_class.new(
root: root,
threading_mode: threading_mode,
use_allocation_tracing: use_allocation_tracing
)
end

context "in single threaded coverage mode" do
let(:threading_mode) { :single }
let(:use_allocation_tracing) { false }

it "collects coverage for each thread separately" do
t1_queue = Thread::Queue.new
Expand Down Expand Up @@ -204,6 +218,16 @@ def thread_local_cov

[t1, t2].each(&:join)
end

context "when allocation tracing is enabled" do
let(:use_allocation_tracing) { true }

it "raises an error" do
expect { thread_local_cov }.to(
raise_error(ArgumentError, "allocation tracing is not supported in single threaded mode")
)
end
end
end

context "in multi threaded code coverage mode" do
Expand Down Expand Up @@ -283,42 +307,58 @@ def thread_local_cov
context "root in app folder" do
let(:root) { absolute_path("app") }

it "tracks coverage for empty model" do
subject.start
context "allocation tracing is enabled" do
it "tracks coverage for empty model" do
subject.start

MyModel.new
expect(calculator.add(1, 2)).to eq(3)
MyModel.new
expect(calculator.add(1, 2)).to eq(3)

coverage = subject.stop
expect(coverage.size).to eq(1)
expect(coverage.keys).to include(absolute_path("app/model/my_model.rb"))
coverage = subject.stop
expect(coverage.size).to eq(1)
expect(coverage.keys).to include(absolute_path("app/model/my_model.rb"))

MyModel.new
MyModel.new

subject.start
coverage = subject.stop
expect(coverage.size).to eq(0)
end
subject.start
coverage = subject.stop
expect(coverage.size).to eq(0)
end

it "does not break when encountering anonymous class or internal Ruby classes implemented in C" do
subject.start
it "does not break when encountering anonymous class or internal Ruby classes implemented in C" do
subject.start

MyModel.new
c = Class.new(Object) do
end
c.new

# Trying to get non-existing constant could caise freezing of Ruby process when
# not safely getting source location of the constant in NEWOBJ trcepoint.
begin
Object.const_get(:fdsfdsfdsfds)
rescue
nil
MyModel.new
c = Class.new(Object) do
end
c.new

# Trying to get non-existing constant could caise freezing of Ruby process when
# not safely getting source location of the constant in NEWOBJ trcepoint.
begin
Object.const_get(:fdsfdsfdsfds)
rescue
nil
end

coverage = subject.stop
expect(coverage.size).to eq(1)
expect(coverage.keys).to include(absolute_path("app/model/my_model.rb"))
end
end

coverage = subject.stop
expect(coverage.size).to eq(1)
expect(coverage.keys).to include(absolute_path("app/model/my_model.rb"))
context "allocation tracing is disabled" do
let(:use_allocation_tracing) { false }

it "does not track coverage for empty model" do
subject.start

MyModel.new
expect(calculator.add(1, 2)).to eq(3)

coverage = subject.stop
expect(coverage.size).to eq(0)
end
end
end
end
Expand Down

0 comments on commit ca32796

Please sign in to comment.