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 +4 -4
- data/README.md +24 -8
- data/bin/vernier +4 -0
- data/examples/minitest.rb +20 -0
- data/examples/ractor.rb +11 -0
- data/examples/rails.rb +125 -0
- data/exe/vernier +38 -0
- data/ext/vernier/vernier.cc +571 -302
- data/lib/vernier/autorun.rb +65 -0
- data/lib/vernier/collector.rb +47 -28
- data/lib/vernier/marker.rb +11 -11
- data/lib/vernier/output/firefox.rb +76 -45
- data/lib/vernier/result.rb +139 -0
- data/lib/vernier/version.rb +1 -1
- data/lib/vernier.rb +6 -133
- data/vernier.gemspec +1 -1
- metadata +12 -5
- data/sig/vernier.rbs +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6050bca74116d0e90f98025fe23d7fbd40c6107c085e3f768d606d4f418ebc60
|
4
|
+
data.tar.gz: e302542d8b06852d28d0ec1e2528f23bba46d5ef9ea9a828c9dbcdb985a292a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d20e5f9d9c894a253bc4aeb6b9da6cb35d916e881abedf996ce19757a9cd92efe43bcc76fda0aa4ce5e334be36582adf7c716a998c4c5e1f403dc57364fb5ab
|
7
|
+
data.tar.gz: 7cf03df7bcb4f961b5b456eb781fd5818de7e18501baa01592ce17528f5eae7b272e90dcff200f861495ebb1a83c87f9146306db66ce20a9d56467ab87bce3c6
|
data/README.md
CHANGED
@@ -1,6 +1,22 @@
|
|
1
1
|
# Vernier
|
2
2
|
|
3
|
-
|
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
|
-
|
30
|
+
### Time
|
16
31
|
|
17
32
|
```
|
18
|
-
|
33
|
+
Vernier.trace(out: "time_profile.json") { some_slow_method }
|
19
34
|
```
|
20
35
|
|
21
|
-
The output can then be viewed in the
|
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
|
-
|
38
|
+
### Retained memory
|
24
39
|
|
25
|
-
|
40
|
+
Record a flamegraph of all **retained** allocations from loading `irb`.
|
26
41
|
|
27
42
|
```
|
28
|
-
Vernier.
|
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,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
|
data/examples/ractor.rb
ADDED
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)
|