Skip to content

Commit ddbb105

Browse files
authored
Merge pull request #165 from Shopify/support-vernier-metadata
Support vernier metadata
2 parents 3237fad + ecb4edd commit ddbb105

7 files changed

Lines changed: 77 additions & 13 deletions

File tree

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ gemspec
66
# Specify the same dependency sources as the application Gemfile
77
gem("activesupport", "~> 5.2")
88
gem("railties", "~> 5.2")
9-
gem("vernier", "~> 1.3.1") if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.2.1")
9+
gem("vernier", "~> 1.7.0") if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.2.1")
1010

1111
gem("google-cloud-storage", "~> 1.21")
1212
gem("rubocop", require: false)

Gemfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ GEM
212212
thread_safe (~> 0.1)
213213
uber (0.1.0)
214214
unicode-display_width (2.5.0)
215-
vernier (1.3.1)
215+
vernier (1.7.0)
216216
webrick (1.7.0)
217217

218218
PLATFORMS
@@ -233,7 +233,7 @@ DEPENDENCIES
233233
rubocop
234234
rubocop-performance
235235
rubocop-shopify
236-
vernier (~> 1.3.1)
236+
vernier (~> 1.7.0)
237237

238238
BUNDLED WITH
239239
2.5.9

lib/app_profiler/backend/vernier_backend.rb

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ class VernierBackend < BaseBackend
1515
:retained,
1616
].freeze
1717

18+
PRIVATE_METADATA = [
19+
:started_at, # started_at uses a monotonic source, not realtime
20+
].freeze
21+
1822
class << self
1923
def name
2024
:vernier
@@ -29,7 +33,9 @@ def start(params = {})
2933
@mode = params.delete(:mode) || DEFAULTS[:mode]
3034
raise ArgumentError unless AVAILABLE_MODES.include?(@mode)
3135

32-
@metadata = params.delete(:metadata)
36+
if Gem.loaded_specs["vernier"].version < Gem::Version.new("1.7.0")
37+
@metadata = params.delete(:metadata)
38+
end
3339
clear
3440

3541
@collector ||= ::Vernier::Collector.new(@mode, **params)
@@ -60,11 +66,19 @@ def results
6066

6167
return unless vernier_profile
6268

69+
# Store all vernier metadata
70+
meta = vernier_profile.meta.reject { |k, v| k == :user_metadata || v.nil? || PRIVATE_METADATA.include?(k) }
71+
meta.merge!(@metadata) if @metadata
72+
73+
# Internal metadata takes precedence over user metadata, but store
74+
# everything in user metadata
75+
vernier_profile.meta[:user_metadata]&.merge!(meta)
76+
6377
# HACK: - "data" is private, but we want to avoid serializing to JSON then
6478
# parsing back from JSON by just directly getting the hash
6579
data = ::Vernier::Output::Firefox.new(vernier_profile).send(:data)
66-
data[:meta][:mode] = @mode # TODO: https://github.com/jhawthorn/vernier/issues/30
67-
data[:meta].merge!(@metadata) if @metadata
80+
data[:meta][:mode] = @mode
81+
data[:meta][:vernierUserMetadata] ||= meta # for compatibility with < 1.7.0
6882
@mode = nil
6983
@metadata = nil
7084

lib/app_profiler/base_profile.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def from_stackprof(data)
2323
end
2424

2525
def from_vernier(data)
26-
options = INTERNAL_METADATA_KEYS.map { |key| [key, data[:meta]&.delete(key)] }.to_h
26+
options = INTERNAL_METADATA_KEYS.map { |key| [key, data[:meta]&.[](:vernierUserMetadata)&.delete(key)] }.to_h
2727

2828
VernierProfile.new(data, **options).tap do |profile|
2929
raise ArgumentError, "invalid profile data" unless profile.valid?

lib/app_profiler/vernier_profile.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ def backend_name
1414

1515
def initialize(data, id: nil, context: nil)
1616
data[:meta] ||= {}
17+
data[:meta][:vernierUserMetadata] ||= {}
1718
super(data, id: id, context: context)
1819
end
1920

@@ -22,7 +23,7 @@ def mode
2223
end
2324

2425
def metadata
25-
@data[:meta]
26+
@data[:meta][:vernierUserMetadata]
2627
end
2728

2829
def format

test/app_profiler/backend/vernier_backend_test.rb

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def teardown
7777
assert_instance_of(AppProfiler::VernierProfile, profile)
7878
assert_equal("wowza", profile.id)
7979
assert_equal("bar", profile.context)
80-
assert_equal("spam", profile[:meta][:extrameta])
80+
assert_equal("spam", profile.metadata[:extrameta])
8181
end
8282

8383
test ".run wall profile" do
@@ -200,6 +200,50 @@ def teardown
200200

201201
assert_nil(AppProfiler.profiler.results)
202202
end
203+
204+
test ".results contain vernierUserMetadata, and extra meta" do
205+
skip "metadata output added in >=1.7.0" if Gem.loaded_specs["vernier"].version < Gem::Version.new("1.7.0")
206+
207+
profile = AppProfiler.profiler.run(
208+
vernier_params(
209+
interval: 2000,
210+
metadata: {
211+
user_data_1: "foo",
212+
user_data_2: "bar",
213+
interval: 1000,
214+
},
215+
),
216+
) do
217+
sleep(0.1)
218+
end
219+
220+
assert_instance_of(AppProfiler::VernierProfile, profile)
221+
222+
# Stores "internal" vernier metadata
223+
assert_equal(:wall, profile.metadata[:mode])
224+
assert_equal(2000, profile.metadata[:interval]) # The internal value takes precedence over the user value
225+
assert_equal(0, profile.metadata[:allocation_interval])
226+
assert_equal(false, profile.metadata[:gc])
227+
228+
# Don't include ignored/private metadata
229+
AppProfiler::Backend::VernierBackend::PRIVATE_METADATA.each do |excluded|
230+
refute_includes(profile.metadata, excluded)
231+
end
232+
233+
# Stores the user supplied data
234+
assert_equal("foo", profile.metadata[:user_data_1])
235+
assert_equal("foo", profile.metadata[:user_data_1])
236+
237+
# Check that "extra" meta (for UI display) is also present
238+
{
239+
user_data_1: "foo",
240+
user_data_2: "bar",
241+
interval: 2000,
242+
mode: :wall,
243+
}.each do |k, v|
244+
assert(profile[:meta][:extra].first[:entries].find { |e| e[:label] == k && e[:value] == v })
245+
end
246+
end
203247
end
204248
end
205249
end

test/app_profiler/profile/vernier_profile_test.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
module AppProfiler
66
class VernierProfileTest < TestCase
77
test ".from_vernier assigns id and context metadata" do
8-
profile = BaseProfile.from_vernier(vernier_profile(meta: { id: "foo", context: "bar" }))
8+
profile = BaseProfile.from_vernier(vernier_profile(meta: { vernierUserMetadata: { id: "foo", context: "bar" } }))
99

1010
assert_equal("foo", profile.id)
1111
assert_equal("foo", ProfileId.current)
@@ -97,14 +97,19 @@ class VernierProfileTest < TestCase
9797
end
9898

9999
test "#[] forwards to profile metadata" do
100-
profile = VernierProfile.new(vernier_profile(meta: { interval: 10_000 }))
100+
profile = VernierProfile.new(vernier_profile(meta: { vernierUserMetadata: { interval: 10_000 } }))
101101

102102
assert_equal(10_000, profile.metadata[:interval])
103103
end
104104

105105
test "#path raises an UnsafeFilename exception given chars not in allow list" do
106106
assert_raises(AppProfiler::BaseProfile::UnsafeFilename) do
107-
profile = BaseProfile.from_vernier(vernier_profile(meta: { id: "|`@${}", context: "bar" }))
107+
profile = BaseProfile.from_vernier(vernier_profile(meta: {
108+
vernierUserMetadata: {
109+
id: "|`@${}",
110+
context: "bar",
111+
},
112+
}))
108113
profile.file
109114
end
110115
end
@@ -126,7 +131,7 @@ class VernierProfileTest < TestCase
126131
test "#file uses custom profile_file_name block when provided" do
127132
old_profile_file_name = AppProfiler.profile_file_name
128133
AppProfiler.profile_file_name = ->(metadata) { "file-name-#{metadata[:id]}" }
129-
profile = VernierProfile.new(vernier_profile(meta: { id: "foo", context: "bar" }))
134+
profile = VernierProfile.new(vernier_profile(meta: { vernierUserMetadata: { id: "foo", context: "bar" } }))
130135
assert_match("file-name-foo.vernier.json", File.basename(profile.file.to_s))
131136
ensure
132137
AppProfiler.profile_file_name = old_profile_file_name

0 commit comments

Comments
 (0)