diff --git a/.gitignore b/.gitignore index 9bc5d4f..13011b7 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ y.tab.c /exts.mk /goruby /id.h +/probes.h /largefile.h /lex.c /libruby*.* diff --git a/Makefile.in b/Makefile.in index 212afaa..9e30bba 100644 --- a/Makefile.in +++ b/Makefile.in @@ -137,6 +137,7 @@ OBJDUMP = @OBJDUMP@ OBJCOPY = @OBJCOPY@ VCS = @VCS@ VCSUP = @VCSUP@ +DTRACE = @DTRACE@ OBJEXT = @OBJEXT@ ASMEXT = S @@ -251,7 +252,7 @@ reconfig config.status: $(srcdir)/configure $(srcdir)/enc/Makefile.in \ $(srcdir)/configure: $(srcdir)/configure.in $(CHDIR) $(srcdir) && exec $(AUTOCONF) -incs: id.h +incs: id.h probes.h # Things which should be considered: # * with gperf v.s. without gperf @@ -308,6 +309,14 @@ enc/unicode/name2ctype.h: enc/unicode/name2ctype.kwd @$(ECHO) preprocessing $< $(Q) $(CPP) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -E $< > $@ +.d.h: + @$(ECHO) translating probes $< + $(Q)if test -n '$(DTRACE)'; then\ + $(DTRACE) -o $@ -h -s $<; \ + else \ + cp $(errinfo), + rb_sourcefile(), + rb_sourceline()); + } EXEC_EVENT_HOOK(th, RUBY_EVENT_RAISE, th->cfp->self, 0, 0); } } diff --git a/gc.c b/gc.c index a5ada1c..6a24d8b 100644 --- a/gc.c +++ b/gc.c @@ -21,6 +21,7 @@ #include "internal.h" #include "gc.h" #include "constant.h" +#include "probes.h" #include #include #include @@ -170,6 +171,9 @@ getrusage_time(void) } #define GC_PROF_TIMER_START do {\ + if (RUBY_GC_BEGIN_ENABLED()) { \ + RUBY_GC_BEGIN(); \ + } \ if (objspace->profile.run) {\ if (!objspace->profile.record) {\ objspace->profile.size = 1000;\ @@ -189,6 +193,9 @@ getrusage_time(void) } while(0) #define GC_PROF_TIMER_STOP(marked) do {\ + if (RUBY_GC_END_ENABLED()) { \ + RUBY_GC_END(); \ + } \ if (objspace->profile.run) {\ gc_time = getrusage_time() - gc_time;\ if (gc_time < 0) gc_time = 0;\ @@ -2377,11 +2384,17 @@ gc_lazy_sweep(rb_objspace_t *objspace) during_gc++; GC_PROF_TIMER_START; + if (RUBY_GC_SWEEP_BEGIN_ENABLED()) { + RUBY_GC_SWEEP_BEGIN(); + } GC_PROF_SWEEP_TIMER_START; if (objspace->heap.sweep_slots) { res = lazy_sweep(objspace); if (res) { + if (RUBY_GC_SWEEP_END_ENABLED()) { + RUBY_GC_SWEEP_END(); + } GC_PROF_SWEEP_TIMER_STOP; GC_PROF_SET_MALLOC_INFO; GC_PROF_TIMER_STOP(Qfalse); @@ -2403,6 +2416,9 @@ gc_lazy_sweep(rb_objspace_t *objspace) set_heaps_increment(objspace); } + if (RUBY_GC_SWEEP_BEGIN_ENABLED()) { + RUBY_GC_SWEEP_BEGIN(); + } GC_PROF_SWEEP_TIMER_START; if (!(res = lazy_sweep(objspace))) { after_gc_sweep(objspace); @@ -2411,6 +2427,9 @@ gc_lazy_sweep(rb_objspace_t *objspace) during_gc = 0; } } + if (RUBY_GC_SWEEP_END_ENABLED()) { + RUBY_GC_SWEEP_END(); + } GC_PROF_SWEEP_TIMER_STOP; GC_PROF_TIMER_STOP(Qtrue); @@ -2648,6 +2667,9 @@ gc_marks(rb_objspace_t *objspace) { struct gc_list *list; rb_thread_t *th = GET_THREAD(); + if (RUBY_GC_MARK_BEGIN_ENABLED()) { + RUBY_GC_MARK_BEGIN(); + } GC_PROF_MARK_TIMER_START; objspace->heap.live_num = 0; @@ -2691,6 +2713,9 @@ gc_marks(rb_objspace_t *objspace) gc_mark_rest(objspace); } } + if (RUBY_GC_MARK_END_ENABLED()) { + RUBY_GC_MARK_END(); + } GC_PROF_MARK_TIMER_STOP; } @@ -2715,8 +2740,14 @@ garbage_collect(rb_objspace_t *objspace) during_gc++; gc_marks(objspace); + if (RUBY_GC_SWEEP_BEGIN_ENABLED()) { + RUBY_GC_SWEEP_BEGIN(); + } GC_PROF_SWEEP_TIMER_START; gc_sweep(objspace); + if (RUBY_GC_SWEEP_END_ENABLED()) { + RUBY_GC_SWEEP_END(); + } GC_PROF_SWEEP_TIMER_STOP; GC_PROF_TIMER_STOP(Qtrue); diff --git a/insns.def b/insns.def index 6404414..a8d6c59 100644 --- a/insns.def +++ b/insns.def @@ -378,7 +378,15 @@ putstring () (VALUE val) { + if(RUBY_OBJECT_CREATE_START_ENABLED()) { + RUBY_OBJECT_CREATE_START("String", rb_sourcefile(), rb_sourceline()); + } + val = rb_str_resurrect(str); + + if(RUBY_OBJECT_CREATE_DONE_ENABLED()) { + RUBY_OBJECT_CREATE_DONE("String", rb_sourcefile(), rb_sourceline()); + } } /** @@ -450,7 +458,15 @@ newarray (...) (VALUE val) // inc += 1 - num; { + if(RUBY_OBJECT_CREATE_START_ENABLED()) { + RUBY_OBJECT_CREATE_START("Array", rb_sourcefile(), rb_sourceline()); + } + val = rb_ary_new4((long)num, STACK_ADDR_FROM_TOP(num)); + + if(RUBY_OBJECT_CREATE_DONE_ENABLED()) { + RUBY_OBJECT_CREATE_DONE("Array", rb_sourcefile(), rb_sourceline()); + } POPN(num); } @@ -592,8 +608,17 @@ newhash (VALUE val) // inc += 1 - num; { rb_num_t i; + + if(RUBY_OBJECT_CREATE_START_ENABLED()) { + RUBY_OBJECT_CREATE_START("Hash", rb_sourcefile(), rb_sourceline()); + } + val = rb_hash_new(); + if(RUBY_OBJECT_CREATE_DONE_ENABLED()) { + RUBY_OBJECT_CREATE_DONE("Hash", rb_sourcefile(), rb_sourceline()); + } + for (i = num; i > 0; i -= 2) { const VALUE v = TOPN(i - 2); const VALUE k = TOPN(i - 1); @@ -875,6 +900,40 @@ trace { rb_event_flag_t flag = (rb_event_flag_t)nf; + if (RUBY_LINE_ENABLED()) { + if (flag == RUBY_EVENT_LINE) { + RUBY_LINE(rb_sourcefile(), rb_sourceline()); + } + } + + if (RUBY_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_FUNCTION_ENTRY( + rb_class2name(klass), + rb_id2name(called_id), + rb_sourcefile(), + rb_sourceline()); + } + } + if (RUBY_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_FUNCTION_RETURN( + rb_class2name(klass), + rb_id2name(called_id), + rb_sourcefile(), + rb_sourceline()); + } + } EXEC_EVENT_HOOK(th, flag, GET_SELF(), 0, 0 /* TODO: id, klass */); } diff --git a/load.c b/load.c index 26d8568..86e1bc8 100644 --- a/load.c +++ b/load.c @@ -7,6 +7,7 @@ #include "internal.h" #include "dln.h" #include "eval_intern.h" +#include "probes.h" VALUE ruby_dln_librefs; @@ -374,6 +375,14 @@ rb_f_load(int argc, VALUE *argv) VALUE fname, wrap, path; rb_scan_args(argc, argv, "11", &fname, &wrap); + + if(RUBY_LOAD_ENTRY_ENABLED()) { + RUBY_LOAD_ENTRY( + StringValuePtr(fname), + rb_sourcefile(), + rb_sourceline()); + } + path = rb_find_file(FilePathValue(fname)); if (!path) { if (!rb_file_load_ok(RSTRING_PTR(fname))) @@ -381,6 +390,11 @@ rb_f_load(int argc, VALUE *argv) path = fname; } rb_load_internal(path, RTEST(wrap)); + + if(RUBY_LOAD_RETURN_ENABLED()) { + RUBY_LOAD_RETURN(StringValuePtr(fname)); + } + return Qtrue; } @@ -614,6 +628,13 @@ rb_require_safe(VALUE fname, int safe) } volatile saved; char *volatile ftptr = 0; + if(RUBY_REQUIRE_ENTRY_ENABLED()) { + RUBY_REQUIRE_ENTRY( + StringValuePtr(fname), + rb_sourcefile(), + rb_sourceline()); + } + PUSH_TAG(); saved.safe = rb_safe_level(); if ((state = EXEC_TAG()) == 0) { @@ -660,6 +681,10 @@ rb_require_safe(VALUE fname, int safe) th->errinfo = errinfo; + if(RUBY_REQUIRE_RETURN_ENABLED()) { + RUBY_REQUIRE_RETURN(StringValuePtr(fname)); + } + return result; } diff --git a/object.c b/object.c index 3c4445c..adf0706 100644 --- a/object.c +++ b/object.c @@ -22,6 +22,7 @@ #include #include "constant.h" #include "internal.h" +#include "probes.h" VALUE rb_cBasicObject; VALUE rb_mKernel; @@ -1675,7 +1676,23 @@ rb_obj_alloc(VALUE klass) if (FL_TEST(klass, FL_SINGLETON)) { rb_raise(rb_eTypeError, "can't create instance of singleton class"); } + + if (RUBY_OBJECT_CREATE_START_ENABLED()) { + const char * file = rb_sourcefile(); + RUBY_OBJECT_CREATE_START(rb_class2name(klass), + file ? file : "", + rb_sourceline()); + } + obj = rb_funcall(klass, ID_ALLOCATOR, 0, 0); + + if (RUBY_OBJECT_CREATE_DONE_ENABLED()) { + const char * file = rb_sourcefile(); + RUBY_OBJECT_CREATE_DONE(rb_class2name(klass), + file ? file : "", + rb_sourceline()); + } + if (rb_obj_class(obj) != rb_class_real(klass)) { rb_raise(rb_eTypeError, "wrong instance allocation"); } diff --git a/probes.d b/probes.d new file mode 100644 index 0000000..b8b1eb8 --- /dev/null +++ b/probes.d @@ -0,0 +1,30 @@ +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__start(const char *, const char *, int); + probe object__create__done(const char *, const char *, int); + + probe gc__begin(); + probe gc__end(); + probe gc__mark__begin(); + probe gc__mark__end(); + probe gc__sweep__begin(); + probe gc__sweep__end(); + + probe line(const char *, int); +}; + +#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 diff --git a/test/dtrace/dummy.rb b/test/dtrace/dummy.rb new file mode 100644 index 0000000..e856142 --- /dev/null +++ b/test/dtrace/dummy.rb @@ -0,0 +1 @@ +# this is a dummy file used by test/dtrace/test_require.rb diff --git a/test/dtrace/helper.rb b/test/dtrace/helper.rb new file mode 100644 index 0000000..ef691b5 --- /dev/null +++ b/test/dtrace/helper.rb @@ -0,0 +1,33 @@ +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 diff --git a/test/dtrace/test_function_entry.rb b/test/dtrace/test_function_entry.rb new file mode 100644 index 0000000..f8f9c43 --- /dev/null +++ b/test/dtrace/test_function_entry.rb @@ -0,0 +1,56 @@ +require 'dtrace/helper' + +module DTrace + class TestFunctionEntry < TestCase + def test_function_entry + probe = <<-eoprobe +ruby$target:::function-entry +{ + 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 +{ + 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 diff --git a/test/dtrace/test_gc.rb b/test/dtrace/test_gc.rb new file mode 100644 index 0000000..b33ce81 --- /dev/null +++ b/test/dtrace/test_gc.rb @@ -0,0 +1,28 @@ +require 'dtrace/helper' + +module DTrace + class TestGC < TestCase + %w{ + gc-begin + gc-end + 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 diff --git a/test/dtrace/test_line.rb b/test/dtrace/test_line.rb new file mode 100644 index 0000000..10af867 --- /dev/null +++ b/test/dtrace/test_line.rb @@ -0,0 +1,18 @@ +require 'dtrace/helper' + +module DTrace + class TestLine < TestCase + def test_line + probe = "ruby$target:::line { printf(\"%s %d\\n\", copyinstr(arg0), arg1); }" + program = 'x = 2; 10.times { x = x ** 2 }' + + trap_probe(probe, program) { |_, rbpath, saw| + saw = saw.map(&:split).find_all { |file, _| + file == rbpath + } + assert_operator saw.length, :>=, 10 + saw.each { |f,l| assert_equal '1', l } + } + end + end +end diff --git a/test/dtrace/test_load.rb b/test/dtrace/test_load.rb new file mode 100644 index 0000000..8ac7e39 --- /dev/null +++ b/test/dtrace/test_load.rb @@ -0,0 +1,52 @@ +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 diff --git a/test/dtrace/test_object_create_done.rb b/test/dtrace/test_object_create_done.rb new file mode 100644 index 0000000..fa91415 --- /dev/null +++ b/test/dtrace/test_object_create_done.rb @@ -0,0 +1,68 @@ +require 'dtrace/helper' + +module DTrace + class TestObjectCreateDone < TestCase + def test_object + 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_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_hash_lit + trap_probe(probe, '{}') { |_,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_array_lit + trap_probe(probe, '[]') { |_,rbfile,saw| + saw = saw.map(&:split).find_all { |klass, file, line| + file == rbfile + } + assert_equal(%w{ Array }, saw.map(&:first)) + assert_equal([rbfile], saw.map { |line| line[1] }) + assert_equal(['1'], saw.map { |line| line[2] }) + } + end + + def test_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 + <<-eoprobe +ruby$target:::object-create-done +{ + printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2); +} + eoprobe + end + end +end diff --git a/test/dtrace/test_object_create_start.rb b/test/dtrace/test_object_create_start.rb new file mode 100644 index 0000000..0a141b0 --- /dev/null +++ b/test/dtrace/test_object_create_start.rb @@ -0,0 +1,68 @@ +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_hash_lit + trap_probe(probe, '{}') { |_,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_array_lit + trap_probe(probe, '[]') { |_,rbfile,saw| + saw = saw.map(&:split).find_all { |klass, file, line| + file == rbfile + } + assert_equal(%w{ Array }, 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 + <<-eoprobe +ruby$target:::object-create-start +{ + printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2); +} + eoprobe + end + end +end diff --git a/test/dtrace/test_raise.rb b/test/dtrace/test_raise.rb new file mode 100644 index 0000000..fedae8e --- /dev/null +++ b/test/dtrace/test_raise.rb @@ -0,0 +1,29 @@ +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 diff --git a/test/dtrace/test_require.rb b/test/dtrace/test_require.rb new file mode 100644 index 0000000..c50037e --- /dev/null +++ b/test/dtrace/test_require.rb @@ -0,0 +1,34 @@ +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 diff --git a/tool/dummy_probe_gen.rb b/tool/dummy_probe_gen.rb new file mode 100644 index 0000000..afdf2ac --- /dev/null +++ b/tool/dummy_probe_gen.rb @@ -0,0 +1,18 @@ +system("dtrace -o probes.h -h -s probes.d") +funcs = File.readlines('probes.h').grep(/^#define\s*(RUBY_.*?)\s*\\$/) { + function = $1 + if function =~ /\(\)$/ + "#define #{function} 0" + else + "#defune #{function}" + end +} + +puts <<-eoheader +#ifndef _PROBES_H +#define _PROBES_H + +#{funcs.join "\n"} + +#endif /* _PROBES_H */ +eoheader diff --git a/vm_eval.c b/vm_eval.c index 6c26b97..26f1b94 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -68,6 +68,18 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, } case VM_METHOD_TYPE_NOTIMPLEMENTED: case VM_METHOD_TYPE_CFUNC: { + if (RUBY_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_FUNCTION_ENTRY( + classname, + methodname, + filename, + rb_sourceline()); + } + } EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, id, klass); { rb_control_frame_t *reg_cfp = th->cfp; @@ -83,6 +95,18 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, } vm_pop_frame(th); } + if (RUBY_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_FUNCTION_RETURN( + classname, + methodname, + filename, + rb_sourceline()); + } + } EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, id, klass); break; } @@ -140,6 +164,7 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, rb_bug("vm_call0: unsupported method type (%d)", def->type); val = Qundef; } + RUBY_VM_CHECK_INTS(); return val; } diff --git a/vm_insnhelper.c b/vm_insnhelper.c index c5a3bd0..4fa8f9e 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -13,6 +13,7 @@ #include #include "constant.h" #include "internal.h" +#include "probes.h" /* control stack frame */ @@ -444,6 +445,18 @@ vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, const rb_method_definition_t *def = me->def; rb_control_frame_t *cfp; + if (RUBY_FUNCTION_ENTRY_ENABLED()) { + const char * classname = rb_class2name(me->klass); + const char * methodname = rb_id2name(me->called_id); + const char * filename = rb_sourcefile(); + if (classname && methodname && filename) { + RUBY_FUNCTION_ENTRY( + classname, + methodname, + filename, + rb_sourceline()); + } + } EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass); cfp = vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, @@ -459,6 +472,18 @@ vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, vm_pop_frame(th); + if (RUBY_FUNCTION_RETURN_ENABLED()) { + const char * classname = rb_class2name(me->klass); + const char * methodname = rb_id2name(me->called_id); + const char * filename = rb_sourcefile(); + if (classname && methodname && filename) { + RUBY_FUNCTION_RETURN( + classname, + methodname, + filename, + rb_sourceline()); + } + } EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->klass); return val; @@ -471,6 +496,13 @@ vm_call_bmethod(rb_thread_t *th, VALUE recv, int argc, const VALUE *argv, rb_proc_t *proc; VALUE val; + if (RUBY_FUNCTION_ENTRY_ENABLED()) { + RUBY_FUNCTION_ENTRY( + rb_class2name(me->klass), + rb_id2name(me->called_id), + rb_sourcefile(), + rb_sourceline()); + } EXEC_EVENT_HOOK(th, RUBY_EVENT_CALL, recv, me->called_id, me->klass); /* control block frame */ @@ -478,6 +510,13 @@ vm_call_bmethod(rb_thread_t *th, VALUE recv, int argc, const VALUE *argv, GetProcPtr(me->def->body.proc, proc); val = rb_vm_invoke_proc(th, proc, recv, argc, argv, blockptr); + if (RUBY_FUNCTION_RETURN_ENABLED()) { + RUBY_FUNCTION_RETURN( + rb_class2name(me->klass), + rb_id2name(me->called_id), + rb_sourcefile(), + rb_sourceline()); + } EXEC_EVENT_HOOK(th, RUBY_EVENT_RETURN, recv, me->called_id, me->klass); return val;