Skip to content

Commit 148aa55

Browse files
dalehamelgmcgibbon
andcommitted
Add support for vernier profile vieweing
Adds RemoteFirefoxViewer which uses the Firefox Profiler via middleware to serve profiles. - Fixes deprecation syntax - Fixes several warnings - Deprecates AppProfiler.viewer - Removes deprecard AppProfiler::Profile constant - Fixes directory structure to make files easier to find Co-authored-by: Gannon McGibbon <gannon.mcgibbon@shopify.com>
1 parent 7a0f861 commit 148aa55

30 files changed

Lines changed: 610 additions & 255 deletions

.rubocop.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ require: rubocop-performance
55

66
Style/StringLiterals:
77
EnforcedStyle: double_quotes
8+
9+
AllCops:
10+
SuggestExtensions: false

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,11 +290,16 @@ report = AppProfiler.run(mode: :cpu) do
290290
# ...
291291
end
292292

293-
report.view # opens the profile locally in speedscope.
293+
report.view # opens the profile locally in speedscope by default
294294
```
295295

296296
Profile files can be found locally in your rails app at `tmp/app_profiler/*.json`.
297297

298+
**Note** In development, if using the `AppProfiler::Viewer::SpeedscopeRemoteViewer` for stackprof
299+
or if using Vernier, a route for `/app_profiler` will be added to the application.
300+
If using Vernier, a route for `/from-url` is also added. These will be handled
301+
in middlewares, before any application routing logic. There is a small chance
302+
that these could shadow existing routes in the application.
298303

299304
## Storage backends
300305

@@ -336,6 +341,8 @@ Rails.application.config.app_profiler.backend = AppProfiler::StackprofBackend #
336341

337342
By default, the stackprof backend will be used.
338343

344+
In local development, changing the backend will change whether the profile is viewed in Speedscope or Firefox Profiler.
345+
339346

340347
## Profile Sampling
341348

lib/app_profiler.rb

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,20 @@ module Storage
3030
module Viewer
3131
autoload :BaseViewer, "app_profiler/viewer/base_viewer"
3232
autoload :SpeedscopeViewer, "app_profiler/viewer/speedscope_viewer"
33+
autoload :BaseMiddleware, "app_profiler/viewer/base_middleware"
3334
autoload :SpeedscopeRemoteViewer, "app_profiler/viewer/speedscope_remote_viewer"
35+
autoload :FirefoxRemoteViewer, "app_profiler/viewer/firefox_remote_viewer"
3436
end
3537

36-
require "app_profiler/middleware"
37-
require "app_profiler/parameters"
38-
require "app_profiler/request_parameters"
39-
require "app_profiler/profile"
40-
require "app_profiler/backend"
41-
require "app_profiler/server"
42-
require "app_profiler/sampler"
38+
autoload(:Middleware, "app_profiler/middleware")
39+
autoload(:Parameters, "app_profiler/parameters")
40+
autoload(:RequestParameters, "app_profiler/request_parameters")
41+
autoload(:BaseProfile, "app_profiler/base_profile")
42+
autoload :StackprofProfile, "app_profiler/stackprof_profile"
43+
autoload :VernierProfile, "app_profiler/vernier_profile"
44+
autoload(:Backend, "app_profiler/backend")
45+
autoload(:Server, "app_profiler/server")
46+
autoload(:Sampler, "app_profiler/sampler")
4347

4448
mattr_accessor :logger, default: Logger.new($stdout)
4549
mattr_accessor :root
@@ -50,11 +54,10 @@ module Viewer
5054
mattr_reader :profile_header, default: "X-Profile"
5155
mattr_accessor :profile_async_header, default: "X-Profile-Async"
5256
mattr_accessor :context, default: nil
53-
mattr_reader :profile_url_formatter,
54-
default: DefaultProfileFormatter
55-
57+
mattr_reader :profile_url_formatter, default: DefaultProfileFormatter
5658
mattr_accessor :storage, default: Storage::FileStorage
57-
mattr_accessor :viewer, default: Viewer::SpeedscopeViewer
59+
mattr_writer :stackprof_viewer, default: nil
60+
mattr_writer :vernier_viewer, default: nil
5861
mattr_accessor :middleware, default: Middleware
5962
mattr_accessor :server, default: Server
6063
mattr_accessor :upload_queue_max_length, default: 10
@@ -68,6 +71,10 @@ module Viewer
6871
mattr_accessor :profile_sampler_config
6972

7073
class << self
74+
def deprecator # :nodoc:
75+
@deprecator ||= ActiveSupport::Deprecation.new("in future releases", "app_profiler")
76+
end
77+
7178
def run(*args, backend: nil, **kwargs, &block)
7279
orig_backend = self.backend
7380
begin
@@ -117,6 +124,14 @@ def backend=(new_backend)
117124
@profiler_backend = new_profiler_backend
118125
end
119126

127+
def stackprof_viewer
128+
@@stackprof_viewer ||= Viewer::SpeedscopeViewer # rubocop:disable Style/ClassVars
129+
end
130+
131+
def vernier_viewer
132+
@@vernier_viewer ||= Viewer::FirefoxRemoteViewer # rubocop:disable Style/ClassVars
133+
end
134+
120135
def profile_sampler_enabled=(value)
121136
if value.is_a?(Proc)
122137
raise ArgumentError,
@@ -207,6 +222,16 @@ def profile_url(upload)
207222
AppProfiler.profile_url_formatter.call(upload)
208223
end
209224

225+
def viewer
226+
deprecator.warn("AppProfiler.viewer is deprecated, please use stackprof_viewer instead.")
227+
stackprof_viewer
228+
end
229+
230+
def viewer=(viewer)
231+
deprecator.warn("AppProfiler.viewer= is deprecated, please use stackprof_viewer= instead.")
232+
self.stackprof_viewer = viewer
233+
end
234+
210235
private
211236

212237
def clear
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
require "active_support/deprecation/constant_accessor"
44

55
module AppProfiler
6-
autoload :StackprofProfile, "app_profiler/profile/stackprof"
7-
autoload :VernierProfile, "app_profiler/profile/vernier"
8-
96
class BaseProfile
107
INTERNAL_METADATA_KEYS = [:id, :context]
118
private_constant :INTERNAL_METADATA_KEYS
@@ -111,7 +108,4 @@ def path
111108
AppProfiler.profile_root.join(filename)
112109
end
113110
end
114-
115-
include ActiveSupport::Deprecation::DeprecatedConstantAccessor
116-
deprecate_constant "Profile", "AppProfiler::BaseProfile", deprecator: ActiveSupport::Deprecation.new
117111
end

lib/app_profiler/middleware.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
require "app_profiler/middleware/base_action"
55
require "app_profiler/middleware/upload_action"
66
require "app_profiler/middleware/view_action"
7-
require "app_profiler/sampler/config"
87

98
module AppProfiler
109
class Middleware

lib/app_profiler/railtie.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ class Railtie < Rails::Railtie
1111
AppProfiler.logger = app.config.app_profiler.logger || Rails.logger
1212
AppProfiler.root = app.config.app_profiler.root || Rails.root
1313
AppProfiler.storage = app.config.app_profiler.storage || Storage::FileStorage
14-
AppProfiler.viewer = app.config.app_profiler.viewer || Viewer::SpeedscopeViewer
14+
if app.config.app_profiler.stackprof_viewer
15+
AppProfiler.stackprof_viewer = app.config.app_profiler.stackprof_viewer
16+
end
17+
if app.config.app_profiler.vernier_viewer
18+
AppProfiler.vernier_viewer = app.config.app_profiler.vernier_viewer
19+
end
1520
AppProfiler.storage.bucket_name = app.config.app_profiler.storage_bucket_name || "profiles"
1621
AppProfiler.storage.credentials = app.config.app_profiler.storage_credentials || {}
1722
AppProfiler.middleware = app.config.app_profiler.middleware || Middleware
@@ -49,8 +54,8 @@ class Railtie < Rails::Railtie
4954

5055
initializer "app_profiler.add_middleware" do |app|
5156
unless AppProfiler.middleware.disabled
52-
if AppProfiler.viewer == Viewer::SpeedscopeRemoteViewer
53-
app.middleware.insert_before(0, Viewer::SpeedscopeRemoteViewer::Middleware)
57+
if (Rails.env.development? || Rails.env.test?) && AppProfiler.stackprof_viewer.remote?
58+
app.middleware.insert_before(0, AppProfiler.viewer::Middleware)
5459
end
5560
app.middleware.insert_before(0, AppProfiler.middleware)
5661
end

lib/app_profiler/sampler.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require "app_profiler/sampler/config"
4+
45
module AppProfiler
56
module Sampler
67
@excluded_cache = {}

lib/app_profiler/sampler/config.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
require "app_profiler/sampler/stackprof_config"
44
require "app_profiler/sampler/vernier_config"
5+
56
module AppProfiler
67
module Sampler
78
class Config
@@ -27,7 +28,7 @@ def initialize(sample_rate: SAMPLE_RATE,
2728

2829
raise ArgumentError, "mode probabilities must sum to 1" unless backends_probability.values.sum == 1.0
2930

30-
ActiveSupport::Deprecation.new.warn("passing paths is deprecated, use targets instead") if paths
31+
AppProfiler.deprecator.warn("passing paths is deprecated, use targets instead") if paths
3132

3233
@sample_rate = sample_rate
3334
@targets = paths || targets
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
module AppProfiler
44
class StackprofProfile < BaseProfile
5-
FILE_EXTENSION = ".json"
5+
FILE_EXTENSION = ".stackprof.json"
66

77
def mode
88
@data[:mode]
@@ -17,7 +17,7 @@ def format
1717
end
1818

1919
def view(params = {})
20-
AppProfiler.viewer.view(self, **params)
20+
AppProfiler.stackprof_viewer.view(self, **params)
2121
end
2222
end
2323
end
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
module AppProfiler
44
class VernierProfile < BaseProfile
5-
FILE_EXTENSION = ".gecko.json"
5+
FILE_EXTENSION = ".vernier.json"
66

77
def mode
88
@data[:meta][:mode]
@@ -17,7 +17,7 @@ def format
1717
end
1818

1919
def view(params = {})
20-
raise NotImplementedError
20+
AppProfiler.vernier_viewer.view(self, **params)
2121
end
2222
end
2323
end

0 commit comments

Comments
 (0)