diff options
Diffstat (limited to 'test/dtrace')
| -rw-r--r-- | test/dtrace/helper.rb | 106 | ||||
| -rw-r--r-- | test/dtrace/test_array_create.rb | 8 | ||||
| -rw-r--r-- | test/dtrace/test_function_entry.rb | 9 | ||||
| -rw-r--r-- | test/dtrace/test_hash_create.rb | 6 | ||||
| -rw-r--r-- | test/dtrace/test_method_cache.rb | 2 | ||||
| -rw-r--r-- | test/dtrace/test_require.rb | 6 | ||||
| -rw-r--r-- | test/dtrace/test_singleton_function.rb | 9 | ||||
| -rw-r--r-- | test/dtrace/test_string.rb | 6 |
8 files changed, 119 insertions, 33 deletions
diff --git a/test/dtrace/helper.rb b/test/dtrace/helper.rb index 539cce9d6f..9e8c7ecd52 100644 --- a/test/dtrace/helper.rb +++ b/test/dtrace/helper.rb @@ -10,26 +10,62 @@ elsif (sudo = ENV["SUDO"]) and !sudo.empty? and (`#{sudo} echo ok` rescue false) else ok = false end + +impl = :dtrace + +# GNU/Linux distros with Systemtap support allows unprivileged users +# in the stapusr and statdev groups to work. +if RUBY_PLATFORM =~ /linux/ + impl = :stap + begin + require 'etc' + ok = (%w[stapusr stapdev].map {|g|(Etc.getgrnam(g) || raise(ArgumentError)).gid} & Process.groups).size == 2 + rescue LoadError, ArgumentError + end unless ok +end + if ok case RUBY_PLATFORM when /darwin/i begin require 'pty' rescue LoadError - ok = false end end end -ok &= (`dtrace -V` rescue false) + +# use miniruby to reduce the amount of trace data we don't care about +rubybin = "miniruby#{RbConfig::CONFIG["EXEEXT"]}" +rubybin = File.join(File.dirname(EnvUtil.rubybin), rubybin) +rubybin = EnvUtil.rubybin unless File.executable?(rubybin) + +# make sure ruby was built with --enable-dtrace and we can run +# dtrace(1) or stap(1): +cmd = "#{rubybin} --disable=gems -eexit" +case impl +when :dtrace; cmd = %W(dtrace -l -n ruby$target:::gc-sweep-end -c #{cmd}) +when :stap; cmd = %W(stap -l process.mark("gc__sweep__end") -c #{cmd}) +else + warn "don't know how to check if built with #{impl} support" + cmd = false +end + +NEEDED_ENVS = [RbConfig::CONFIG["LIBPATHENV"], "RUBY", "RUBYOPT"].compact + +if cmd and ok + sudocmd = [] + if sudo + sudocmd << sudo + NEEDED_ENVS.each {|name| val = ENV[name] and sudocmd << "#{name}=#{val}"} + end + ok = system(*sudocmd, *cmd, err: IO::NULL, out: IO::NULL) +end + module DTrace class TestCase < Test::Unit::TestCase INCLUDE = File.expand_path('..', File.dirname(__FILE__)) - case RUBY_PLATFORM - when /solaris/i - # increase bufsize to 8m (default 4m on Solaris) - DTRACE_CMD = %w[dtrace -b 8m] - when /darwin/i + if RUBY_PLATFORM =~ /darwin/i READ_PROBES = proc do |cmd| lines = nil PTY.spawn(*cmd) do |io, _, pid| @@ -37,7 +73,40 @@ module DTrace Process.wait(pid) end lines + end if defined?(PTY) + end + + # only handles simple cases, use a Hash for d_program + # if there are more complex cases + def dtrace2systemtap(d_program) + translate = lambda do |str| + # dtrace starts args with '0', systemtap with '1' and prefixes '$' + str = str.gsub(/\barg(\d+)/) { "$arg#{$1.to_i + 1}" } + # simple function mappings: + str.gsub!(/\bcopyinstr\b/, 'user_string') + str.gsub!(/\bstrstr\b/, 'isinstr') + str + end + out = '' + cond = nil + d_program.split(/^/).each do |l| + case l + when /\bruby\$target:::([a-z-]+)/ + name = $1.gsub(/-/, '__') + out << %Q{probe process.mark("#{name}")\n} + when %r{/(.+)/} + cond = translate.call($1) + when "{\n" + out << l + out << "if (#{cond}) {\n" if cond + when "}\n" + out << "}\n" if cond + out << l + else + out << translate.call(l) + end end + out end DTRACE_CMD ||= %w[dtrace] @@ -46,10 +115,13 @@ module DTrace IO.popen(cmd, err: [:child, :out], &:readlines) end - exeext = Regexp.quote(RbConfig::CONFIG["EXEEXT"]) - RUBYBIN = EnvUtil.rubybin.sub(/\/ruby-runner(?=#{exeext}\z)/, '/miniruby') - def trap_probe d_program, ruby_program + if Hash === d_program + d_program = d_program[IMPL] or + omit "#{d_program} not implemented for #{IMPL}" + elsif String === d_program && IMPL == :stap + d_program = dtrace2systemtap(d_program) + end d = Tempfile.new(%w'probe .d') d.write d_program d.flush @@ -60,11 +132,15 @@ module DTrace d_path = d.path rb_path = rb.path - - cmd = [*DTRACE_CMD, "-q", "-s", d_path, "-c", "#{RUBYBIN} -I#{INCLUDE} #{rb_path}"] + cmd = "#{RUBYBIN} --disable=gems -I#{INCLUDE} #{rb_path}" + if IMPL == :stap + cmd = %W(stap #{d_path} -c #{cmd}) + else + cmd = [*DTRACE_CMD, "-q", "-s", d_path, "-c", cmd ] + end if sudo = @@sudo - [RbConfig::CONFIG["LIBPATHENV"], "RUBY", "RUBYOPT"].each do |name| - if name and val = ENV[name] + NEEDED_ENVS.each do |name| + if val = ENV[name] cmd.unshift("#{name}=#{val}") end end @@ -80,4 +156,6 @@ end if ok if ok DTrace::TestCase.class_variable_set(:@@sudo, sudo) + DTrace::TestCase.const_set(:IMPL, impl) + DTrace::TestCase.const_set(:RUBYBIN, rubybin) end diff --git a/test/dtrace/test_array_create.rb b/test/dtrace/test_array_create.rb index 44d4657b61..1bf20085ba 100644 --- a/test/dtrace/test_array_create.rb +++ b/test/dtrace/test_array_create.rb @@ -14,11 +14,11 @@ module DTrace end def test_many_lit - trap_probe(probe, '[1,2,3,4]') { |_,rbfile,saw| - saw = saw.map(&:split).find_all { |num, file, line| + trap_probe(probe, '[1,2,3,4]') { |_,rbfile,orig| + saw = orig.map(&:split).find_all { |num, file, line| file == rbfile && num == '4' && line == '1' } - assert_operator saw.length, :>, 0 + assert_operator saw.length, :>, 0, orig } end @@ -26,7 +26,7 @@ module DTrace def probe type = 'array' <<-eoprobe ruby$target:::#{type}-create -/arg1/ +/arg1 && arg2/ { printf("%d %s %d\\n", arg0, copyinstr(arg1), arg2); } diff --git a/test/dtrace/test_function_entry.rb b/test/dtrace/test_function_entry.rb index fc07ccc455..e2395ab15a 100644 --- a/test/dtrace/test_function_entry.rb +++ b/test/dtrace/test_function_entry.rb @@ -17,8 +17,8 @@ ruby$target:::method-entry row.first == 'Foo' && row[1] == 'foo' } - assert_equal 10, foo_calls.length - line = '2' + assert_equal 10, foo_calls.length, probes + line = '3' foo_calls.each { |f| assert_equal line, f[3] } foo_calls.each { |f| assert_equal rb_file, f[2] } } @@ -38,8 +38,8 @@ ruby$target:::method-return row.first == 'Foo' && row[1] == 'foo' } - assert_equal 10, foo_calls.length - line = '2' + assert_equal 10, foo_calls.length, probes.inspect + line = '3' foo_calls.each { |f| assert_equal line, f[3] } foo_calls.each { |f| assert_equal rb_file, f[2] } } @@ -77,6 +77,7 @@ ruby$target:::method-return private def ruby_program <<-eoruby + TracePoint.new{}.__enable(nil, nil, Thread.current) class Foo def foo; end end diff --git a/test/dtrace/test_hash_create.rb b/test/dtrace/test_hash_create.rb index 83a4d0062c..603ee21872 100644 --- a/test/dtrace/test_hash_create.rb +++ b/test/dtrace/test_hash_create.rb @@ -22,11 +22,11 @@ module DTrace end def test_hash_lit_elements - trap_probe(probe, '{ :foo => :bar }') { |_,rbfile,saw| - saw = saw.map(&:split).find_all { |num, file, line| + trap_probe(probe, '{ :foo => :bar }') { |_,rbfile,orig| + saw = orig.map(&:split).find_all { |num, file, line| file == rbfile && num == '2' } - assert_operator saw.length, :>, 0 + assert_operator saw.length, :>, 0, orig } end diff --git a/test/dtrace/test_method_cache.rb b/test/dtrace/test_method_cache.rb index a101b5ddec..88b927464a 100644 --- a/test/dtrace/test_method_cache.rb +++ b/test/dtrace/test_method_cache.rb @@ -19,7 +19,7 @@ module DTrace def probe <<-eoprobe ruby$target:::method-cache-clear -/arg1/ +/arg1 && arg2/ { printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2); } diff --git a/test/dtrace/test_require.rb b/test/dtrace/test_require.rb index 9fa6c0e87c..da5c08f7fc 100644 --- a/test/dtrace/test_require.rb +++ b/test/dtrace/test_require.rb @@ -25,6 +25,12 @@ ruby$target:::require-return printf("%s\\n", copyinstr(arg0)); } 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 private diff --git a/test/dtrace/test_singleton_function.rb b/test/dtrace/test_singleton_function.rb index 3698a02c93..bad1fa0692 100644 --- a/test/dtrace/test_singleton_function.rb +++ b/test/dtrace/test_singleton_function.rb @@ -17,8 +17,8 @@ ruby$target:::method-entry row.first == 'Foo' && row[1] == 'foo' } - assert_equal 10, foo_calls.length - line = '2' + assert_equal 10, foo_calls.length, probes.inspect + line = '3' foo_calls.each { |f| assert_equal line, f[3] } foo_calls.each { |f| assert_equal rb_file, f[2] } } @@ -37,8 +37,8 @@ ruby$target:::method-return row.first == 'Foo' && row[1] == 'foo' } - assert_equal 10, foo_calls.length - line = '2' + assert_equal 10, foo_calls.length, probes.inspect + line = '3' foo_calls.each { |f| assert_equal line, f[3] } foo_calls.each { |f| assert_equal rb_file, f[2] } } @@ -46,6 +46,7 @@ ruby$target:::method-return def ruby_program <<-eoruby + TracePoint.new{}.__enable(nil, nil, Thread.current) class Foo def self.foo; end end diff --git a/test/dtrace/test_string.rb b/test/dtrace/test_string.rb index 407280b1fc..a72f989f63 100644 --- a/test/dtrace/test_string.rb +++ b/test/dtrace/test_string.rb @@ -4,11 +4,11 @@ require_relative 'helper' module DTrace class TestStringProbes < TestCase def test_object_create_start_string_lit - trap_probe(probe, '"omglolwutbbq"') { |_,rbfile,saw| - saw = saw.map(&:split).find_all { |klass, file, line, len| + trap_probe(probe, '"omglolwutbbq"') { |_,rbfile,orig| + saw = orig.map(&:split).find_all { |klass, file, line, len| file == rbfile && len == '12' && line == '1' } - assert_equal(%w{ String }, saw.map(&:first)) + assert_equal(%w{ String }, saw.map(&:first), orig.inspect) assert_equal([rbfile], saw.map { |line| line[1] }) assert_equal(['1'], saw.map { |line| line[2] }) } |
