diff --git a/common.mk b/common.mk index 916484a6fec..53e832c6370 100644 --- a/common.mk +++ b/common.mk @@ -1975,7 +1975,9 @@ gc/distclean gc/realclean:: modular-gc-precheck: modular-gc: probes.h gc/Makefile - $(Q) $(RUNRUBY) $(srcdir)/ext/extmk.rb \ + $(Q) DTRACE='$(DTRACE)' DTRACE_EXT='$(DTRACE_EXT)' \ + DTRACE_OBJ='$(DTRACE_OBJ)' DTRACE_REBUILD='$(DTRACE_REBUILD)' \ + $(RUNRUBY) $(srcdir)/ext/extmk.rb \ $(SCRIPT_ARGS) \ --make='$(MAKE)' --make-flags="V=$(V) MINIRUBY='$(MINIRUBY)'" \ --gnumake=$(gnumake) --extflags="$(EXTLDFLAGS)" \ diff --git a/gc/default/default.c b/gc/default/default.c index 27b9646d975..611f404fbb4 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -34,9 +34,7 @@ #include "gc/gc.h" #include "gc/gc_impl.h" -#ifndef BUILDING_MODULAR_GC -# include "probes.h" -#endif +#include "probes.h" #ifdef BUILDING_MODULAR_GC # define RB_DEBUG_COUNTER_INC(_name) ((void)0) @@ -8940,12 +8938,8 @@ gc_prof_timer_stop(rb_objspace_t *objspace) } } -#ifdef BUILDING_MODULAR_GC -# define RUBY_DTRACE_GC_HOOK(name) -#else -# define RUBY_DTRACE_GC_HOOK(name) \ +#define RUBY_DTRACE_GC_HOOK(name) \ do {if (RUBY_DTRACE_GC_##name##_ENABLED()) RUBY_DTRACE_GC_##name();} while (0) -#endif static inline void gc_prof_mark_timer_start(rb_objspace_t *objspace) diff --git a/gc/extconf_base.rb b/gc/extconf_base.rb index 2a224b9b0e3..4a99a809105 100644 --- a/gc/extconf_base.rb +++ b/gc/extconf_base.rb @@ -4,11 +4,49 @@ require "mkmf" srcdir = File.join(__dir__, "..") $INCFLAGS << " -I#{srcdir}" - $CPPFLAGS << " -DBUILDING_MODULAR_GC" append_cflags("-fPIC") +DTRACE_RULES = <<~MAKE + + probes.stamp: $(GC_DTRACE_OBJS) + $(Q) if test -f $@ -o -f $(GC_DTRACE_OBJ); then \ + $(RM) $(GC_DTRACE_OBJS) $@; \ + $(ECHO) rebuilding objects which were modified by "dtrace -G"; \ + $(MAKE) $(GC_DTRACE_OBJS); \ + fi + $(Q) touch $@ + + $(GC_DTRACE_OBJ): $(top_srcdir)/probes.d $(GC_DTRACE_REBUILD:yes=probes.stamp) + $(ECHO) processing GC probes in object files + $(Q) CC="$(CC)" CFLAGS="$(CFLAGS) $(INCFLAGS) $(CPPFLAGS)" $(GC_DTRACE) -G -C $(INCFLAGS) -s $(top_srcdir)/probes.d -o $@ $(GC_DTRACE_OBJS) +MAKE + def create_gc_makefile(name, &block) - create_makefile("librubygc.#{name}", &block) + dtrace_obj = ENV.fetch("DTRACE_OBJ", "") + dtrace_enabled = (name == "default" && !dtrace_obj.empty?) + + if name == "default" + $INCFLAGS << " -I$(topdir)" + $headers << "$(topdir)/probes.h" + $cleanfiles << "probes.stamp" + end + + create_makefile("librubygc.#{name}") do |conf| + conf = block.call(conf) if block + next conf unless dtrace_enabled + + gc_objs = $objs.join(" ") + conf = Array(conf).join.sub(/^OBJS = .*$/, "OBJS = #{gc_objs} #{dtrace_obj}") + conf + <<~MAKE + + GC_DTRACE = #{ENV.fetch("DTRACE")} + GC_DTRACE_OBJ = #{dtrace_obj} + GC_DTRACE_OBJS = #{gc_objs} + GC_DTRACE_REBUILD = #{ENV.fetch("DTRACE_REBUILD", "")} + MAKE + end + + File.write("Makefile", DTRACE_RULES, mode: "ab") if dtrace_enabled end diff --git a/test/dtrace/helper.rb b/test/dtrace/helper.rb index 9e8c7ecd52c..4fd218e5394 100644 --- a/test/dtrace/helper.rb +++ b/test/dtrace/helper.rb @@ -115,7 +115,7 @@ module DTrace IO.popen(cmd, err: [:child, :out], &:readlines) end - def trap_probe d_program, ruby_program + def trap_probe d_program, ruby_program, env: {} if Hash === d_program d_program = d_program[IMPL] or omit "#{d_program} not implemented for #{IMPL}" @@ -132,15 +132,17 @@ module DTrace d_path = d.path rb_path = rb.path - cmd = "#{RUBYBIN} --disable=gems -I#{INCLUDE} #{rb_path}" + ruby_env = env + env_prefix = ruby_env.map {|key, val| "#{key}=#{val}" } + cmd = [*env_prefix, "#{RUBYBIN} --disable=gems -I#{INCLUDE} #{rb_path}"].join(" ") 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] + (NEEDED_ENVS + ruby_env.keys).uniq.each do |name| + if val = ruby_env[name] || ENV[name] cmd.unshift("#{name}=#{val}") end end diff --git a/test/dtrace/test_gc_modular.rb b/test/dtrace/test_gc_modular.rb new file mode 100644 index 00000000000..68665c78139 --- /dev/null +++ b/test/dtrace/test_gc_modular.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: false +require_relative 'helper' + +module DTrace + class TestModularGC < TestCase + MODULAR_GC_DIR = RbConfig::CONFIG["modular_gc_dir"] + DLEXT = RbConfig::CONFIG["DLEXT"] + DEFAULT_GC = MODULAR_GC_DIR && File.join(MODULAR_GC_DIR, "librubygc.default.#{DLEXT}") + + def setup + super + omit "Ruby was not configured with --with-modular-gc" if MODULAR_GC_DIR.nil? || MODULAR_GC_DIR.empty? + omit "default modular GC is not installed" unless DEFAULT_GC && File.file?(DEFAULT_GC) + end + + %w[ + gc-mark-begin + gc-mark-end + gc-sweep-begin + gc-sweep-end + ].each do |probe_name| + define_method(:"test_modular_#{probe_name.gsub(/-/, '_')}") do + probe = "ruby$target:::#{probe_name} { printf(\"#{probe_name}\\n\"); }" + + trap_probe(probe, ruby_program, env: {"RUBY_GC_LIBRARY" => "default"}) do |_, _, saw| + assert_operator saw.length, :>, 0 + end + end + end + + private + + def ruby_program + "100000.times { Object.new }" + end + end +end if defined?(DTrace::TestCase)