vernier 0.2.1 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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)