Feature #2565 » probes.diff
| .gitignore | ||
|---|---|---|
|
/exts.mk
|
||
|
/goruby
|
||
|
/id.h
|
||
|
/probes.h
|
||
|
/largefile.h
|
||
|
/lex.c
|
||
|
/libruby*.*
|
||
| Makefile.in | ||
|---|---|---|
|
OBJCOPY = @OBJCOPY@
|
||
|
VCS = @VCS@
|
||
|
VCSUP = @VCSUP@
|
||
|
DTRACE = @DTRACE@
|
||
|
OBJEXT = @OBJEXT@
|
||
|
ASMEXT = S
|
||
| ... | ... | |
|
$(srcdir)/configure: $(srcdir)/configure.in
|
||
|
$(CHDIR) $(srcdir) && exec $(AUTOCONF)
|
||
|
incs: id.h probes.h
|
||
|
# Things which should be considered:
|
||
|
# * with gperf v.s. without gperf
|
||
|
# * committers may have various versions of gperf
|
||
| ... | ... | |
|
@$(ECHO) preprocessing $<
|
||
|
$(Q) $(CPP) $(warnflags) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -E $< > $@
|
||
|
.d.h:
|
||
|
@$(ECHO) translating probes $<
|
||
|
$(Q)if test -n '$(DTRACE)'; then\
|
||
|
$(DTRACE) -o $@.tmp -h -s $<; \
|
||
|
sed -e 's/RUBY_/RUBY_DTRACE_/g' $@.tmp | sed -e 's/PROBES_H_TMP/PROBES_H/g' >$@; \
|
||
|
rm $@.tmp; \
|
||
|
else \
|
||
|
sed -f $(srcdir)/tool/gen_dummy_probes.sed $< > $@; \
|
||
|
fi
|
||
|
clean-local::
|
||
|
$(Q)$(RM) ext/extinit.c ext/extinit.$(OBJEXT) ext/ripper/y.output \
|
||
|
enc/encinit.c enc/encinit.$(OBJEXT)
|
||
| array.c | ||
|---|---|---|
|
#include "ruby/st.h"
|
||
|
#include "ruby/encoding.h"
|
||
|
#include "internal.h"
|
||
|
#include "probes.h"
|
||
|
#ifndef ARRAY_DEBUG
|
||
|
# define NDEBUG
|
||
| ... | ... | |
|
}
|
||
|
static VALUE
|
||
|
empty_ary_alloc(VALUE klass)
|
||
|
{
|
||
|
if(RUBY_DTRACE_ARRAY_CREATE_ENABLED()) {
|
||
|
RUBY_DTRACE_ARRAY_CREATE(0, rb_sourcefile(), rb_sourceline());
|
||
|
}
|
||
|
return ary_alloc(klass);
|
||
|
}
|
||
|
static VALUE
|
||
|
ary_new(VALUE klass, long capa)
|
||
|
{
|
||
|
VALUE ary;
|
||
| ... | ... | |
|
if (capa > ARY_MAX_SIZE) {
|
||
|
rb_raise(rb_eArgError, "array size too big");
|
||
|
}
|
||
|
if(RUBY_DTRACE_ARRAY_CREATE_ENABLED()) {
|
||
|
RUBY_DTRACE_ARRAY_CREATE(capa, rb_sourcefile(), rb_sourceline());
|
||
|
}
|
||
|
ary = ary_alloc(klass);
|
||
|
if (capa > RARRAY_EMBED_LEN_MAX) {
|
||
|
FL_UNSET_EMBED(ary);
|
||
| ... | ... | |
|
rb_cArray = rb_define_class("Array", rb_cObject);
|
||
|
rb_include_module(rb_cArray, rb_mEnumerable);
|
||
|
rb_define_alloc_func(rb_cArray, ary_alloc);
|
||
|
rb_define_alloc_func(rb_cArray, empty_ary_alloc);
|
||
|
rb_define_singleton_method(rb_cArray, "[]", rb_ary_s_create, -1);
|
||
|
rb_define_singleton_method(rb_cArray, "try_convert", rb_ary_s_try_convert, 1);
|
||
|
rb_define_method(rb_cArray, "initialize", rb_ary_initialize, -1);
|
||
| common.mk | ||
|---|---|---|
|
lib: $(LIBRUBY)
|
||
|
dll: $(LIBRUBY_SO)
|
||
|
.SUFFIXES: .inc .h .c .y .i
|
||
|
.SUFFIXES: .inc .h .c .y .i .d
|
||
|
# V=0 quiet, V=1 verbose. other values don't work.
|
||
|
V = 0
|
||
| ... | ... | |
|
{$(VPATH)}subst.h
|
||
|
ENCODING_H_INCLUDES= {$(VPATH)}encoding.h {$(VPATH)}oniguruma.h
|
||
|
ID_H_INCLUDES = {$(VPATH)}id.h
|
||
|
PROBES_H_INCLUDES = {$(VPATH)}probes.h
|
||
|
VM_CORE_H_INCLUDES = {$(VPATH)}vm_core.h {$(VPATH)}thread_$(THREAD_MODEL).h \
|
||
|
{$(VPATH)}node.h {$(VPATH)}method.h {$(VPATH)}atomic.h \
|
||
|
$(ID_H_INCLUDES)
|
||
|
addr2line.$(OBJEXT): {$(VPATH)}addr2line.c {$(VPATH)}addr2line.h {$(VPATH)}config.h
|
||
|
array.$(OBJEXT): {$(VPATH)}array.c $(RUBY_H_INCLUDES) {$(VPATH)}util.h \
|
||
|
$(ENCODING_H_INCLUDES) {$(VPATH)}internal.h
|
||
|
$(ENCODING_H_INCLUDES) {$(VPATH)}internal.h $(PROBES_H_INCLUDES)
|
||
|
bignum.$(OBJEXT): {$(VPATH)}bignum.c $(RUBY_H_INCLUDES) {$(VPATH)}util.h \
|
||
|
{$(VPATH)}thread.h {$(VPATH)}internal.h
|
||
|
class.$(OBJEXT): {$(VPATH)}class.c $(RUBY_H_INCLUDES) \
|
||
| ... | ... | |
|
eval.$(OBJEXT): {$(VPATH)}eval.c {$(VPATH)}eval_intern.h {$(VPATH)}vm.h \
|
||
|
$(RUBY_H_INCLUDES) $(VM_CORE_H_INCLUDES) {$(VPATH)}eval_error.c \
|
||
|
{$(VPATH)}eval_jump.c {$(VPATH)}debug.h {$(VPATH)}gc.h {$(VPATH)}iseq.h \
|
||
|
$(ENCODING_H_INCLUDES) {$(VPATH)}internal.h
|
||
|
$(ENCODING_H_INCLUDES) {$(VPATH)}internal.h $(PROBES_H_INCLUDES)
|
||
|
load.$(OBJEXT): {$(VPATH)}load.c {$(VPATH)}eval_intern.h \
|
||
|
{$(VPATH)}util.h $(RUBY_H_INCLUDES) $(VM_CORE_H_INCLUDES) \
|
||
|
{$(VPATH)}dln.h {$(VPATH)}debug.h \
|
||
|
{$(VPATH)}internal.h
|
||
|
{$(VPATH)}internal.h $(PROBES_H_INCLUDES)
|
||
|
file.$(OBJEXT): {$(VPATH)}file.c $(RUBY_H_INCLUDES) {$(VPATH)}io.h \
|
||
|
$(ENCODING_H_INCLUDES) {$(VPATH)}util.h {$(VPATH)}dln.h \
|
||
|
{$(VPATH)}internal.h
|
||
| ... | ... | |
|
{$(VPATH)}regex.h $(ENCODING_H_INCLUDES) $(VM_CORE_H_INCLUDES) \
|
||
|
{$(VPATH)}gc.h {$(VPATH)}io.h {$(VPATH)}eval_intern.h {$(VPATH)}util.h \
|
||
|
{$(VPATH)}debug.h {$(VPATH)}internal.h {$(VPATH)}constant.h \
|
||
|
{$(VPATH)}thread.h
|
||
|
{$(VPATH)}thread.h $(PROBES_H_INCLUDES)
|
||
|
hash.$(OBJEXT): {$(VPATH)}hash.c $(RUBY_H_INCLUDES) {$(VPATH)}util.h \
|
||
|
$(ENCODING_H_INCLUDES)
|
||
|
$(ENCODING_H_INCLUDES) $(PROBES_H_INCLUDES)
|
||
|
inits.$(OBJEXT): {$(VPATH)}inits.c $(RUBY_H_INCLUDES) \
|
||
|
{$(VPATH)}internal.h
|
||
|
io.$(OBJEXT): {$(VPATH)}io.c $(RUBY_H_INCLUDES) {$(VPATH)}io.h \
|
||
| ... | ... | |
|
numeric.$(OBJEXT): {$(VPATH)}numeric.c $(RUBY_H_INCLUDES) \
|
||
|
{$(VPATH)}util.h $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h
|
||
|
object.$(OBJEXT): {$(VPATH)}object.c $(RUBY_H_INCLUDES) {$(VPATH)}util.h \
|
||
|
{$(VPATH)}internal.h {$(VPATH)}constant.h $(ENCODING_H_INCLUDES)
|
||
|
{$(VPATH)}internal.h {$(VPATH)}constant.h $(ENCODING_H_INCLUDES) $(PROBES_H_INCLUDES)
|
||
|
pack.$(OBJEXT): {$(VPATH)}pack.c $(RUBY_H_INCLUDES) {$(VPATH)}encoding.h \
|
||
|
{$(VPATH)}oniguruma.h
|
||
|
parse.$(OBJEXT): {$(VPATH)}parse.c $(RUBY_H_INCLUDES) {$(VPATH)}node.h \
|
||
| ... | ... | |
|
strftime.$(OBJEXT): {$(VPATH)}strftime.c $(RUBY_H_INCLUDES) \
|
||
|
{$(VPATH)}timev.h $(ENCODING_H_INCLUDES)
|
||
|
string.$(OBJEXT): {$(VPATH)}string.c $(RUBY_H_INCLUDES) {$(VPATH)}re.h \
|
||
|
{$(VPATH)}regex.h $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h
|
||
|
{$(VPATH)}regex.h $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h $(PROBES_H_INCLUDES)
|
||
|
struct.$(OBJEXT): {$(VPATH)}struct.c $(RUBY_H_INCLUDES) {$(VPATH)}internal.h
|
||
|
thread.$(OBJEXT): {$(VPATH)}thread.c {$(VPATH)}eval_intern.h \
|
||
|
$(RUBY_H_INCLUDES) {$(VPATH)}gc.h $(VM_CORE_H_INCLUDES) \
|
||
| ... | ... | |
|
iseq.$(OBJEXT): {$(VPATH)}iseq.c {$(VPATH)}gc.h {$(VPATH)}iseq.h \
|
||
|
$(RUBY_H_INCLUDES) $(VM_CORE_H_INCLUDES) {$(VPATH)}insns.inc \
|
||
|
{$(VPATH)}insns_info.inc {$(VPATH)}node_name.inc {$(VPATH)}debug.h {$(VPATH)}internal.h
|
||
|
{$(VPATH)}vm_insnhelper.c:$(PROBES_H_INCLUDES)
|
||
|
vm.$(OBJEXT): {$(VPATH)}vm.c {$(VPATH)}gc.h {$(VPATH)}iseq.h \
|
||
|
{$(VPATH)}eval_intern.h $(RUBY_H_INCLUDES) $(ENCODING_H_INCLUDES) \
|
||
|
$(VM_CORE_H_INCLUDES) {$(VPATH)}vm_method.c {$(VPATH)}vm_eval.c \
|
||
|
{$(VPATH)}vm_insnhelper.c {$(VPATH)}vm_insnhelper.h {$(VPATH)}vm_exec.c \
|
||
|
{$(VPATH)}vm_exec.h {$(VPATH)}insns.def {$(VPATH)}vmtc.inc \
|
||
|
{$(VPATH)}vm.inc {$(VPATH)}insns.inc {$(VPATH)}debug.h \
|
||
|
{$(VPATH)}internal.h {$(VPATH)}vm.h {$(VPATH)}constant.h
|
||
|
{$(VPATH)}internal.h {$(VPATH)}vm.h {$(VPATH)}constant.h $(PROBES_H_INCLUDES)
|
||
|
vm_dump.$(OBJEXT): {$(VPATH)}vm_dump.c $(RUBY_H_INCLUDES) \
|
||
|
$(VM_CORE_H_INCLUDES) {$(VPATH)}debug.h {$(VPATH)}addr2line.h \
|
||
|
{$(VPATH)}internal.h
|
||
| compile.c | ||
|---|---|---|
|
printf("---------------------\n");
|
||
|
}
|
||
|
const char *
|
||
|
rb_insns_name(int i)
|
||
|
{
|
||
|
return insn_name_info[i];
|
||
|
}
|
||
|
VALUE
|
||
|
rb_insns_name_array(void)
|
||
|
{
|
||
| configure.in | ||
|---|---|---|
|
if test x"${build}" != x"${host}"; then
|
||
|
AC_CHECK_TOOL(CC, gcc)
|
||
|
fi
|
||
|
AC_CHECK_TOOL(DTRACE, dtrace)
|
||
|
AC_PROG_CC
|
||
|
AC_PROG_CXX
|
||
|
RUBY_MINGW32
|
||
| eval.c | ||
|---|---|---|
|
#include "ruby/encoding.h"
|
||
|
#include "internal.h"
|
||
|
#include "vm_core.h"
|
||
|
#include "probes.h"
|
||
|
#define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
|
||
| ... | ... | |
|
}
|
||
|
if (tag != TAG_FATAL) {
|
||
|
if(RUBY_DTRACE_RAISE_ENABLED()) {
|
||
|
RUBY_DTRACE_RAISE(rb_obj_classname(th->errinfo),
|
||
|
rb_sourcefile(),
|
||
|
rb_sourceline());
|
||
|
}
|
||
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_RAISE, th->cfp->self, 0, 0);
|
||
|
}
|
||
|
}
|
||
| gc.c | ||
|---|---|---|
|
#include "gc.h"
|
||
|
#include "constant.h"
|
||
|
#include "atomic.h"
|
||
|
#include "probes.h"
|
||
|
#include <stdio.h>
|
||
|
#include <setjmp.h>
|
||
|
#include <sys/types.h>
|
||
| ... | ... | |
|
size_t allocate_limit;
|
||
|
} gc_profile_record;
|
||
|
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
|
||
|
#pragma pack(push, 1) /* magic for reducing sizeof(RVALUE): 24 -> 20 */
|
||
|
#endif
|
||
| ... | ... | |
|
rb_raise(rb_eArgError, "wrong type argument %s (should be callable)",
|
||
|
rb_obj_classname(block));
|
||
|
}
|
||
|
return define_final0(obj, block);
|
||
|
}
|
||
| ... | ... | |
|
during_gc++;
|
||
|
gc_marks(objspace);
|
||
|
if (RUBY_DTRACE_GC_SWEEP_BEGIN_ENABLED()) {
|
||
|
RUBY_DTRACE_GC_SWEEP_BEGIN();
|
||
|
}
|
||
|
gc_prof_sweep_timer_start(objspace);
|
||
|
gc_sweep(objspace);
|
||
|
gc_prof_sweep_timer_stop(objspace);
|
||
| ... | ... | |
|
static inline void
|
||
|
gc_prof_mark_timer_start(rb_objspace_t *objspace)
|
||
|
{
|
||
|
if (RUBY_DTRACE_GC_MARK_BEGIN_ENABLED()) {
|
||
|
RUBY_DTRACE_GC_MARK_BEGIN();
|
||
|
}
|
||
|
}
|
||
|
static inline void
|
||
|
gc_prof_mark_timer_stop(rb_objspace_t *objspace)
|
||
|
{
|
||
|
if (RUBY_DTRACE_GC_MARK_END_ENABLED()) {
|
||
|
RUBY_DTRACE_GC_MARK_END();
|
||
|
}
|
||
|
}
|
||
|
static inline void
|
||
|
gc_prof_sweep_timer_start(rb_objspace_t *objspace)
|
||
|
{
|
||
|
if (RUBY_DTRACE_GC_SWEEP_BEGIN_ENABLED()) {
|
||
|
RUBY_DTRACE_GC_SWEEP_BEGIN();
|
||
|
}
|
||
|
}
|
||
|
static inline void
|
||
|
gc_prof_sweep_timer_stop(rb_objspace_t *objspace)
|
||
|
{
|
||
|
if (RUBY_DTRACE_GC_SWEEP_END_ENABLED()) {
|
||
|
RUBY_DTRACE_GC_SWEEP_END();
|
||
|
}
|
||
|
}
|
||
|
static inline void
|
||
| ... | ... | |
|
static inline void
|
||
|
gc_prof_mark_timer_start(rb_objspace_t *objspace)
|
||
|
{
|
||
|
if (RUBY_DTRACE_GC_MARK_BEGIN_ENABLED()) {
|
||
|
RUBY_DTRACE_GC_MARK_BEGIN();
|
||
|
}
|
||
|
if (objspace->profile.run) {
|
||
|
size_t count = objspace->profile.count;
|
||
| ... | ... | |
|
static inline void
|
||
|
gc_prof_mark_timer_stop(rb_objspace_t *objspace)
|
||
|
{
|
||
|
if (RUBY_DTRACE_GC_MARK_END_ENABLED()) {
|
||
|
RUBY_DTRACE_GC_MARK_END();
|
||
|
}
|
||
|
if (objspace->profile.run) {
|
||
|
double mark_time = 0;
|
||
|
size_t count = count;
|
||
| ... | ... | |
|
static inline void
|
||
|
gc_prof_sweep_timer_start(rb_objspace_t *objspace)
|
||
|
{
|
||
|
if (RUBY_DTRACE_GC_SWEEP_BEGIN_ENABLED()) {
|
||
|
RUBY_DTRACE_GC_SWEEP_BEGIN();
|
||
|
}
|
||
|
if (objspace->profile.run) {
|
||
|
size_t count = objspace->profile.count;
|
||
| ... | ... | |
|
static inline void
|
||
|
gc_prof_sweep_timer_stop(rb_objspace_t *objspace)
|
||
|
{
|
||
|
if (RUBY_DTRACE_GC_SWEEP_END_ENABLED()) {
|
||
|
RUBY_DTRACE_GC_SWEEP_END();
|
||
|
}
|
||
|
if (objspace->profile.run) {
|
||
|
double sweep_time = 0;
|
||
|
size_t count = objspace->profile.count;
|
||
| hash.c | ||
|---|---|---|
|
#include "ruby/util.h"
|
||
|
#include "ruby/encoding.h"
|
||
|
#include <errno.h>
|
||
|
#include "probes.h"
|
||
|
#ifdef __APPLE__
|
||
|
#include <crt_externs.h>
|
||
| ... | ... | |
|
return (VALUE)hash;
|
||
|
}
|
||
|
static VALUE
|
||
|
empty_hash_alloc(VALUE klass)
|
||
|
{
|
||
|
if(RUBY_DTRACE_HASH_CREATE_ENABLED()) {
|
||
|
RUBY_DTRACE_HASH_CREATE(0, rb_sourcefile(), rb_sourceline());
|
||
|
}
|
||
|
return hash_alloc(klass);
|
||
|
}
|
||
|
VALUE
|
||
|
rb_hash_new(void)
|
||
|
{
|
||
| ... | ... | |
|
rb_include_module(rb_cHash, rb_mEnumerable);
|
||
|
rb_define_alloc_func(rb_cHash, hash_alloc);
|
||
|
rb_define_alloc_func(rb_cHash, empty_hash_alloc);
|
||
|
rb_define_singleton_method(rb_cHash, "[]", rb_hash_s_create, -1);
|
||
|
rb_define_singleton_method(rb_cHash, "try_convert", rb_hash_s_try_convert, 1);
|
||
|
rb_define_method(rb_cHash,"initialize", rb_hash_initialize, -1);
|
||
| insns.def | ||
|---|---|---|
|
(VALUE val) // inc += 1 - num;
|
||
|
{
|
||
|
rb_num_t i;
|
||
|
if(RUBY_DTRACE_HASH_CREATE_ENABLED()) {
|
||
|
RUBY_DTRACE_HASH_CREATE(num, rb_sourcefile(), rb_sourceline());
|
||
|
}
|
||
|
val = rb_hash_new();
|
||
|
for (i = num; i > 0; i -= 2) {
|
||
| ... | ... | |
|
{
|
||
|
rb_event_flag_t flag = (rb_event_flag_t)nf;
|
||
|
if (RUBY_DTRACE_FUNCTION_ENTRY_ENABLED()) {
|
||
|
if (flag == RUBY_EVENT_CALL || flag == RUBY_EVENT_C_CALL) {
|
||
|
VALUE klass;
|
||
|
ID called_id;
|
||
|
rb_thread_method_id_and_class(th, &called_id, &klass);
|
||
|
RUBY_DTRACE_FUNCTION_ENTRY(
|
||
|
RSTRING_PTR(rb_inspect(klass)),
|
||
|
rb_id2name(called_id),
|
||
|
rb_sourcefile(),
|
||
|
rb_sourceline());
|
||
|
}
|
||
|
}
|
||
|
if (RUBY_DTRACE_FUNCTION_RETURN_ENABLED()) {
|
||
|
if (flag == RUBY_EVENT_RETURN || flag == RUBY_EVENT_C_RETURN) {
|
||
|
VALUE klass;
|
||
|
ID called_id;
|
||
|
rb_thread_method_id_and_class(th, &called_id, &klass);
|
||
|
RUBY_DTRACE_FUNCTION_RETURN(
|
||
|
RSTRING_PTR(rb_inspect(klass)),
|
||
|
rb_id2name(called_id),
|
||
|
rb_sourcefile(),
|
||
|
rb_sourceline());
|
||
|
}
|
||
|
}
|
||
|
EXEC_EVENT_HOOK(th, flag, GET_SELF(), 0, 0 /* TODO: id, klass */);
|
||
|
}
|
||
| internal.h | ||
|---|---|---|
|
int rb_local_defined(ID);
|
||
|
int rb_parse_in_eval(void);
|
||
|
int rb_parse_in_main(void);
|
||
|
const char * rb_insns_name(int i);
|
||
|
VALUE rb_insns_name_array(void);
|
||
|
/* cont.c */
|
||
| load.c | ||
|---|---|---|
|
#include "internal.h"
|
||
|
#include "dln.h"
|
||
|
#include "eval_intern.h"
|
||
|
#include "probes.h"
|
||
|
VALUE ruby_dln_librefs;
|
||
| ... | ... | |
|
VALUE fname, wrap, path;
|
||
|
rb_scan_args(argc, argv, "11", &fname, &wrap);
|
||
|
if(RUBY_DTRACE_LOAD_ENTRY_ENABLED()) {
|
||
|
RUBY_DTRACE_LOAD_ENTRY(
|
||
|
StringValuePtr(fname),
|
||
|
rb_sourcefile(),
|
||
|
rb_sourceline());
|
||
|
}
|
||
|
path = rb_find_file(FilePathValue(fname));
|
||
|
if (!path) {
|
||
|
if (!rb_file_load_ok(RSTRING_PTR(fname)))
|
||
| ... | ... | |
|
path = fname;
|
||
|
}
|
||
|
rb_load_internal(path, RTEST(wrap));
|
||
|
if(RUBY_DTRACE_LOAD_RETURN_ENABLED()) {
|
||
|
RUBY_DTRACE_LOAD_RETURN(StringValuePtr(fname));
|
||
|
}
|
||
|
return Qtrue;
|
||
|
}
|
||
| ... | ... | |
|
} volatile saved;
|
||
|
char *volatile ftptr = 0;
|
||
|
if(RUBY_DTRACE_REQUIRE_ENTRY_ENABLED()) {
|
||
|
RUBY_DTRACE_REQUIRE_ENTRY(
|
||
|
StringValuePtr(fname),
|
||
|
rb_sourcefile(),
|
||
|
rb_sourceline());
|
||
|
}
|
||
|
PUSH_TAG();
|
||
|
saved.safe = rb_safe_level();
|
||
|
if ((state = EXEC_TAG()) == 0) {
|
||
| ... | ... | |
|
th->errinfo = errinfo;
|
||
|
if(RUBY_DTRACE_REQUIRE_RETURN_ENABLED()) {
|
||
|
RUBY_DTRACE_REQUIRE_RETURN(StringValuePtr(fname));
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
| object.c | ||
|---|---|---|
|
#include <float.h>
|
||
|
#include "constant.h"
|
||
|
#include "internal.h"
|
||
|
#include "probes.h"
|
||
|
VALUE rb_cBasicObject;
|
||
|
VALUE rb_mKernel;
|
||
| ... | ... | |
|
klass);
|
||
|
}
|
||
|
if (RUBY_DTRACE_OBJECT_CREATE_ENABLED()) {
|
||
|
const char * file = rb_sourcefile();
|
||
|
RUBY_DTRACE_OBJECT_CREATE(rb_class2name(klass),
|
||
|
file ? file : "",
|
||
|
rb_sourceline());
|
||
|
}
|
||
|
obj = (*allocator)(klass);
|
||
|
if (rb_obj_class(obj) != rb_class_real(klass)) {
|
||
|
rb_raise(rb_eTypeError, "wrong instance allocation");
|
||
|
}
|
||
| parse.y | ||
|---|---|---|
|
#include <stdio.h>
|
||
|
#include <errno.h>
|
||
|
#include <ctype.h>
|
||
|
#include "probes.h"
|
||
|
#define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
|
||
| ... | ... | |
|
#ifndef RIPPER
|
||
|
parser->parser_token_info_enabled = !compile_for_eval && RTEST(ruby_verbose);
|
||
|
#endif
|
||
|
#ifndef RIPPER
|
||
|
if(RUBY_DTRACE_PARSE_BEGIN_ENABLED()) {
|
||
|
RUBY_DTRACE_PARSE_BEGIN(parser->parser_ruby_sourcefile,
|
||
|
parser->parser_ruby_sourceline);
|
||
|
}
|
||
|
#endif
|
||
|
n = yyparse((void*)parser);
|
||
|
#ifndef RIPPER
|
||
|
if(RUBY_DTRACE_PARSE_END_ENABLED()) {
|
||
|
RUBY_DTRACE_PARSE_END(parser->parser_ruby_sourcefile,
|
||
|
parser->parser_ruby_sourceline);
|
||
|
}
|
||
|
#endif
|
||
|
ruby_debug_lines = 0;
|
||
|
ruby_coverage = 0;
|
||
|
compile_for_eval = 0;
|
||
| probes.d | ||
|---|---|---|
|
provider ruby {
|
||
|
probe function__entry(const char *, const char *, const char *, int);
|
||
|
probe function__return(const char *, const char *, const char *, int);
|
||
|
probe require__entry(const char *, const char *, int);
|
||
|
probe require__return(const char *);
|
||
|
probe load__entry(const char *, const char *, int);
|
||
|
probe load__return(const char *);
|
||
|
probe raise(const char *, const char *, int);
|
||
|
probe object__create(const char *, const char *, int);
|
||
|
probe array__create(long, const char *, int);
|
||
|
probe hash__create(long, const char *, int);
|
||
|
probe string__create(long, const char *, int);
|
||
|
probe parse__begin(const char *, int);
|
||
|
probe parse__end(const char *, int);
|
||
|
probe insn(const char *);
|
||
|
probe insn__operand(const char *, const char *);
|
||
|
probe gc__mark__begin();
|
||
|
probe gc__mark__end();
|
||
|
probe gc__sweep__begin();
|
||
|
probe gc__sweep__end();
|
||
|
};
|
||
|
#pragma D attributes Stable/Evolving/Common provider ruby provider
|
||
|
#pragma D attributes Stable/Evolving/Common provider ruby module
|
||
|
#pragma D attributes Stable/Evolving/Common provider ruby function
|
||
|
#pragma D attributes Evolving/Evolving/Common provider ruby name
|
||
|
#pragma D attributes Evolving/Evolving/Common provider ruby args
|
||
| string.c | ||
|---|---|---|
|
#include "ruby/re.h"
|
||
|
#include "ruby/encoding.h"
|
||
|
#include "internal.h"
|
||
|
#include "probes.h"
|
||
|
#include <assert.h>
|
||
|
#define BEG(no) (regs->beg[(no)])
|
||
| ... | ... | |
|
return (VALUE)str;
|
||
|
}
|
||
|
static inline VALUE
|
||
|
empty_str_alloc(VALUE klass)
|
||
|
{
|
||
|
if(RUBY_DTRACE_STRING_CREATE_ENABLED()) {
|
||
|
RUBY_DTRACE_STRING_CREATE(0, rb_sourcefile(), rb_sourceline());
|
||
|
}
|
||
|
return str_alloc(klass);
|
||
|
}
|
||
|
static VALUE
|
||
|
str_new(VALUE klass, const char *ptr, long len)
|
||
|
{
|
||
| ... | ... | |
|
rb_raise(rb_eArgError, "negative string size (or size too big)");
|
||
|
}
|
||
|
if(RUBY_DTRACE_STRING_CREATE_ENABLED()) {
|
||
|
RUBY_DTRACE_STRING_CREATE(len, rb_sourcefile(), rb_sourceline());
|
||
|
}
|
||
|
str = str_alloc(klass);
|
||
|
if (len > RSTRING_EMBED_LEN_MAX) {
|
||
|
RSTRING(str)->as.heap.aux.capa = len;
|
||
| ... | ... | |
|
VALUE
|
||
|
rb_str_resurrect(VALUE str)
|
||
|
{
|
||
|
if(RUBY_DTRACE_STRING_CREATE_ENABLED()) {
|
||
|
RUBY_DTRACE_STRING_CREATE(
|
||
|
RSTRING_LEN(str), rb_sourcefile(), rb_sourceline());
|
||
|
}
|
||
|
return str_replace(str_alloc(rb_cString), str);
|
||
|
}
|
||
| ... | ... | |
|
rb_cString = rb_define_class("String", rb_cObject);
|
||
|
rb_include_module(rb_cString, rb_mComparable);
|
||
|
rb_define_alloc_func(rb_cString, str_alloc);
|
||
|
rb_define_alloc_func(rb_cString, empty_str_alloc);
|
||
|
rb_define_singleton_method(rb_cString, "try_convert", rb_str_s_try_convert, 1);
|
||
|
rb_define_method(rb_cString, "initialize", rb_str_init, -1);
|
||
|
rb_define_method(rb_cString, "initialize_copy", rb_str_replace, 1);
|
||
| test/dtrace/dummy.rb | ||
|---|---|---|
|
# this is a dummy file used by test/dtrace/test_require.rb
|
||
| test/dtrace/helper.rb | ||
|---|---|---|
|
require 'minitest/autorun'
|
||
|
require 'tempfile'
|
||
|
module DTrace
|
||
|
class TestCase < MiniTest::Unit::TestCase
|
||
|
INCLUDE = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
||
|
def setup
|
||
|
skip "must be setuid 0 to run dtrace tests" unless Process.euid == 0
|
||
|
end
|
||
|
def trap_probe d_program, ruby_program
|
||
|
d = Tempfile.new('probe.d')
|
||
|
d.write d_program
|
||
|
d.flush
|
||
|
rb = Tempfile.new('probed.rb')
|
||
|
rb.write ruby_program
|
||
|
rb.flush
|
||
|
d_path = d.path
|
||
|
rb_path = rb.path
|
||
|
cmd = "dtrace -q -s #{d_path} -c '#{Gem.ruby} -I#{INCLUDE} #{rb_path}'"
|
||
|
probes = IO.popen(cmd) do |io|
|
||
|
io.readlines
|
||
|
end
|
||
|
d.close(true)
|
||
|
rb.close(true)
|
||
|
yield(d_path, rb_path, probes)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
| test/dtrace/test_array_create.rb | ||
|---|---|---|
|
require 'dtrace/helper'
|
||
|
module DTrace
|
||
|
class TestArrayCreate < TestCase
|
||
|
def test_lit
|
||
|
trap_probe(probe, '[]') { |_,rbfile,saw|
|
||
|
saw = saw.map(&:split).find_all { |num, file, line|
|
||
|
file == rbfile && num == '0'
|
||
|
}
|
||
|
assert_equal([rbfile], saw.map { |line| line[1] })
|
||
|
assert_equal(['1'], saw.map { |line| line[2] })
|
||
|
}
|
||
|
end
|
||
|
def test_many_lit
|
||
|
trap_probe(probe, '[1,2,3,4]') { |_,rbfile,saw|
|
||
|
saw = saw.map(&:split).find_all { |num, file, line|
|
||
|
file == rbfile && num == '4' && line == '1'
|
||
|
}
|
||
|
assert_operator saw.length, :>, 0
|
||
|
}
|
||
|
end
|
||
|
private
|
||
|
def probe type = 'array'
|
||
|
<<-eoprobe
|
||
|
ruby$target:::#{type}-create
|
||
|
/arg1/
|
||
|
{
|
||
|
printf("%d %s %d\\n", arg0, copyinstr(arg1), arg2);
|
||
|
}
|
||
|
eoprobe
|
||
|
end
|
||
|
end
|
||
|
end
|
||
| test/dtrace/test_function_entry.rb | ||
|---|---|---|
|
require 'dtrace/helper'
|
||
|
module DTrace
|
||
|
class TestFunctionEntry < TestCase
|
||
|
def test_function_entry
|
||
|
probe = <<-eoprobe
|
||
|
ruby$target:::function-entry
|
||
|
/arg0 && arg1 && arg2/
|
||
|
{
|
||
|
printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
|
||
|
}
|
||
|
eoprobe
|
||
|
trap_probe(probe, ruby_program) { |d_file, rb_file, probes|
|
||
|
foo_calls = probes.map { |line| line.split }.find_all { |row|
|
||
|
row.first == 'Foo' && row[1] == 'foo'
|
||
|
}
|
||
|
assert_equal 10, foo_calls.length
|
||
|
line = '2'
|
||
|
foo_calls.each { |f| assert_equal line, f[3] }
|
||
|
foo_calls.each { |f| assert_equal rb_file, f[2] }
|
||
|
}
|
||
|
end
|
||
|
def test_function_return
|
||
|
probe = <<-eoprobe
|
||
|
ruby$target:::function-return
|
||
|
/arg0 && arg1 && arg2/
|
||
|
{
|
||
|
printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
|
||
|
}
|
||
|
eoprobe
|
||
|
trap_probe(probe, ruby_program) { |d_file, rb_file, probes|
|
||
|
foo_calls = probes.map { |line| line.split }.find_all { |row|
|
||
|
row.first == 'Foo' && row[1] == 'foo'
|
||
|
}
|
||
|
assert_equal 10, foo_calls.length
|
||
|
line = '2'
|
||
|
foo_calls.each { |f| assert_equal line, f[3] }
|
||
|
foo_calls.each { |f| assert_equal rb_file, f[2] }
|
||
|
}
|
||
|
end
|
||
|
private
|
||
|
def ruby_program
|
||
|
<<-eoruby
|
||
|
class Foo
|
||
|
def foo; end
|
||
|
end
|
||
|
x = Foo.new
|
||
|
10.times { x.foo }
|
||
|
eoruby
|
||
|
end
|
||
|
end
|
||
|
end
|
||
| test/dtrace/test_gc.rb | ||
|---|---|---|
|
require 'dtrace/helper'
|
||
|
module DTrace
|
||
|
class TestGC < TestCase
|
||
|
%w{
|
||
|
gc-mark-begin
|
||
|
gc-mark-end
|
||
|
gc-sweep-begin
|
||
|
gc-sweep-end
|
||
|
}.each do |probe_name|
|
||
|
define_method(:"test_#{probe_name.gsub(/-/, '_')}") do
|
||
|
probe = "ruby$target:::#{probe_name} { printf(\"#{probe_name}\\n\"); }"
|
||
|
trap_probe(probe, ruby_program) { |_, _, saw|
|
||
|
assert_operator saw.length, :>, 0
|
||
|
}
|
||
|
end
|
||
|
end
|
||
|
private
|
||
|
def ruby_program
|
||
|
"100000.times { Object.new }"
|
||
|
end
|
||
|
end
|
||
|
end
|
||
| test/dtrace/test_hash_create.rb | ||
|---|---|---|
|
require 'dtrace/helper'
|
||
|
module DTrace
|
||
|
class TestHashCreate < TestCase
|
||
|
def test_hash_new
|
||
|
trap_probe(probe, 'Hash.new') { |_,rbfile,saw|
|
||
|
saw = saw.map(&:split).find_all { |num, file, line|
|
||
|
file == rbfile && num == '0'
|
||
|
}
|
||
|
assert_operator saw.length, :>, 0
|
||
|
}
|
||
|
end
|
||
|
def test_hash_lit
|
||
|
trap_probe(probe, '{}') { |_,rbfile,saw|
|
||
|
saw = saw.map(&:split).find_all { |num, file, line|
|
||
|
file == rbfile && num == '0'
|
||
|
}
|
||
|
assert_operator saw.length, :>, 0
|
||
|
}
|
||
|
end
|
||
|
def test_hash_lit_elements
|
||
|
trap_probe(probe, '{ :foo => :bar }') { |_,rbfile,saw|
|
||
|
saw = saw.map(&:split).find_all { |num, file, line|
|
||
|
file == rbfile && num == '2'
|
||
|
}
|
||
|
assert_operator saw.length, :>, 0
|
||
|
}
|
||
|
end
|
||
|
def test_hash_lit_elements_string
|
||
|
trap_probe(probe, '{ :foo => :bar, :bar => "baz" }') { |_,rbfile,saw|
|
||
|
saw = saw.map(&:split).find_all { |num, file, line|
|
||
|
file == rbfile && num == '4'
|
||
|
}
|
||
|
assert_operator saw.length, :>, 0
|
||
|
}
|
||
|
end
|
||
|
private
|
||
|
def probe
|
||
|
<<-eoprobe
|
||
|
ruby$target:::hash-create
|
||
|
/arg1/
|
||
|
{
|
||
|
printf("%d %s %d\\n", arg0, copyinstr(arg1), arg2);
|
||
|
}
|
||
|
eoprobe
|
||
|
end
|
||
|
end
|
||
|
end
|
||
| test/dtrace/test_load.rb | ||
|---|---|---|
|
require 'dtrace/helper'
|
||
|
require 'tempfile'
|
||
|
module DTrace
|
||
|
class TestLoad < TestCase
|
||
|
def setup
|
||
|
super
|
||
|
@rbfile = Tempfile.new(['omg', 'rb'])
|
||
|
@rbfile.write 'x = 10'
|
||
|
end
|
||
|
def teardown
|
||
|
super
|
||
|
@rbfile.close(true) if @rbfile
|
||
|
end
|
||
|
def test_load_entry
|
||
|
probe = <<-eoprobe
|
||
|
ruby$target:::load-entry
|
||
|
{
|
||
|
printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
|
||
|
}
|
||
|
eoprobe
|
||
|
trap_probe(probe, program) { |dpath, rbpath, saw|
|
||
|
saw = saw.map(&:split).find_all { |loaded, _, _|
|
||
|
loaded == @rbfile.path
|
||
|
}
|
||
|
assert_equal 10, saw.length
|
||
|
}
|
||
|
end
|
||
|
def test_load_return
|
||
|
probe = <<-eoprobe
|
||
|
ruby$target:::load-return
|
||
|
{
|
||
|
printf("%s\\n", copyinstr(arg0));
|
||
|
}
|
||
|
eoprobe
|
||
|
trap_probe(probe, program) { |dpath, rbpath, saw|
|
||
|
saw = saw.map(&:split).find_all { |loaded, _, _|
|
||
|
loaded == @rbfile.path
|
||
|
}
|
||
|
assert_equal 10, saw.length
|
||
|
}
|
||
|
end
|
||
|
private
|
||
|
def program
|
||
|
"10.times { load '#{@rbfile.path}' }"
|
||
|
end
|
||
|
end
|
||
|
end
|
||
| test/dtrace/test_object_create_start.rb | ||
|---|---|---|
|
require 'dtrace/helper'
|
||
|
module DTrace
|
||
|
class TestObjectCreateStart < TestCase
|
||
|
def test_object_create_start
|
||
|
trap_probe(probe, '10.times { Object.new }') { |_,rbfile,saw|
|
||
|
saw = saw.map(&:split).find_all { |_, file, _|
|
||
|
file == rbfile
|
||
|
}
|
||
|
assert_equal 10, saw.length
|
||
|
}
|
||
|
end
|
||
|
def test_object_create_start_name
|
||
|
trap_probe(probe, 'Hash.new') { |_,rbfile,saw|
|
||
|
saw = saw.map(&:split).find_all { |klass, file, line|
|
||
|
file == rbfile
|
||
|
}
|
||
|
assert_equal(%w{ Hash }, saw.map(&:first))
|
||
|
assert_equal([rbfile], saw.map { |line| line[1] })
|
||
|
assert_equal(['1'], saw.map { |line| line[2] })
|
||
|
}
|
||
|
end
|
||
|
def test_object_create_start_string_lit
|
||
|
trap_probe(probe, '"omg"') { |_,rbfile,saw|
|
||
|
saw = saw.map(&:split).find_all { |klass, file, line|
|
||
|
file == rbfile
|
||
|
}
|
||
|
assert_equal(%w{ String }, saw.map(&:first))
|
||
|
assert_equal([rbfile], saw.map { |line| line[1] })
|
||
|
assert_equal(['1'], saw.map { |line| line[2] })
|
||
|
}
|
||
|
end
|
||
|
private
|
||
|
def probe type = "object"
|
||
|
<<-eoprobe
|
||
|
ruby$target:::#{type}-create
|
||
|
{
|
||
|
printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
|
||
|
}
|
||
|
eoprobe
|
||
|
end
|
||
|
end
|
||
|
end
|
||
| test/dtrace/test_raise.rb | ||
|---|---|---|
|
require 'dtrace/helper'
|
||
|
module DTrace
|
||
|
class TestRaise < TestCase
|
||
|
def test_raise
|
||
|
probe = <<-eoprobe
|
||
|
ruby$target:::raise
|
||
|
{
|
||
|
printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
|
||
|
}
|
||
|
eoprobe
|
||
|
trap_probe(probe, program) { |dpath, rbpath, saw|
|
||
|
saw = saw.map(&:split).find_all { |_, source_file, _|
|
||
|
source_file == rbpath
|
||
|
}
|
||
|
assert_equal 10, saw.length
|
||
|
saw.each do |klass, _, source_line|
|
||
|
assert_equal 'RuntimeError', klass
|
||
|
assert_equal '1', source_line
|
||
|
end
|
||
|
}
|
||
|
end
|
||
|
private
|
||
|
def program
|
||
|
'10.times { raise rescue nil }'
|
||
|
end
|
||
|
end
|
||
|
end
|
||
| test/dtrace/test_require.rb | ||
|---|---|---|
|
require 'dtrace/helper'
|
||
|
module DTrace
|
||
|
class TestRequire < TestCase
|
||
|
def test_require_entry
|
||
|
probe = <<-eoprobe
|
||
|
ruby$target:::require-entry
|
||
|
{
|
||
|
printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
|
||
|
}
|
||
|
eoprobe
|
||
|
trap_probe(probe, ruby_program) { |d_file, rb_file, saw|
|
||
|
required = saw.map { |s| s.split }.find_all do |(required, _)|
|
||
|
required == 'dtrace/dummy'
|
||
|
end
|
||
|
assert_equal 10, required.length
|
||
|
}
|
||
|
end
|
||
|
def test_require_return
|
||
|
probe = <<-eoprobe
|
||
|
ruby$target:::require-return
|
||
|
{
|
||
|
printf("%s\\n", copyinstr(arg0));
|
||
|
}
|
||
|
eoprobe
|
||
|
end
|
||
|
private
|
||
|
def ruby_program
|
||
|
"10.times { require 'dtrace/dummy' }"
|
||
|
end
|
||
|
end
|
||
|
end
|
||
| test/dtrace/test_singleton_function.rb | ||
|---|---|---|
|
require 'dtrace/helper'
|
||
|
module DTrace
|
||
|
class TestSingletonFunctionEntry < TestCase
|
||
|
def test_entry
|
||
|
probe = <<-eoprobe
|
||
|
ruby$target:::function-entry
|
||
|
/strstr(copyinstr(arg0), "Foo") != NULL/
|
||
|
{
|
||
|
printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
|
||
|
}
|
||
|
eoprobe
|
||
|
trap_probe(probe, ruby_program) { |d_file, rb_file, probes|
|
||
|
foo_calls = probes.map { |line| line.split }.find_all { |row|
|
||
|
row.first == '#<Class:Foo>' && row[1] == 'foo'
|
||
|
}
|
||
|
assert_equal 10, foo_calls.length
|
||
|
line = '2'
|
||
|
foo_calls.each { |f| assert_equal line, f[3] }
|
||
|
foo_calls.each { |f| assert_equal rb_file, f[2] }
|
||
|
}
|
||
|
end
|
||
|
def test_exit
|
||
|
probe = <<-eoprobe
|
||
|
ruby$target:::function-return
|
||
|
{
|
||
|
printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
|
||
|
}
|
||
|
eoprobe
|
||
|
trap_probe(probe, ruby_program) { |d_file, rb_file, probes|
|
||
|
foo_calls = probes.map { |line| line.split }.find_all { |row|
|
||
|
row.first == '#<Class:Foo>' && row[1] == 'foo'
|
||
|
}
|
||
|
assert_equal 10, foo_calls.length
|
||
|
line = '2'
|
||
|
foo_calls.each { |f| assert_equal line, f[3] }
|
||
|
foo_calls.each { |f| assert_equal rb_file, f[2] }
|
||
|
}
|
||
|
end
|
||
|
def ruby_program
|
||
|
<<-eoruby
|
||
|
class Foo
|
||
|
def self.foo; end
|
||
|
end
|
||
|
10.times { Foo.foo }
|
||
|
eoruby
|
||
|
end
|
||
|
end
|
||
|
end
|
||
| tool/gen_dummy_probes.sed | ||
|---|---|---|
|
# upper case everything
|
||
|
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
|
||
|
# remove the pragma declarations
|
||
|
s/^#PRAGMA.*$//
|
||
|
# replace the provider section with the start of the header file
|
||
|
s/PROVIDER RUBY {/#ifndef _PROBES_H\
|
||
|
#define _PROBES_H/
|
||
|
# finish up the #ifndef sandwich
|
||
|
s/};/#endif \/* _PROBES_H *\//
|
||
|
s/__/_/g
|
||
|
s/([^,)]\{1,\})/(arg0)/
|
||
|
s/([^,)]\{1,\},[^,)]\{1,\})/(arg0, arg1)/
|
||
|
s/([^,)]\{1,\},[^,)]\{1,\},[^,)]\{1,\})/(arg0, arg1, arg2)/
|
||
|
s/([^,)]\{1,\},[^,)]\{1,\},[^,)]\{1,\},[^,)]\{1,\})/(arg0, arg1, arg2, arg3)/
|
||
|
s/([^,)]\{1,\},[^,)]\{1,\},[^,)]\{1,\},[^,)]\{1,\},[^,)]\{1,\})/(arg0, arg1, arg2, arg3, arg4)/
|
||
|
s/[ ]*PROBE[ ]\([^\(]*\)\(([^\)]*)\);/#define RUBY_DTRACE_\1_ENABLED() 0\
|
||
|
#define RUBY_DTRACE_\1\2\ do \{ \} while\(0\)/
|
||
| vm.c | ||
|---|---|---|
|
#include "vm_core.h"
|
||
|
#include "iseq.h"
|
||
|
#include "eval_intern.h"
|
||
|
#include "probes.h"
|
||
|
static inline VALUE *
|
||
|
VM_EP_LEP(VALUE *ep)
|
||
| ... | ... | |
|
#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,
|
||
| ... | ... | |
|
if (UNLIKELY(VM_FRAME_TYPE(th->cfp) == VM_FRAME_MAGIC_CFUNC)) {
|
||
|
const rb_method_entry_t *me = th->cfp->me;
|
||
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, th->cfp->self, me->called_id, me->klass);
|
||
|
RUBY_DTRACE_FUNC_RETURN_HOOK(me->klass, me->called_id);
|
||
|
}
|
||
|
th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
|
||
|
}
|
||
| ... | ... | |
|
VALUE hash = rb_hash_new();
|
||
|
int i;
|
||
|
if(RUBY_DTRACE_HASH_CREATE_ENABLED()) {
|
||
|
RUBY_DTRACE_HASH_CREATE(RARRAY_LEN(ary), rb_sourcefile(), rb_sourceline());
|
||
|
}
|
||
|
for (i=0; i<RARRAY_LEN(ary); i+=2) {
|
||
|
rb_hash_aset(hash, RARRAY_PTR(ary)[i], RARRAY_PTR(ary)[i+1]);
|
||
|
}
|
||
| ... | ... | |
|
return ruby_vm_debug_ptr(GET_VM());
|
||
|
}
|
||
|
/* 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);
|
||
|
#if VM_COLLECT_USAGE_DETAILS
|
||
|
#define HASH_ASET(h, k, v) st_insert(RHASH_TBL(h), (st_data_t)(k), (st_data_t)(v))
|
||
| ... | ... | |
|
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)
|
||
|
{
|
||
| ... | ... | |
|
return Qnil;
|
||
|
}
|
||
|
#else
|
||
|
void (*ruby_vm_collect_usage_func_insn)(int insn) = NULL;
|
||
|
void (*ruby_vm_collect_usage_func_operand)(int insn, int n, VALUE op) = NULL;
|
||
|
void (*ruby_vm_collect_usage_func_register)(int reg, int isset) = NULL;
|
||
|
#endif
|
||
|
/* @param insn instruction number */
|
||
|
static void
|
||
|
vm_collect_usage_insn(int insn)
|
||
|
{
|
||
|
if (RUBY_DTRACE_INSN_ENABLED()) {
|
||
|
RUBY_DTRACE_INSN(rb_insns_name(insn));
|
||
|
}
|
||
|
if (ruby_vm_collect_usage_func_insn)
|
||
|
(*ruby_vm_collect_usage_func_insn)(insn);
|
||
|
}
|
||
| ... | ... | |
|
static void
|
||
|
vm_collect_usage_operand(int insn, int n, VALUE op)
|
||
|
{
|
||
|
if (RUBY_DTRACE_INSN_OPERAND_ENABLED()) {
|
||
|
VALUE valstr;
|
||
|
valstr = insn_operand_intern(GET_THREAD()->cfp->iseq, insn, n, op, 0, 0, 0, 0);
|
||
|
RUBY_DTRACE_INSN_OPERAND(RSTRING_PTR(valstr), rb_insns_name(insn));
|
||
|
}
|
||
|
if (ruby_vm_collect_usage_func_operand)
|
||
|
(*ruby_vm_collect_usage_func_operand)(insn, n, op);
|
||
|
}
|
||
| ... | ... | |
|
(*ruby_vm_collect_usage_func_register)(reg, isset);
|
||
|
}
|
||
|
#endif
|
||
| vm_core.h | ||
|---|---|---|
|
#include "id.h"
|
||
|
#include "method.h"
|
||
|
#include "atomic.h"
|
||
|
#include "probes.h"
|
||
|
#if defined(_WIN32)
|
||
|
#include "thread_win32.h"
|
||
| ... | ... | |
|
} \
|
||
|
} while (0)
|
||
|
#define RUBY_DTRACE_FUNC_ENTRY_HOOK(klass, id) \
|
||
|
if (RUBY_DTRACE_FUNCTION_ENTRY_ENABLED()) { \
|
||
|
const char * classname = rb_class2name((klass)); \
|
||
|
const char * methodname = rb_id2name((id)); \
|
||
|
const char * filename = rb_sourcefile(); \
|
||
|
if (classname && methodname && filename) { \
|
||
|
RUBY_DTRACE_FUNCTION_ENTRY( \
|
||
|
classname, \
|
||
|
methodname, \
|
||
|
filename, \
|
||
|
rb_sourceline()); \
|
||
|
} \
|
||
|
} \
|
||
|
#define RUBY_DTRACE_FUNC_RETURN_HOOK(klass, id) \
|
||
|
if (RUBY_DTRACE_FUNCTION_RETURN_ENABLED()) { \
|
||
|
const char * classname = rb_class2name((klass)); \
|
||
|
const char * methodname = rb_id2name((id)); \
|
||
|
const char * filename = rb_sourcefile(); \
|
||
|
if (classname && methodname && filename) { \
|
||
|
RUBY_DTRACE_FUNCTION_RETURN( \
|
||
|
classname, \
|
||
|
methodname, \
|
||
|
filename, \
|
||
|
rb_sourceline()); \
|
||
|
} \
|
||
|
} \
|
||
|
#if defined __GNUC__ && __GNUC__ >= 4
|
||
|
#pragma GCC visibility push(default)
|
||
|
#endif
|
||
| vm_dump.c | ||
|---|---|---|
|
#include "addr2line.h"
|
||
|
#include "vm_core.h"
|
||
|
#include "internal.h"
|
||
|
#include "probes.h"
|
||
|
/* see vm_insnhelper.h for the values */
|
||
|
#ifndef VMDEBUG
|
||
| vm_eval.c | ||
|---|---|---|
|
{
|
||
|
VALUE val;
|
||
|
RUBY_DTRACE_FUNC_ENTRY_HOOK(ci->defined_class, ci->mid);
|
||
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, ci->mid, ci->defined_class);
|
||
|
{
|
||
|
rb_control_frame_t *reg_cfp = th->cfp;
|
||
| ... | ... | |
|
}
|
||
|
}
|
||
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, ci->mid, ci->defined_class);
|
||
|
RUBY_DTRACE_FUNC_RETURN_HOOK(ci->defined_class, ci->mid);
|
||
|
return val;
|
||
|
}
|
||
| ... | ... | |
|
{
|
||
|
VALUE val;
|
||
|
RUBY_DTRACE_FUNC_ENTRY_HOOK(ci->defined_class, ci->mid);
|
||
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, ci->mid, ci->defined_class);
|
||
|
{
|
||
|
rb_control_frame_t *reg_cfp = th->cfp;
|
||
| ... | ... | |
|
vm_pop_frame(th);
|
||
|
}
|
||
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, ci->mid, ci->defined_class);
|
||
|
RUBY_DTRACE_FUNC_RETURN_HOOK(ci->defined_class, ci->mid);
|
||
|
return val;
|
||
|
}
|
||
| ... | ... | |
|
{
|
||
|
VALUE defined_class;
|
||
|
rb_method_entry_t *me = rb_search_method_entry(recv, mid, &defined_class);
|
||
|
rb_thread_t *th = GET_THREAD();
|
||
|
int call_status = rb_method_call_status(th, me, scope, self);
|
||
| ... | ... | |
|
if (UNLIKELY(VM_FRAME_TYPE(th->cfp) == VM_FRAME_MAGIC_CFUNC)) {
|
||
|
const rb_method_entry_t *me = th->cfp->me;
|
||
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, th->cfp->self, me->called_id, me->klass);
|
||
|
RUBY_DTRACE_FUNC_RETURN_HOOK(me->klass, me->called_id);
|
||
|
}
|
||
|
th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
|
||
| vm_exec.c | ||
|---|---|---|
|
#include <math.h>
|
||
|
void vm_analysis_insn(int insn);
|
||
|
#if VMDEBUG > 0
|
||
|
#define DECL_SC_REG(type, r, reg) register type reg_##r
|
||
| vm_insnhelper.c | ||
|---|---|---|
|
#include <math.h>
|
||
|
#include "constant.h"
|
||
|
#include "internal.h"
|
||
|
#include "probes.h"
|
||
|
/* control stack frame */
|
||
| ... | ... | |
|
int len = cfunc->argc;
|
||
|
VALUE recv = ci->recv;
|
||
|
RUBY_DTRACE_FUNC_ENTRY_HOOK(me->klass, me->called_id);
|
||
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass);
|
||
|
vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, recv, ci->defined_class,
|
||
| ... | ... | |
|
vm_pop_frame(th);
|
||
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->klass);
|
||
|
RUBY_DTRACE_FUNC_RETURN_HOOK(me->klass, me->called_id);
|
||
|
return val;
|
||
|
}
|
||
| ... | ... | |
|
if (len >= 0) rb_check_arity(ci->argc, len, len);
|
||
|
RUBY_DTRACE_FUNC_ENTRY_HOOK(me->klass, me->called_id);
|
||
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass);
|
||
|
if (!(ci->me->flag & NOEX_PROTECTED) &&
|
||
| ... | ... | |
|
val = vm_call_cfunc_latter(th, reg_cfp, ci);
|
||
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->klass);
|
||
|
RUBY_DTRACE_FUNC_RETURN_HOOK(me->klass, me->called_id);
|
||
|
return val;
|
||
|
}
|
||
| ... | ... | |
|
rb_proc_t *proc;
|
||
|
VALUE val;
|
||
|
RUBY_DTRACE_FUNC_ENTRY_HOOK(ci->me->klass, ci->me->called_id);
|
||
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_CALL, ci->recv, ci->me->called_id, ci->me->klass);
|
||
|
/* control block frame */
|
||
| ... | ... | |
|
val = vm_invoke_proc(th, proc, ci->recv, ci->defined_class, ci->argc, argv, ci->blockptr);
|
||
|
EXEC_EVENT_HOOK(th, RUBY_EVENT_RETURN, ci->recv, ci->me->called_id, ci->me->klass);
|
||
|
RUBY_DTRACE_FUNC_RETURN_HOOK(ci->me->klass, ci->me->called_id);
|
||
|
return val;
|
||
|
}
|
||
| vm_insnhelper.h | ||
|---|---|---|
|
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)))
|
||