vernier 1.7.1 → 1.8.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/.ruby-version +1 -0
- data/Rakefile +1 -0
- data/exe/vernier +6 -2
- data/lib/vernier/autorun.rb +7 -4
- data/lib/vernier/collector.rb +2 -1
- data/lib/vernier/middleware.rb +1 -1
- data/lib/vernier/output/cpuprofile.rb +145 -0
- data/lib/vernier/result.rb +16 -4
- data/lib/vernier/version.rb +1 -1
- data/lib/vernier.rb +1 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb2a2674d6f0654668b9fbfc03ec935fa21640585ae0b4dbd7567b472f975f26
|
4
|
+
data.tar.gz: 660264f955f4f2e9a9ae7e64acbb1b59d4d91f87d1a28d7a62aed0adabc9c51a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa249d63850b2ea08a3165e9b7045dcec5542173198aeef05c4c39047f604a5faa12b0d72c6f8fa7983e45a7011a3a5529dc4e4b7fbee426b2461f14b5d6b6c3
|
7
|
+
data.tar.gz: 93f82cd596d454db747afb11abe669b4ed53b6fd6691ebf9ab43ed53e5f2be9406ce93424e3b1179c9a7d13df28cdcbface7d2e80544aa1d2ba45d8a19385696
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.4.4
|
data/Rakefile
CHANGED
data/exe/vernier
CHANGED
@@ -9,10 +9,9 @@ module Vernier
|
|
9
9
|
module CLI
|
10
10
|
class Metadata < Array
|
11
11
|
require 'json'
|
12
|
-
require 'base64'
|
13
12
|
|
14
13
|
def to_s
|
15
|
-
|
14
|
+
[to_json].pack("m")
|
16
15
|
end
|
17
16
|
end
|
18
17
|
|
@@ -52,6 +51,9 @@ FLAGS:
|
|
52
51
|
options[:metadata] ||= Metadata.new
|
53
52
|
options[:metadata] << [key, value]
|
54
53
|
end
|
54
|
+
o.on('--format [FORMAT]', String, "output format: firefox (default) or cpuprofile") do |output_format|
|
55
|
+
options[:format] = output_format
|
56
|
+
end
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
@@ -88,6 +90,8 @@ run = Vernier::CLI.run(options)
|
|
88
90
|
view = Vernier::CLI.view(options)
|
89
91
|
|
90
92
|
case ARGV.shift
|
93
|
+
when "-v", "--version"
|
94
|
+
puts Vernier::VERSION
|
91
95
|
when "run"
|
92
96
|
run.parse!
|
93
97
|
run.abort(run.help) if ARGV.empty?
|
data/lib/vernier/autorun.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require "tempfile"
|
2
2
|
require "vernier"
|
3
|
-
require "base64"
|
4
3
|
require "json"
|
5
4
|
|
6
5
|
module Vernier
|
@@ -22,7 +21,7 @@ module Vernier
|
|
22
21
|
allocation_interval = options.fetch(:allocation_interval, 0).to_i
|
23
22
|
hooks = options.fetch(:hooks, "").split(",")
|
24
23
|
metadata = if options[:metadata]
|
25
|
-
JSON.parse(
|
24
|
+
JSON.parse(@options[:metadata].unpack1("m")).to_h { |k, v| [k.to_sym, v] }
|
26
25
|
else
|
27
26
|
{}
|
28
27
|
end
|
@@ -49,12 +48,16 @@ module Vernier
|
|
49
48
|
end
|
50
49
|
prefix = "profile-"
|
51
50
|
timestamp = Time.now.strftime("%Y%m%d-%H%M%S")
|
52
|
-
suffix = "
|
51
|
+
suffix = if options[:format] == "cpuprofile"
|
52
|
+
".vernier.cpuprofile"
|
53
|
+
else
|
54
|
+
".vernier.json.gz"
|
55
|
+
end
|
53
56
|
|
54
57
|
output_path = File.expand_path("#{output_dir}/#{prefix}#{timestamp}-#{$$}#{suffix}")
|
55
58
|
end
|
56
59
|
|
57
|
-
result.write(out: output_path)
|
60
|
+
result.write(out: output_path, format: options[:format] || "firefox")
|
58
61
|
|
59
62
|
STDERR.puts(result.inspect)
|
60
63
|
STDERR.puts("written to #{output_path}")
|
data/lib/vernier/collector.rb
CHANGED
@@ -11,6 +11,7 @@ module Vernier
|
|
11
11
|
|
12
12
|
@mode = mode
|
13
13
|
@out = options[:out]
|
14
|
+
@format = options[:format]
|
14
15
|
|
15
16
|
@markers = []
|
16
17
|
@hooks = []
|
@@ -142,7 +143,7 @@ module Vernier
|
|
142
143
|
#result.instance_variable_set(:@markers, markers)
|
143
144
|
|
144
145
|
if @out
|
145
|
-
result.write(out: @out)
|
146
|
+
result.write(out: @out, format: @format)
|
146
147
|
end
|
147
148
|
|
148
149
|
result
|
data/lib/vernier/middleware.rb
CHANGED
@@ -18,7 +18,7 @@ module Vernier
|
|
18
18
|
result = Vernier.trace(interval:, allocation_interval:, hooks: [:rails]) do
|
19
19
|
@app.call(env)
|
20
20
|
end
|
21
|
-
body = result.
|
21
|
+
body = result.to_firefox(gzip: true)
|
22
22
|
filename = "#{request.path.gsub("/", "_")}_#{DateTime.now.strftime("%Y-%m-%d-%H-%M-%S")}.vernier.json.gz"
|
23
23
|
headers = {
|
24
24
|
"Content-Type" => "application/octet-stream",
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module Vernier
|
4
|
+
module Output
|
5
|
+
class Cpuprofile
|
6
|
+
def initialize(profile)
|
7
|
+
@profile = profile
|
8
|
+
end
|
9
|
+
|
10
|
+
def output
|
11
|
+
JSON.generate(data)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :profile
|
17
|
+
|
18
|
+
def ns_to_us(timestamp)
|
19
|
+
(timestamp / 1_000.0).to_i
|
20
|
+
end
|
21
|
+
|
22
|
+
def data
|
23
|
+
# Get the main thread data (cpuprofile format is single-threaded)
|
24
|
+
main_thread = profile.main_thread
|
25
|
+
return empty_profile if main_thread.nil?
|
26
|
+
|
27
|
+
samples = main_thread[:samples]
|
28
|
+
timestamps = main_thread[:timestamps] || []
|
29
|
+
|
30
|
+
nodes = build_nodes
|
31
|
+
sample_node_ids = samples.map { |stack_idx| stack_to_node_id(stack_idx) }
|
32
|
+
time_deltas = calculate_time_deltas(timestamps)
|
33
|
+
|
34
|
+
{
|
35
|
+
nodes: nodes,
|
36
|
+
startTime: ns_to_us(profile.started_at),
|
37
|
+
endTime: ns_to_us(profile.end_time),
|
38
|
+
samples: sample_node_ids,
|
39
|
+
timeDeltas: time_deltas
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def empty_profile
|
44
|
+
{
|
45
|
+
nodes: [root_node],
|
46
|
+
startTime: 0,
|
47
|
+
endTime: 0,
|
48
|
+
samples: [],
|
49
|
+
timeDeltas: []
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def build_nodes
|
54
|
+
stack_table = profile.stack_table
|
55
|
+
|
56
|
+
nodes = []
|
57
|
+
@node_id_map = {}
|
58
|
+
|
59
|
+
root = root_node
|
60
|
+
nodes << root
|
61
|
+
@node_id_map[nil] = 0
|
62
|
+
|
63
|
+
stack_table.stack_count.times do |stack_idx|
|
64
|
+
create_node_for_stack(stack_idx, nodes, stack_table)
|
65
|
+
end
|
66
|
+
|
67
|
+
nodes
|
68
|
+
end
|
69
|
+
|
70
|
+
def root_node
|
71
|
+
{
|
72
|
+
id: 0,
|
73
|
+
callFrame: {
|
74
|
+
functionName: "(root)",
|
75
|
+
scriptId: "0",
|
76
|
+
url: "",
|
77
|
+
lineNumber: -1,
|
78
|
+
columnNumber: -1,
|
79
|
+
},
|
80
|
+
hitCount: 0,
|
81
|
+
children: []
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
def create_node_for_stack(stack_idx, nodes, stack_table)
|
86
|
+
return @node_id_map[stack_idx] if @node_id_map.key?(stack_idx)
|
87
|
+
|
88
|
+
frame_idx = stack_table.stack_frame_idx(stack_idx)
|
89
|
+
parent_stack_idx = stack_table.stack_parent_idx(stack_idx)
|
90
|
+
|
91
|
+
parent_node_id = if parent_stack_idx.nil?
|
92
|
+
0 # root node
|
93
|
+
else
|
94
|
+
create_node_for_stack(parent_stack_idx, nodes, stack_table)
|
95
|
+
end
|
96
|
+
|
97
|
+
func_idx = stack_table.frame_func_idx(frame_idx)
|
98
|
+
line = stack_table.frame_line_no(frame_idx) - 1
|
99
|
+
|
100
|
+
func_name = stack_table.func_name(func_idx)
|
101
|
+
filename = stack_table.func_filename(func_idx)
|
102
|
+
|
103
|
+
node_id = nodes.length
|
104
|
+
node = {
|
105
|
+
id: node_id,
|
106
|
+
callFrame: {
|
107
|
+
functionName: func_name || "(anonymous)",
|
108
|
+
scriptId: func_idx.to_s,
|
109
|
+
url: filename || "",
|
110
|
+
lineNumber: line || 0,
|
111
|
+
columnNumber: 0
|
112
|
+
},
|
113
|
+
hitCount: 0,
|
114
|
+
children: []
|
115
|
+
}
|
116
|
+
|
117
|
+
nodes << node
|
118
|
+
@node_id_map[stack_idx] = node_id
|
119
|
+
|
120
|
+
parent_node = nodes[parent_node_id]
|
121
|
+
parent_node[:children] << node_id unless parent_node[:children].include?(node_id)
|
122
|
+
end
|
123
|
+
|
124
|
+
def stack_to_node_id(stack_idx)
|
125
|
+
@node_id_map[stack_idx] || 0
|
126
|
+
end
|
127
|
+
|
128
|
+
def calculate_time_deltas(timestamps)
|
129
|
+
return [] if timestamps.empty?
|
130
|
+
|
131
|
+
deltas = []
|
132
|
+
|
133
|
+
timestamps.each_with_index do |timestamp, i|
|
134
|
+
if i == 0
|
135
|
+
deltas << 0
|
136
|
+
else
|
137
|
+
deltas << ns_to_us(timestamp - timestamps[i - 1])
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
deltas
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
data/lib/vernier/result.rb
CHANGED
@@ -29,13 +29,25 @@ module Vernier
|
|
29
29
|
(current_time_real_ns - current_time_mono_ns + started_at_mono_ns)
|
30
30
|
end
|
31
31
|
|
32
|
-
def
|
32
|
+
def to_firefox(gzip: false)
|
33
33
|
Output::Firefox.new(self).output(gzip:)
|
34
34
|
end
|
35
|
+
alias_method :to_gecko, :to_firefox
|
35
36
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
37
|
+
def to_cpuprofile
|
38
|
+
Output::Cpuprofile.new(self).output
|
39
|
+
end
|
40
|
+
|
41
|
+
def write(out:, format: "firefox")
|
42
|
+
case format
|
43
|
+
when "cpuprofile"
|
44
|
+
File.binwrite(out, to_cpuprofile)
|
45
|
+
when nil, "firefox"
|
46
|
+
gzip = out.end_with?(".gz")
|
47
|
+
File.binwrite(out, to_firefox(gzip:))
|
48
|
+
else
|
49
|
+
raise ArgumentError, "unknown format: #{format}"
|
50
|
+
end
|
39
51
|
end
|
40
52
|
|
41
53
|
def elapsed_seconds
|
data/lib/vernier/version.rb
CHANGED
data/lib/vernier.rb
CHANGED
@@ -8,6 +8,7 @@ require_relative "vernier/result"
|
|
8
8
|
require_relative "vernier/hooks"
|
9
9
|
require_relative "vernier/vernier"
|
10
10
|
require_relative "vernier/output/firefox"
|
11
|
+
require_relative "vernier/output/cpuprofile"
|
11
12
|
require_relative "vernier/output/top"
|
12
13
|
require_relative "vernier/output/file_listing"
|
13
14
|
require_relative "vernier/output/filename_filter"
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vernier
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Hawthorn
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: activesupport
|
@@ -61,6 +61,7 @@ extensions:
|
|
61
61
|
- ext/vernier/extconf.rb
|
62
62
|
extra_rdoc_files: []
|
63
63
|
files:
|
64
|
+
- ".ruby-version"
|
64
65
|
- CODE_OF_CONDUCT.md
|
65
66
|
- Gemfile
|
66
67
|
- LICENSE.txt
|
@@ -93,6 +94,7 @@ files:
|
|
93
94
|
- lib/vernier/hooks/memory_usage.rb
|
94
95
|
- lib/vernier/marker.rb
|
95
96
|
- lib/vernier/middleware.rb
|
97
|
+
- lib/vernier/output/cpuprofile.rb
|
96
98
|
- lib/vernier/output/file_listing.rb
|
97
99
|
- lib/vernier/output/filename_filter.rb
|
98
100
|
- lib/vernier/output/firefox.rb
|
@@ -125,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
127
|
- !ruby/object:Gem::Version
|
126
128
|
version: '0'
|
127
129
|
requirements: []
|
128
|
-
rubygems_version: 3.6.
|
130
|
+
rubygems_version: 3.6.7
|
129
131
|
specification_version: 4
|
130
132
|
summary: A next generation CRuby profiler
|
131
133
|
test_files: []
|