Class: Canopus::Debug::Session

Inherits:
Object
  • Object
show all
Defined in:
lib/canopus/debug/session.rb

Constant Summary collapse

DEFAULT_TIMEOUT =
10
CONNECT_INTERVAL =
0.05

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root:, configuration:, adapter:, breakpoints:, client: nil, timeout: DEFAULT_TIMEOUT) ⇒ Session

Returns a new instance of Session.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/canopus/debug/session.rb', line 15

def initialize(root:, configuration:, adapter:, breakpoints:, client: nil, timeout: DEFAULT_TIMEOUT)
  @root = File.realpath(root)
  @configuration = configuration
  @adapter = adapter
  @breakpoints = breakpoints
  @client = client
  @timeout = timeout
  unless timeout.is_a?(Numeric) && timeout.finite? && timeout.positive?
    raise ArgumentError, "debug session timeout must be positive and finite"
  end
  @handlers = {}
  @lock = Mutex.new
  @closed = false
rescue SystemCallError, TypeError => error
  raise Error, "invalid debug session: #{error.message}"
end

Instance Attribute Details

#client ⇒ Object (readonly)

Returns the value of attribute client.



13
14
15
# File 'lib/canopus/debug/session.rb', line 13

def client
  @client
end

Instance Method Details

#close ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/canopus/debug/session.rb', line 85

def close
  client, waiter, log = @lock.synchronize do
    next if @closed

    @closed = true
    [@client, @process, @log]
  end
  return nil unless client || waiter || log

  failure = nil
  begin
    client&.close
  rescue StandardError => error
    failure = error
  ensure
    begin
      stop_process(waiter)
    rescue StandardError => error
      failure ||= error
    ensure
      begin
        log&.close!
      rescue StandardError => error
        failure ||= error
      end
    end
  end
  raise debug_error(failure) if failure
  nil
end

#evaluate(expression, frame_id:, context: "watch") ⇒ Object



48
# File 'lib/canopus/debug/session.rb', line 48

def evaluate(expression, frame_id:, context: "watch") = inspection_client.evaluate(expression, frame_id: frame_id, context: context)

#on(event, &handler) ⇒ Object

Raises:

  • (ArgumentError)


32
33
34
35
36
37
# File 'lib/canopus/debug/session.rb', line 32

def on(event, &handler)
  raise ArgumentError, "debug event handler required" unless handler

  @lock.synchronize { (@handlers[event.to_sym] ||= []) << handler }
  handler
end

#scopes(frame_id) ⇒ Object



46
# File 'lib/canopus/debug/session.rb', line 46

def scopes(frame_id) = inspection_client.scopes(frame_id)

#stack_frames(levels: 100) ⇒ Object



41
42
43
44
# File 'lib/canopus/debug/session.rb', line 41

def stack_frames(levels: 100)
  client, thread_id = inspection_state
  client.stack_trace(thread_id, start: 0, levels: levels)
end

#start ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/canopus/debug/session.rb', line 50

def start
  deadline = monotonic_time + @timeout
  initialized = Queue.new
  client = @client || build_client(deadline)
  install_client(client)
  client.on(:initialized) { initialized << true }
  client.on(:stopped) { |event| stopped(client, event) }
  client.on(:continued) { clear_stopped_thread; emit(:continued) }
  client.on(:output) { |event| emit(:output, event) }
  client.on(:terminated) { clear_stopped_thread; emit(:terminated) }
  client.on(:exited) { clear_stopped_thread; emit(:terminated) }

  capabilities = client.start(adapter_id: adapter_id, timeout: remaining(deadline))
  request = request_start(client)
  wait_for_initialized(initialized, request, deadline)
  set_breakpoints(client, capabilities, deadline)
  if capabilities["supportsConfigurationDoneRequest"]
    client.configuration_done.await(timeout: remaining(deadline))
  end
  request.await(timeout: remaining(deadline))
  self
rescue StandardError => error
  begin
    request.await(timeout: 0) if request&.done?
  rescue StandardError => start_error
    error = start_error
  end
  begin
    close
  rescue StandardError
    nil
  end
  raise debug_error(error)
end

#stopped_thread_id ⇒ Object



39
# File 'lib/canopus/debug/session.rb', line 39

def stopped_thread_id = @lock.synchronize { @stopped_thread_id }

#variables(reference) ⇒ Object



47
# File 'lib/canopus/debug/session.rb', line 47

def variables(reference) = inspection_client.variables(reference)