* 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
This commit is contained in:
ko1 2012-10-04 12:31:05 +00:00
parent b1ad79774d
commit 23dc0dbc4a
7 changed files with 295 additions and 177 deletions

View File

@ -1,3 +1,40 @@
Thu Oct 4 21:15:26 2012 Koichi Sasada <ko1@atdot.net>
* 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.
Thu Oct 4 18:59:14 2012 Koichi Sasada <ko1@atdot.net>
* test/ruby/test_settracefunc.rb (test_tracepoint):

23
iseq.c
View File

@ -963,7 +963,7 @@ id_to_name(ID id, VALUE default_value)
return str;
}
static VALUE
VALUE
insn_operand_intern(rb_iseq_t *iseq,
VALUE insn, int op_no, VALUE op,
int len, size_t pos, VALUE *pnop, VALUE child)
@ -991,13 +991,18 @@ insn_operand_intern(rb_iseq_t *iseq,
}
case TS_DINDEX:{
if (insn == BIN(getdynamic) || insn == BIN(setdynamic)) {
rb_iseq_t *diseq = iseq;
VALUE level = *pnop, i;
if (pnop) {
rb_iseq_t *diseq = iseq;
VALUE level = *pnop, i;
for (i = 0; i < level; i++) {
diseq = diseq->parent_iseq;
for (i = 0; i < level; i++) {
diseq = diseq->parent_iseq;
}
ret = id_to_name(diseq->local_table[diseq->local_size - op], INT2FIX('*'));
}
else {
ret = rb_sprintf("%"PRIuVALUE, op);
}
ret = id_to_name(diseq->local_table[diseq->local_size - op], INT2FIX('*'));
}
else {
ret = rb_inspect(INT2FIX(op));
@ -1011,7 +1016,9 @@ insn_operand_intern(rb_iseq_t *iseq,
op = obj_resurrect(op);
ret = rb_inspect(op);
if (CLASS_OF(op) == rb_cISeq) {
rb_ary_push(child, op);
if (child) {
rb_ary_push(child, op);
}
}
break;
@ -1049,7 +1056,7 @@ insn_operand_intern(rb_iseq_t *iseq,
break;
default:
rb_bug("rb_iseq_disasm: unknown operand type: %c", type);
rb_bug("insn_operand_intern: unknown operand type: %c", type);
}
return ret;
}

View File

@ -791,9 +791,9 @@ class RubyVM
end
def make_header_analysis insn
commit " USAGE_ANALYSIS_INSN(BIN(#{insn.name}));"
commit " COLLECT_USAGE_INSN(BIN(#{insn.name}));"
insn.opes.each_with_index{|op, i|
commit " USAGE_ANALYSIS_OPERAND(BIN(#{insn.name}), #{i}, #{op[1]});"
commit " COLLECT_USAGE_OPERAND(BIN(#{insn.name}), #{i}, #{op[1]});"
}
end

222
vm.c
View File

@ -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

139
vm_dump.c
View File

@ -395,145 +395,6 @@ rb_vmdebug_debug_print_post(rb_thread_t *th, rb_control_frame_t *cfp
#endif
}
#ifdef COLLECT_USAGE_ANALYSIS
/* uh = {
* insn(Fixnum) => ihash(Hash)
* }
* ihash = {
* -1(Fixnum) => count, # insn usage
* 0(Fixnum) => ophash, # operand usage
* }
* ophash = {
* val(interned string) => count(Fixnum)
* }
*/
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;
}
/* from disasm.c */
extern VALUE insn_operand_intern(int insn, int op_no, VALUE op,
int len, int pos, VALUE child);
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(insn, n, op, 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));
}
void
vm_analysis_register(int reg, int isset)
{
ID usage_hash;
VALUE uh;
VALUE rhash;
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 < sizeof(regstrs) / sizeof(regstrs[0]); i++) {
int j;
for (j = 0; j < 2; j++) {
snfprintf(stderr, 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));
}
#endif
VALUE
rb_vmdebug_thread_dump_state(VALUE self)
{

View File

@ -18,16 +18,6 @@ typedef unsigned long dindex_t;
typedef rb_num_t GENTRY;
typedef rb_iseq_t *ISEQ;
#ifdef COLLECT_USAGE_ANALYSIS
#define USAGE_ANALYSIS_INSN(insn) vm_analysis_insn(insn)
#define USAGE_ANALYSIS_OPERAND(insn, n, op) vm_analysis_operand((insn), (n), (VALUE)(op))
#define USAGE_ANALYSIS_REGISTER(reg, s) vm_analysis_register((reg), (s))
#else
#define USAGE_ANALYSIS_INSN(insn) /* none */
#define USAGE_ANALYSIS_OPERAND(insn, n, op) /* none */
#define USAGE_ANALYSIS_REGISTER(reg, s) /* none */
#endif
#ifdef __GCC__
/* TODO: machine dependent prefetch instruction */
#define PREFETCH(pc)

View File

@ -62,6 +62,15 @@ enum {
extern char ruby_vm_redefined_flag[BOP_LAST_];
extern VALUE ruby_vm_const_missing_count;
#if VM_COLLECT_USAGE_DETAILS
#define COLLECT_USAGE_INSN(insn) vm_collect_usage_insn(insn)
#define COLLECT_USAGE_OPERAND(insn, n, op) vm_collect_usage_operand((insn), (n), ((VALUE)(op)))
#define COLLECT_USAGE_REGISTER(reg, s) vm_collect_usage_register((reg), (s))
#else
#define COLLECT_USAGE_INSN(insn) /* none */
#define COLLECT_USAGE_OPERAND(insn, n, op) /* none */
#define COLLECT_USAGE_REGISTER(reg, s) /* none */
#endif
/**********************************************************/
/* deal with stack */
@ -104,16 +113,16 @@ enum vm_regan_acttype {
VM_REGAN_ACT_SET = 1,
};
#ifdef COLLECT_USAGE_ANALYSIS
#define USAGE_ANALYSIS_REGISTER_HELPER(a, b, v) \
(USAGE_ANALYSIS_REGISTER((VM_REGAN_#a), (VM_REGAN_ACT_#b)), (v))
#if VM_COLLECT_USAGE_DETAILS
#define COLLECT_USAGE_REGISTER_HELPER(a, b, v) \
(COLLECT_USAGE_REGISTER((VM_REGAN_##a), (VM_REGAN_ACT_##b)), (v))
#else
#define USAGE_ANALYSIS_REGISTER_HELPER(a, b, v) (v)
#define COLLECT_USAGE_REGISTER_HELPER(a, b, v) (v)
#endif
/* PC */
#define GET_PC() (USAGE_ANALYSIS_REGISTER_HELPER(PC, GET, REG_PC))
#define SET_PC(x) (REG_PC = (USAGE_ANALYSIS_REGISTER_HELPER(PC, SET, (x))))
#define GET_PC() (COLLECT_USAGE_REGISTER_HELPER(PC, GET, REG_PC))
#define SET_PC(x) (REG_PC = (COLLECT_USAGE_REGISTER_HELPER(PC, SET, (x))))
#define GET_CURRENT_INSN() (*GET_PC())
#define GET_OPERAND(n) (GET_PC()[(n)])
#define ADD_PC(n) (SET_PC(REG_PC + (n)))
@ -122,16 +131,16 @@ enum vm_regan_acttype {
#define JUMP(dst) (REG_PC += (dst))
/* frame pointer, environment pointer */
#define GET_CFP() (USAGE_ANALYSIS_REGISTER_HELPER(CFP, GET, REG_CFP))
#define GET_EP() (USAGE_ANALYSIS_REGISTER_HELPER(EP, GET, REG_EP))
#define SET_EP(x) (REG_EP = (USAGE_ANALYSIS_REGISTER_HELPER(EP, SET, (x))))
#define GET_CFP() (COLLECT_USAGE_REGISTER_HELPER(CFP, GET, REG_CFP))
#define GET_EP() (COLLECT_USAGE_REGISTER_HELPER(EP, GET, REG_EP))
#define SET_EP(x) (REG_EP = (COLLECT_USAGE_REGISTER_HELPER(EP, SET, (x))))
#define GET_LEP() (VM_EP_LEP(GET_EP()))
/* SP */
#define GET_SP() (USAGE_ANALYSIS_REGISTER_HELPER(SP, GET, REG_SP))
#define SET_SP(x) (REG_SP = (USAGE_ANALYSIS_REGISTER_HELPER(SP, SET, (x))))
#define INC_SP(x) (REG_SP += (USAGE_ANALYSIS_REGISTER_HELPER(SP, SET, (x))))
#define DEC_SP(x) (REG_SP -= (USAGE_ANALYSIS_REGISTER_HELPER(SP, SET, (x))))
#define GET_SP() (COLLECT_USAGE_REGISTER_HELPER(SP, GET, REG_SP))
#define SET_SP(x) (REG_SP = (COLLECT_USAGE_REGISTER_HELPER(SP, SET, (x))))
#define INC_SP(x) (REG_SP += (COLLECT_USAGE_REGISTER_HELPER(SP, SET, (x))))
#define DEC_SP(x) (REG_SP -= (COLLECT_USAGE_REGISTER_HELPER(SP, SET, (x))))
#define SET_SV(x) (*GET_SP() = (x))
/* set current stack value as x */
@ -155,7 +164,7 @@ enum vm_regan_acttype {
/* deal with values */
/**********************************************************/
#define GET_SELF() (USAGE_ANALYSIS_REGISTER_HELPER(5, 0, GET_CFP()->self))
#define GET_SELF() (COLLECT_USAGE_REGISTER_HELPER(SELF, GET, GET_CFP()->self))
/**********************************************************/
/* deal with control flow 2: method/iterator */