Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use SIGWINCH to print thread backtraces #2195

Merged
merged 1 commit into from Mar 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions History.md
Expand Up @@ -7,6 +7,7 @@
* `GC.compact` is called before fork if available (#2093)
* Add `requests_count` to workers stats. (#2106)
* Increases maximum URI path length from 2048 to 8196 bytes (#2167)
* Sending SIGWINCH to any Puma worker now prints currently active threads and their backtraces (#2195)

* Deprecations, Removals and Breaking API Changes
* `Puma.stats` now returns a Hash instead of a JSON string (#2086)
Expand Down
2 changes: 1 addition & 1 deletion docs/signals.md
Expand Up @@ -40,7 +40,7 @@ Puma cluster responds to these signals:
- `USR1` restart workers in phases, a rolling restart. This will not reload configuration file.
- `HUP` reopen log files defined in stdout_redirect configuration parameter. If there is no stdout_redirect option provided it will behave like `INT`
- `INT` equivalent of sending Ctrl-C to cluster. Will attempt to finish then exit.
- `CHLD`
- `WINCH` (`INFO` on BSD-based systems) prints a thread backtrace. This is useful for debugging infinite loops or slow performance.

## Callbacks order in case of different signals

Expand Down
20 changes: 16 additions & 4 deletions lib/puma/launcher.rb
Expand Up @@ -452,13 +452,18 @@ def setup_signals
log "*** SIGHUP not implemented, signal based logs reopening unavailable!"
end

begin
Signal.trap "SIGWINCH" do
log_backtrace
end
rescue Exception
log "*** SIGWINCH not implemented, signal based thread backtraces unavailable!"
end

begin
unless Puma.jruby? # INFO in use by JVM already
Signal.trap "SIGINFO" do
thread_status do |name, backtrace|
@events.log name
@events.log backtrace.map { |bt| " #{bt}" }
end
log_backtrace
end
end
rescue Exception
Expand All @@ -467,6 +472,13 @@ def setup_signals
end
end

def log_backtrace
thread_status do |name, backtrace|
@events.log name
@events.log backtrace.map { |bt| " #{bt}" }
end
end

def require_rubygems_min_version!(min_version, feature)
return if min_version <= Gem::Version.new(Gem::VERSION)

Expand Down
28 changes: 19 additions & 9 deletions test/test_integration_cluster.rb
Expand Up @@ -33,18 +33,16 @@ def test_pre_existing_unix
end
end

def test_sigwinch_thread_print
skip_unless_signal_exist? :WINCH

signal_thread_backtrace :WINCH
end

def test_siginfo_thread_print
skip_unless_signal_exist? :INFO

cli_server "-w #{WORKERS} -q test/rackup/hello.ru"
worker_pids = get_worker_pids
output = []
t = Thread.new { output << @server.readlines }
Process.kill :INFO, worker_pids.first
Process.kill :INT , @pid
t.join

assert_match "Thread: TID", output.join
signal_thread_backtrace :INFO
end

def test_usr2_restart
Expand Down Expand Up @@ -135,6 +133,18 @@ def test_stuck_phased_restart

private

def signal_thread_backtrace(signal)
cli_server "-w #{WORKERS} -q test/rackup/hello.ru"
worker_pids = get_worker_pids
output = []
t = Thread.new { output << @server.readlines }
Process.kill signal, worker_pids.first
Process.kill :INT , @pid
t.join

assert_match "Thread: TID", output.join
end

# Send requests 10 per second. Send 10, then :TERM server, then send another 30.
# No more than 10 should throw Errno::ECONNRESET.
def term_closes_listeners(unix: false)
Expand Down