CrackedRuby logo

CrackedRuby

Debug Gem Commands

Overview

Ruby's debug gem provides an interactive debugging environment with a comprehensive set of commands for controlling program execution, inspecting variables, setting breakpoints, and analyzing call stacks. The debug gem replaces the legacy debugger and byebug gems, offering improved performance and modern debugging capabilities.

The debug gem operates through a command-line interface that activates when debugger statements execute or breakpoints trigger. The debugging session provides access to the current execution context, allowing inspection of local variables, instance variables, method parameters, and object state at any point during program execution.

Core command categories include execution control (step, next, continue), breakpoint management (break, delete), program inspection (list, whereami, backtrace), and variable examination (info, eval, display). Commands accept various options and parameters to customize their behavior and output format.

# Basic debugging session
def calculate_total(items)
  debugger  # Debugging session starts here
  total = 0
  items.each do |item|
    total += item[:price] * item[:quantity]
  end
  total
end

# When debugger hits, available commands:
# (rdbg) info locals     # Show local variables
# (rdbg) step           # Step into each iteration
# (rdbg) continue       # Resume execution

The debug gem integrates with Ruby's execution model, providing access to the current binding, method definitions, class hierarchies, and module inclusion chains. Commands operate within the context of the current stack frame, with options to navigate up and down the call stack to examine different execution contexts.

class OrderProcessor
  def process(order)
    validate_order(order)
    debugger  # Session starts in process method context
    calculate_shipping(order)
  end

  private

  def validate_order(order)
    raise "Invalid order" if order.nil?
  end
end

# Debug session provides access to:
# - Current method context (process)
# - Instance variables (@order_count, etc.)
# - Method parameters (order)
# - Call stack (process -> calling method)

Basic Usage

Debug gem commands follow a consistent syntax pattern with command names, optional parameters, and flags. Most commands accept abbreviated forms for faster typing during debugging sessions. The debug prompt (rdbg) indicates an active debugging session waiting for command input.

Execution control commands manage program flow. The step command executes the next line and steps into method calls, while next executes the next line in the current method without stepping into called methods. The continue command resumes program execution until the next breakpoint or program termination.

def fibonacci(n)
  return n if n <= 1
  debugger
  result = fibonacci(n - 1) + fibonacci(n - 2)  # Step will enter recursive calls
  result
end

# Debug session:
# (rdbg) step      # Steps into first fibonacci(n-1) call
# (rdbg) next      # Executes current line without stepping into calls
# (rdbg) continue  # Runs until next debugger or program end

Breakpoint commands control where execution pauses. The break command sets breakpoints at specific lines, methods, or conditions. Breakpoints persist across method calls and can include conditional logic to trigger only when specific conditions occur.

class DataProcessor
  def process_batch(data)
    data.each_with_index do |item, index|
      process_item(item, index)
    end
  end

  def process_item(item, index)
    # Complex processing logic
    result = transform_data(item)
    store_result(result)
  end
end

# Setting breakpoints:
# (rdbg) break DataProcessor#process_item    # Break on method entry
# (rdbg) break 15                          # Break on line 15
# (rdbg) break 20 if index > 100           # Conditional breakpoint

Information and inspection commands examine program state. The list command displays source code around the current execution point, whereami shows the exact current location with context, and backtrace displays the complete call stack with method names and line numbers.

def complex_calculation(data)
  step1 = data.map { |x| x * 2 }
  step2 = step1.select { |x| x > 10 }
  debugger
  step3 = step2.reduce(:+)  # Current execution point
  step3 / step2.length
end

# Information commands:
# (rdbg) list          # Shows surrounding source code
# (rdbg) list 1, 20    # Shows lines 1-20
# (rdbg) whereami      # Shows current location with context
# (rdbg) backtrace     # Shows complete call stack

Variable inspection uses the info command with various parameters. info locals displays local variables, info instance shows instance variables, and info const lists constants. The eval command executes arbitrary Ruby expressions within the current context.

class ReportGenerator
  def initialize(data)
    @data = data
    @report_date = Time.now
  end

  def generate
    filtered_data = @data.select { |item| item.valid? }
    debugger
    summary = calculate_summary(filtered_data)
  end

  private

  def calculate_summary(data)
    # Implementation
  end
end

# Variable inspection:
# (rdbg) info locals       # Shows filtered_data, summary
# (rdbg) info instance     # Shows @data, @report_date
# (rdbg) eval @data.size   # Executes expression: returns data size
# (rdbg) p filtered_data   # Pretty-prints variable value

Error Handling & Debugging

Debug gem commands provide specialized functionality for investigating errors, exceptions, and unexpected program behavior. Exception handling debugging involves examining the call stack at the point of error, inspecting variable states that led to the exception, and tracing execution flow through error conditions.

The catch command sets breakpoints on exception types, automatically pausing execution when specific exceptions occur. This allows examination of the program state immediately before exception handling begins, providing insight into the conditions that triggered the error.

class FileProcessor
  def process_files(file_paths)
    file_paths.each do |path|
      begin
        content = File.read(path)
        process_content(content)
      rescue StandardError => e
        debugger  # Examine error state
        handle_error(e, path)
      end
    end
  end

  def process_content(content)
    # May raise various exceptions
    parsed_data = JSON.parse(content)
    validate_data(parsed_data)
  end
end

# Exception debugging:
# (rdbg) catch StandardError    # Break on any StandardError
# (rdbg) catch JSON::ParserError # Break on specific exception type
# (rdbg) info exception        # Show current exception details
# (rdbg) eval e.backtrace       # Examine exception backtrace

Stack frame navigation commands help trace the execution path leading to errors. The up and down commands move between stack frames, allowing inspection of variables and context at different levels of the call stack. The frame command jumps directly to specific stack frames.

def outer_method(data)
  processed = data.map { |item| middle_method(item) }
  processed.compact
end

def middle_method(item)
  inner_method(item[:value])
rescue ArgumentError => e
  debugger  # Error occurred in inner_method
  nil
end

def inner_method(value)
  raise ArgumentError, "Invalid value" if value.nil?
  value.to_s.upcase
end

# Stack navigation during error:
# (rdbg) backtrace            # Show full call stack
# (rdbg) up                   # Move to middle_method frame
# (rdbg) up                   # Move to outer_method frame
# (rdbg) frame 0              # Jump to inner_method frame
# (rdbg) info locals          # Show locals at current frame

Conditional debugging addresses intermittent errors and complex state-dependent bugs. Conditional breakpoints and watchpoints monitor specific variable states and execution conditions, triggering only when problematic conditions occur.

class BatchProcessor
  def process_batch(items)
    items.each_with_index do |item, index|
      debugger if item.nil? && index > 1000  # Conditional debugging
      result = process_single_item(item)
      update_progress(index, result)
    end
  end

  def process_single_item(item)
    return nil if item.nil?
    # Complex processing that sometimes fails
    item.transform.validate.finalize
  end
end

# Conditional debugging commands:
# (rdbg) break process_single_item if item.nil?
# (rdbg) watch @progress_count > 1000
# (rdbg) trace method process_single_item  # Trace all calls
# (rdbg) trace line                       # Trace line execution

Debug command error recovery helps when debugging sessions encounter problems. The quit command exits debugging cleanly, restart reinitializes the debugging session, and help provides command documentation when debugging behavior seems unexpected.

# Error recovery scenarios:
# (rdbg) eval some_complex_expression     # If eval crashes debugger
# (rdbg) quit                            # Clean exit from debug session
# (rdbg) restart                         # Restart debugging session
# (rdbg) help break                      # Get help on break command
# (rdbg) help                           # List all available commands

Production Patterns

Debug gem commands adapt to production debugging scenarios where traditional debugging approaches may not be feasible. Production debugging requires careful consideration of performance impact, log management, and minimal disruption to running systems.

Remote debugging capabilities allow debugging sessions to connect to running production processes. The debug gem supports TCP connections and Unix socket connections for remote debugging, enabling developers to debug production issues without direct server access.

# Production debugging setup
require 'debug'
require 'debug/open'

class ProductionService
  def handle_request(request)
    if Rails.env.production? && request.headers['DEBUG_TOKEN'] == ENV['DEBUG_TOKEN']
      debugger  # Only activate with valid debug token
    end

    process_request(request)
  rescue StandardError => e
    if should_debug_error?(e)
      binding.break  # Conditional production debugging
    end
    raise
  end

  private

  def should_debug_error?(error)
    error.class.name.include?('Critical') &&
    Time.current.hour.between?(2, 6)  # Debug during low traffic
  end
end

# Remote debugging connection:
# RUBY_DEBUG_PORT=12345 rails server
# Then connect: rdbg --attach localhost:12345

Logging integration combines debug commands with application logging to capture debugging information without interactive sessions. The debug gem can output command results to log files, capturing variable states and execution traces for later analysis.

class OrderProcessor
  def process_order(order)
    debug_context = {
      order_id: order.id,
      user_id: order.user_id,
      timestamp: Time.current
    }

    if Rails.env.production?
      # Non-interactive debugging for production
      Debug.capture_locals(debug_context) if order.total > 10000
    else
      debugger if order.total > 10000
    end

    validate_order(order)
    calculate_totals(order)
    finalize_order(order)
  rescue ValidationError => e
    # Capture debug information on validation errors
    Debug.log_context(locals: true, backtrace: true) if Rails.env.production?
    raise
  end
end

# Production debug logging configuration:
# RUBY_DEBUG_LOG_LEVEL=info
# RUBY_DEBUG_LOG_FILE=tmp/debug.log

Performance-conscious debugging minimizes the impact of debugging commands on production performance. Conditional breakpoints, sampling-based debugging, and time-limited debugging sessions prevent debugging from significantly affecting system performance.

class PerformanceCriticalService
  def process_batch(items)
    debug_sample_rate = ENV.fetch('DEBUG_SAMPLE_RATE', 0).to_f

    items.each_with_index do |item, index|
      should_debug = rand < debug_sample_rate ||
                     item.priority == 'critical' ||
                     index % 1000 == 0

      if should_debug
        debugger(do: 'info locals; continue')  # Auto-continue debugging
      end

      process_item(item)
    end
  end

  def process_item(item)
    start_time = Time.current
    result = expensive_operation(item)

    duration = Time.current - start_time
    if duration > 5.seconds
      # Debug slow operations
      debugger(do: 'eval duration; eval item.attributes; continue')
    end

    result
  end
end

# Performance debugging commands:
# (rdbg) time                    # Show execution timing
# (rdbg) profile start          # Start performance profiling
# (rdbg) profile stop           # Stop and show profile results

Monitoring integration connects debug commands with application monitoring systems. Debug sessions can emit metrics, alerts, and structured data to monitoring platforms, providing observability into debugging activities and their outcomes.

class MonitoredService
  def critical_operation(data)
    operation_id = SecureRandom.uuid

    begin
      validate_data(data)
      result = perform_operation(data)

      if result.anomalous?
        # Debug anomalous results with monitoring
        debug_anomaly(operation_id, data, result)
      end

      result
    rescue StandardError => e
      emit_debug_metrics(operation_id, e)
      debugger if should_debug_error?(e)
      raise
    end
  end

  private

  def debug_anomaly(operation_id, input, output)
    debug_info = {
      operation_id: operation_id,
      input_hash: Digest::SHA256.hexdigest(input.to_json),
      output_anomaly_score: output.anomaly_score,
      context: Debug.capture_context(locals: true, frames: 3)
    }

    Rails.logger.info("Debug anomaly detected", debug_info)
    Metrics.increment('debug.anomaly.detected')

    debugger if Rails.env.development?
  end

  def emit_debug_metrics(operation_id, error)
    Metrics.increment('debug.error.captured', tags: {
      error_class: error.class.name,
      operation: 'critical_operation'
    })
  end
end

Common Pitfalls

Debug gem commands present several common misconceptions and error-prone usage patterns that can lead to frustrating debugging experiences or incorrect program analysis. Understanding these pitfalls prevents wasted debugging time and improves debugging effectiveness.

Stepping behavior misconceptions cause confusion about when and how execution control commands navigate through code. The difference between step, next, and continue becomes critical when debugging complex method call chains, blocks, and recursive functions.

def process_data(items)
  items.map do |item|          # Line 2
    transform_item(item)       # Line 3 - next vs step difference
  end.select do |result|       # Line 4
    validate_result(result)    # Line 5 - another method call
  end                          # Line 6
end

def transform_item(item)
  item.upcase.reverse          # Method implementation
end

# Common stepping mistakes:
# (rdbg) step   # From line 3: steps INTO transform_item method
# (rdbg) next   # From line 3: executes transform_item, stops at line 4
# (rdbg) step   # From line 5: steps INTO validate_result method
#
# Pitfall: Expecting 'step' to go to next line in current method
# Reality: 'step' follows execution flow into method calls

Variable scope confusion occurs when inspecting variables across different stack frames and execution contexts. Debug commands operate within the current frame context, and variable availability changes as execution moves through different scopes and method boundaries.

class DataAnalyzer
  def initialize
    @instance_var = "analyzer"
  end

  def analyze(data)
    local_var = "analysis"

    data.each do |item|
      block_var = item.process
      debugger  # Context: inside block
      calculate_stats(block_var)
    end
  end

  private

  def calculate_stats(value)
    stats_local = value * 2
    debugger  # Context: calculate_stats method
  end
end

# Variable scope pitfalls:
# Inside block context:
# (rdbg) info locals     # Shows: item, block_var, local_var
# (rdbg) info instance   # Shows: @instance_var

# Inside calculate_stats context:
# (rdbg) info locals     # Shows: value, stats_local
# (rdbg) info instance   # Shows: @instance_var
# (rdbg) eval local_var  # ERROR: undefined local variable
#
# Pitfall: Assuming variables from outer scopes are accessible
# Reality: Only current frame variables are directly accessible

Breakpoint persistence and management problems arise when breakpoints accumulate across debugging sessions or interfere with program flow. Breakpoints remain active until explicitly deleted, and conditional breakpoints may have unintended side effects or performance implications.

class ServiceManager
  def start_service(name)
    debugger  # Temporary breakpoint for current issue
    service = find_service(name)
    service.initialize_resources
    service.start
  end

  def stop_service(name)
    service = find_service(name)
    debugger if service.nil?  # Conditional breakpoint
    service.cleanup
    service.stop
  end
end

# Breakpoint management pitfalls:
# (rdbg) break ServiceManager#start_service    # Set breakpoint
# (rdbg) break 25 if name == 'critical'       # Conditional breakpoint
#
# Problem: Breakpoints persist across sessions
# Solution: Always clean up breakpoints
# (rdbg) info break          # List all active breakpoints
# (rdbg) delete 1           # Delete specific breakpoint
# (rdbg) delete             # Delete all breakpoints
#
# Pitfall: Leaving debug breakpoints in production code
# Reality: debugger statements execute in all environments

Expression evaluation context issues occur when using eval commands with complex expressions or when the evaluation context differs from expectations. Eval operates within the current binding, but method calls and constant resolution may behave unexpectedly.

module Analytics
  THRESHOLD = 100

  class Calculator
    def initialize(multiplier)
      @multiplier = multiplier
    end

    def process(value)
      intermediate = value * @multiplier
      debugger
      result = apply_threshold(intermediate)
    end

    private

    def apply_threshold(val)
      val > THRESHOLD ? val : 0
    end
  end
end

# Expression evaluation pitfalls:
# (rdbg) eval intermediate         # Works: local variable
# (rdbg) eval @multiplier         # Works: instance variable
# (rdbg) eval THRESHOLD           # ERROR: uninitialized constant
# (rdbg) eval Analytics::THRESHOLD # Works: fully qualified constant
# (rdbg) eval apply_threshold(50)  # Works: private method call
# (rdbg) eval Calculator.new(2)    # ERROR: uninitialized constant
# (rdbg) eval self.class.new(2)   # Works: uses current context
#
# Pitfall: Assuming eval has global scope access
# Reality: eval operates within current method binding

Performance impact misconceptions lead to inappropriate use of debugging commands in performance-sensitive code or production environments. Debug operations have computational overhead, and some commands can significantly impact execution performance.

class HighPerformanceProcessor
  def process_large_dataset(data)
    data.each_with_index do |item, index|
      # WRONG: Performance-killing debug usage
      debugger if complex_condition?(item)  # Evaluates condition every iteration

      result = expensive_operation(item)

      # WRONG: Expensive debug operations in tight loops
      if Rails.env.development?
        Debug.eval("expensive_debug_calculation(item)")  # Runs every iteration
      end
    end
  end

  def better_debugging(data)
    debug_enabled = Rails.env.development? && ENV['ENABLE_DEBUG']

    data.each_with_index do |item, index|
      # BETTER: Conditional debugging with pre-computed flags
      if debug_enabled && index % 1000 == 0  # Sample debugging
        debugger(do: 'info locals; continue')  # Auto-continue
      end

      result = expensive_operation(item)
    end
  end
end

# Performance debugging best practices:
# (rdbg) disable               # Temporarily disable all breakpoints
# (rdbg) enable                # Re-enable breakpoints
# (rdbg) time                  # Monitor debug command execution time

Reference

Core Execution Control Commands

Command Parameters Returns Description
step [count] count (Integer) nil Execute next line, stepping into method calls
next [count] count (Integer) nil Execute next line in current method without stepping into calls
continue None nil Resume program execution until next breakpoint
finish None nil Execute until current method returns
quit None Exit Terminate debugging session and program
restart None nil Restart the debugging session from beginning

Breakpoint Management Commands

Command Parameters Returns Description
break <location> location (String/Integer) nil Set breakpoint at line number or method
break <location> if <condition> location, condition (String) nil Set conditional breakpoint
delete [id] id (Integer, optional) nil Delete specific breakpoint or all breakpoints
disable [id] id (Integer, optional) nil Disable specific breakpoint or all breakpoints
enable [id] id (Integer, optional) nil Enable specific breakpoint or all breakpoints
info break None Array List all breakpoints with status

Program Inspection Commands

Command Parameters Returns Description
list [start, end] start, end (Integer) nil Display source code around current line
whereami None nil Show current execution location with context
backtrace [count] count (Integer, optional) nil Display call stack with line numbers
up [count] count (Integer) nil Move up stack frames
down [count] count (Integer) nil Move down stack frames
frame <number> number (Integer) nil Jump to specific stack frame

Variable and Context Inspection

Command Parameters Returns Description
info locals None Hash Display local variables in current scope
info instance None Hash Display instance variables
info const None Array Display constants in current scope
info exception None Object Display current exception details
eval <expression> expression (String) Any Evaluate Ruby expression in current context
p <expression> expression (String) Any Pretty-print expression result
pp <expression> expression (String) Any Pretty-print with formatting

Advanced Debugging Commands

Command Parameters Returns Description
display <expression> expression (String) nil Auto-display expression value at each stop
undisplay [id] id (Integer, optional) nil Remove auto-display expression
watch <expression> expression (String) nil Break when expression value changes
catch <exception> exception (String) nil Break on specific exception type
trace line None nil Trace line execution
trace method None nil Trace method calls
untrace None nil Disable all tracing

Session Management and Help

Command Parameters Returns Description
help [command] command (String, optional) nil Display help for specific command or all commands
show None nil Display current debug session configuration
set <option> <value> option, value (String) nil Configure debug session options
source <filename> filename (String) nil Execute debug commands from file
save <filename> filename (String) nil Save current session commands to file

Breakpoint Location Formats

Format Example Description
Line number 15 Break at line 15 in current file
Method name MyClass#method_name Break at instance method
Class method MyClass.class_method Break at class method
File and line lib/processor.rb:25 Break at specific file and line
Module method MyModule#method Break at module method

Conditional Breakpoint Operators

Operator Example Description
== break 15 if count == 10 Equality comparison
!= break method if value != nil Inequality comparison
>, <, >=, <= break 20 if index > 100 Numerical comparisons
&&, || break 25 if flag && count > 5 Logical operators
=~ break 30 if name =~ /pattern/ Regular expression match

Debug Session Configuration Options

Option Values Default Description
autolist on, off on Automatically list code at breakpoints
autoeval on, off off Automatically evaluate expressions
listsize Integer 10 Number of lines to display with list command
width Integer 80 Terminal width for output formatting
prompt String (rdbg) Debug prompt string

Error Types and Exception Handling

Exception Class Common Causes Debug Strategy
NoMethodError Undefined method calls Use info locals, check object type
NameError Undefined variables/constants Use info locals, info const
ArgumentError Wrong number/type of arguments Use backtrace, inspect parameters
TypeError Type mismatch operations Use eval to check object types
StandardError General application errors Use catch StandardError for broad catching