From 3f1527b3e38208fb76c0df3795dd80c74f192762 Mon Sep 17 00:00:00 2001 From: schneems Date: Wed, 3 Nov 2021 12:57:09 -0500 Subject: [PATCH] Add profiler generation to test suite You can output profiler data to the `tmp` directory by running: ``` $ DEBUG_PERF=1 bundle exec rspec spec/integration/dead_end_spec.rb ``` Some outputs are in text format, some are html. See https://ruby-prof.github.io/#reports for more info. One interesting one, is the "kcachegrind" interface. To view this on mac: ``` $ brew install qcachegrind ``` Open: ``` $ qcachegrind tmp//profile.callgrind.out.14508 ``` --- Gemfile | 1 + Gemfile.lock | 4 +++- README.md | 22 +++++++++++++++++++- lib/dead_end.rb | 10 +++++++++ lib/dead_end/code_search.rb | 3 +-- spec/integration/dead_end_spec.rb | 34 +++++++++++++++++++++++-------- 6 files changed, 62 insertions(+), 12 deletions(-) diff --git a/Gemfile b/Gemfile index b0065fe..de47413 100644 --- a/Gemfile +++ b/Gemfile @@ -9,3 +9,4 @@ gem "rake", "~> 12.0" gem "rspec", "~> 3.0" gem "stackprof" gem "standard" +gem "ruby-prof" diff --git a/Gemfile.lock b/Gemfile.lock index eb0c618..4e5230c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -42,6 +42,7 @@ GEM rubocop-performance (1.11.5) rubocop (>= 1.7.0, < 2.0) rubocop-ast (>= 0.4.0) + ruby-prof (1.4.3) ruby-progressbar (1.11.0) stackprof (0.2.16) standard (1.3.0) @@ -56,8 +57,9 @@ DEPENDENCIES dead_end! rake (~> 12.0) rspec (~> 3.0) + ruby-prof stackprof standard BUNDLED WITH - 2.2.29 + 2.2.30 diff --git a/README.md b/README.md index d1f670e..6932e7f 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,27 @@ To install this gem onto your local machine, run `bundle exec rake install`. To You can see changes to output against a variety of invalid code by running specs and using the `DEBUG_DISPLAY=1` environment variable. For example: ``` -$ DEBUG_DISPLAY=1 be rspec spec/ --format=failures +$ DEBUG_DISPLAY=1 bundle exec rspec spec/ --format=failures +``` + +### Run profiler + +You can output profiler data to the `tmp` directory by running: + +``` +$ DEBUG_PERF=1 bundle exec rspec spec/integration/dead_end_spec.rb +``` + +Some outputs are in text format, some are html, the raw marshaled data is available in `raw.rb.marshal`. See https://ruby-prof.github.io/#reports for more info. One interesting one, is the "kcachegrind" interface. To view this on mac: + +``` +$ brew install qcachegrind +``` + +Open: + +``` +$ qcachegrind tmp/last/profile.callgrind.out. ``` ## Contributing diff --git a/lib/dead_end.rb b/lib/dead_end.rb index 900f6e5..f93a276 100644 --- a/lib/dead_end.rb +++ b/lib/dead_end.rb @@ -28,6 +28,16 @@ def self.handle_error(e) raise e end + def self.record_dir(dir) + time = Time.now.strftime("%Y-%m-%d-%H-%M-%s-%N") + dir = Pathname(dir) + symlink = dir.join("last").tap { |path| path.delete if path.exist? } + dir.join(time).tap { |path| + path.mkpath + FileUtils.symlink(path.basename, symlink) + } + end + def self.call(source:, filename: DEFAULT_VALUE, terminal: DEFAULT_VALUE, record_dir: nil, timeout: TIMEOUT_DEFAULT, io: $stderr) search = nil filename = nil if filename == DEFAULT_VALUE diff --git a/lib/dead_end/code_search.rb b/lib/dead_end/code_search.rb index 400cf32..c90a33b 100644 --- a/lib/dead_end/code_search.rb +++ b/lib/dead_end/code_search.rb @@ -43,8 +43,7 @@ class CodeSearch def initialize(source, record_dir: ENV["DEAD_END_RECORD_DIR"] || ENV["DEBUG"] ? "tmp" : nil) if record_dir - @time = Time.now.strftime("%Y-%m-%d-%H-%M-%s-%N") - @record_dir = Pathname(record_dir).join(@time).tap { |p| p.mkpath } + @record_dir = DeadEnd.record_dir(record_dir) @write_count = 0 end diff --git a/spec/integration/dead_end_spec.rb b/spec/integration/dead_end_spec.rb index e21e9a8..dc093bb 100644 --- a/spec/integration/dead_end_spec.rb +++ b/spec/integration/dead_end_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative "../spec_helper" +require "ruby-prof" module DeadEnd RSpec.describe "Integration tests that don't spawn a process (like using the cli)" do @@ -80,17 +81,34 @@ module DeadEnd source: file.read, filename: file ) - - expect(io.string).to_not include("def ruby_install_binstub_path") - expect(io.string).to include(<<~'EOM') - ❯ 1067 def add_yarn_binary - ❯ 1068 return [] if yarn_preinstalled? - ❯ 1069 | - ❯ 1075 end - EOM end debug_display(io.string) debug_display(benchmark) + + expect(io.string).to_not include("def ruby_install_binstub_path") + expect(io.string).to include(<<~'EOM') + ❯ 1067 def add_yarn_binary + ❯ 1068 return [] if yarn_preinstalled? + ❯ 1069 | + ❯ 1075 end + EOM + + if ENV["DEBUG_PERF"] + result = RubyProf.profile do + DeadEnd.call( + io: io, + source: file.read, + filename: file + ) + end + + dir = DeadEnd.record_dir("tmp") + + printer = RubyProf::MultiPrinter.new(result, [:flat, :graph, :graph_html, :tree, :call_tree, :stack, :dot]) + printer.print(path: dir, profile: "profile") + + dir.join("raw.rb.marshal").write(Marshal.dump(result)) + end end it "handles heredocs" do