Overview
The debug gem provides remote debugging capabilities that establish network connections between debugger clients and target Ruby processes. The gem creates a debug server within the target application, accepting connections from external debugger clients over TCP sockets. This architecture separates the debugging interface from the running process, enabling debugging across network boundaries.
Remote debugging operates through a client-server model where the target Ruby process acts as the debug server. The debug/open
require statement initializes the debug server, binding to a specified host and port. External clients connect using the rdbg
command-line tool or compatible debugger interfaces.
# Start remote debug server in target application
require 'debug/open'
class CalculationService
def process_data(numbers)
binding.b # Breakpoint triggers remote debugging session
numbers.sum / numbers.length
end
end
service = CalculationService.new
result = service.process_data([10, 20, 30])
The debug gem supports multiple transport protocols including TCP sockets and Unix domain sockets. TCP connections enable debugging across network boundaries, while Unix sockets provide local inter-process debugging with reduced network overhead.
# Configure remote debugging with specific host and port
require 'debug/open_nonstop'
ENV['RUBY_DEBUG_HOST'] = '192.168.1.100'
ENV['RUBY_DEBUG_PORT'] = '12345'
# Application continues execution while debug server runs
puts "Debug server available at #{ENV['RUBY_DEBUG_HOST']}:#{ENV['RUBY_DEBUG_PORT']}"
The remote debugging session maintains full debugging capabilities including breakpoints, step execution, variable inspection, and expression evaluation. The debug server serializes debugging state and commands between client and target process, maintaining debugging context across the network connection.
Basic Usage
Remote debugging requires configuring the target application as a debug server and connecting external clients to the debugging session. The debug/open
module initializes the server automatically when required, accepting default configuration or environment-based customization.
Starting a basic remote debug server requires adding the debug gem to the application and requiring the appropriate module:
# Basic remote debugging setup
require 'debug/open'
def fibonacci(n)
return n if n <= 1
binding.b # Remote debugger pauses here
fibonacci(n - 1) + fibonacci(n - 2)
end
puts fibonacci(8)
The debug server binds to localhost:12345
by default. External clients connect using the rdbg
command with the --attach
option:
# Connect to remote debug session
rdbg --attach 12345
Environment variables control server configuration without modifying application code. The debug gem reads these variables during initialization:
# Configure through environment variables
ENV['RUBY_DEBUG_HOST'] = '0.0.0.0' # Accept connections from any host
ENV['RUBY_DEBUG_PORT'] = '9001' # Use custom port
ENV['RUBY_DEBUG_SOCK_PATH'] = '/tmp/debug.sock' # Unix socket path
require 'debug/open'
class DataProcessor
def initialize(config)
@config = config
binding.b if config[:debug] # Conditional breakpoint
end
def transform(data)
data.map { |item| process_item(item) }
end
private
def process_item(item)
# Processing logic here
item.upcase
end
end
The debug/open_nonstop
variant starts the debug server without immediately pausing execution. This approach enables attaching debuggers to running applications without interrupting normal operation:
require 'debug/open_nonstop'
# Server starts in background, application continues
loop do
puts "Application running... #{Time.now}"
sleep 1
end
Remote debugging sessions support multiple concurrent client connections. Each client receives independent debugging views while sharing access to the same target process state. The debug server handles command serialization and state synchronization across connected clients.
Client connections persist across breakpoint encounters and continue operations. When execution reaches a breakpoint, all connected clients receive notification and can inspect the debugging context simultaneously.
Error Handling & Debugging
Remote debugging introduces network-related failure modes beyond standard debugging concerns. Connection failures, network interruptions, and port conflicts require specific error handling approaches to maintain debugging reliability.
Network connectivity issues manifest as connection timeouts or refused connections when clients attempt to attach to debug servers. The debug gem provides connection status feedback through error messages and exit codes:
# Robust debug server with error handling
begin
require 'debug/open'
rescue StandardError => e
puts "Debug server failed to start: #{e.message}"
puts "Continuing without debugging..."
end
class RobustService
def critical_method(data)
validate_data(data)
binding.b # Only activates if debug server started successfully
process_validated_data(data)
rescue ValidationError => e
# Handle validation errors appropriately
log_error(e)
raise
end
private
def validate_data(data)
raise ValidationError, "Invalid data format" unless data.is_a?(Hash)
end
def process_validated_data(data)
data.transform_values(&:to_s)
end
def log_error(error)
puts "[ERROR] #{error.class}: #{error.message}"
end
end
Port binding conflicts occur when multiple applications attempt to use the same debug port or when previous debug sessions leave sockets in use. The debug gem exits with error messages indicating port availability issues:
# Handle port conflicts with fallback configuration
debug_port = ENV.fetch('RUBY_DEBUG_PORT', '12345').to_i
begin
ENV['RUBY_DEBUG_PORT'] = debug_port.to_s
require 'debug/open_nonstop'
puts "Debug server started on port #{debug_port}"
rescue Errno::EADDRINUSE
debug_port += 1
retry if debug_port < 13000 # Try up to 500 ports
puts "No available debug ports found"
end
Connection drops during active debugging sessions cause client disconnection without affecting the target process. The debug server continues running and accepts new client connections. Lost debugging state requires re-establishing breakpoints and navigation context:
# Application with debugging hooks for connection monitoring
require 'debug/open_nonstop'
class MonitoredApplication
def initialize
@debug_active = debug_server_running?
setup_debug_hooks if @debug_active
end
def main_loop
iteration = 0
loop do
iteration += 1
binding.b if iteration % 100 == 0 # Periodic debug checkpoint
process_iteration(iteration)
end
end
private
def debug_server_running?
# Check if debug server is accessible
ENV['RUBY_DEBUG_PORT'] && !ENV['RUBY_DEBUG_PORT'].empty?
end
def setup_debug_hooks
puts "Debug server available for attachment"
puts "Connect with: rdbg --attach #{ENV['RUBY_DEBUG_PORT']}"
end
def process_iteration(num)
# Main application logic
sleep 0.1
end
end
Debugging remote applications with restricted network access requires careful firewall and security configuration. The debug server accepts connections from configured network interfaces, potentially exposing debugging capabilities to unauthorized clients. Production environments should restrict debug server access through network segmentation or authentication mechanisms.
Advanced Usage
Advanced remote debugging scenarios involve complex network topologies, custom transport configurations, and integration with deployment orchestration systems. The debug gem supports sophisticated debugging workflows through extensive configuration options and programmatic interfaces.
Multi-hop debugging enables debugging applications running in containerized environments or behind network proxies. SSH tunneling and port forwarding establish secure debugging channels across network boundaries:
# SSH tunnel for secure remote debugging
ssh -L 9001:localhost:12345 user@remote-server
# Application on remote server
RUBY_DEBUG_PORT=12345 ruby application.rb
# Local debugger connects through tunnel
rdbg --attach 9001
Container-based debugging requires exposing debug ports and configuring network access. Docker containers must publish debug ports and configure appropriate networking modes:
# Container-optimized debug configuration
ENV['RUBY_DEBUG_HOST'] = '0.0.0.0' # Accept external connections
ENV['RUBY_DEBUG_PORT'] = ENV['DEBUG_PORT'] || '12345'
require 'debug/open_nonstop'
class ContainerizedService
def self.start
puts "Debug server: #{ENV['RUBY_DEBUG_HOST']}:#{ENV['RUBY_DEBUG_PORT']}"
new.run
end
def run
setup_signal_handlers
main_service_loop
end
private
def setup_signal_handlers
trap('TERM') { shutdown_gracefully }
trap('INT') { shutdown_gracefully }
end
def main_service_loop
loop do
binding.b if should_debug?
process_request
end
end
def should_debug?
ENV['ENABLE_DEBUGGING'] == 'true'
end
def process_request
# Service logic here
sleep 1
end
def shutdown_gracefully
puts "Shutting down service..."
exit 0
end
end
Custom debug server configuration enables specialized debugging workflows. The debug gem accepts configuration through Ruby code in addition to environment variables:
require 'debug'
# Programmatic debug server configuration
DEBUG.configure do |config|
config.host = '192.168.1.100'
config.port = 9001
config.sock_path = nil # Disable Unix socket
config.sock_mode = nil
config.log_level = 'INFO'
end
require 'debug/open_nonstop'
class AdvancedDebuggingExample
def initialize(options = {})
@options = options
@debug_enabled = options[:debug] || ENV['ENABLE_DEBUG'] == '1'
end
def process_with_conditional_debugging(data_set)
data_set.each_with_index do |item, index|
# Conditional breakpoint based on data characteristics
binding.b if @debug_enabled && complex_condition?(item, index)
result = transform_data(item)
yield result if block_given?
end
end
private
def complex_condition?(item, index)
# Complex debugging condition logic
item.is_a?(Hash) && item.key?(:error) && index > 10
end
def transform_data(item)
case item
when Hash
item.transform_keys(&:to_sym)
when Array
item.map(&:to_s)
else
item.to_s
end
end
end
Debugging distributed applications requires coordinating debug sessions across multiple service instances. Custom debugging hooks enable synchronized debugging workflows:
require 'debug/open_nonstop'
class DistributedServiceDebugger
def self.setup_coordinated_debugging(service_id, coordinator_url)
new(service_id, coordinator_url).setup
end
def initialize(service_id, coordinator_url)
@service_id = service_id
@coordinator_url = coordinator_url
@debug_port = calculate_debug_port(service_id)
end
def setup
ENV['RUBY_DEBUG_PORT'] = @debug_port.to_s
register_with_coordinator
setup_cleanup_hooks
end
private
def calculate_debug_port(service_id)
# Generate unique port based on service ID
12000 + (service_id.hash.abs % 1000)
end
def register_with_coordinator
puts "Service #{@service_id} debug server: port #{@debug_port}"
# Registration logic would connect to coordinator service
end
def setup_cleanup_hooks
at_exit { cleanup_debug_registration }
end
def cleanup_debug_registration
puts "Cleaning up debug registration for service #{@service_id}"
end
end
Production Patterns
Remote debugging in production environments requires careful security considerations, performance impact assessment, and operational monitoring. Production debugging workflows balance debugging accessibility with system stability and security requirements.
Security-hardened remote debugging restricts network access and implements authentication mechanisms. Production debug servers should bind to internal network interfaces and require authenticated connections:
# Production-safe debug configuration
if ENV['RAILS_ENV'] == 'production' && ENV['ENABLE_PRODUCTION_DEBUG'] == '1'
# Restrict to internal network only
ENV['RUBY_DEBUG_HOST'] = '10.0.0.0' # Internal network interface
ENV['RUBY_DEBUG_PORT'] = ENV['DEBUG_PORT'] || '12345'
# Authentication would be handled by network security
require 'debug/open_nonstop'
Rails.logger.info "Production debug server enabled on #{ENV['RUBY_DEBUG_HOST']}:#{ENV['RUBY_DEBUG_PORT']}"
end
class ProductionController < ApplicationController
def critical_operation
if Rails.env.production? && debug_conditions_met?
binding.b # Conditional production debugging
end
perform_critical_logic
rescue StandardError => e
Rails.logger.error "Critical operation failed: #{e.message}"
raise
end
private
def debug_conditions_met?
# Strict conditions for production debugging
current_user&.admin? &&
ENV['ENABLE_PRODUCTION_DEBUG'] == '1' &&
Time.current.hour.in?(9..17) # Business hours only
end
def perform_critical_logic
# Business logic implementation
end
end
Load balancer and reverse proxy configurations must account for debug server ports. Infrastructure components require configuration updates to route debug traffic appropriately:
# Kubernetes/Docker production setup
class KubernetesDebugSetup
def self.configure_pod_debugging
return unless debugging_enabled?
debug_port = ENV['DEBUG_PORT'] || '12345'
pod_ip = ENV['POD_IP'] || '0.0.0.0'
ENV['RUBY_DEBUG_HOST'] = pod_ip
ENV['RUBY_DEBUG_PORT'] = debug_port
require 'debug/open_nonstop'
puts "Pod debugging available at #{pod_ip}:#{debug_port}"
puts "Use kubectl port-forward for external access"
end
def self.debugging_enabled?
ENV['ENABLE_DEBUG'] == '1' &&
ENV['RAILS_ENV'] != 'production' ||
ENV['PRODUCTION_DEBUG_OVERRIDE'] == '1'
end
end
# Initialize in application startup
KubernetesDebugSetup.configure_pod_debugging if defined?(Rails)
Monitoring and logging debug server activity enables tracking debugging usage and detecting unauthorized access attempts. Production systems should log debug connections and session activity:
require 'debug/open_nonstop'
class MonitoredDebugServer
def self.setup_monitoring
return unless debug_server_active?
monitor = new
monitor.setup_connection_logging
monitor.setup_metrics_collection
end
def setup_connection_logging
# Debug server connection monitoring would be implemented here
puts "Debug monitoring active - connections logged to #{log_file_path}"
end
def setup_metrics_collection
Thread.new do
loop do
collect_debug_metrics
sleep 60 # Collect metrics every minute
end
end
end
private
def self.debug_server_active?
ENV['RUBY_DEBUG_PORT'] && !ENV['RUBY_DEBUG_PORT'].empty?
end
def log_file_path
Rails.root.join('log', 'debug_server.log') if defined?(Rails)
end
def collect_debug_metrics
# Collect metrics about debug server usage
debug_port = ENV['RUBY_DEBUG_PORT']
puts "Debug server metrics: port=#{debug_port}, time=#{Time.current}"
end
end
Performance impact monitoring ensures debug server overhead remains within acceptable bounds. Production debugging should include performance metrics and automatic disabling mechanisms for high-load conditions:
class PerformanceAwareDebugging
PERFORMANCE_THRESHOLD = 100 # milliseconds
def self.conditional_debug_point(context = nil)
return unless debugging_enabled?
return if system_under_load?
start_time = Time.current
binding.b
# Monitor debugging session impact
duration = (Time.current - start_time) * 1000
log_debug_performance(duration, context) if duration > PERFORMANCE_THRESHOLD
end
def self.system_under_load?
# Check system load metrics
load_average = `uptime`.match(/load average: ([\d.]+)/)[1].to_f rescue 0
load_average > 2.0 # Disable debugging under high load
end
def self.debugging_enabled?
ENV['ENABLE_DEBUG'] == '1' && Time.current.hour.in?(1..5) # Off-peak hours only
end
def self.log_debug_performance(duration, context)
Rails.logger.warn "Debug session took #{duration}ms in context: #{context}" if defined?(Rails)
end
end
Reference
Environment Variables
Variable | Default | Description |
---|---|---|
RUBY_DEBUG_HOST |
localhost |
Debug server bind address |
RUBY_DEBUG_PORT |
12345 |
Debug server port number |
RUBY_DEBUG_SOCK_PATH |
/tmp/ruby-debug-sock-<uid> |
Unix domain socket path |
RUBY_DEBUG_SOCK_MODE |
0600 |
Unix socket file permissions |
RUBY_DEBUG_LOG_LEVEL |
WARN |
Debug server log verbosity |
Debug Gem Modules
Module | Behavior | Use Case |
---|---|---|
debug/open |
Starts server, pauses execution | Interactive debugging from start |
debug/open_nonstop |
Starts server, continues execution | Attach to running processes |
debug |
Local debugging only | Standard debugging without remote access |
Client Connection Commands
Command | Description |
---|---|
rdbg --attach <port> |
Connect to TCP debug server |
rdbg --attach <host>:<port> |
Connect to remote TCP debug server |
rdbg --attach unix:<path> |
Connect via Unix domain socket |
Debugging Session Commands
Command | Description |
---|---|
c or continue |
Continue execution until next breakpoint |
n or next |
Execute next line in current method |
s or step |
Step into method calls |
b <location> |
Set breakpoint at specified location |
info |
Display debugging session information |
p <expression> |
Evaluate and print expression |
pp <expression> |
Pretty-print expression result |
l or list |
Show source code around current location |
bt or backtrace |
Display call stack |
up |
Move up call stack |
down |
Move down call stack |
kill |
Terminate debugging session |
Network Configuration Options
Configuration | TCP Sockets | Unix Sockets |
---|---|---|
Host binding | IP address or hostname | Not applicable |
Port specification | 1024-65535 | Not applicable |
File permissions | Not applicable | 0600-0777 |
Cross-machine access | Yes | No |
Security | Network-dependent | Local file system |
Performance | Network overhead | Lower latency |
Error Codes and Diagnostics
Error | Cause | Resolution |
---|---|---|
Errno::EADDRINUSE |
Port already in use | Choose different port or stop conflicting process |
Errno::EACCES |
Permission denied | Use port >1024 or run with appropriate permissions |
Errno::ECONNREFUSED |
Cannot connect to debug server | Verify server is running and port is correct |
Errno::ENETUNREACH |
Network unreachable | Check network connectivity and firewall settings |
Breakpoint Location Formats
Format | Example | Description |
---|---|---|
Line number | b 42 |
Set breakpoint at line 42 in current file |
File and line | b app.rb:42 |
Set breakpoint at line 42 in app.rb |
Method name | b Class#method |
Set breakpoint at method definition |
Class method | b Class.method |
Set breakpoint at class method |
Conditional | b 42 if condition |
Set conditional breakpoint |
Security Considerations
Aspect | Risk Level | Mitigation |
---|---|---|
Network exposure | High | Bind to internal interfaces only |
Authentication | High | Use network security controls |
Code access | High | Restrict to authorized personnel |
Performance impact | Medium | Monitor and limit debug sessions |
Log exposure | Medium | Secure debug logs appropriately |