summaryrefslogtreecommitdiff
path: root/thread.c
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 /thread.c
parentaa87ae7a04b9c4845ef4326b63150027ed1036a9 (diff)
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
Diffstat (limited to 'thread.c')
-rw-r--r--thread.c107
1 files changed, 73 insertions, 34 deletions
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;
}