Skip to content

Commit

Permalink
Implement firefox viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
gmcgibbon committed Oct 24, 2024
1 parent ad67c69 commit 8cebc39
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 46 deletions.
30 changes: 17 additions & 13 deletions lib/app_profiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module Storage
module Viewer
autoload :BaseViewer, "app_profiler/viewer/base_viewer"
autoload :SpeedscopeViewer, "app_profiler/viewer/speedscope"
autoload :FirefoxViewer, "app_profiler/viewer/firefox"
autoload :BaseMiddleware, "app_profiler/viewer/middleware/base"
autoload :SpeedscopeRemoteViewer, "app_profiler/viewer/remote/speedscope"
autoload :FirefoxRemoteViewer, "app_profiler/viewer/remote/firefox"
Expand Down Expand Up @@ -57,8 +58,8 @@ module Viewer

mattr_accessor :gecko_viewer_package, default: "https://github.com/tenderlove/profiler#v0.0.2"
mattr_accessor :storage, default: Storage::FileStorage
mattr_accessor :viewer, default: Viewer::SpeedscopeViewer # DEPRECATED
mattr_accessor :speedscope_viewer, default: Viewer::SpeedscopeViewer
mattr_accessor :stackprof_viewer, default: Viewer::SpeedscopeViewer
mattr_accessor :vernier_viewer, default: Viewer::FirefoxViewer
mattr_accessor :middleware, default: Middleware
mattr_accessor :server, default: Server
mattr_accessor :upload_queue_max_length, default: 10
Expand All @@ -72,6 +73,10 @@ module Viewer
mattr_accessor :profile_sampler_config

class << self
def deprecator # :nodoc:
@deprecator ||= ActiveSupport::Deprecation.new("in future releases", "app_profiler")
end

def run(*args, backend: nil, **kwargs, &block)
orig_backend = self.backend
begin
Expand Down Expand Up @@ -211,23 +216,22 @@ def profile_url(upload)
AppProfiler.profile_url_formatter.call(upload)
end

def viewer
deprecator.warn("AppProfiler.viewer is deprecated, please use speedscope_viewer instead.")
stackprof_viewer
end

def viewer=(viewer)
deprecator.warn("AppProfiler.viewer= is deprecated, please use speedscope_viewer= instead.")
self.stackprof_viewer = viewer
end

private

def clear
@backend.stop if @backend&.running?
@backend = nil
end

# DEPRECATIONS
def viewer
ActiveSupport::Deprecation.warn("viewer is deprecated, use speedscope_viewer instead")
@viewer
end

def viewer=(viewer)
ActiveSupport::Deprecation.warn("viewer= is deprecated, use speedscope_viewer= instead")
@viewer = viewer
end
end

require "app_profiler/railtie" if defined?(Rails::Railtie)
Expand Down
35 changes: 35 additions & 0 deletions lib/app_profiler/exec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module AppProfiler
module Exec # :nodoc:
protected

def valid_commands
raise NotImplementedError
end

def ensure_command_valid(command)
unless valid_command?(command)
raise YarnError, "Illegal command: #{command.join(" ")}."
end
end

def valid_command?(command)
valid_commands.any? do |valid_command|
next unless valid_command.size == command.size

valid_command.zip(command).all? do |valid_part, part|
part.match?(valid_part)
end
end
end

def exec(*command, silent: false, environment: {})
ensure_command_valid(command)

if silent
system(environment, *command, out: File::NULL).tap { |return_code| yield unless return_code }
else
system(environment, *command).tap { |return_code| yield unless return_code }
end
end
end
end
2 changes: 1 addition & 1 deletion lib/app_profiler/profile/stackprof.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def format
end

def view(params = {})
AppProfiler.speedscope_viewer.view(self, **params)
AppProfiler.stackprof_viewer.view(self, **params)
end
end
end
2 changes: 1 addition & 1 deletion lib/app_profiler/profile/vernier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def format
end

def view(params = {})
Viewer::FirefoxRemoteViewer.view(self, **params)
AppProfiler.vernier_viewer.view(self, **params)
end
end
end
16 changes: 12 additions & 4 deletions lib/app_profiler/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ class Railtie < Rails::Railtie
AppProfiler.logger = app.config.app_profiler.logger || Rails.logger
AppProfiler.root = app.config.app_profiler.root || Rails.root
AppProfiler.storage = app.config.app_profiler.storage || Storage::FileStorage
AppProfiler.viewer = app.config.app_profiler.viewer || Viewer::SpeedscopeRemoteViewer
AppProfiler.speedscope_viewer = app.config.app_profiler.speedscope_viewer || AppProfiler.viewer
AppProfiler.stackprof_viewer = app.config.app_profiler.speedscope_viewer || AppProfiler::Viewer::SpeedscopeViewer
AppProfiler.vernier_viewer = app.config.app_profiler.vernier_viewer || AppProfiler::Viewer::FirefoxViewer
AppProfiler.storage.bucket_name = app.config.app_profiler.storage_bucket_name || "profiles"
AppProfiler.storage.credentials = app.config.app_profiler.storage_credentials || {}
AppProfiler.middleware = app.config.app_profiler.middleware || Middleware
Expand Down Expand Up @@ -49,13 +49,21 @@ class Railtie < Rails::Railtie
AppProfiler.gecko_viewer_package = app.config.app_profiler.gecko_viewer_package || "https://github.com/firefox-devtools/profiler"
end

if Rails.gem_version >= Gem::Version.new("7.1.0.alpha")
initializer "app_profiler.register_deprecator" do |app|
app.deprecators[:vitess_rails] = AppProfiler.deprecator
end
end

initializer "app_profiler.add_middleware" do |app|
unless AppProfiler.middleware.disabled
if Rails.env.development? || Rails.env.test?
if AppProfiler.speedscope_viewer == Viewer::SpeedscopeRemoteViewer
if AppProfiler.stackprof_viewer == Viewer::SpeedscopeRemoteViewer
app.middleware.insert_before(0, Viewer::SpeedscopeRemoteViewer::Middleware)
end
app.middleware.insert_before(0, Viewer::FirefoxRemoteViewer::Middleware)
if AppProfiler.vernier_viewer == Viewer::FirefoxRemoteViewer
app.middleware.insert_before(0, Viewer::FirefoxRemoteViewer::Middleware)
end
end
app.middleware.insert_before(0, AppProfiler.middleware)
end
Expand Down
74 changes: 74 additions & 0 deletions lib/app_profiler/viewer/firefox.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# frozen_string_literal: true

require "app_profiler/exec"

module AppProfiler
module Viewer
class FirefoxViewer < BaseViewer
include Exec

class ProfileViewerError < StandardError; end

VALID_COMMANDS = [
["which", "profile-viewer"],
["gem", "install", "profile-viewer"],
["profile-viewer", /.*\.json/],
]
private_constant(:VALID_COMMANDS)

class << self
def view(profile, params = {})
new(profile).view(**params)
end
end

def valid_commands
VALID_COMMANDS
end

def initialize(profile)
super()
@profile = profile
end

def view(_params = {})
profile_viewer(@profile.file.to_s)
end

private

def setup_profile_viewer
exec("which", "profile-viewer", silent: true) do
gem_install("profile_viewer")
end
@profile_viewer_initialized = true
end

def profile_viewer_setup
@profile_viewer_initialized || false
end

def gem_install(gem)
exec("gem", "install", gem) do
raise ProfileViewerError, "Failed to run gem install #{gem}."
end
end

def profile_viewer(path)
setup_profile_viewer unless profile_viewer_setup

pid = fork do
Bundler.with_clean_env do
exec("profile-viewer", path) do
raise ProfileViewerError, "Failed to run profile-viewer #{path}."
end
end
end

sleep(1)

Process.kill(pid)
end
end
end
end
35 changes: 8 additions & 27 deletions lib/app_profiler/yarn/command.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# frozen_string_literal: true

require "app_profiler/exec"

module AppProfiler
module Yarn
module Command
include Exec

class YarnError < StandardError; end

VALID_COMMANDS = [
Expand All @@ -14,9 +18,12 @@ class YarnError < StandardError; end
["yarn", "--cwd", %r{.*/firefox-profiler}],
["yarn", "--cwd", %r{.*/firefox-profiler}, "build-prod"],
]

private_constant(:VALID_COMMANDS)

def valid_commands
VALID_COMMANDS
end

def yarn(command, *options)
setup_yarn unless yarn_setup

Expand All @@ -41,22 +48,6 @@ def yarn_setup=(state)

private

def ensure_command_valid(command)
unless valid_command?(command)
raise YarnError, "Illegal command: #{command.join(" ")}."
end
end

def valid_command?(command)
VALID_COMMANDS.any? do |valid_command|
next unless valid_command.size == command.size

valid_command.zip(command).all? do |valid_part, part|
part.match?(valid_part)
end
end
end

def ensure_yarn_installed
exec("which", "yarn", silent: true) do
raise(
Expand All @@ -73,16 +64,6 @@ def ensure_yarn_installed
def package_json_exists?
AppProfiler.root.join("package.json").exist?
end

def exec(*command, silent: false)
ensure_command_valid(command)

if silent
system(*command, out: File::NULL).tap { |return_code| yield unless return_code }
else
system(*command).tap { |return_code| yield unless return_code }
end
end
end
end
end

0 comments on commit 8cebc39

Please sign in to comment.