avellable avellable - 4 months ago 12
Ruby Question

How does the coverage.so works?

I am trying to understand how ruby's internal library coverage.so works. By "works", I do not mean the usage of it. I mean how this calculates the line's execution count. I went through the source but, looks like after some method calls it's beyond my understanding.

Here's what I went through:


Answer

The coverage.so module just connects to ruby core to get information, search for rb_get_coverages() function to get this https://github.com/ruby/ruby/blob/38ea561319864fecf92bc61af0d9b9b1f49df6a0/thread.c

VALUE
rb_get_coverages(void)
{
    return GET_VM()->coverages;
}

void
rb_set_coverages(VALUE coverages)
{
    GET_VM()->coverages = coverages;
    rb_add_event_hook(update_coverage, RUBY_EVENT_COVERAGE, Qnil);
}

And this file does the increments (count = old_count +1):

static void
update_coverage(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
{
    VALUE coverage = rb_iseq_coverage(GET_THREAD()->cfp->iseq);
    if (RB_TYPE_P(coverage, T_ARRAY) && !RBASIC_CLASS(coverage)) {
    long line = rb_sourceline() - 1;
    long count;
    if (line >= RARRAY_LEN(coverage)) { /* no longer tracked */
        return;
    }
    count = FIX2LONG(RARRAY_AREF(coverage, line)) + 1;
    if (POSFIXABLE(count)) {
        RARRAY_ASET(coverage, line, LONG2FIX(count));
    }
    }
}

Actual hash created in parse.y https://github.com/ruby/ruby/blob/4c4f809e932417137d06e334d79065a4849dda0b/parse.y#L5491

static VALUE
coverage(VALUE fname, int n)
{
    VALUE coverages = rb_get_coverages();
    if (RTEST(coverages) && RBASIC(coverages)->klass == 0) {
    VALUE lines = n > 0 ? rb_ary_tmp_new_fill(n) : rb_ary_tmp_new(0);
    rb_hash_aset(coverages, fname, lines);
    return lines;
    }
    return 0;
}
Comments