aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormame <mame@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-12-05 07:16:42 +0000
committermame <mame@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-12-05 07:16:42 +0000
commit0a6816ecd79fac5dfb32eb237f4c31bb45c9460d (patch)
tree63c3def6ba4957dace4d3fba56f1da964b9750de
parentaa87ae7a04b9c4845ef4326b63150027ed1036a9 (diff)
downloadruby-0a6816ecd79fac5dfb32eb237f4c31bb45c9460d.tar.gz
Revamp method coverage to support define_method
Traditionally, method coverage measurement was implemented by inserting `trace2` instruction to the head of method iseq. So, it just measured methods defined by `def` keyword. This commit drastically changes the measuring mechanism of method coverage; at `RUBY_EVENT_CALL`, it keeps a hash from rb_method_entry_t* to runs (i.e., it counts the runs per method entry), and at `Coverage.result`, it creates the result hash by enumerating all `rb_method_entry_t*` objects (by `ObjectSpace.each_object`). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61023 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--compile.c14
-rw-r--r--ext/coverage/coverage.c95
-rw-r--r--hash.c8
-rw-r--r--internal.h4
-rw-r--r--iseq.h1
-rw-r--r--method.h3
-rw-r--r--test/coverage/test_coverage.rb113
-rw-r--r--thread.c107
-rw-r--r--tool/run-lcov.rb8
-rw-r--r--tool/test-coverage.rb9
-rw-r--r--vm_core.h2
11 files changed, 284 insertions, 80 deletions
diff --git a/compile.c b/compile.c
index f9fe6aab53..4da76748c1 100644
--- a/compile.c
+++ b/compile.c
@@ -294,19 +294,6 @@ struct iseq_compile_data_ensure_node_stack {
ADD_INSN2((seq), (first_line), trace2, INT2FIX(RUBY_EVENT_COVERAGE), INT2FIX(counter_idx * 16 + COVERAGE_INDEX_BRANCHES)); \
} \
} while (0)
-#define ADD_TRACE_METHOD_COVERAGE(seq, line, method_name) \
- do { \
- if (ISEQ_COVERAGE(iseq) && \
- ISEQ_METHOD_COVERAGE(iseq) && \
- (line) > 0) { \
- VALUE methods = ISEQ_METHOD_COVERAGE(iseq); \
- long counter_idx = RARRAY_LEN(methods) / 3; \
- rb_ary_push(methods, ID2SYM(method_name)); \
- rb_ary_push(methods, INT2FIX(line)); \
- rb_ary_push(methods, INT2FIX(0)); \
- ADD_INSN2((seq), (line), trace2, INT2FIX(RUBY_EVENT_COVERAGE), INT2FIX(counter_idx * 16 + COVERAGE_INDEX_METHODS)); \
- } \
- } while (0)
static void iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level);
static void iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level);
@@ -670,7 +657,6 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
case ISEQ_TYPE_METHOD:
{
ADD_TRACE(ret, RUBY_EVENT_CALL);
- ADD_TRACE_METHOD_COVERAGE(ret, FIX2INT(iseq->body->location.first_lineno), rb_intern_str(iseq->body->location.label));
CHECK(COMPILE(ret, "scoped node", node->nd_body));
ADD_TRACE(ret, RUBY_EVENT_RETURN);
break;
diff --git a/ext/coverage/coverage.c b/ext/coverage/coverage.c
index 368f7b1f8c..08c386e79b 100644
--- a/ext/coverage/coverage.c
+++ b/ext/coverage/coverage.c
@@ -10,8 +10,10 @@
#include "ruby.h"
#include "vm_core.h"
+#include "gc.h"
static int current_mode;
+static VALUE me2counter = Qnil;
/*
* call-seq:
@@ -55,13 +57,20 @@ rb_coverage_start(int argc, VALUE *argv, VALUE klass)
}
}
+ if (mode & COVERAGE_TARGET_METHODS) {
+ me2counter = rb_hash_new_compare_by_id();
+ }
+ else {
+ me2counter = Qnil;
+ }
+
coverages = rb_get_coverages();
if (!RTEST(coverages)) {
coverages = rb_hash_new();
rb_obj_hide(coverages);
current_mode = mode;
if (mode == 0) mode = COVERAGE_TARGET_LINES;
- rb_set_coverages(coverages, mode);
+ rb_set_coverages(coverages, mode, me2counter);
}
else if (current_mode != mode) {
rb_raise(rb_eRuntimeError, "cannot change the measuring target during coverage measurement");
@@ -101,21 +110,60 @@ branch_coverage(VALUE branches)
return ret;
}
-static VALUE
-method_coverage(VALUE methods)
+static int
+method_coverage_i(void *vstart, void *vend, size_t stride, void *data)
{
- VALUE ret = rb_hash_new();
- int i;
- long id = 0;
+ /*
+ * ObjectSpace.each_object(Module){|mod|
+ * mod.instance_methods.each{|mid|
+ * m = mod.instance_method(mid)
+ * if loc = m.source_location
+ * p [m.name, loc, $g_method_cov_counts[m]]
+ * end
+ * }
+ * }
+ */
+ VALUE ncoverages = *(VALUE*)data, v;
- for (i = 0; i < RARRAY_LEN(methods); ) {
- VALUE method_name = RARRAY_AREF(methods, i++);
- VALUE lineno = RARRAY_AREF(methods, i++);
- VALUE counter = RARRAY_AREF(methods, i++);
- rb_hash_aset(ret, rb_ary_new_from_args(3, method_name, LONG2FIX(id++), lineno), counter);
- }
+ for (v = (VALUE)vstart; v != (VALUE)vend; v += stride) {
+ if (RB_TYPE_P(v, T_IMEMO) && imemo_type(v) == imemo_ment) {
+ const rb_method_entry_t *me = (rb_method_entry_t *) v;
+ VALUE path = Qundef, first_lineno = Qundef;
+ VALUE data[2], ncoverage, methods;
+ VALUE methods_id = ID2SYM(rb_intern("methods"));
+ VALUE klass;
+ const rb_method_entry_t *me2 = rb_resolve_me_location(me, data);
+ if (me != me2) continue;
+ klass = me->owner;
+ if (RB_TYPE_P(klass, T_ICLASS)) {
+ rb_bug("T_ICLASS");
+ }
+ path = data[0];
+ first_lineno = data[1];
+ if (FIX2LONG(first_lineno) <= 0) continue;
+ ncoverage = rb_hash_aref(ncoverages, path);
+ if (NIL_P(ncoverage)) continue;
+ methods = rb_hash_aref(ncoverage, methods_id);
- return ret;
+ {
+ VALUE method_id = ID2SYM(me->def->original_id);
+ VALUE rcount = rb_hash_aref(me2counter, (VALUE) me);
+ VALUE key = rb_ary_new_from_args(3, klass, method_id, first_lineno);
+ VALUE rcount2 = rb_hash_aref(methods, key);
+
+ if (NIL_P(rcount)) rcount = LONG2FIX(0);
+ if (NIL_P(rcount2)) rcount2 = LONG2FIX(0);
+ if (!POSFIXABLE(FIX2LONG(rcount) + FIX2LONG(rcount2))) {
+ rcount = LONG2FIX(FIXNUM_MAX);
+ }
+ else {
+ rcount = LONG2FIX(FIX2LONG(rcount) + FIX2LONG(rcount2));
+ }
+ rb_hash_aset(methods, key, rcount);
+ }
+ }
+ }
+ return 0;
}
static int
@@ -132,25 +180,23 @@ coverage_peek_result_i(st_data_t key, st_data_t val, st_data_t h)
}
else {
VALUE h = rb_hash_new();
- VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
- VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
- VALUE methods = RARRAY_AREF(coverage, COVERAGE_INDEX_METHODS);
- if (lines) {
+ if (current_mode & COVERAGE_TARGET_LINES) {
+ VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
lines = rb_ary_dup(lines);
rb_ary_freeze(lines);
rb_hash_aset(h, ID2SYM(rb_intern("lines")), lines);
}
- if (branches) {
+ if (current_mode & COVERAGE_TARGET_BRANCHES) {
+ VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
rb_hash_aset(h, ID2SYM(rb_intern("branches")), branch_coverage(branches));
}
- if (methods) {
- rb_hash_aset(h, ID2SYM(rb_intern("methods")), method_coverage(methods));
+ if (current_mode & COVERAGE_TARGET_METHODS) {
+ rb_hash_aset(h, ID2SYM(rb_intern("methods")), rb_hash_new());
}
- rb_hash_freeze(h);
coverage = h;
}
@@ -178,6 +224,11 @@ rb_coverage_peek_result(VALUE klass)
rb_raise(rb_eRuntimeError, "coverage measurement is not enabled");
}
st_foreach(RHASH_TBL(coverages), coverage_peek_result_i, ncoverages);
+
+ if (current_mode & COVERAGE_TARGET_METHODS) {
+ rb_objspace_each_objects(method_coverage_i, &ncoverages);
+ }
+
rb_hash_freeze(ncoverages);
return ncoverages;
}
@@ -194,6 +245,7 @@ rb_coverage_result(VALUE klass)
{
VALUE ncoverages = rb_coverage_peek_result(klass);
rb_reset_coverages();
+ me2counter = Qnil;
return ncoverages;
}
@@ -252,4 +304,5 @@ Init_coverage(void)
rb_define_module_function(rb_mCoverage, "result", rb_coverage_result, 0);
rb_define_module_function(rb_mCoverage, "peek_result", rb_coverage_peek_result, 0);
rb_define_module_function(rb_mCoverage, "running?", rb_coverage_running, 0);
+ rb_global_variable(&me2counter);
}
diff --git a/hash.c b/hash.c
index c8d1406cc7..cd1cc1437f 100644
--- a/hash.c
+++ b/hash.c
@@ -427,6 +427,14 @@ rb_hash_new(void)
}
VALUE
+rb_hash_new_compare_by_id(void)
+{
+ VALUE hash = rb_hash_new();
+ RHASH(hash)->ntbl = rb_init_identtable();
+ return hash;
+}
+
+VALUE
rb_hash_new_with_size(st_index_t size)
{
VALUE ret = rb_hash_new();
diff --git a/internal.h b/internal.h
index 81ecb876ad..229a555533 100644
--- a/internal.h
+++ b/internal.h
@@ -1272,6 +1272,9 @@ void ruby_sized_xfree(void *x, size_t size);
/* hash.c */
struct st_table *rb_hash_tbl_raw(VALUE hash);
VALUE rb_hash_new_with_size(st_index_t size);
+RUBY_SYMBOL_EXPORT_BEGIN
+VALUE rb_hash_new_compare_by_id(void);
+RUBY_SYMBOL_EXPORT_END
VALUE rb_hash_has_key(VALUE hash, VALUE key);
VALUE rb_hash_default_value(VALUE hash, VALUE key);
VALUE rb_hash_set_default_proc(VALUE hash, VALUE proc);
@@ -1758,7 +1761,6 @@ struct timeval rb_time_timeval(VALUE);
/* thread.c */
#define COVERAGE_INDEX_LINES 0
#define COVERAGE_INDEX_BRANCHES 1
-#define COVERAGE_INDEX_METHODS 2
#define COVERAGE_TARGET_LINES 1
#define COVERAGE_TARGET_BRANCHES 2
#define COVERAGE_TARGET_METHODS 4
diff --git a/iseq.h b/iseq.h
index 7c1c385855..8d2281881b 100644
--- a/iseq.h
+++ b/iseq.h
@@ -51,7 +51,6 @@ iseq_mark_ary_create(int flip_cnt)
#define ISEQ_COVERAGE_SET(iseq, cov) RARRAY_ASET(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_COVERAGE, cov)
#define ISEQ_LINE_COVERAGE(iseq) RARRAY_AREF(ISEQ_COVERAGE(iseq), COVERAGE_INDEX_LINES)
#define ISEQ_BRANCH_COVERAGE(iseq) RARRAY_AREF(ISEQ_COVERAGE(iseq), COVERAGE_INDEX_BRANCHES)
-#define ISEQ_METHOD_COVERAGE(iseq) RARRAY_AREF(ISEQ_COVERAGE(iseq), COVERAGE_INDEX_METHODS)
#define ISEQ_FLIP_CNT(iseq) FIX2INT(RARRAY_AREF(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_FLIP_CNT))
diff --git a/method.h b/method.h
index 0ec1a09759..584332561b 100644
--- a/method.h
+++ b/method.h
@@ -190,6 +190,9 @@ const rb_method_entry_t *rb_method_entry_at(VALUE obj, ID id);
const rb_method_entry_t *rb_method_entry(VALUE klass, ID id);
const rb_method_entry_t *rb_method_entry_without_refinements(VALUE klass, ID id, VALUE *defined_class);
const rb_method_entry_t *rb_resolve_refined_method(VALUE refinements, const rb_method_entry_t *me);
+RUBY_SYMBOL_EXPORT_BEGIN
+const rb_method_entry_t *rb_resolve_me_location(const rb_method_entry_t *, VALUE[2]);
+RUBY_SYMBOL_EXPORT_END
const rb_callable_method_entry_t *rb_callable_method_entry(VALUE klass, ID id);
const rb_callable_method_entry_t *rb_callable_method_entry_with_refinements(VALUE klass, ID id, VALUE *defined_class);
diff --git a/test/coverage/test_coverage.rb b/test/coverage/test_coverage.rb
index 14e58ee64c..b361d8bcde 100644
--- a/test/coverage/test_coverage.rb
+++ b/test/coverage/test_coverage.rb
@@ -188,7 +188,15 @@ class TestCoverage < Test::Unit::TestCase
Coverage.start(#{ opt })
tmp = Dir.pwd
require tmp + '/test.rb'
- p Coverage.result[tmp + "/test.rb"]
+ r = Coverage.result[tmp + "/test.rb"]
+ if r[:methods]
+ h = {}
+ r[:methods].keys.sort_by {|key| key.drop(1) }.each do |key|
+ h[key] = r[:methods][key]
+ end
+ r[:methods].replace h
+ end
+ p r
end;
}
}
@@ -332,9 +340,9 @@ class TestCoverage < Test::Unit::TestCase
def test_method_coverage
result = {
:methods => {
- [:foo, 0, 1] => 2,
- [:bar, 1, 2] => 1,
- [:baz, 2, 4] => 0,
+ [Object, :bar, 2] => 1,
+ [Object, :baz, 4] => 0,
+ [Object, :foo, 1] => 2,
}
}
assert_coverage(<<-"end;", { methods: true }, result)
@@ -348,4 +356,101 @@ class TestCoverage < Test::Unit::TestCase
bar
end;
end
+
+ def test_method_coverage_for_define_method
+ result = {
+ :methods => {
+ [Object, :bar, 2] => 1,
+ [Object, :baz, 4] => 0,
+ [Object, :foo, 1] => 2,
+ }
+ }
+ assert_coverage(<<-"end;", { methods: true }, result)
+ define_method(:foo) {}
+ define_method(:bar) {
+ }
+ f = proc {}
+ define_method(:baz, &f)
+
+ foo
+ foo
+ bar
+ end;
+ end
+
+ class DummyConstant < String
+ def inspect
+ self
+ end
+ end
+
+ def test_method_coverage_for_alias
+ _C = DummyConstant.new("C")
+ _M = DummyConstant.new("M")
+ code = <<-"end;"
+ module M
+ def foo
+ end
+ alias bar foo
+ end
+ class C
+ include M
+ def baz
+ end
+ alias qux baz
+ end
+ end;
+
+ result = {
+ :methods => {
+ [_C, :baz, 8] => 0,
+ [_M, :foo, 2] => 0,
+ }
+ }
+ assert_coverage(code, { methods: true }, result)
+
+ result = {
+ :methods => {
+ [_C, :baz, 8] => 12,
+ [_M, :foo, 2] => 3,
+ }
+ }
+ assert_coverage(code + <<-"end;", { methods: true }, result)
+ obj = C.new
+ 1.times { obj.foo }
+ 2.times { obj.bar }
+ 4.times { obj.baz }
+ 8.times { obj.qux }
+ end;
+ end
+
+ def test_method_coverage_for_singleton_class
+ _singleton_Foo = DummyConstant.new("#<Class:Foo>")
+ _Foo = DummyConstant.new("Foo")
+ code = <<-"end;"
+ class Foo
+ def foo
+ end
+ alias bar foo
+ def self.baz
+ end
+ class << self
+ alias qux baz
+ end
+ end
+
+ 1.times { Foo.new.foo }
+ 2.times { Foo.new.bar }
+ 4.times { Foo.baz }
+ 8.times { Foo.qux }
+ end;
+
+ result = {
+ :methods => {
+ [_singleton_Foo, :baz, 5] => 12,
+ [_Foo, :foo, 2] => 3,
+ }
+ }
+ assert_coverage(code, { methods: true }, result)
+ end
end
diff --git a/thread.c b/thread.c
index 08a4463f2d..58c1505cbc 100644
--- a/thread.c
+++ b/thread.c
@@ -71,6 +71,7 @@
#include "ruby/thread_native.h"
#include "ruby/debug.h"
#include "internal.h"
+#include "iseq.h"
#ifndef USE_NATIVE_THREAD_PRIORITY
#define USE_NATIVE_THREAD_PRIORITY 0
@@ -4093,7 +4094,6 @@ clear_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
VALUE coverage = (VALUE)val;
VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
- VALUE methods = RARRAY_AREF(coverage, COVERAGE_INDEX_METHODS);
if (lines) {
for (i = 0; i < RARRAY_LEN(lines); i++) {
@@ -4108,11 +4108,6 @@ clear_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
RARRAY_ASET(counters, i, INT2FIX(0));
}
}
- if (methods) {
- for (i = 2; i < RARRAY_LEN(methods); i += 3) {
- RARRAY_ASET(methods, i, INT2FIX(0));
- }
- }
return ST_CONTINUE;
}
@@ -5019,20 +5014,72 @@ update_coverage(VALUE data, const rb_trace_arg_t *trace_arg)
}
break;
}
- case COVERAGE_INDEX_METHODS: {
- VALUE methods = RARRAY_AREF(coverage, COVERAGE_INDEX_METHODS);
- if (methods) {
- long count;
- long idx = arg / 16 * 3 + 2;
- VALUE num = RARRAY_AREF(methods, idx);
- count = FIX2LONG(num) + 1;
- if (POSFIXABLE(count)) {
- RARRAY_ASET(methods, idx, LONG2FIX(count));
- }
- }
+ }
+ }
+}
+
+const rb_method_entry_t *
+rb_resolve_me_location(const rb_method_entry_t *me, VALUE resolved_location[2])
+{
+ VALUE path, first_lineno;
+
+ retry:
+ switch (me->def->type) {
+ case VM_METHOD_TYPE_ISEQ: {
+ rb_iseq_location_t loc = me->def->body.iseq.iseqptr->body->location;
+ path = loc.pathobj;
+ first_lineno = loc.first_lineno;
+ break;
+ }
+ case VM_METHOD_TYPE_BMETHOD: {
+ const rb_iseq_t *iseq = rb_proc_get_iseq(me->def->body.proc, 0);
+ if (iseq) {
+ rb_iseq_check(iseq);
+ path = rb_iseq_path(iseq);
+ first_lineno = iseq->body->location.first_lineno;
break;
- }
}
+ return NULL;
+ }
+ case VM_METHOD_TYPE_ALIAS:
+ me = me->def->body.alias.original_me;
+ goto retry;
+ case VM_METHOD_TYPE_REFINED:
+ me = me->def->body.refined.orig_me;
+ if (!me) return NULL;
+ goto retry;
+ default:
+ return NULL;
+ }
+
+ /* found */
+ if (RB_TYPE_P(path, T_ARRAY)) {
+ path = rb_ary_entry(path, 1);
+ if (!RB_TYPE_P(path, T_STRING)) return NULL; /* just for the case... */
+ }
+ if (resolved_location) {
+ resolved_location[0] = path;
+ resolved_location[1] = first_lineno;
+ }
+ return me;
+}
+
+static void
+update_method_coverage(VALUE me2counter, rb_trace_arg_t *trace_arg)
+{
+ const rb_control_frame_t *cfp = GET_EC()->cfp;
+ const rb_callable_method_entry_t *cme = rb_vm_frame_method_entry(cfp);
+ const rb_method_entry_t *me = (const rb_method_entry_t *)cme;
+ VALUE rcount;
+ long count;
+
+ me = rb_resolve_me_location(me, 0);
+ if (!me) return;
+
+ rcount = rb_hash_aref(me2counter, (VALUE) me);
+ count = FIXNUM_P(rcount) ? FIX2LONG(rcount) + 1 : 1;
+ if (POSFIXABLE(count)) {
+ rb_hash_aset(me2counter, (VALUE) me, LONG2FIX(count));
}
}
@@ -5043,11 +5090,14 @@ rb_get_coverages(void)
}
void
-rb_set_coverages(VALUE coverages, int mode)
+rb_set_coverages(VALUE coverages, int mode, VALUE me2counter)
{
GET_VM()->coverages = coverages;
GET_VM()->coverage_mode = mode;
rb_add_event_hook2((rb_event_hook_func_t) update_coverage, RUBY_EVENT_COVERAGE, Qnil, RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
+ if (mode & COVERAGE_TARGET_METHODS) {
+ rb_add_event_hook2((rb_event_hook_func_t) update_method_coverage, RUBY_EVENT_CALL, me2counter, RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
+ }
}
/* Make coverage arrays empty so old covered files are no longer tracked. */
@@ -5057,10 +5107,8 @@ reset_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
VALUE coverage = (VALUE)val;
VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
- VALUE methods = RARRAY_AREF(coverage, COVERAGE_INDEX_METHODS);
if (lines) rb_ary_clear(lines);
if (branches) rb_ary_clear(branches);
- if (methods) rb_ary_clear(methods);
return ST_CONTINUE;
}
@@ -5071,13 +5119,16 @@ rb_reset_coverages(void)
st_foreach(rb_hash_tbl_raw(coverages), reset_coverage_i, 0);
GET_VM()->coverages = Qfalse;
rb_remove_event_hook((rb_event_hook_func_t) update_coverage);
+ if (GET_VM()->coverage_mode & COVERAGE_TARGET_METHODS) {
+ rb_remove_event_hook((rb_event_hook_func_t) update_method_coverage);
+ }
}
VALUE
rb_default_coverage(int n)
{
VALUE coverage = rb_ary_tmp_new_fill(3);
- VALUE lines = Qfalse, branches = Qfalse, methods = Qfalse;
+ VALUE lines = Qfalse, branches = Qfalse;
int mode = GET_VM()->coverage_mode;
if (mode & COVERAGE_TARGET_LINES) {
@@ -5105,18 +5156,6 @@ rb_default_coverage(int n)
}
RARRAY_ASET(coverage, COVERAGE_INDEX_BRANCHES, branches);
- if (mode & COVERAGE_TARGET_METHODS) {
- methods = rb_ary_tmp_new(0);
- /* internal data structures for method coverage:
- *
- * [symbol_of_method_name, lineno_of_method_head, counter,
- * ...]
- *
- * Example: [:foobar, 1, 0, ...]
- */
- }
- RARRAY_ASET(coverage, COVERAGE_INDEX_METHODS, methods);
-
return coverage;
}
diff --git a/tool/run-lcov.rb b/tool/run-lcov.rb
index 3536339ba6..f27578200a 100644
--- a/tool/run-lcov.rb
+++ b/tool/run-lcov.rb
@@ -87,15 +87,15 @@ def gen_rb_lcov(file)
# function coverage
total = covered = 0
- cov[:methods].each do |(name, _, lineno), count|
- f.puts "FN:#{ lineno },#{ name }"
+ cov[:methods].each do |(klass, name, lineno), count|
+ f.puts "FN:#{ lineno },#{ klass }##{ name }"
total += 1
covered += 1 if count > 0
end
f.puts "FNF:#{ total }"
f.puts "FNF:#{ covered }"
- cov[:methods].each do |(name, _), count|
- f.puts "FNDA:#{ count },#{ name }"
+ cov[:methods].each do |(klass, name, _), count|
+ f.puts "FNDA:#{ count },#{ klass }##{ name }"
end
# line coverage
diff --git a/tool/test-coverage.rb b/tool/test-coverage.rb
index 0719e5e703..d1c324af0d 100644
--- a/tool/test-coverage.rb
+++ b/tool/test-coverage.rb
@@ -41,6 +41,15 @@ def add_count(h, key, count)
end
def save_coverage_data(res1)
+ res1.each do |_path, cov|
+ if cov[:methods]
+ h = {}
+ cov[:methods].each do |(klass, *key), count|
+ h[[klass.inspect, *key]] = count
+ end
+ cov[:methods].replace h
+ end
+ end
File.open(TEST_COVERAGE_DATA_FILE, File::RDWR | File::CREAT | File::BINARY) do |f|
f.flock(File::LOCK_EX)
s = f.read
diff --git a/vm_core.h b/vm_core.h
index fbd50dc4c2..031cc7b95b 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -1753,7 +1753,7 @@ RUBY_SYMBOL_EXPORT_BEGIN
int rb_thread_check_trap_pending(void);
extern VALUE rb_get_coverages(void);
-extern void rb_set_coverages(VALUE, int);
+extern void rb_set_coverages(VALUE, int, VALUE);
extern void rb_reset_coverages(void);
void rb_postponed_job_flush(rb_vm_t *vm);