diff --git a/bin/vernier b/bin/vernier index 39c621e8..b97d053d 100755 --- a/bin/vernier +++ b/bin/vernier @@ -1,4 +1,27 @@ #!/usr/bin/env ruby # frozen_string_literal: true -load "#{__dir__}/../exe/vernier" +# +# This file was generated by Bundler. +# +# The application 'vernier' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +bundle_binstub = File.expand_path("bundle", __dir__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("vernier", "vernier") diff --git a/examples/rails.rb b/examples/rails.rb index 0538ce85..c5e74d76 100644 --- a/examples/rails.rb +++ b/examples/rails.rb @@ -100,7 +100,7 @@ def show # warm up make_request.call -Vernier.trace(out: "rails.json") do |collector| +Vernier.trace(out: "rails.json", mode: :jit_side_exit) do |collector| ActiveSupport::Notifications.monotonic_subscribe do |name, start, finish, id, payload| collector.add_marker( name:, diff --git a/examples/threaded_http_requests.rb b/examples/threaded_http_requests.rb index f84fdb51..629fec96 100644 --- a/examples/threaded_http_requests.rb +++ b/examples/threaded_http_requests.rb @@ -1,6 +1,6 @@ require "vernier" -Vernier.trace(out: "http_requests.json") do +Vernier.trace(out: "http_requests.json", mode: :jit_side_exit) do require "net/http" require "uri" diff --git a/exe/vernier b/exe/vernier index 4b533645..ae65fefa 100755 --- a/exe/vernier +++ b/exe/vernier @@ -22,6 +22,9 @@ parser = OptionParser.new(banner) do |o| o.on('--start-paused', "don't automatically start the profiler") do options[:start_paused] = true end + o.on('--mode [MODE]', String) do |s| + options[:mode] = s + end end parser.parse! diff --git a/ext/vernier/vernier.cc b/ext/vernier/vernier.cc index 5882e310..732f2656 100644 --- a/ext/vernier/vernier.cc +++ b/ext/vernier/vernier.cc @@ -949,6 +949,65 @@ class CustomCollector : public BaseCollector { } }; +class SideExitCollector : public BaseCollector { + SampleList samples; + + void sample() { + RawSample sample; + sample.sample(); + int stack_index = frame_list.stack_index(sample); + + native_thread_id_t thread_id = 0; + samples.record_sample(stack_index, TimeStamp::Now(), thread_id, CATEGORY_NORMAL); + } + + static void side_exit_cb(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass) { + SideExitCollector *collector = static_cast((void *)NUM2ULL(data)); + collector->sample(); + } + + bool start() { + if (!BaseCollector::start()) { + return false; + } + + rb_add_event_hook(side_exit_cb, RUBY_INTERNAL_EVENT_JIT_SIDE_EXIT, PTR2NUM(this)); + + return true; + } + + VALUE stop() { + BaseCollector::stop(); + + frame_list.finalize(); + + VALUE result = build_collector_result(); + + reset(); + + return result; + } + + VALUE build_collector_result() { + VALUE result = BaseCollector::build_collector_result(); + + VALUE threads = rb_hash_new(); + rb_ivar_set(result, rb_intern("@threads"), threads); + + VALUE thread_hash = rb_hash_new(); + samples.write_result(thread_hash); + + rb_hash_aset(threads, ULL2NUM(0), thread_hash); + rb_hash_aset(thread_hash, sym("tid"), ULL2NUM(0)); + rb_hash_aset(thread_hash, sym("name"), rb_str_new_cstr("side exits")); + rb_hash_aset(thread_hash, sym("started_at"), ULL2NUM(started_at.nanoseconds())); + + frame_list.write_result(result); + + return result; + } +}; + class RetainedCollector : public BaseCollector { void reset() { object_frames.clear(); @@ -1403,6 +1462,8 @@ static VALUE collector_new(VALUE self, VALUE mode, VALUE options) { collector = new RetainedCollector(); } else if (mode == sym("custom")) { collector = new CustomCollector(); + } else if (mode == sym("jit_side_exit")) { + collector = new SideExitCollector(); } else if (mode == sym("wall")) { VALUE intervalv = rb_hash_aref(options, sym("interval")); TimeStamp interval;