[前][次][番号順一覧][スレッド一覧]

ruby-changes:6268

From: mame <ko1@a...>
Date: Wed, 2 Jul 2008 01:56:03 +0900 (JST)
Subject: [ruby-changes:6268] Ruby:r17781 (trunk): * Add coverage measurement constant COVERAGE__. This constant is not

mame	2008-07-02 01:55:30 +0900 (Wed, 02 Jul 2008)

  New Revision: 17781

  Added files:
    trunk/lib/coverage.rb
  Modified files:
    trunk/ChangeLog
    trunk/compile.h
    trunk/error.c
    trunk/insns.def
    trunk/iseq.c
    trunk/load.c
    trunk/parse.y
    trunk/thread.c
    trunk/vm_core.h
    trunk/vm_eval.c

  Log:
    * Add coverage measurement constant COVERAGE__.  This constant is not
      for casual use.  Usage: (1) assign {} to COVERAGE__, (2) require or
      load Ruby source file, and (3) COVERAGE__["sourcefilepath"] will
      return an array whose elements represent number of executions per
      line of source code.
    
    * vm_core.h: add field of coverage array to iseq.
    
    * iseq.c (prepare_iseq_build): ditto.
    
    * insns.def (trace): update coverage array.
    
    * parse.y (coverage): create and initialize coverage array.
    
    * compile.h (ADD_TRACE): add trace instruction to update covearge
      array.
    
    * thread.c (clear_coverage): delete coverage array when forking.
      Otherwise, double count of coverage may occur.
    
    * lib/coverage.rb: sample coverage measurement tool.
    
    * error.c: distinguish explicitly between parse_in_eval and
      mild_compile_error.
    
    * load.c: ditto.
    
    * vm_eval.c: ditto.



  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=17781

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/load.c?r1=17781&r2=17780&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/parse.y?r1=17781&r2=17780&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/ChangeLog?r1=17781&r2=17780&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/thread.c?r1=17781&r2=17780&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/vm_core.h?r1=17781&r2=17780&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/coverage.rb?revision=17781&view=markup
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/coverage.rb?r1=17781&r2=17780&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/iseq.c?r1=17781&r2=17780&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/error.c?r1=17781&r2=17780&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/compile.h?r1=17781&r2=17780&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/vm_eval.c?r1=17781&r2=17780&diff_format=u
  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/insns.def?r1=17781&r2=17780&diff_format=u

Index: insns.def
===================================================================
--- insns.def	(revision 17780)
+++ insns.def	(revision 17781)
@@ -847,11 +847,21 @@
  */
 DEFINE_INSN
 trace
-(rb_num_t nf)
+(rb_num_t nf, VALUE coverage)
 ()
 ()
 {
     rb_event_flag_t flag = nf;
+    if (coverage) {
+	long line = rb_sourceline() - 1;
+	if (RARRAY_PTR(coverage)[line] == Qnil) {
+	    rb_bug("bug");
+	}
+	long count = FIX2LONG(RARRAY_PTR(coverage)[line]) + 1;
+	if (POSFIXABLE(count)) {
+	    RARRAY_PTR(coverage)[line] = LONG2FIX(count);
+	}
+    }
     EXEC_EVENT_HOOK(th, flag, GET_SELF(), 0, 0 /* TODO: id, klass */);
 }
 
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 17780)
+++ ChangeLog	(revision 17781)
@@ -1,3 +1,34 @@
+Wed Jul  2 01:53:40 2008  Yusuke Endoh  <mame@t...>
+
+	* Add coverage measurement constant COVERAGE__.  This constant is not
+	  for casual use.  Usage: (1) assign {} to COVERAGE__, (2) require or
+	  load Ruby source file, and (3) COVERAGE__["sourcefilepath"] will
+	  return an array whose elements represent number of executions per
+	  line of source code.
+
+	* vm_core.h: add field of coverage array to iseq.
+
+	* iseq.c (prepare_iseq_build): ditto.
+
+	* insns.def (trace): update coverage array.
+
+	* parse.y (coverage): create and initialize coverage array.
+
+	* compile.h (ADD_TRACE): add trace instruction to update covearge
+	  array.
+
+	* thread.c (clear_coverage): delete coverage array when forking.
+	  Otherwise, double count of coverage may occur.
+
+	* lib/coverage.rb: sample coverage measurement tool.
+
+	* error.c: distinguish explicitly between parse_in_eval and
+	  mild_compile_error.
+
+	* load.c: ditto.
+
+	* vm_eval.c: ditto.
+
 Tue Jul  1 21:32:43 2008  Yusuke Endoh  <mame@t...>
 
 	* lib/test/unit/ui/console/testrunner.rb: prevent destructive
Index: vm_core.h
===================================================================
--- vm_core.h	(revision 17780)
+++ vm_core.h	(revision 17781)
@@ -201,6 +201,7 @@
     VALUE *iseq_encoded; /* encoded iseq */
     unsigned long iseq_size;
     VALUE mark_ary;	/* Array: includes operands which should be GC marked */
+    VALUE coverage;     /* coverage array */
 
     /* insn info, must be freed */
     struct iseq_insn_info_entry *insn_info_table;
@@ -448,6 +449,7 @@
     struct rb_vm_trap_tag *trap_tag;
 
     int parse_in_eval;
+    int mild_compile_error;
 
     /* storage */
     st_table *local_storage;
Index: iseq.c
===================================================================
--- iseq.c	(revision 17780)
+++ iseq.c	(revision 17781)
@@ -81,6 +81,7 @@
 	RUBY_MARK_UNLESS_NULL(iseq->filename);
 	RUBY_MARK_UNLESS_NULL((VALUE)iseq->cref_stack);
 	RUBY_MARK_UNLESS_NULL(iseq->klass);
+	RUBY_MARK_UNLESS_NULL(iseq->coverage);
 /* 	RUBY_MARK_UNLESS_NULL((VALUE)iseq->node); */
 /*	RUBY_MARK_UNLESS_NULL(iseq->cached_special_block); */
 
@@ -191,6 +192,17 @@
 
     set_relation(iseq, parent);
 
+    iseq->coverage = Qfalse;
+    if (!GET_THREAD()->parse_in_eval) {
+	if (rb_const_defined_at(rb_cObject, rb_intern("COVERAGE__"))) {
+	    VALUE hash = rb_const_get_at(rb_cObject, rb_intern("COVERAGE__"));
+	    if (TYPE(hash) == T_HASH) {
+		iseq->coverage = rb_hash_aref(hash, filename);
+		if (NIL_P(iseq->coverage)) iseq->coverage = Qfalse;
+	    }
+	}
+    }
+
     return Qtrue;
 }
 
Index: load.c
===================================================================
--- load.c	(revision 17780)
+++ load.c	(revision 17781)
@@ -240,8 +240,8 @@
     rb_thread_t *th = GET_THREAD();
     volatile VALUE wrapper = th->top_wrapper;
     volatile VALUE self = th->top_self;
-    volatile int parse_in_eval;
     volatile int loaded = Qfalse;
+    volatile int mild_compile_error;
 #ifndef __GNUC__
     rb_thread_t *volatile th0 = th;
 #endif
@@ -267,19 +267,19 @@
 	rb_extend_object(th->top_self, th->top_wrapper);
     }
 
-    parse_in_eval = th->parse_in_eval;
+    mild_compile_error = th->mild_compile_error;
     PUSH_TAG();
     state = EXEC_TAG();
     if (state == 0) {
 	NODE *node;
 	VALUE iseq;
 
-	th->parse_in_eval++;
+	th->mild_compile_error++;
 	node = (NODE *)rb_load_file(RSTRING_PTR(fname));
-	th->parse_in_eval--;
 	loaded = Qtrue;
 	iseq = rb_iseq_new(node, rb_str_new2("<top (required)>"),
 			   fname, Qfalse, ISEQ_TYPE_TOP);
+	th->mild_compile_error--;
 	rb_iseq_eval(iseq);
     }
     POP_TAG();
@@ -288,7 +288,7 @@
     th = th0;
     fname = RB_GC_GUARD(fname);
 #endif
-    th->parse_in_eval = parse_in_eval;
+    th->mild_compile_error = mild_compile_error;
     th->top_self = self;
     th->top_wrapper = wrapper;
 
Index: lib/coverage.rb
===================================================================
--- lib/coverage.rb	(revision 0)
+++ lib/coverage.rb	(revision 17781)
@@ -0,0 +1,57 @@
+COVERAGE__ ||= {}
+ext = ENV["COVERUBY_EXT"] || ".cov"
+accum = ENV["COVERUBY_ACCUM"]
+accum = !accum || accum == "" || !(%w(f n 0).include?(accum[0]))
+pwd = Dir.pwd
+
+at_exit do
+  Dir.chdir(pwd) do
+    COVERAGE__.each do |sfile, covs|
+      cfile = sfile + ext
+
+      writable = proc do |f|
+        File.writable?(f) || File.writable?(File.dirname(f))
+      end
+      unless writable[cfile]
+        cfile = cfile.gsub(File.PATH_SEPARATOR, "#")
+        next unless writable[cfile]
+      end
+
+      readlines = proc do |f|
+        File.read(f).force_encoding("ASCII-8BIT").lines.to_a
+      end
+
+      sources = (readlines[sfile] rescue [])
+
+      pcovs = []
+      if accum
+        pcovs = (readlines[cfile] rescue []).map.with_index do |line, idx|
+          if line[/^\s*(?:(#####)|(\d+)|-):\s*\d+:(.*)$/n]
+            cov, line = $1 ? 0 : ($2 ? $2.to_i : nil), $3
+            if !sources[idx] || sources[idx].chomp != line.chomp
+              warn("source file changed, ignoring: `#{ cfile }'")
+              break []
+            end
+            cov
+          else
+	    p line
+            warn("coverage file corrupted, ignoring: #{ cfile }")
+            break []
+          end
+        end
+        unless pcovs.empty? || pcovs.size == covs.size
+          warn("coverage file changed, ignoring: `#{ cfile }'")
+          pcovs = []
+        end
+      end
+
+      open(cfile, "w") do |out|
+        covs.zip(sources, pcovs).each_with_index do |(cov, line, pcov), idx|
+          cov += pcov || 0 if cov
+          cov = (cov ? (cov == 0 ? "#####" : cov.to_s) : "-").rjust(9)
+          out.puts("%s:% 5d:%s" % [cov, idx + 1, line])
+        end
+      end
+    end
+  end
+end

Property changes on: lib/coverage.rb
___________________________________________________________________
Name: svn:eol-style
   + LF

Index: vm_eval.c
===================================================================
--- vm_eval.c	(revision 17780)
+++ vm_eval.c	(revision 17781)
@@ -667,6 +667,7 @@
     rb_env_t *env = NULL;
     rb_block_t block;
     volatile int parse_in_eval;
+    volatile int mild_compile_error;
 
     if (file == 0) {
 	file = rb_sourcefile();
@@ -674,6 +675,7 @@
     }
 
     parse_in_eval = th->parse_in_eval;
+    mild_compile_error = th->mild_compile_error;
     PUSH_TAG();
     if ((state = EXEC_TAG()) == 0) {
 	rb_iseq_t *iseq;
@@ -708,7 +710,9 @@
 
 	/* make eval iseq */
 	th->parse_in_eval++;
+	th->mild_compile_error++;
 	iseqval = rb_iseq_compile(src, rb_str_new2(file), INT2FIX(line));
+	th->mild_compile_error--;
 	th->parse_in_eval--;
 
 	vm_set_eval_stack(th, iseqval, cref);
@@ -730,6 +734,7 @@
 	result = vm_eval_body(th);
     }
     POP_TAG();
+    th->mild_compile_error = mild_compile_error;
     th->parse_in_eval = parse_in_eval;
 
     if (state) {
Index: compile.h
===================================================================
--- compile.h	(revision 17780)
+++ compile.h	(revision 17781)
@@ -163,9 +163,16 @@
                          (VALUE)id, (VALUE)argc, (VALUE)block, (VALUE)flag))
 
 #define ADD_TRACE(seq, line, event) \
-  if (iseq->compile_data->option->trace_instruction) { \
-      ADD_INSN1(seq, line, trace, INT2FIX(event)); \
-  }
+  do { \
+      VALUE coverage = Qfalse; \
+      if ((event) == RUBY_EVENT_LINE && iseq->coverage && RARRAY_PTR(iseq->coverage)[(line) - 1] == Qnil) { \
+          RARRAY_PTR(iseq->coverage)[(line) - 1] = INT2FIX(0); \
+          coverage = iseq->coverage; \
+      } \
+      if (iseq->compile_data->option->trace_instruction || coverage) { \
+          ADD_INSN2(seq, line, trace, INT2FIX(event), coverage); \
+      } \
+  }while(0);
 
 /* add label */
 #define ADD_LABEL(seq, label) \
Index: thread.c
===================================================================
--- thread.c	(revision 17780)
+++ thread.c	(revision 17781)
@@ -2100,6 +2100,31 @@
 }
 
 static int
+clear_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
+{
+    int i;
+    VALUE lines = (VALUE)val;
+
+    for (i = 0; i < RARRAY_LEN(lines); i++) {
+	if (RARRAY_PTR(lines)[i] != Qnil) {
+	    RARRAY_PTR(lines)[i] = INT2FIX(0);
+	}
+    }
+    return ST_CONTINUE;
+}
+
+static void
+clear_coverage(void)
+{
+    if (rb_const_defined_at(rb_cObject, rb_intern("COVERAGE__"))) {
+	VALUE hash = rb_const_get_at(rb_cObject, rb_intern("COVERAGE__"));
+	if (TYPE(hash) == T_HASH) {
+	    st_foreach(RHASH_TBL(hash), clear_coverage_i, 0);
+	}
+    }
+}
+
+static int
 terminate_atfork_i(st_data_t key, st_data_t val, rb_thread_t *current_th)
 {
     VALUE thval = key;
@@ -2124,6 +2149,7 @@
     st_clear(vm->living_threads);
     st_insert(vm->living_threads, thval, (st_data_t) th->thread_id);
     vm->sleeper = 0;
+    clear_coverage();
     rb_reset_random_seed();
 }
 
@@ -2152,6 +2178,7 @@
     st_clear(vm->living_threads);
     st_insert(vm->living_threads, thval, (st_data_t) th->thread_id);
     vm->sleeper = 0;
+    clear_coverage();
 }
 
 struct thgroup {
Index: parse.y
===================================================================
--- parse.y	(revision 17780)
+++ parse.y	(revision 17781)
@@ -249,6 +249,7 @@
     NODE *parser_eval_tree_begin;
     NODE *parser_eval_tree;
     VALUE debug_lines;
+    VALUE coverage;
     int nerr;
 #else
     /* Ripper only */
@@ -322,6 +323,7 @@
 #define ruby_eval_tree		(parser->parser_eval_tree)
 #define ruby_eval_tree_begin	(parser->parser_eval_tree_begin)
 #define ruby_debug_lines	(parser->debug_lines)
+#define ruby_coverage		(parser->coverage)
 #endif
 
 static int yylex(void*, void*);
@@ -4668,6 +4670,32 @@
 }
 
 static VALUE
+coverage(const char *f, int n)
+{
+    if (rb_const_defined_at(rb_cObject, rb_intern("COVERAGE__"))) {
+	VALUE hash = rb_const_get_at(rb_cObject, rb_intern("COVERAGE__"));
+	if (TYPE(hash) == T_HASH) {
+	    VALUE fname = rb_str_new2(f);
+	    VALUE lines = rb_ary_new2(n);
+	    int i;
+	    for (i = 0; i < n; i++) RARRAY_PTR(lines)[i] = Qnil;
+	    RARRAY(lines)->len = n;
+	    rb_hash_aset(hash, fname, lines);
+	    return lines;
+	}
+    }
+    return 0;
+}
+
+static int
+e_option_supplied(struct parser_params *parser)
+{
+    if (strcmp(ruby_sourcefile, "-e") == 0)
+	return Qtrue;
+    return Qfalse;
+}
+
+static VALUE
 yycompile0(VALUE arg, int tracing)
 {
     int n;
@@ -4683,11 +4711,19 @@
 		rb_ary_push(ruby_debug_lines, str);
 	    } while (--n);
 	}
+
+	if (!e_option_supplied(parser)) {
+	    ruby_coverage = coverage(ruby_sourcefile, ruby_sourceline);
+	}
     }
 
     parser_prepare(parser);
     n = yyparse((void*)parser);
+    if (ruby_coverage) {
+	rb_ary_freeze(ruby_coverage);
+    }
     ruby_debug_lines = 0;
+    ruby_coverage = 0;
     compile_for_eval = 0;
 
     lex_strterm = 0;
@@ -4750,6 +4786,9 @@
     if (ruby_debug_lines && !NIL_P(line)) {
 	rb_ary_push(ruby_debug_lines, line);
     }
+    if (ruby_coverage && !NIL_P(line)) {
+	rb_ary_push(ruby_coverage, Qnil);
+    }
 #endif
     return line;
 }
@@ -8126,14 +8165,6 @@
     return 1;
 }
 
-static int
-e_option_supplied(struct parser_params *parser)
-{
-    if (strcmp(ruby_sourcefile, "-e") == 0)
-	return Qtrue;
-    return Qfalse;
-}
-
 static void
 warn_unless_e_option(struct parser_params *parser, NODE *node, const char *str)
 {
Index: error.c
===================================================================
--- error.c	(revision 17780)
+++ error.c	(revision 17781)
@@ -1546,7 +1546,7 @@
     rb_thread_t *th = GET_THREAD();
     VALUE err = th->errinfo;
 
-    if (th->parse_in_eval) {
+    if (th->mild_compile_error) {
 	if (!RTEST(err)) {
 	    err = rb_exc_new2(rb_eSyntaxError, s);
 	    th->errinfo = err;

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]