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\.