From 964a91c463299ce28af69589ffa840df87593b4d Mon Sep 17 00:00:00 2001 From: Daniel Colson Date: Thu, 24 Oct 2019 22:53:13 -0400 Subject: [PATCH] Add pumactl command to print thread backtraces Completes 1 of 2 items from #1964 This commit adds an endpoint to the status app to print thread backtraces, and control cli command to call that endpoint. --- History.md | 1 + lib/puma/app/status.rb | 5 +++++ lib/puma/control_cli.rb | 5 +++-- lib/puma/launcher.rb | 32 ++++++++++++++++---------------- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/History.md b/History.md index 8e2f1e4e37..8fda4467ca 100644 --- a/History.md +++ b/History.md @@ -4,6 +4,7 @@ * Strip whitespace at end of HTTP headers (#2010) * Optimize HTTP parser for JRuby (#2012) * Add SSL support for the control app (#2046) + * Add pumactl `thread-backtraces` command to print thread backtraces (#2053) * Bugfixes * Fix Errno::EINVAL when SSL is enabled and browser rejects cert (#1564) diff --git a/lib/puma/app/status.rb b/lib/puma/app/status.rb index 2e4b4e4c3e..f4ef9f2eeb 100644 --- a/lib/puma/app/status.rb +++ b/lib/puma/app/status.rb @@ -55,6 +55,11 @@ def call(env) when /\/stats$/ rack_response(200, @cli.stats) + + when /\/thread-backtraces$/ + strings = Puma::Events.strings + @cli.log_thread_status(strings) + rack_response(200, strings.stdout.string) else rack_response 404, "Unsupported action", 'text/plain' end diff --git a/lib/puma/control_cli.rb b/lib/puma/control_cli.rb index 6bad828b88..4ba2661511 100644 --- a/lib/puma/control_cli.rb +++ b/lib/puma/control_cli.rb @@ -11,7 +11,8 @@ module Puma class ControlCLI - COMMANDS = %w{halt restart phased-restart start stats status stop reload-worker-directory gc gc-stats} + COMMANDS = %w{halt restart phased-restart start stats status stop reload-worker-directory gc gc-stats thread-backtraces} + PRINTABLE_COMMANDS = %w{gc-stats stats thread-backtraces} def initialize(argv, stdout=STDOUT, stderr=STDERR) @state = nil @@ -181,7 +182,7 @@ def send_request end message "Command #{@command} sent success" - message response.last if @command == "stats" || @command == "gc-stats" + message response.last if PRINTABLE_COMMANDS.include?(@command) end ensure server.close if server && !server.closed? diff --git a/lib/puma/launcher.rb b/lib/puma/launcher.rb index 2a0cd9be39..0c93fb7663 100644 --- a/lib/puma/launcher.rb +++ b/lib/puma/launcher.rb @@ -205,6 +205,21 @@ def close_binder_listeners @binder.close_listeners end + def log_thread_status(events) + Thread.list.each do |thread| + events.log "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}" + logstr = "Thread: TID-#{thread.object_id.to_s(36)}" + logstr += " #{thread.name}" if thread.respond_to?(:name) + events.log logstr + + if thread.backtrace + events.log thread.backtrace.join("\n") + else + events.log "" + end + end + end + private # If configured, write the pid of the current process out @@ -323,21 +338,6 @@ def graceful_stop log "- Goodbye!" end - def log_thread_status - Thread.list.each do |thread| - log "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}" - logstr = "Thread: TID-#{thread.object_id.to_s(36)}" - logstr += " #{thread.name}" if thread.respond_to?(:name) - log logstr - - if thread.backtrace - log thread.backtrace.join("\n") - else - log "" - end - end - end - def set_process_title Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title end @@ -457,7 +457,7 @@ def setup_signals begin Signal.trap "SIGINFO" do - log_thread_status + log_thread_status(@events) end rescue Exception # Not going to log this one, as SIGINFO is *BSD only and would be pretty annoying