vernier 1.1.1 → 1.2.0
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/exe/vernier +123 -31
- data/ext/vernier/vernier.cc +9 -11
- data/lib/vernier/result.rb +1 -1
- data/lib/vernier/version.rb +1 -1
- data/vernier.gemspec +4 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56faced9b5a5fe99a124ab7743609eb23a981ca27888a5a8957e509d28f508ef
|
4
|
+
data.tar.gz: 0b258dc2f1f4c143568527213a3f39055a44aed3c983c84a14f71d6af68c900a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '096133a98cfc38236c89025c51f70a0c4def9c6208031775d11640291d08b17d53fb2817d6f9b7fea1e7d0551ac0e58471d757fd5568c732969d73134ca20fc3'
|
7
|
+
data.tar.gz: 063ce46032770d8d2cd161071587dea386af0497a9437d8bc19148c54263e32c2d4d00b164c926ff19f1236f704f1c76aabff8be63bb2b66cd7409f69742004b
|
data/exe/vernier
CHANGED
@@ -1,44 +1,136 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require "optparse"
|
4
|
+
require "vernier/version"
|
4
5
|
|
5
|
-
|
6
|
+
module Vernier
|
7
|
+
module CLI
|
8
|
+
def self.run(options)
|
9
|
+
banner = <<-END
|
6
10
|
Usage: vernier run [FLAGS] -- COMMAND
|
7
11
|
|
8
12
|
FLAGS:
|
9
|
-
END
|
13
|
+
END
|
10
14
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
15
|
+
OptionParser.new(banner) do |o|
|
16
|
+
o.version = Vernier::VERSION
|
17
|
+
|
18
|
+
o.on('--output [FILENAME]', String, "output filename") do |s|
|
19
|
+
options[:output] = s
|
20
|
+
end
|
21
|
+
o.on('--interval [MICROSECONDS]', Integer, "sampling interval (default 500)") do |i|
|
22
|
+
options[:interval] = i
|
23
|
+
end
|
24
|
+
o.on('--allocation_sample_rate [ALLOCATIONS]', Integer, "allocation sampling interval (default 0 disabled)") do |i|
|
25
|
+
options[:allocation_sample_rate] = i
|
26
|
+
end
|
27
|
+
o.on('--signal [NAME]', String, "specify a signal to start and stop the profiler") do |s|
|
28
|
+
options[:signal] = s
|
29
|
+
end
|
30
|
+
o.on('--start-paused', "don't automatically start the profiler") do
|
31
|
+
options[:start_paused] = true
|
32
|
+
end
|
33
|
+
o.on('--hooks [HOOKS]', String, "Enable instrumentation hooks. Currently supported: rails") do |s|
|
34
|
+
options[:hooks] = s
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.view(options)
|
40
|
+
banner = <<-END
|
41
|
+
Usage: vernier view [FLAGS] FILENAME
|
42
|
+
|
43
|
+
FLAGS:
|
44
|
+
END
|
45
|
+
|
46
|
+
OptionParser.new(banner) do |o|
|
47
|
+
o.on('--top [COUNT]', Integer, "number of frames to show (default 20)") do |i|
|
48
|
+
options[:top] = i
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.inverted_tree(top, file)
|
54
|
+
# Print the inverted tree from a Vernier profile
|
55
|
+
require "json"
|
56
|
+
|
57
|
+
is_gzip = File.binread(file, 2) == "\x1F\x8B".b # check for gzip header
|
58
|
+
|
59
|
+
json = if is_gzip
|
60
|
+
require "zlib"
|
61
|
+
Zlib::GzipReader.open(file) { |gz| gz.read }
|
62
|
+
else
|
63
|
+
File.read file
|
64
|
+
end
|
65
|
+
|
66
|
+
info = JSON.load json
|
67
|
+
|
68
|
+
main = info["threads"].find { |thread| thread["isMainThread"] }
|
69
|
+
|
70
|
+
weight_by_frame = Hash.new(0)
|
71
|
+
|
72
|
+
stack_frames = main["stackTable"]["frame"]
|
73
|
+
frame_table = main["frameTable"]["func"]
|
74
|
+
func_table = main["funcTable"]["name"]
|
75
|
+
string_array = main["stringArray"]
|
76
|
+
|
77
|
+
main["samples"]["stack"].zip(main["samples"]["weight"]).each do |stack, weight|
|
78
|
+
top_frame_index = stack_frames[stack]
|
79
|
+
func_index = frame_table[top_frame_index]
|
80
|
+
string_index = func_table[func_index]
|
81
|
+
str = string_array[string_index]
|
82
|
+
weight_by_frame[str] += weight
|
83
|
+
end
|
84
|
+
|
85
|
+
total = weight_by_frame.values.inject :+
|
86
|
+
|
87
|
+
header = ["Samples", "%", ""]
|
88
|
+
widths = header.map(&:bytesize)
|
89
|
+
|
90
|
+
columns = weight_by_frame.sort_by { |k,v| v }.reverse.first(top).map { |k,v|
|
91
|
+
entry = [v.to_s, ((v / total.to_f) * 100).round(1).to_s, k]
|
92
|
+
entry.each_with_index { |str, i| widths[i] = str.bytesize if widths[i] < str.bytesize }
|
93
|
+
entry
|
94
|
+
}
|
95
|
+
|
96
|
+
print_separator widths
|
97
|
+
print_row header, widths
|
98
|
+
print_separator widths
|
99
|
+
columns.each { print_row(_1, widths) }
|
100
|
+
print_separator widths
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.print_row(list, widths)
|
104
|
+
puts("|" + list.map.with_index { |str, i| " " + str.ljust(widths[i] + 1) }.join("|") + "|")
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.print_separator(widths)
|
108
|
+
puts("+" + widths.map { |i| "-" * (i + 2) }.join("+") + "+")
|
109
|
+
end
|
30
110
|
end
|
31
111
|
end
|
32
112
|
|
33
|
-
|
34
|
-
|
35
|
-
|
113
|
+
options = {}
|
114
|
+
run = Vernier::CLI.run(options)
|
115
|
+
view = Vernier::CLI.view(options)
|
36
116
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
vernier_path = File.expand_path('../lib', __dir__)
|
42
|
-
env['RUBYOPT'] = "-I #{vernier_path} -r vernier/autorun #{ENV['RUBYOPT']}"
|
117
|
+
case ARGV.shift
|
118
|
+
when "run"
|
119
|
+
run.parse!
|
120
|
+
run.abort(run.help) if ARGV.empty?
|
43
121
|
|
44
|
-
|
122
|
+
env = {}
|
123
|
+
options.each do |k, v|
|
124
|
+
env["VERNIER_#{k.to_s.upcase}"] = v.to_s
|
125
|
+
end
|
126
|
+
vernier_path = File.expand_path('../lib', __dir__)
|
127
|
+
env['RUBYOPT'] = "-I #{vernier_path} -r vernier/autorun #{ENV['RUBYOPT']}"
|
128
|
+
|
129
|
+
Kernel.exec(env, *ARGV)
|
130
|
+
when "view"
|
131
|
+
view.parse!
|
132
|
+
view.abort(view.help) if ARGV.empty?
|
133
|
+
Vernier::CLI.inverted_tree(options[:top] || 20, ARGV.shift)
|
134
|
+
else
|
135
|
+
run.abort(run.help + "\n" + view.help)
|
136
|
+
end
|
data/ext/vernier/vernier.cc
CHANGED
@@ -672,10 +672,6 @@ StackTable::stack_table_convert(VALUE self, VALUE original_tableval, VALUE origi
|
|
672
672
|
StackTable *original_table = get_stack_table(original_tableval);
|
673
673
|
int original_idx = NUM2INT(original_idxval);
|
674
674
|
|
675
|
-
if (original_idx == -1) {
|
676
|
-
return original_idxval;
|
677
|
-
}
|
678
|
-
|
679
675
|
int original_size;
|
680
676
|
{
|
681
677
|
const std::lock_guard<std::mutex> lock(original_table->stack_mutex);
|
@@ -1027,13 +1023,15 @@ class SampleList {
|
|
1027
1023
|
}
|
1028
1024
|
|
1029
1025
|
void record_sample(int stack_index, TimeStamp time, Category category) {
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1026
|
+
// FIXME: probably better to avoid generating -1 higher up.
|
1027
|
+
// Currently this happens when we measure an empty stack. Ideally we would have a better representation
|
1028
|
+
if (stack_index < 0)
|
1029
|
+
return;
|
1030
|
+
|
1031
|
+
if (!empty() && stacks.back() == stack_index &&
|
1032
|
+
categories.back() == category) {
|
1033
|
+
// We don't compare timestamps for de-duplication
|
1034
|
+
weights.back() += 1;
|
1037
1035
|
} else {
|
1038
1036
|
stacks.push_back(stack_index);
|
1039
1037
|
timestamps.push_back(time);
|
data/lib/vernier/result.rb
CHANGED
data/lib/vernier/version.rb
CHANGED
data/vernier.gemspec
CHANGED
@@ -12,7 +12,10 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.description = "Next-generation Ruby 3.2.1+ sampling profiler. Tracks multiple threads, GVL activity, GC pauses, idle time, and more."
|
13
13
|
spec.homepage = "https://github.com/jhawthorn/vernier"
|
14
14
|
spec.license = "MIT"
|
15
|
-
|
15
|
+
|
16
|
+
unless ENV["IGNORE_REQUIRED_RUBY_VERSION"]
|
17
|
+
spec.required_ruby_version = ">= 3.2.1"
|
18
|
+
end
|
16
19
|
|
17
20
|
spec.metadata["homepage_uri"] = spec.homepage
|
18
21
|
spec.metadata["source_code_uri"] = spec.homepage
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vernier
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Hawthorn
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -102,7 +102,7 @@ metadata:
|
|
102
102
|
homepage_uri: https://github.com/jhawthorn/vernier
|
103
103
|
source_code_uri: https://github.com/jhawthorn/vernier
|
104
104
|
changelog_uri: https://github.com/jhawthorn/vernier
|
105
|
-
post_install_message:
|
105
|
+
post_install_message:
|
106
106
|
rdoc_options: []
|
107
107
|
require_paths:
|
108
108
|
- lib
|
@@ -117,8 +117,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
117
|
- !ruby/object:Gem::Version
|
118
118
|
version: '0'
|
119
119
|
requirements: []
|
120
|
-
rubygems_version: 3.5.
|
121
|
-
signing_key:
|
120
|
+
rubygems_version: 3.5.16
|
121
|
+
signing_key:
|
122
122
|
specification_version: 4
|
123
123
|
summary: A next generation CRuby profiler
|
124
124
|
test_files: []
|