summaryrefslogtreecommitdiff
path: root/test/dtrace
diff options
context:
space:
mode:
Diffstat (limited to 'test/dtrace')
-rw-r--r--test/dtrace/dummy.rb2
-rw-r--r--test/dtrace/helper.rb161
-rw-r--r--test/dtrace/test_array_create.rb36
-rw-r--r--test/dtrace/test_cmethod.rb50
-rw-r--r--test/dtrace/test_function_entry.rb89
-rw-r--r--test/dtrace/test_gc.rb27
-rw-r--r--test/dtrace/test_hash_create.rb53
-rw-r--r--test/dtrace/test_load.rb53
-rw-r--r--test/dtrace/test_method_cache.rb29
-rw-r--r--test/dtrace/test_object_create_start.rb36
-rw-r--r--test/dtrace/test_raise.rb30
-rw-r--r--test/dtrace/test_require.rb41
-rw-r--r--test/dtrace/test_singleton_function.rb57
-rw-r--r--test/dtrace/test_string.rb28
14 files changed, 692 insertions, 0 deletions
diff --git a/test/dtrace/dummy.rb b/test/dtrace/dummy.rb
new file mode 100644
index 0000000000..932cb4e625
--- /dev/null
+++ b/test/dtrace/dummy.rb
@@ -0,0 +1,2 @@
+# frozen_string_literal: false
+# 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 0000000000..9e8c7ecd52
--- /dev/null
+++ b/test/dtrace/helper.rb
@@ -0,0 +1,161 @@
+# -*- coding: us-ascii -*-
+# frozen_string_literal: false
+require 'test/unit'
+require 'tempfile'
+
+if Process.euid == 0
+ ok = true
+elsif (sudo = ENV["SUDO"]) and !sudo.empty? and (`#{sudo} echo ok` rescue false)
+ ok = true
+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
+ end
+ end
+end
+
+# 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__))
+
+ if RUBY_PLATFORM =~ /darwin/i
+ READ_PROBES = proc do |cmd|
+ lines = nil
+ PTY.spawn(*cmd) do |io, _, pid|
+ lines = io.readlines.each {|line| line.sub!(/\r$/, "")}
+ 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]
+
+ READ_PROBES ||= proc do |cmd|
+ IO.popen(cmd, err: [:child, :out], &:readlines)
+ end
+
+ 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
+
+ rb = Tempfile.new(%w'probed .rb')
+ rb.write ruby_program
+ rb.flush
+
+ d_path = d.path
+ rb_path = 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
+ NEEDED_ENVS.each do |name|
+ if val = ENV[name]
+ cmd.unshift("#{name}=#{val}")
+ end
+ end
+ cmd.unshift(sudo)
+ end
+ probes = READ_PROBES.(cmd)
+ d.close(true)
+ rb.close(true)
+ yield(d_path, rb_path, probes)
+ end
+ end
+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
new file mode 100644
index 0000000000..1bf20085ba
--- /dev/null
+++ b/test/dtrace/test_array_create.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: false
+require_relative '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,orig|
+ saw = orig.map(&:split).find_all { |num, file, line|
+ file == rbfile && num == '4' && line == '1'
+ }
+ assert_operator saw.length, :>, 0, orig
+ }
+ end
+
+ private
+ def probe type = 'array'
+ <<-eoprobe
+ruby$target:::#{type}-create
+/arg1 && arg2/
+{
+ printf("%d %s %d\\n", arg0, copyinstr(arg1), arg2);
+}
+ eoprobe
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/test/dtrace/test_cmethod.rb b/test/dtrace/test_cmethod.rb
new file mode 100644
index 0000000000..a0aa1b646b
--- /dev/null
+++ b/test/dtrace/test_cmethod.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: false
+require_relative 'helper'
+
+module DTrace
+ class TestCMethod < TestCase
+ def test_entry
+ probe = <<-eoprobe
+ruby$target:::cmethod-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[1] == 'times'
+ }
+
+ assert_equal 1, foo_calls.length
+ }
+ end
+
+ def test_exit
+ probe = <<-eoprobe
+ruby$target:::cmethod-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[1] == 'times'
+ }
+
+ assert_equal 1, foo_calls.length
+ }
+ end
+
+ def ruby_program
+ <<-eoruby
+ class Foo
+ def self.foo; end
+ end
+ 10.times { Foo.foo }
+ eoruby
+ end
+ end
+end if defined?(DTrace::TestCase)
+
diff --git a/test/dtrace/test_function_entry.rb b/test/dtrace/test_function_entry.rb
new file mode 100644
index 0000000000..e2395ab15a
--- /dev/null
+++ b/test/dtrace/test_function_entry.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: false
+require_relative 'helper'
+
+module DTrace
+ class TestFunctionEntry < TestCase
+ def test_function_entry
+ probe = <<-eoprobe
+ruby$target:::method-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, probes
+ line = '3'
+ 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:::method-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, probes.inspect
+ line = '3'
+ foo_calls.each { |f| assert_equal line, f[3] }
+ foo_calls.each { |f| assert_equal rb_file, f[2] }
+ }
+ end
+
+ def test_return_from_raise
+ program = <<-eoruby
+ class Foo
+ def bar; raise; end
+ def baz
+ bar
+ rescue
+ end
+ end
+
+ Foo.new.baz
+ eoruby
+
+ probe = <<-eoprobe
+ruby$target:::method-return
+/arg0 && arg1 && arg2/
+{
+ printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
+}
+ eoprobe
+
+ trap_probe(probe, program) { |d_file, rb_file, probes|
+ foo_calls = probes.map { |line| line.split }.find_all { |row|
+ row.first == 'Foo' && row[1] == 'bar'
+ }
+ assert foo_calls.any?
+ }
+ end
+
+ private
+ def ruby_program
+ <<-eoruby
+ TracePoint.new{}.__enable(nil, nil, Thread.current)
+ class Foo
+ def foo; end
+ end
+ x = Foo.new
+ 10.times { x.foo }
+ eoruby
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/test/dtrace/test_gc.rb b/test/dtrace/test_gc.rb
new file mode 100644
index 0000000000..77de7998dd
--- /dev/null
+++ b/test/dtrace/test_gc.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: false
+require_relative '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 if defined?(DTrace::TestCase)
diff --git a/test/dtrace/test_hash_create.rb b/test/dtrace/test_hash_create.rb
new file mode 100644
index 0000000000..603ee21872
--- /dev/null
+++ b/test/dtrace/test_hash_create.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: false
+require_relative '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,orig|
+ saw = orig.map(&:split).find_all { |num, file, line|
+ file == rbfile && num == '2'
+ }
+ assert_operator saw.length, :>, 0, orig
+ }
+ 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 if defined?(DTrace::TestCase)
diff --git a/test/dtrace/test_load.rb b/test/dtrace/test_load.rb
new file mode 100644
index 0000000000..1c208bd488
--- /dev/null
+++ b/test/dtrace/test_load.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: false
+require_relative '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 if defined?(DTrace::TestCase)
diff --git a/test/dtrace/test_method_cache.rb b/test/dtrace/test_method_cache.rb
new file mode 100644
index 0000000000..88b927464a
--- /dev/null
+++ b/test/dtrace/test_method_cache.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: false
+require_relative 'helper'
+
+module DTrace
+ class TestMethodCacheClear < TestCase
+ def test_method_cache_clear
+ trap_probe(probe, <<-code) do |_,rbfile,lines|
+ class String; end
+ class String; def abc() end end
+ class Object; def abc() end end
+ code
+ assert_not_include lines, "String #{rbfile} 1\n"
+ assert_include lines, "String #{rbfile} 2\n"
+ assert_include lines, "global #{rbfile} 3\n"
+ end
+ end
+
+ private
+ def probe
+ <<-eoprobe
+ruby$target:::method-cache-clear
+/arg1 && arg2/
+{
+ printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
+}
+ eoprobe
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/test/dtrace/test_object_create_start.rb b/test/dtrace/test_object_create_start.rb
new file mode 100644
index 0000000000..81f8ed2110
--- /dev/null
+++ b/test/dtrace/test_object_create_start.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: false
+require_relative '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
+
+ private
+ def probe
+ <<-eoprobe
+ruby$target:::object-create
+{
+ printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
+}
+ eoprobe
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/test/dtrace/test_raise.rb b/test/dtrace/test_raise.rb
new file mode 100644
index 0000000000..81e64e8c16
--- /dev/null
+++ b/test/dtrace/test_raise.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: false
+require_relative '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 if defined?(DTrace::TestCase)
diff --git a/test/dtrace/test_require.rb b/test/dtrace/test_require.rb
new file mode 100644
index 0000000000..da5c08f7fc
--- /dev/null
+++ b/test/dtrace/test_require.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: false
+require_relative '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
+ 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
+ def ruby_program
+ "10.times { require 'dtrace/dummy' }"
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/test/dtrace/test_singleton_function.rb b/test/dtrace/test_singleton_function.rb
new file mode 100644
index 0000000000..bad1fa0692
--- /dev/null
+++ b/test/dtrace/test_singleton_function.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: false
+require_relative 'helper'
+
+module DTrace
+ class TestSingletonFunctionEntry < TestCase
+ def test_entry
+ probe = <<-eoprobe
+ruby$target:::method-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 == 'Foo' && row[1] == 'foo'
+ }
+
+ 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] }
+ }
+ end
+
+ def test_exit
+ probe = <<-eoprobe
+ruby$target:::method-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, probes.inspect
+ line = '3'
+ foo_calls.each { |f| assert_equal line, f[3] }
+ foo_calls.each { |f| assert_equal rb_file, f[2] }
+ }
+ end
+
+ def ruby_program
+ <<-eoruby
+ TracePoint.new{}.__enable(nil, nil, Thread.current)
+ class Foo
+ def self.foo; end
+ end
+ 10.times { Foo.foo }
+ eoruby
+ end
+ end
+end if defined?(DTrace::TestCase)
diff --git a/test/dtrace/test_string.rb b/test/dtrace/test_string.rb
new file mode 100644
index 0000000000..a72f989f63
--- /dev/null
+++ b/test/dtrace/test_string.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: false
+require_relative 'helper'
+
+module DTrace
+ class TestStringProbes < TestCase
+ def test_object_create_start_string_lit
+ 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), orig.inspect)
+ assert_equal([rbfile], saw.map { |line| line[1] })
+ assert_equal(['1'], saw.map { |line| line[2] })
+ }
+ end
+
+ private
+ def probe
+ <<-eoprobe
+ruby$target:::string-create
+/arg1/
+{
+ printf("String %s %d %d\\n", copyinstr(arg1), arg2, arg0);
+}
+ eoprobe
+ end
+ end
+end if defined?(DTrace::TestCase)