diff --git a/.gitignore b/.gitignore index 25619c47f9538f231eea6b63316f362310b4923c..26f8ea93a88f33922a3ef3eb5dbf7e80a70e2498 100644 --- a/.gitignore +++ b/.gitignore @@ -133,6 +133,7 @@ **/vendor/bundle/ruby/*/gems/ruby-progressbar-*/ **/vendor/bundle/ruby/*/gems/simplecov-*/ **/vendor/bundle/ruby/*/gems/simplecov-html-*/ +**/vendor/bundle/ruby/*/gems/stackprof-*/ **/vendor/bundle/ruby/*/gems/thor-*/ **/vendor/bundle/ruby/*/gems/unf_ext-*/ **/vendor/bundle/ruby/*/gems/unf-*/ diff --git a/Library/Homebrew/brew.rb b/Library/Homebrew/brew.rb index d33b6948abf4a652d7ce79680d4b08e276095974..ee3b9206ff5dfe9d63fa14e0c8c6d521abebc55c 100644 --- a/Library/Homebrew/brew.rb +++ b/Library/Homebrew/brew.rb @@ -1,5 +1,12 @@ # frozen_string_literal: true +if ENV["HOMEBREW_STACKPROF"] + require_relative "utils/gems" + Homebrew.setup_gem_environment! + require "stackprof" + StackProf.start(mode: :wall, raw: true) +end + raise "HOMEBREW_BREW_FILE was not exported! Please call bin/brew directly!" unless ENV["HOMEBREW_BREW_FILE"] std_trap = trap("INT") { exit! 130 } # no backtrace thanks @@ -186,4 +193,9 @@ rescue Exception => e # rubocop:disable Lint/RescueException exit 1 else exit 1 if Homebrew.failed? +ensure + if ENV["HOMEBREW_STACKPROF"] + StackProf.stop + StackProf.results("prof/stackprof.dump") + end end diff --git a/Library/Homebrew/dev-cmd/prof.rb b/Library/Homebrew/dev-cmd/prof.rb index d75f59e9dd19955f0d203ed0ef5e90ef7138a5aa..3abb9b9ab3564447572ad57f0cd14e17f2fdfdc1 100644 --- a/Library/Homebrew/dev-cmd/prof.rb +++ b/Library/Homebrew/dev-cmd/prof.rb @@ -10,17 +10,32 @@ module Homebrew usage_banner <<~EOS `prof` [<command>] - Run Homebrew with the Ruby profiler, e.g. `brew prof readall`. + Run Homebrew with a Ruby profiler, e.g. `brew prof readall`. EOS + switch "--stackprof", + description: "Use `stackprof` instead of `ruby-prof` (the default)." end end def prof args = prof_args.parse - Homebrew.install_gem_setup_path! "ruby-prof", version: "0.18.0" - FileUtils.mkdir_p "prof" brew_rb = (HOMEBREW_LIBRARY_PATH/"brew.rb").resolved_path - safe_system "ruby-prof", "--printer=multi", "--file=prof", brew_rb, "--", *args.named + FileUtils.mkdir_p "prof" + + if args.stackprof? + Homebrew.install_gem_setup_path! "stackprof" + with_env HOMEBREW_STACKPROF: "1" do + safe_system ENV["HOMEBREW_RUBY_PATH"], brew_rb, *args.named + end + output_filename = "prof/d3-flamegraph.html" + safe_system "stackprof --d3-flamegraph prof/stackprof.dump > #{output_filename}" + else + Homebrew.install_gem_setup_path! "ruby-prof" + output_filename = "prof/call_stack.html" + safe_system "ruby-prof", "--printer=call_stack", "--file=#{output_filename}", brew_rb, "--", *args.named + end + + exec_browser output_filename end end diff --git a/docs/Manpage.md b/docs/Manpage.md index 3c0ebe39db17619cf2ee21afa2a144714488b068..3cef1f11406a528b646f82e1e37d396e0c6fd910 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -1082,7 +1082,10 @@ Apply the bottle commit and publish bottles to Bintray. ### `prof` [*`command`*] -Run Homebrew with the Ruby profiler, e.g. `brew prof readall`. +Run Homebrew with a Ruby profiler, e.g. `brew prof readall`. + +* `--stackprof`: + Use `stackprof` instead of `ruby-prof` (the default). ### `release-notes` [*`options`*] [*`previous_tag`*] [*`end_ref`*] diff --git a/manpages/brew.1 b/manpages/brew.1 index 1fda57a44403bf4c71cf63cf6494cea92e08410a..1434de7a4be5616931e9321f83b87e444329242b 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -1498,7 +1498,11 @@ Upload to the specified Bintray organisation (default: \fBhomebrew\fR)\. Use the specified \fIURL\fR as the root of the bottle\'s URL instead of Homebrew\'s default\. . .SS "\fBprof\fR [\fIcommand\fR]" -Run Homebrew with the Ruby profiler, e\.g\. \fBbrew prof readall\fR\. +Run Homebrew with a Ruby profiler, e\.g\. \fBbrew prof readall\fR\. +. +.TP +\fB\-\-stackprof\fR +Use \fBstackprof\fR instead of \fBruby\-prof\fR (the default)\. . .SS "\fBrelease\-notes\fR [\fIoptions\fR] [\fIprevious_tag\fR] [\fIend_ref\fR]" Print the merged pull requests on Homebrew/brew between two Git refs\. If no \fIprevious_tag\fR is provided it defaults to the latest tag\. If no \fIend_ref\fR is provided it defaults to \fBorigin/master\fR\.