mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
add performance counting mechanism for MRI debug/tuning purpose.
* How to enable this feature? * define USE_DEBUG_COUNTER as 1. * you can disable to output the result with RUBY_DEBUG_COUNTER_DISABLE environment variable even if USE_DEBUG_COUNTER == 1. * How to add new counter? * add COUNTER(<name>) line on debug_counter.h. * include "debug_counter.h" * insert RB_DEBUG_COUNTER_INC(<name>) line on your favorite place. * counter output example: [RUBY_DEBUG_COUNTER] mc_inline_hit 999 [RUBY_DEBUG_COUNTER] mc_inline_miss 3 [RUBY_DEBUG_COUNTER] mc_global_hit 23 [RUBY_DEBUG_COUNTER] mc_global_miss 273 [RUBY_DEBUG_COUNTER] mc_global_state_miss 3 [RUBY_DEBUG_COUNTER] mc_class_serial_miss 0 [RUBY_DEBUG_COUNTER] mc_cme_complement 0 [RUBY_DEBUG_COUNTER] mc_cme_complement_hit 0 [RUBY_DEBUG_COUNTER] mc_search_super 1384 [RUBY_DEBUG_COUNTER] ivar_get_hit 0 [RUBY_DEBUG_COUNTER] ivar_get_miss 0 [RUBY_DEBUG_COUNTER] ivar_set_hit 0 [RUBY_DEBUG_COUNTER] ivar_set_miss 0 [RUBY_DEBUG_COUNTER] ivar_get 431 [RUBY_DEBUG_COUNTER] ivar_set 465 * mc_... is related to method caching. * ivar_... is related to instance variable accesses. * compare with dtrace/system tap features, there are completely no performacne penalties when it is disabled. * This feature is supported only on __GNUC__ compilers. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@57676 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
51de3aa2b2
commit
76c4cca19c
7 changed files with 145 additions and 3 deletions
13
common.mk
13
common.mk
|
@ -79,6 +79,7 @@ COMMONOBJS = array.$(OBJEXT) \
|
||||||
complex.$(OBJEXT) \
|
complex.$(OBJEXT) \
|
||||||
cont.$(OBJEXT) \
|
cont.$(OBJEXT) \
|
||||||
debug.$(OBJEXT) \
|
debug.$(OBJEXT) \
|
||||||
|
debug_counter.$(OBJEXT) \
|
||||||
dir.$(OBJEXT) \
|
dir.$(OBJEXT) \
|
||||||
dln_find.$(OBJEXT) \
|
dln_find.$(OBJEXT) \
|
||||||
encoding.$(OBJEXT) \
|
encoding.$(OBJEXT) \
|
||||||
|
@ -849,10 +850,15 @@ incs: $(INSNS) {$(VPATH)}node_name.inc {$(VPATH)}known_errors.inc \
|
||||||
{$(VPATH)}vm_call_iseq_optimized.inc $(srcdir)/revision.h \
|
{$(VPATH)}vm_call_iseq_optimized.inc $(srcdir)/revision.h \
|
||||||
$(REVISION_H) \
|
$(REVISION_H) \
|
||||||
$(UNICODE_DATA_HEADERS) $(srcdir)/enc/jis/props.h \
|
$(UNICODE_DATA_HEADERS) $(srcdir)/enc/jis/props.h \
|
||||||
{$(VPATH)}id.h {$(VPATH)}probes.dmyh
|
{$(VPATH)}id.h {$(VPATH)}probes.dmyh \
|
||||||
|
{$(VPATH)}debug_counter_names.inc
|
||||||
|
|
||||||
insns: $(INSNS)
|
insns: $(INSNS)
|
||||||
|
|
||||||
|
debug_counter_names.inc: $(srcdir)/tool/debug_counter.rb $(srcdir)/debug_counter.h
|
||||||
|
$(ECHO) generating $@
|
||||||
|
$(Q) $(BASERUBY) $(srcdir)/tool/debug_counter.rb $(srcdir)/debug_counter.h > $@
|
||||||
|
|
||||||
id.h: $(srcdir)/tool/generic_erb.rb $(srcdir)/template/id.h.tmpl $(srcdir)/defs/id.def
|
id.h: $(srcdir)/tool/generic_erb.rb $(srcdir)/template/id.h.tmpl $(srcdir)/defs/id.def
|
||||||
$(ECHO) generating $@
|
$(ECHO) generating $@
|
||||||
$(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb --output=$@ \
|
$(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb --output=$@ \
|
||||||
|
@ -1420,6 +1426,9 @@ debug.$(OBJEXT): {$(VPATH)}util.h
|
||||||
debug.$(OBJEXT): {$(VPATH)}vm_core.h
|
debug.$(OBJEXT): {$(VPATH)}vm_core.h
|
||||||
debug.$(OBJEXT): {$(VPATH)}vm_debug.h
|
debug.$(OBJEXT): {$(VPATH)}vm_debug.h
|
||||||
debug.$(OBJEXT): {$(VPATH)}vm_opts.h
|
debug.$(OBJEXT): {$(VPATH)}vm_opts.h
|
||||||
|
debug_counter.$(OBJEXT): {$(VPATH)}debug_counter.h
|
||||||
|
debug_counter.$(OBJEXT): {$(VPATH)}debug_counter_names.inc
|
||||||
|
debug_counter.$(OBJEXT): {$(VPATH)}debug_counter.c
|
||||||
dir.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
dir.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||||
dir.$(OBJEXT): $(top_srcdir)/include/ruby.h
|
dir.$(OBJEXT): $(top_srcdir)/include/ruby.h
|
||||||
dir.$(OBJEXT): {$(VPATH)}config.h
|
dir.$(OBJEXT): {$(VPATH)}config.h
|
||||||
|
@ -2534,6 +2543,7 @@ variable.$(OBJEXT): $(top_srcdir)/include/ruby.h
|
||||||
variable.$(OBJEXT): {$(VPATH)}config.h
|
variable.$(OBJEXT): {$(VPATH)}config.h
|
||||||
variable.$(OBJEXT): {$(VPATH)}constant.h
|
variable.$(OBJEXT): {$(VPATH)}constant.h
|
||||||
variable.$(OBJEXT): {$(VPATH)}defines.h
|
variable.$(OBJEXT): {$(VPATH)}defines.h
|
||||||
|
variable.$(OBJEXT): {$(VPATH)}debug_counter.h
|
||||||
variable.$(OBJEXT): {$(VPATH)}encoding.h
|
variable.$(OBJEXT): {$(VPATH)}encoding.h
|
||||||
variable.$(OBJEXT): {$(VPATH)}id.h
|
variable.$(OBJEXT): {$(VPATH)}id.h
|
||||||
variable.$(OBJEXT): {$(VPATH)}id_table.h
|
variable.$(OBJEXT): {$(VPATH)}id_table.h
|
||||||
|
@ -2568,6 +2578,7 @@ vm.$(OBJEXT): {$(VPATH)}constant.h
|
||||||
vm.$(OBJEXT): {$(VPATH)}defines.h
|
vm.$(OBJEXT): {$(VPATH)}defines.h
|
||||||
vm.$(OBJEXT): {$(VPATH)}encoding.h
|
vm.$(OBJEXT): {$(VPATH)}encoding.h
|
||||||
vm.$(OBJEXT): {$(VPATH)}eval_intern.h
|
vm.$(OBJEXT): {$(VPATH)}eval_intern.h
|
||||||
|
vm.$(OBJEXT): {$(VPATH)}debug_counter.h
|
||||||
vm.$(OBJEXT): {$(VPATH)}gc.h
|
vm.$(OBJEXT): {$(VPATH)}gc.h
|
||||||
vm.$(OBJEXT): {$(VPATH)}id.h
|
vm.$(OBJEXT): {$(VPATH)}id.h
|
||||||
vm.$(OBJEXT): {$(VPATH)}id_table.h
|
vm.$(OBJEXT): {$(VPATH)}id_table.h
|
||||||
|
|
39
debug_counter.c
Normal file
39
debug_counter.c
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/**********************************************************************
|
||||||
|
|
||||||
|
debug_counter.c -
|
||||||
|
|
||||||
|
created at: Tue Feb 21 16:51:18 2017
|
||||||
|
|
||||||
|
Copyright (C) 2017 Koichi Sasada
|
||||||
|
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
#include "debug_counter.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#if USE_DEBUG_COUNTER
|
||||||
|
|
||||||
|
/* do not modify manually. use a script above */
|
||||||
|
const char * const debug_counter_names[] = {
|
||||||
|
#include "debug_counter_names.inc"
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t rb_debug_counter[RB_DEBUG_COUNTER_MAX + 1];
|
||||||
|
|
||||||
|
__attribute__((destructor))
|
||||||
|
static void
|
||||||
|
rb_debug_counter_show_results(void)
|
||||||
|
{
|
||||||
|
const char *env = getenv("RUBY_DEBUG_COUNTER_DISABLE");
|
||||||
|
if (env == NULL || strcmp("1", env) != 0) {
|
||||||
|
int i;
|
||||||
|
for (i=0; i<RB_DEBUG_COUNTER_MAX; i++) {
|
||||||
|
fprintf(stderr, "[RUBY_DEBUG_COUNTER]\t%s\t%"PRIuSIZE"\n",
|
||||||
|
debug_counter_names[i],
|
||||||
|
rb_debug_counter[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* USE_DEBUG_COUNTER */
|
67
debug_counter.h
Normal file
67
debug_counter.h
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/**********************************************************************
|
||||||
|
|
||||||
|
debug_counter.h -
|
||||||
|
|
||||||
|
created at: Tue Feb 21 16:51:18 2017
|
||||||
|
|
||||||
|
Copyright (C) 2017 Koichi Sasada
|
||||||
|
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
#ifndef RUBY_DEBUG_COUNTER_H
|
||||||
|
#define RUBY_DEBUG_COUNTER_H 1
|
||||||
|
|
||||||
|
#ifndef USE_DEBUG_COUNTER
|
||||||
|
#define USE_DEBUG_COUNTER 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(__GNUC__) && USE_DEBUG_COUNTER
|
||||||
|
#error "USE_DEBUG_COUNTER is not supported by other than __GNUC__"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum rb_debug_counter_type {
|
||||||
|
#define COUNTER(name) RB_DEBUG_COUNTER_##name
|
||||||
|
COUNTER(mc_inline_hit),
|
||||||
|
COUNTER(mc_inline_miss),
|
||||||
|
COUNTER(mc_global_hit),
|
||||||
|
COUNTER(mc_global_miss),
|
||||||
|
COUNTER(mc_global_state_miss),
|
||||||
|
COUNTER(mc_class_serial_miss),
|
||||||
|
COUNTER(mc_cme_complement),
|
||||||
|
COUNTER(mc_cme_complement_hit),
|
||||||
|
COUNTER(mc_search_super),
|
||||||
|
COUNTER(ivar_get_hit),
|
||||||
|
COUNTER(ivar_get_miss),
|
||||||
|
COUNTER(ivar_set_hit),
|
||||||
|
COUNTER(ivar_set_miss),
|
||||||
|
COUNTER(ivar_get),
|
||||||
|
COUNTER(ivar_set),
|
||||||
|
RB_DEBUG_COUNTER_MAX
|
||||||
|
#undef COUNTER
|
||||||
|
};
|
||||||
|
|
||||||
|
#if USE_DEBUG_COUNTER
|
||||||
|
#include "ruby/ruby.h"
|
||||||
|
|
||||||
|
extern size_t rb_debug_counter[RB_DEBUG_COUNTER_MAX + 1];
|
||||||
|
|
||||||
|
inline static int
|
||||||
|
rb_debug_counter_add(enum rb_debug_counter_type type, int add, int cond)
|
||||||
|
{
|
||||||
|
if (cond) {
|
||||||
|
rb_debug_counter[(int)type] += add;
|
||||||
|
}
|
||||||
|
return cond;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RB_DEBUG_COUNTER_INC(type) rb_debug_counter_add(RB_DEBUG_COUNTER_##type, 1, 1)
|
||||||
|
#define RB_DEBUG_COUNTER_INC_UNLESS(type, cond) (!rb_debug_counter_add(RB_DEBUG_COUNTER_##type, 1, !(cond)))
|
||||||
|
#define RB_DEBUG_COUNTER_INC_IF(type, cond) rb_debug_counter_add(RB_DEBUG_COUNTER_##type, 1, (cond))
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define RB_DEBUG_COUNTER_INC(type) ((void)0)
|
||||||
|
#define RB_DEBUG_COUNTER_INC_UNLESS(type, cond) (cond)
|
||||||
|
#define RB_DEBUG_COUNTER_INC_IF(type, cond) (cond)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* RUBY_DEBUG_COUNTER_H */
|
6
tool/debug_counter.rb
Normal file
6
tool/debug_counter.rb
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
ARGF.each_line{|line|
|
||||||
|
if /^\s+COUNTER\((.+)\),$/ =~ line
|
||||||
|
puts "\"#{$1}\","
|
||||||
|
end
|
||||||
|
}
|
|
@ -19,6 +19,7 @@
|
||||||
#include "id.h"
|
#include "id.h"
|
||||||
#include "ccan/list/list.h"
|
#include "ccan/list/list.h"
|
||||||
#include "id_table.h"
|
#include "id_table.h"
|
||||||
|
#include "debug_counter.h"
|
||||||
|
|
||||||
struct rb_id_table *rb_global_tbl;
|
struct rb_id_table *rb_global_tbl;
|
||||||
static ID autoload, classpath, tmp_classpath, classid;
|
static ID autoload, classpath, tmp_classpath, classid;
|
||||||
|
@ -1209,6 +1210,7 @@ VALUE
|
||||||
rb_ivar_get(VALUE obj, ID id)
|
rb_ivar_get(VALUE obj, ID id)
|
||||||
{
|
{
|
||||||
VALUE iv = rb_ivar_lookup(obj, id, Qundef);
|
VALUE iv = rb_ivar_lookup(obj, id, Qundef);
|
||||||
|
RB_DEBUG_COUNTER_INC(ivar_get);
|
||||||
|
|
||||||
if (iv == Qundef) {
|
if (iv == Qundef) {
|
||||||
if (RTEST(ruby_verbose))
|
if (RTEST(ruby_verbose))
|
||||||
|
@ -1315,6 +1317,8 @@ rb_ivar_set(VALUE obj, ID id, VALUE val)
|
||||||
struct ivar_update ivup;
|
struct ivar_update ivup;
|
||||||
uint32_t i, len;
|
uint32_t i, len;
|
||||||
|
|
||||||
|
RB_DEBUG_COUNTER_INC(ivar_set);
|
||||||
|
|
||||||
rb_check_frozen(obj);
|
rb_check_frozen(obj);
|
||||||
|
|
||||||
switch (BUILTIN_TYPE(obj)) {
|
switch (BUILTIN_TYPE(obj)) {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "probes.h"
|
#include "probes.h"
|
||||||
#include "probes_helper.h"
|
#include "probes_helper.h"
|
||||||
#include "ruby/config.h"
|
#include "ruby/config.h"
|
||||||
|
#include "debug_counter.h"
|
||||||
|
|
||||||
/* control stack frame */
|
/* control stack frame */
|
||||||
|
|
||||||
|
@ -894,6 +895,7 @@ vm_getivar(VALUE obj, ID id, IC ic, struct rb_call_cache *cc, int is_attr)
|
||||||
rb_warning("instance variable %"PRIsVALUE" not initialized", QUOTE_ID(id));
|
rb_warning("instance variable %"PRIsVALUE" not initialized", QUOTE_ID(id));
|
||||||
val = Qnil;
|
val = Qnil;
|
||||||
}
|
}
|
||||||
|
RB_DEBUG_COUNTER_INC(ivar_get_hit);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -918,6 +920,8 @@ vm_getivar(VALUE obj, ID id, IC ic, struct rb_call_cache *cc, int is_attr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* USE_IC_FOR_IVAR */
|
#endif /* USE_IC_FOR_IVAR */
|
||||||
|
RB_DEBUG_COUNTER_INC(ivar_get_miss);
|
||||||
|
|
||||||
if (is_attr)
|
if (is_attr)
|
||||||
return rb_attr_get(obj, id);
|
return rb_attr_get(obj, id);
|
||||||
return rb_ivar_get(obj, id);
|
return rb_ivar_get(obj, id);
|
||||||
|
@ -941,6 +945,7 @@ vm_setivar(VALUE obj, ID id, VALUE val, IC ic, struct rb_call_cache *cc, int is_
|
||||||
|
|
||||||
if (index < ROBJECT_NUMIV(obj)) {
|
if (index < ROBJECT_NUMIV(obj)) {
|
||||||
RB_OBJ_WRITE(obj, &ptr[index], val);
|
RB_OBJ_WRITE(obj, &ptr[index], val);
|
||||||
|
RB_DEBUG_COUNTER_INC(ivar_set_hit);
|
||||||
return val; /* inline cache hit */
|
return val; /* inline cache hit */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -963,6 +968,7 @@ vm_setivar(VALUE obj, ID id, VALUE val, IC ic, struct rb_call_cache *cc, int is_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* USE_IC_FOR_IVAR */
|
#endif /* USE_IC_FOR_IVAR */
|
||||||
|
RB_DEBUG_COUNTER_INC(ivar_set_miss);
|
||||||
return rb_ivar_set(obj, id, val);
|
return rb_ivar_set(obj, id, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1220,13 +1226,17 @@ vm_search_method(const struct rb_call_info *ci, struct rb_call_cache *cc, VALUE
|
||||||
VALUE klass = CLASS_OF(recv);
|
VALUE klass = CLASS_OF(recv);
|
||||||
|
|
||||||
#if OPT_INLINE_METHOD_CACHE
|
#if OPT_INLINE_METHOD_CACHE
|
||||||
if (LIKELY(GET_GLOBAL_METHOD_STATE() == cc->method_state && RCLASS_SERIAL(klass) == cc->class_serial)) {
|
if (LIKELY(RB_DEBUG_COUNTER_INC_UNLESS(mc_global_state_miss,
|
||||||
|
GET_GLOBAL_METHOD_STATE() == cc->method_state) &&
|
||||||
|
RB_DEBUG_COUNTER_INC_UNLESS(mc_class_serial_miss,
|
||||||
|
RCLASS_SERIAL(klass) == cc->class_serial))) {
|
||||||
/* cache hit! */
|
/* cache hit! */
|
||||||
VM_ASSERT(cc->call != NULL);
|
VM_ASSERT(cc->call != NULL);
|
||||||
|
RB_DEBUG_COUNTER_INC(mc_inline_hit);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
RB_DEBUG_COUNTER_INC(mc_inline_miss);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cc->me = rb_callable_method_entry(klass, ci->mid);
|
cc->me = rb_callable_method_entry(klass, ci->mid);
|
||||||
VM_ASSERT(callable_method_entry_p(cc->me));
|
VM_ASSERT(callable_method_entry_p(cc->me));
|
||||||
cc->call = vm_call_general;
|
cc->call = vm_call_general;
|
||||||
|
|
|
@ -695,6 +695,7 @@ search_method(VALUE klass, ID id, VALUE *defined_class_ptr)
|
||||||
rb_method_entry_t *me;
|
rb_method_entry_t *me;
|
||||||
|
|
||||||
for (me = 0; klass; klass = RCLASS_SUPER(klass)) {
|
for (me = 0; klass; klass = RCLASS_SUPER(klass)) {
|
||||||
|
RB_DEBUG_COUNTER_INC(mc_search_super);
|
||||||
if ((me = lookup_method_table(klass, id)) != 0) break;
|
if ((me = lookup_method_table(klass, id)) != 0) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -778,10 +779,12 @@ method_entry_get(VALUE klass, ID id, VALUE *defined_class_ptr)
|
||||||
verify_method_cache(klass, id, ent->defined_class, ent->me);
|
verify_method_cache(klass, id, ent->defined_class, ent->me);
|
||||||
#endif
|
#endif
|
||||||
if (defined_class_ptr) *defined_class_ptr = ent->defined_class;
|
if (defined_class_ptr) *defined_class_ptr = ent->defined_class;
|
||||||
|
RB_DEBUG_COUNTER_INC(mc_global_hit);
|
||||||
return ent->me;
|
return ent->me;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
RB_DEBUG_COUNTER_INC(mc_global_miss);
|
||||||
return method_entry_get_without_cache(klass, id, defined_class_ptr);
|
return method_entry_get_without_cache(klass, id, defined_class_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -798,6 +801,7 @@ prepare_callable_method_entry(VALUE defined_class, ID id, const rb_method_entry_
|
||||||
const rb_callable_method_entry_t *cme;
|
const rb_callable_method_entry_t *cme;
|
||||||
|
|
||||||
if (me && me->defined_class == 0) {
|
if (me && me->defined_class == 0) {
|
||||||
|
RB_DEBUG_COUNTER_INC(mc_cme_complement);
|
||||||
VM_ASSERT(RB_TYPE_P(defined_class, T_ICLASS) || RB_TYPE_P(defined_class, T_MODULE));
|
VM_ASSERT(RB_TYPE_P(defined_class, T_ICLASS) || RB_TYPE_P(defined_class, T_MODULE));
|
||||||
VM_ASSERT(me->defined_class == 0);
|
VM_ASSERT(me->defined_class == 0);
|
||||||
|
|
||||||
|
@ -806,6 +810,7 @@ prepare_callable_method_entry(VALUE defined_class, ID id, const rb_method_entry_
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rb_id_table_lookup(mtbl, id, (VALUE *)&me)) {
|
if (rb_id_table_lookup(mtbl, id, (VALUE *)&me)) {
|
||||||
|
RB_DEBUG_COUNTER_INC(mc_cme_complement_hit);
|
||||||
cme = (rb_callable_method_entry_t *)me;
|
cme = (rb_callable_method_entry_t *)me;
|
||||||
VM_ASSERT(callable_method_entry_p(cme));
|
VM_ASSERT(callable_method_entry_p(cme));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue