From 7e0d0a34591e22d093c993763b2e1a7eb7114786 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Sun, 5 Jul 2020 00:57:33 -0400 Subject: [PATCH] Basic support for profiling with bin/rubocop-profile --- .gitignore | 4 ++++ CHANGELOG.md | 4 ++++ Gemfile | 1 + bin/rubocop-profile | 16 ++++++++++++++++ tasks/prof.rake | 40 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 65 insertions(+) create mode 100755 bin/rubocop-profile create mode 100644 tasks/prof.rake diff --git a/.gitignore b/.gitignore index 35e381ad50c..b90a35666a1 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,7 @@ TAGS # For bundle binstubs bin/* +!bin/rubocop-profile + +# For stackprof or others +tmp/* diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f6fbe691bb..97a8a82b850 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## master (unreleased) +### New features + +* [#8242](https://github.com/rubocop-hq/rubocop/pull/8242): Internal profiling available with `bin/rubocop-profile` and rake tasks. ([@marcandre][]) + ## 0.87.1 (2020-07-07) ### Bug fixes diff --git a/Gemfile b/Gemfile index c5fde9f4d8f..20deacb12c1 100644 --- a/Gemfile +++ b/Gemfile @@ -14,6 +14,7 @@ gem 'rubocop-rspec', '~> 1.39.0' # Stop upgrading SimpleCov until the following issue will be resolved. # https://github.com/codeclimate/test-reporter/issues/418 gem 'simplecov', '~> 0.10', '< 0.18' +gem 'stackprof', platform: :mri gem 'test-queue' gem 'yard', '~> 0.9' diff --git a/bin/rubocop-profile b/bin/rubocop-profile new file mode 100755 index 00000000000..7942e1bec8f --- /dev/null +++ b/bin/rubocop-profile @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'stackprof' + +StackProf.start +start = Process.clock_gettime(Process::CLOCK_MONOTONIC) +begin + load "#{__dir__}/../exe/rubocop" +ensure + delta = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start + StackProf.stop + Dir.mkdir('tmp') unless File.exist?('tmp') + StackProf.results('tmp/stackprof.dump') + puts "Finished in #{delta.round(1)} seconds" +end diff --git a/tasks/prof.rake b/tasks/prof.rake new file mode 100644 index 00000000000..d6da3edb473 --- /dev/null +++ b/tasks/prof.rake @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +namespace :prof do + DUMP_PATH = 'tmp/stackprof.dump' + + desc 'Run RuboCop on itself with profiling on' + task :run, [:path] do |_task, args| + path = args.fetch(:path, '.') + cmd = "bin/rubocop-profile #{path}" + system cmd + end + + task :run_if_needed, [:path] do + Rake::Task[:run].run unless File.exist?(DUMP_PATH) + end + + desc 'List the slowest cops' + task slow_cops: :run_if_needed do + method = 'RuboCop::Cop::Commissioner#trigger_responding_cops' + cmd = "stackprof #{DUMP_PATH} --text --method '#{method}'" + puts cmd + output = `#{cmd}` + _header, list, _code = *output + .lines + .grep_v(/RuboCop::Cop::Commissioner/) # ignore internal calls + .slice_when { |line| line.match?(/callees.*:|code:/) } + puts list.first(40) + end + + desc 'Check a particular method by walking through the callstack' + task :walk, [:method] => :run_if_needed do |_task, args| + method = args.fetch(:method) do + warn 'usage: bundle exec rake walk[Class#method]' + exit! + end + cmd = "stackprof #{DUMP_PATH} --walk --method '#{method}'" + puts cmd + system cmd + end +end