vernier 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dcfae18cfffd67e1e8a52c0c9de2654e7523b674d492964dfa3447c967d981e3
4
- data.tar.gz: 4ff2a81a37d19f974914a0ff3fa28937302a636721b8732f7e7b282f895db9f3
3
+ metadata.gz: 6050bca74116d0e90f98025fe23d7fbd40c6107c085e3f768d606d4f418ebc60
4
+ data.tar.gz: e302542d8b06852d28d0ec1e2528f23bba46d5ef9ea9a828c9dbcdb985a292a3
5
5
  SHA512:
6
- metadata.gz: bd4e4f001047bb3f20c9c0670339747b9e29abf6e44bcd77dbb38fcf7c16f42d33eef893045ab7a899b155d9b98765950e91af79864234a2d66243490d367d49
7
- data.tar.gz: 14cecc8aa1058b4e38f130bed6ec1d43e8a59a636c125862ef6c61df13f885e1d76b1a142d0fa8fab16ea6bfee2642b7cd606bb562020f800ce3518b2c7fac2c
6
+ metadata.gz: 9d20e5f9d9c894a253bc4aeb6b9da6cb35d916e881abedf996ce19757a9cd92efe43bcc76fda0aa4ce5e334be36582adf7c716a998c4c5e1f403dc57364fb5ab
7
+ data.tar.gz: 7cf03df7bcb4f961b5b456eb781fd5818de7e18501baa01592ce17528f5eae7b272e90dcff200f861495ebb1a83c87f9146306db66ce20a9d56467ab87bce3c6
data/README.md CHANGED
@@ -1,6 +1,22 @@
1
1
  # Vernier
2
2
 
3
- Experimental next-generation Ruby profiler.
3
+ Next-generation Ruby sampling profiler. Tracks multiple threads, GVL activity, GC pauses, idle time, and more.
4
+
5
+ ## Examples
6
+
7
+ [Livestreamed demo: Pairin' with Aaron (YouTube)](https://www.youtube.com/watch?v=9nvX3OHykGQ#t=27m43)
8
+
9
+ Sidekiq jobs from Mastodon (time, threded)
10
+ : https://share.firefox.dev/44jZRf3
11
+
12
+ Puma web requests from Mastodon (time, threded)
13
+ : https://share.firefox.dev/48FOTnF
14
+
15
+ Rails benchmark - lobste.rs (time)
16
+ : https://share.firefox.dev/3Ld89id
17
+
18
+ `require "irb"` (retained memory)
19
+ : https://share.firefox.dev/3DhLsFa
4
20
 
5
21
  ## Installation
6
22
 
@@ -10,24 +26,24 @@ gem 'vernier'
10
26
 
11
27
  ## Usage
12
28
 
13
- ### Retained memory
14
29
 
15
- Record a flamegraph of all **retained** allocations from loading `irb`.
30
+ ### Time
16
31
 
17
32
  ```
18
- ruby -r vernier -e 'Vernier.trace_retained(out: "irb_profile.json") { require "irb" }'
33
+ Vernier.trace(out: "time_profile.json") { some_slow_method }
19
34
  ```
20
35
 
21
- The output can then be viewed in the [Firefox Profiler (demo)](https://share.firefox.dev/3DhLsFa)
36
+ The output can then be viewed in the Firefox Profiler (demo) or the [`profile-viewer` gem](https://github.com/tenderlove/profiler/tree/ruby) (a Ruby-customized version of the firefox profiler.
22
37
 
23
- ![Screenshot 2023-07-16 at 21-06-19 Ruby_Vernier – 1970-01-01 12 00 00 a m UTC – Firefox Profiler](https://github.com/jhawthorn/vernier/assets/131752/9ca0b593-70fb-4c8b-aed9-cb33e0e0bc06)
38
+ ### Retained memory
24
39
 
25
- ### Time
40
+ Record a flamegraph of all **retained** allocations from loading `irb`.
26
41
 
27
42
  ```
28
- Vernier.trace(out: "time_profile.json") { some_slow_method }
43
+ ruby -r vernier -e 'Vernier.trace_retained(out: "irb_profile.json") { require "irb" }'
29
44
  ```
30
45
 
46
+
31
47
  ## Development
32
48
 
33
49
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/bin/vernier ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ load "#{__dir__}/../exe/vernier"
@@ -0,0 +1,20 @@
1
+ ENV["MT_CPU"] = "3"
2
+
3
+ require "minitest/autorun"
4
+
5
+ class TestMeme < Minitest::Test
6
+ parallelize_me!
7
+
8
+ def test_sleeping
9
+ sleep 1
10
+ end
11
+
12
+ def test_also_sleeping
13
+ sleep 1
14
+ end
15
+
16
+ def test_prime
17
+ require "prime"
18
+ assert_equal 1299709, Prime.first(100000).last
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ def tarai(x, y, z) =
2
+ x <= y ? y : tarai(tarai(x-1, y, z),
3
+ tarai(y-1, z, x),
4
+ tarai(z-1, x, y))
5
+
6
+ require 'vernier'
7
+ Vernier.trace(out: "ractor.json") do
8
+ 4.times.map do
9
+ Ractor.new { tarai(14, 7, 0) }
10
+ end.each(&:take)
11
+ end
data/examples/rails.rb ADDED
@@ -0,0 +1,125 @@
1
+ require 'bundler/inline'
2
+
3
+ gemfile do
4
+ source 'https://rubygems.org'
5
+ rails_version = ENV["RAILS"]
6
+ case rails_version
7
+ when nil
8
+ gem "rails"
9
+ when "edge", "main"
10
+ gem "rails", github: "rails/rails"
11
+ when /\//
12
+ gem "rails", path: rails_version
13
+ else
14
+ gem "rails", rails_version
15
+ end
16
+ gem "sqlite3"
17
+ gem "vernier", path: "#{__dir__}/.."
18
+ end
19
+
20
+ require "action_controller"
21
+ require "action_view"
22
+ require "active_record"
23
+ require "rails"
24
+
25
+ ENV["RAILS_ENV"] = "production"
26
+
27
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
28
+ class MyApp < Rails::Application
29
+ config.load_defaults 7.0
30
+
31
+ config.eager_load = true
32
+ config.cache_classes = true
33
+ config.hosts << ->(_){ true }
34
+ config.secret_key_base = SecureRandom.hex
35
+ config.consider_all_requests_local = true
36
+ config.public_file_server.enabled = false
37
+ config.cache_store = :null_store
38
+ config.log_level = :warn
39
+ config.enable_site_error_reporting = false
40
+
41
+ logger = ActiveSupport::Logger.new(STDOUT)
42
+ logger.formatter = config.log_formatter
43
+ config.logger = ActiveSupport::TaggedLogging.new(logger)
44
+
45
+ routes.append do
46
+ root to: "home#show"
47
+ end
48
+
49
+ MyApp.initialize!
50
+ end
51
+
52
+ def silence(stream=STDOUT)
53
+ old = stream.dup
54
+ stream.reopen IO::NULL
55
+ yield
56
+ stream.reopen(old)
57
+ old.dup
58
+ end
59
+
60
+ silence do
61
+ ActiveRecord::Schema.define do
62
+ create_table :posts, force: true do |t|
63
+ t.string :title
64
+ t.text :body
65
+ t.integer :likes
66
+ end
67
+ create_table :comments, force: true do |t|
68
+ t.belongs_to :post
69
+ t.string :title
70
+ t.text :body
71
+ t.datetime :posted_at
72
+ end
73
+ end
74
+ end
75
+
76
+ class Post < ActiveRecord::Base
77
+ has_many :comments
78
+ end
79
+
80
+ class Comment < ActiveRecord::Base
81
+ belongs_to :post
82
+ end
83
+
84
+ 0.upto(100) do |i|
85
+ post = Post.create!(title: "Post number #{i}", body: "blog " * 50, likes: ((i * 1337) % 30))
86
+ 5.times do
87
+ post.comments.create!(post: post, title: "nice post!", body: "keep it up!", posted_at: Time.now)
88
+ end
89
+ end
90
+
91
+ class HomeController < ActionController::Base
92
+ def show
93
+ posts = Post.order(likes: :desc).includes(:comments).first(10)
94
+ render json: posts
95
+ end
96
+ end
97
+
98
+ app = Rails.application
99
+ env = Rack::MockRequest.env_for("http://example.org/")
100
+ make_request = -> () do
101
+ status, headers, body = app.call(env)
102
+ body.close if body.respond_to?(:close)
103
+ if status
104
+ end
105
+ body = body.each.to_a.join("")
106
+ raise body if status != 200
107
+ [status, headers, body]
108
+ end
109
+
110
+ # warm up
111
+ make_request.call
112
+
113
+ Vernier.trace(out: "rails.json") do |collector|
114
+ ActiveSupport::Notifications.monotonic_subscribe do |name, start, finish, id, payload|
115
+ collector.add_marker(
116
+ name:,
117
+ start: (start * 1_000_000_000).to_i,
118
+ finish: (finish * 1_000_000_000).to_i,
119
+ )
120
+ end
121
+
122
+ 1000.times do
123
+ make_request.call
124
+ end
125
+ end
data/exe/vernier ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "optparse"
4
+
5
+ banner = <<-END
6
+ Usage: vernier run [FLAGS] -- COMMAND
7
+
8
+ FLAGS:
9
+ END
10
+
11
+ options = {}
12
+ parser = OptionParser.new(banner) do |o|
13
+ o.on('--output [FILENAME]', String, "output filename") do |s|
14
+ options[:output] = s
15
+ end
16
+ o.on('--interval [MICROSECONDS]', Integer, "sampling interval (default 500)") do |i|
17
+ options[:interval] = i
18
+ end
19
+ o.on('--signal [NAME]', String, "specify a signal to start and stop the profiler") do |s|
20
+ options[:signal] = s
21
+ end
22
+ o.on('--start-paused', "don't automatically start the profiler") do
23
+ options[:start_paused] = true
24
+ end
25
+ end
26
+
27
+ parser.parse!
28
+ parser.abort(parser.help) if ARGV.shift != "run"
29
+ parser.abort(parser.help) if ARGV.empty?
30
+
31
+ env = {}
32
+ options.each do |k, v|
33
+ env["VERNIER_#{k.to_s.upcase}"] = v.to_s
34
+ end
35
+ vernier_path = File.expand_path('../lib', __dir__)
36
+ env['RUBYOPT'] = "-I #{vernier_path} -r vernier/autorun #{ENV['RUBYOPT']}"
37
+
38
+ Kernel.exec(env, *ARGV)