diff options
author | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2012-10-04 12:31:05 +0000 |
---|---|---|
committer | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2012-10-04 12:31:05 +0000 |
commit | 23dc0dbc4ad3354a9df899c0105c57bf42e2d243 (patch) | |
tree | 9fdcd41591a161885d19cdd96949d743fdc69102 /vm.c | |
parent | b1ad79774de9789a2dd60c19c907356fa823ebd9 (diff) |
* vm.c (VM_COLLECT_USAGE_DETAILS): make new VM usage analysis
hooks (old macro name is COLLECT_USAGE_ANALYSIS).
This feature is only for VM developers. (I'm not sure I can use
`VM developers' (the plural form) in this sentence).
If VM_COLLECT_USAGE_DETAILS is not 0, VM enables the following
usage collection features:
(1) insntruction: collect intruction usages.
(2) operand: collect operand usages.
(3) register: collect register usages.
The results are stored in
RubyVM::USAGE_ANALYSIS_INSN for (1, 2),
RubyVM::USAGE_ANALYSIS_INSN_BIGRAM for (1) and
RubyVM::USAGE_ANALYSIS_REGS for (3).
You can stop collecting usages with
RubyVM::USAGE_ANALYSIS_INSN_STOP(),
RubyVM::USAGE_ANALYSIS_OPERAND_STOP(),
RubyVM::USAGE_ANALYSIS_REGISTER_STOP()
for (1), (2), (3) respectively.
You can also change the hook functions by setting
C level global variables
`ruby_vm_collect_usage_func_(insn|operand|register)'
for (1), (2), (3) respectively.
See codes for more details.
* tool/instruction.rb: fix macro names.
* iseq.c (insn_operand_intern): make it export (used in vm.c).
fix to skip several processes if not needed (pointer is 0).
* vm_dump.c: move codes for collection features to vm.c.
* vm_exec.h: rename macro and function names.
* vm_insnhelper.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37085 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'vm.c')
-rw-r--r-- | vm.c | 222 |
1 files changed, 218 insertions, 4 deletions
@@ -61,6 +61,16 @@ rb_vm_control_frame_block_ptr(rb_control_frame_t *cfp) return VM_CF_BLOCK_PTR(cfp); } +#ifndef VM_COLLECT_USAGE_DETAILS +#define VM_COLLECT_USAGE_DETAILS 0 +#endif + +#if VM_COLLECT_USAGE_DETAILS +static void vm_collect_usage_operand(int insn, int n, VALUE op); +static void vm_collect_usage_register(int reg, int isset); +static void vm_collect_usage_insn(int insn); +#endif + static VALUE vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, VALUE self, VALUE defined_class, int argc, const VALUE *argv, const rb_block_t *blockptr); @@ -91,10 +101,6 @@ rb_event_flag_t ruby_vm_event_flags; static void thread_free(void *ptr); -void vm_analysis_operand(int insn, int n, VALUE op); -void vm_analysis_register(int reg, int isset); -void vm_analysis_insn(int insn); - void rb_vm_change_state(void) { @@ -2070,6 +2076,12 @@ nsdr(void) return ary; } +#if VM_COLLECT_USAGE_DETAILS +static VALUE usage_analysis_insn_stop(VALUE self); +static VALUE usage_analysis_operand_stop(VALUE self); +static VALUE usage_analysis_register_stop(VALUE self); +#endif + void Init_VM(void) { @@ -2109,10 +2121,18 @@ Init_VM(void) rb_cThread = rb_define_class("Thread", rb_cObject); rb_undef_alloc_func(rb_cThread); +#if VM_COLLECT_USAGE_DETAILS /* ::RubyVM::USAGE_ANALYSIS_* */ rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_INSN", rb_hash_new()); rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_REGS", rb_hash_new()); rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_INSN_BIGRAM", rb_hash_new()); + + rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_INSN_STOP", usage_analysis_insn_stop, 0); + rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_OPERAND_STOP", usage_analysis_operand_stop, 0); + rb_define_singleton_method(rb_cRubyVM, "USAGE_ANALYSIS_REGISTER_STOP", usage_analysis_register_stop, 0); +#endif + + /* ::RubyVM::OPTS, which shows vm build options */ rb_define_const(rb_cRubyVM, "OPTS", opts = rb_ary_new()); #if OPT_DIRECT_THREADED_CODE @@ -2281,3 +2301,197 @@ rb_ruby_debug_ptr(void) { return ruby_vm_debug_ptr(GET_VM()); } + +#if VM_COLLECT_USAGE_DETAILS + +/* uh = { + * insn(Fixnum) => ihash(Hash) + * } + * ihash = { + * -1(Fixnum) => count, # insn usage + * 0(Fixnum) => ophash, # operand usage + * } + * ophash = { + * val(interned string) => count(Fixnum) + * } + */ +static void +vm_analysis_insn(int insn) +{ + ID usage_hash; + ID bigram_hash; + static int prev_insn = -1; + + VALUE uh; + VALUE ihash; + VALUE cv; + + CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN"); + CONST_ID(bigram_hash, "USAGE_ANALYSIS_INSN_BIGRAM"); + uh = rb_const_get(rb_cRubyVM, usage_hash); + if ((ihash = rb_hash_aref(uh, INT2FIX(insn))) == Qnil) { + ihash = rb_hash_new(); + rb_hash_aset(uh, INT2FIX(insn), ihash); + } + if ((cv = rb_hash_aref(ihash, INT2FIX(-1))) == Qnil) { + cv = INT2FIX(0); + } + rb_hash_aset(ihash, INT2FIX(-1), INT2FIX(FIX2INT(cv) + 1)); + + /* calc bigram */ + if (prev_insn != -1) { + VALUE bi; + VALUE ary[2]; + VALUE cv; + + ary[0] = INT2FIX(prev_insn); + ary[1] = INT2FIX(insn); + bi = rb_ary_new4(2, &ary[0]); + + uh = rb_const_get(rb_cRubyVM, bigram_hash); + if ((cv = rb_hash_aref(uh, bi)) == Qnil) { + cv = INT2FIX(0); + } + rb_hash_aset(uh, bi, INT2FIX(FIX2INT(cv) + 1)); + } + prev_insn = insn; +} + +/* iseq.c */ +VALUE insn_operand_intern(rb_iseq_t *iseq, + VALUE insn, int op_no, VALUE op, + int len, size_t pos, VALUE *pnop, VALUE child); + +static void +vm_analysis_operand(int insn, int n, VALUE op) +{ + ID usage_hash; + + VALUE uh; + VALUE ihash; + VALUE ophash; + VALUE valstr; + VALUE cv; + + CONST_ID(usage_hash, "USAGE_ANALYSIS_INSN"); + + uh = rb_const_get(rb_cRubyVM, usage_hash); + if ((ihash = rb_hash_aref(uh, INT2FIX(insn))) == Qnil) { + ihash = rb_hash_new(); + rb_hash_aset(uh, INT2FIX(insn), ihash); + } + if ((ophash = rb_hash_aref(ihash, INT2FIX(n))) == Qnil) { + ophash = rb_hash_new(); + rb_hash_aset(ihash, INT2FIX(n), ophash); + } + /* intern */ + valstr = insn_operand_intern(GET_THREAD()->cfp->iseq, insn, n, op, 0, 0, 0, 0); + + /* set count */ + if ((cv = rb_hash_aref(ophash, valstr)) == Qnil) { + cv = INT2FIX(0); + } + rb_hash_aset(ophash, valstr, INT2FIX(FIX2INT(cv) + 1)); +} + +static void +vm_analysis_register(int reg, int isset) +{ + ID usage_hash; + VALUE uh; + VALUE valstr; + static const char regstrs[][5] = { + "pc", /* 0 */ + "sp", /* 1 */ + "ep", /* 2 */ + "cfp", /* 3 */ + "self", /* 4 */ + "iseq", /* 5 */ + }; + static const char getsetstr[][4] = { + "get", + "set", + }; + static VALUE syms[sizeof(regstrs) / sizeof(regstrs[0])][2]; + + VALUE cv; + + CONST_ID(usage_hash, "USAGE_ANALYSIS_REGS"); + if (syms[0] == 0) { + char buff[0x10]; + int i; + + for (i = 0; i < (int)(sizeof(regstrs) / sizeof(regstrs[0])); i++) { + int j; + for (j = 0; j < 2; j++) { + snprintf(buff, 0x10, "%d %s %-4s", i, getsetstr[j], regstrs[i]); + syms[i][j] = ID2SYM(rb_intern(buff)); + } + } + } + valstr = syms[reg][isset]; + + uh = rb_const_get(rb_cRubyVM, usage_hash); + if ((cv = rb_hash_aref(uh, valstr)) == Qnil) { + cv = INT2FIX(0); + } + rb_hash_aset(uh, valstr, INT2FIX(FIX2INT(cv) + 1)); +} + +void (*ruby_vm_collect_usage_func_insn)(int insn) = vm_analysis_insn; +void (*ruby_vm_collect_usage_func_operand)(int insn, int n, VALUE op) = vm_analysis_operand; +void (*ruby_vm_collect_usage_func_register)(int reg, int isset) = vm_analysis_register; + +/* :nodoc: */ +static VALUE +usage_analysis_insn_stop(VALUE self) +{ + ruby_vm_collect_usage_func_insn = 0; + return Qnil; +} + +/* :nodoc: */ +static VALUE +usage_analysis_operand_stop(VALUE self) +{ + ruby_vm_collect_usage_func_operand = 0; + return Qnil; +} + +/* :nodoc: */ +static VALUE +usage_analysis_register_stop(VALUE self) +{ + ruby_vm_collect_usage_func_register = 0; + return Qnil; +} + +/* @param insn instruction number */ +static void +vm_collect_usage_insn(int insn) +{ + if (ruby_vm_collect_usage_func_insn) + (*ruby_vm_collect_usage_func_insn)(insn); +} + +/* @param insn instruction number + * @param n n-th operand + * @param op operand value + */ +static void +vm_collect_usage_operand(int insn, int n, VALUE op) +{ + if (ruby_vm_collect_usage_func_operand) + (*ruby_vm_collect_usage_func_operand)(insn, n, op); +} + +/* @param reg register id. see code of vm_analysis_register() */ +/* @param iseset 0: read, 1: write */ +static void +vm_collect_usage_register(int reg, int isset) +{ + if (ruby_vm_collect_usage_func_register) + (*ruby_vm_collect_usage_func_register)(reg, isset); +} + +#endif |