module Byebug

Summary

This is a singleton class allows controlling byebug. Use it to start/stop byebug, set/remove breakpoints, etc.

Main Container for all of Byebug's code

Main debugger's container module. Everything is defined under this module

Namespace for all of byebug's code

frozen_string_literal: true

Remote debugging functionality.

@todo Refactor & add tests

frozen_string_literal: true

Reopen main module to define the library version

Constants

PORT

Port number used for remote debugging

VERSION

Attributes

actual_control_port[R]
actual_port[RW]

The actual port that the server is started at

wait_connection[RW]

If in remote mode, wait for the remote connection

displays[RW]

Debugger's display expressions

init_file[RW]

Configuration file used for startup commands. Default value is .byebugrc

mode[RW]

Running mode of the debugger. Can be either:

  • :attached => Attached to a running program through the `byebug` method.

  • :standalone => Started through `bin/byebug` script.

Public Class Methods

add_catchpoint(exception) → exception click to toggle source

Adds a new exception to the catchpoints hash.

static VALUE
Add_catchpoint(VALUE self, VALUE value)
{
  UNUSED(self);

  if (TYPE(value) != T_STRING)
    rb_raise(rb_eTypeError, "value of a catchpoint must be String");

  rb_hash_aset(catchpoints, rb_str_dup(value), INT2FIX(0));
  return value;
}
attach() click to toggle source

Enters byebug right before (or right after if before is false) return events occur. Before entering byebug the init script is read.

# File lib/byebug/attacher.rb, line 9
def self.attach
  require 'byebug/core'

  unless started?
    self.mode = :attached

    start
    run_init_script
  end

  current_context.step_out(3, true)
end
breakpoints → array click to toggle source

Returns an array of breakpoints.

static VALUE
Breakpoints(VALUE self)
{
  UNUSED(self);

  if (NIL_P(breakpoints))
    breakpoints = rb_ary_new();

  return breakpoints;
}
catchpoints → hash click to toggle source

Returns the catchpoints hash.

static VALUE
Catchpoints(VALUE self)
{
  UNUSED(self);

  return catchpoints;
}
contexts → array click to toggle source

Returns an array of all contexts.

static VALUE
Contexts(VALUE self)
{
  volatile VALUE list;
  volatile VALUE new_list;
  VALUE context;
  threads_table_t *t_tbl;
  debug_context_t *dc;
  int i;

  UNUSED(self);

  check_started();

  new_list = rb_ary_new();
  list = rb_funcall(rb_cThread, rb_intern("list"), 0);

  for (i = 0; i < RARRAY_LENINT(list); i++)
  {
    VALUE thread = rb_ary_entry(list, i);

    thread_context_lookup(thread, &context);
    rb_ary_push(new_list, context);
  }

  Data_Get_Struct(threads, threads_table_t, t_tbl);
  st_clear(t_tbl->tbl);

  for (i = 0; i < RARRAY_LENINT(new_list); i++)
  {
    context = rb_ary_entry(new_list, i);
    Data_Get_Struct(context, debug_context_t, dc);
    st_insert(t_tbl->tbl, dc->thread, context);
  }

  return new_list;
}
current_context → context click to toggle source

Returns the current context.

<i>Note:</i> Byebug.current_context.thread == Thread.current
static VALUE
Current_context(VALUE self)
{
  VALUE context;

  UNUSED(self);

  thread_context_lookup(rb_thread_current(), &context);

  return context;
}
debug_load(file, stop = false) → nil click to toggle source

Same as Kernel#load but resets current context's frames. stop parameter forces byebug to stop at the first line of code in file

static VALUE
Debug_load(int argc, VALUE * argv, VALUE self)
{
  VALUE file, stop, context;
  debug_context_t *dc;
  VALUE status = Qnil;
  int state = 0;

  UNUSED(self);

  if (rb_scan_args(argc, argv, "11", &file, &stop) == 1)
    stop = Qfalse;

  Start(self);

  context = Current_context(self);
  Data_Get_Struct(context, debug_context_t, dc);

  dc->calced_stack_size = 1;

  if (RTEST(stop))
    dc->steps = 1;

  rb_load_protect(file, 0, &state);
  if (0 != state)
  {
    status = rb_errinfo();
    reset_stepping_stop_points(dc);
  }

  return status;
}
handle_post_mortem() click to toggle source

Saves information about the unhandled exception and gives a byebug prompt back to the user before program termination.

# File lib/byebug/core.rb, line 68
def self.handle_post_mortem
  return unless raised_exception

  context = raised_exception.__bb_context

  PostMortemProcessor.new(context).at_line
end
interrupt() click to toggle source

Interrupts the current thread

# File lib/byebug/remote.rb, line 24
def interrupt
  current_context.interrupt
end
load_settings() click to toggle source
# File lib/byebug/core.rb, line 53
def self.load_settings
  Dir.glob(File.expand_path('../settings/*.rb', __FILE__)).each do |file|
    require file
  end

  constants.grep(/[a-z]Setting/).map do |name|
    setting = const_get(name).new
    Byebug::Setting.settings[setting.to_sym] = setting
  end
end
parse_host_and_port(host_port_spec) click to toggle source
# File lib/byebug/remote.rb, line 100
def parse_host_and_port(host_port_spec)
  location = host_port_spec.split(':')
  location[1] ? [location[0], location[1].to_i] : ['localhost', location[0]]
end
post_mortem = bool click to toggle source

Sets post-moterm flag.

static VALUE
Set_post_mortem(VALUE self, VALUE value)
{
  UNUSED(self);

  post_mortem = RTEST(value) ? Qtrue : Qfalse;
  return value;
}
post_mortem? → bool click to toggle source

Returns true if post-mortem debugging is enabled.

static VALUE
Post_mortem(VALUE self)
{
  UNUSED(self);

  return post_mortem;
}
raised_exception → exception click to toggle source

Returns raised exception when in post_mortem mode.

static VALUE
Raised_exception(VALUE self)
{
  UNUSED(self);

  return raised_exception;
}
start → bool click to toggle source

The return value is the value of !Byebug.started? before issuing the start; That is, true is returned, unless byebug was previously started.

static VALUE
Start(VALUE self)
{
  if (IS_STARTED)
    return Qfalse;

  catchpoints = rb_hash_new();

  threads = create_threads_table();

  register_tracepoints(self);

  return Qtrue;
}
start_client(host = 'localhost', port = PORT) click to toggle source

Connects to the remote byebug

# File lib/byebug/remote.rb, line 76
def start_client(host = 'localhost', port = PORT)
  interface = LocalInterface.new
  puts 'Connecting to byebug server...'
  socket = TCPSocket.new(host, port)
  puts 'Connected.'

  while (line = socket.gets)
    case line
    when /^PROMPT (.*)$/
      input = interface.read_command(Regexp.last_match[1])
      break unless input
      socket.puts input
    when /^CONFIRM (.*)$/
      input = interface.readline(Regexp.last_match[1])
      break unless input
      socket.puts input
    else
      puts line
    end
  end

  socket.close
end
start_control(host = nil, ctrl_port = PORT + 1) click to toggle source
# File lib/byebug/remote.rb, line 57
def start_control(host = nil, ctrl_port = PORT + 1)
  return @actual_control_port if @control_thread
  server = TCPServer.new(host, ctrl_port)
  @actual_control_port = server.addr[1]

  @control_thread = DebugThread.new do
    while (session = server.accept)
      Context.interface = RemoteInterface.new(session)

      ControlProcessor.new(Byebug.current_context).process_commands
    end
  end

  @actual_control_port
end
start_server(host = nil, port = PORT) { || ... } click to toggle source

Starts a remote byebug

# File lib/byebug/remote.rb, line 31
def start_server(host = nil, port = PORT)
  return if @thread

  Context.interface = nil
  start

  start_control(host, port.zero? ? 0 : port + 1)

  mutex = Mutex.new
  proceed = ConditionVariable.new

  server = TCPServer.new(host, port)
  self.actual_port = server.addr[1]

  yield if block_given?

  @thread = DebugThread.new do
    while (session = server.accept)
      Context.interface = RemoteInterface.new(session)
      mutex.synchronize { proceed.signal } if wait_connection
    end
  end

  mutex.synchronize { proceed.wait(mutex) } if wait_connection
end
started? → bool click to toggle source

Returns true byebug is started.

static VALUE
Started(VALUE self)
{
  UNUSED(self);

  return IS_STARTED ? Qtrue : Qfalse;
}
stop → bool click to toggle source

This method disables byebug. It returns true if byebug was already disabled, otherwise it returns false.

static VALUE
Stop(VALUE self)
{
  UNUSED(self);

  if (IS_STARTED)
  {
    clear_tracepoints(self);

    breakpoints = Qnil;
    catchpoints = Qnil;

    return Qfalse;
  }

  return Qtrue;
}
stoppable?() click to toggle source
static VALUE
Stoppable(VALUE self)
{
  VALUE context;
  debug_context_t *dc;

  if (!IS_STARTED)
    return Qfalse;

  if (!NIL_P(breakpoints) && rb_funcall(breakpoints, idEmpty, 0) == Qfalse)
    return Qfalse;

  if (!NIL_P(catchpoints) && rb_funcall(catchpoints, idEmpty, 0) == Qfalse)
    return Qfalse;

  if (post_mortem == Qtrue)
    return Qfalse;

  if (RTEST(tracing))
    return Qfalse;

  context = Current_context(self);
  if (!NIL_P(context))
  {
    Data_Get_Struct(context, debug_context_t, dc);

    if (dc->steps > 0)
      return Qfalse;
  }

  return Qtrue;
}
thread_context(thread) → context click to toggle source

Returns context of the thread passed as an argument.

static VALUE
Thread_context(VALUE self, VALUE thread)
{
  VALUE context;

  UNUSED(self);

  check_started();

  thread_context_lookup(thread, &context);

  return context;
}
tracing = bool click to toggle source

Sets the global tracing flag.

static VALUE
Set_tracing(VALUE self, VALUE value)
{
  UNUSED(self);

  tracing = RTEST(value) ? Qtrue : Qfalse;
  return value;
}
tracing? → bool click to toggle source

Returns true if global tracing is enabled.

static VALUE
Tracing(VALUE self)
{
  UNUSED(self);

  return tracing;
}
verbose = bool click to toggle source

Sets the global verbose flag for TracePoint API events is enabled.

static VALUE
Set_verbose(VALUE self, VALUE value)
{
  UNUSED(self);

  verbose = RTEST(value) ? Qtrue : Qfalse;
  return value;
}
verbose? → bool click to toggle source

Returns true if global verbose flag for TracePoint API events is enabled.

static VALUE
Verbose(VALUE self)
{
  UNUSED(self);

  return verbose;
}

Public Instance Methods

after_repl() click to toggle source
# File lib/byebug/processors/script_processor.rb, line 26
def after_repl
  interface.close
end
prompt() click to toggle source

Prompt shown before reading a command.

# File lib/byebug/processors/script_processor.rb, line 33
def prompt
  '(byebug:ctrl) '
end
run_init_script() click to toggle source

Runs normal byebug initialization scripts.

Reads and executes the commands from init file (if any) in the current working directory. This is only done if the current directory is different from your home directory. Thus, you can have more than one init file, one generic in your home directory, and another, specific to the program you are debugging, in the directory where you invoke byebug.

# File lib/byebug/core.rb, line 49
def run_init_script
  rc_dirs.each { |dir| run_rc_file(dir) }
end

Private Instance Methods

rc_dirs() click to toggle source

List of folders to load rc files from

@note Files will be loaded in the order specified here.

# File lib/byebug/core.rb, line 101
def rc_dirs
  [ENV['HOME'], Dir.pwd].compact.uniq
end
run_rc_file(base_path) click to toggle source

Runs a initialization script file

# File lib/byebug/core.rb, line 83
def run_rc_file(base_path)
  old_interface = Context.interface

  rc_file = File.expand_path(File.join(base_path, init_file))
  return unless File.exist?(rc_file)

  Context.interface = ScriptInterface.new(rc_file)

  ScriptProcessor.new(nil).process_commands
ensure
  Context.interface = old_interface
end
without_exceptions() { || ... } click to toggle source
# File lib/byebug/processors/script_processor.rb, line 39
def without_exceptions
  yield
rescue
  nil
end