debug 1.7.1 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/debug/console.rb CHANGED
@@ -5,30 +5,9 @@ module DEBUGGER__
5
5
  raise LoadError if CONFIG[:no_reline]
6
6
  require 'reline'
7
7
 
8
- # reline 0.2.7 or later is required.
9
- raise LoadError if Reline::VERSION < '0.2.7'
10
-
11
8
  require_relative 'color'
12
- include Color
13
-
14
- begin
15
- prev = trap(:SIGWINCH, nil)
16
- trap(:SIGWINCH, prev)
17
- SIGWINCH_SUPPORTED = true
18
- rescue ArgumentError
19
- SIGWINCH_SUPPORTED = false
20
- end
21
9
 
22
- # 0.2.7 has SIGWINCH issue on non-main thread
23
- class ::Reline::LineEditor
24
- m = Module.new do
25
- def reset(prompt = '', encoding:)
26
- super
27
- Signal.trap(:SIGWINCH, nil)
28
- end
29
- end
30
- prepend m
31
- end if SIGWINCH_SUPPORTED
10
+ include Color
32
11
 
33
12
  def parse_input buff, commands
34
13
  c, rest = get_command buff
@@ -56,10 +35,10 @@ module DEBUGGER__
56
35
  Reline.prompt_proc = -> args, *kw do
57
36
  case state = parse_input(args.first, commands)
58
37
  when nil, :command
59
- [prompt, prompt]
38
+ [prompt]
60
39
  when :ruby
61
- [prompt.sub('rdbg'){colorize('ruby', [:RED])}] * 2
62
- end
40
+ [prompt.sub('rdbg'){colorize('ruby', [:RED])}]
41
+ end * args.size
63
42
  end
64
43
 
65
44
  Reline.completion_proc = -> given do
@@ -96,7 +75,7 @@ module DEBUGGER__
96
75
  when nil
97
76
  buff
98
77
  when :ruby
99
- colorize_code(buff.chomp)
78
+ colorize_code(buff)
100
79
  end
101
80
  end unless CONFIG[:no_hint]
102
81
 
@@ -224,11 +203,11 @@ module DEBUGGER__
224
203
  end
225
204
 
226
205
  def load_history
227
- read_history_file.count{|line|
206
+ read_history_file.each{|line|
228
207
  line.strip!
229
208
  history << line unless line.empty?
230
- }
209
+ } if history.empty?
210
+ history.count
231
211
  end
232
212
  end # class Console
233
213
  end
234
-
@@ -0,0 +1,336 @@
1
+ module DEBUGGER__
2
+ module DAP_TraceInspector
3
+ class MultiTracer < Tracer
4
+ def initialize ui, evts, trace_params, max_log_size: nil, **kw
5
+ @evts = evts
6
+ @log = []
7
+ @trace_params = trace_params
8
+ if max_log_size
9
+ @max_log_size = max_log_size
10
+ else
11
+ @max_log_size = 50000
12
+ end
13
+ @dropped_trace_cnt = 0
14
+ super(ui, **kw)
15
+ @type = 'multi'
16
+ @name = 'TraceInspector'
17
+ end
18
+
19
+ attr_accessor :dropped_trace_cnt
20
+ attr_reader :log
21
+
22
+ def setup
23
+ @tracer = TracePoint.new(*@evts){|tp|
24
+ next if skip?(tp)
25
+
26
+ case tp.event
27
+ when :call, :c_call, :b_call
28
+ if @trace_params
29
+ params = parameters_info tp
30
+ end
31
+ append(call_trace_log(tp, params: params))
32
+ when :return, :c_return, :b_return
33
+ return_str = DEBUGGER__.safe_inspect(tp.return_value, short: true, max_length: 4096)
34
+ append(call_trace_log(tp, return_str: return_str))
35
+ when :line
36
+ append(line_trace_log(tp))
37
+ end
38
+ }
39
+ end
40
+
41
+ def parameters_info tp
42
+ b = tp.binding
43
+ tp.parameters.map{|_type, name|
44
+ begin
45
+ { name: name, value: DEBUGGER__.safe_inspect(b.local_variable_get(name), short: true, max_length: 4096) }
46
+ rescue NameError, TypeError
47
+ nil
48
+ end
49
+ }.compact
50
+ end
51
+
52
+ def call_identifier_str tp
53
+ if tp.defined_class
54
+ minfo(tp)
55
+ else
56
+ "block"
57
+ end
58
+ end
59
+
60
+ def append log
61
+ if @log.size >= @max_log_size
62
+ @dropped_trace_cnt += 1
63
+ @log.shift
64
+ end
65
+ @log << log
66
+ end
67
+
68
+ def call_trace_log tp, return_str: nil, params: nil
69
+ log = {
70
+ depth: DEBUGGER__.frame_depth,
71
+ name: call_identifier_str(tp),
72
+ threadId: Thread.current.instance_variable_get(:@__thread_client_id),
73
+ location: {
74
+ path: tp.path,
75
+ line: tp.lineno
76
+ }
77
+ }
78
+ log[:returnValue] = return_str if return_str
79
+ log[:parameters] = params if params && params.size > 0
80
+ log
81
+ end
82
+
83
+ def line_trace_log tp
84
+ {
85
+ depth: DEBUGGER__.frame_depth,
86
+ threadId: Thread.current.instance_variable_get(:@__thread_client_id),
87
+ location: {
88
+ path: tp.path,
89
+ line: tp.lineno
90
+ }
91
+ }
92
+ end
93
+
94
+ def skip? tp
95
+ super || !@evts.include?(tp.event)
96
+ end
97
+
98
+ def skip_with_pattern?(tp)
99
+ super && !tp.method_id&.match?(@pattern)
100
+ end
101
+ end
102
+
103
+ class Custom_Recorder < ThreadClient::Recorder
104
+ def initialize max_log_size: nil
105
+ if max_log_size
106
+ @max_log_size = max_log_size
107
+ else
108
+ @max_log_size = 50000
109
+ end
110
+ @dropped_trace_cnt = 0
111
+ super()
112
+ end
113
+
114
+ attr_accessor :dropped_trace_cnt
115
+
116
+ def append frames
117
+ if @log.size >= @max_log_size
118
+ @dropped_trace_cnt += 1
119
+ @log.shift
120
+ end
121
+ @log << frames
122
+ end
123
+ end
124
+
125
+ module Custom_UI_DAP
126
+ def custom_dap_request_rdbgTraceInspector(req)
127
+ @q_msg << req
128
+ end
129
+ end
130
+
131
+ module Custom_Session
132
+ def process_trace_cmd req
133
+ cmd = req.dig('arguments', 'subCommand')
134
+ case cmd
135
+ when 'enable'
136
+ events = req.dig('arguments', 'events')
137
+ evts = []
138
+ trace_params = false
139
+ filter = req.dig('arguments', 'filterRegExp')
140
+ max_log_size = req.dig('arguments', 'maxLogSize')
141
+ events.each{|evt|
142
+ case evt
143
+ when 'traceLine'
144
+ evts << :line
145
+ when 'traceCall'
146
+ evts << :call
147
+ evts << :b_call
148
+ when 'traceReturn'
149
+ evts << :return
150
+ evts << :b_return
151
+ when 'traceParams'
152
+ trace_params = true
153
+ when 'traceClanguageCall'
154
+ evts << :c_call
155
+ when 'traceClanguageReturn'
156
+ evts << :c_return
157
+ else
158
+ raise "unknown trace type #{evt}"
159
+ end
160
+ }
161
+ add_tracer MultiTracer.new @ui, evts, trace_params, max_log_size: max_log_size, pattern: filter
162
+ @ui.respond req, {}
163
+ when 'disable'
164
+ if t = find_multi_trace
165
+ t.disable
166
+ end
167
+ @ui.respond req, {}
168
+ when 'collect'
169
+ logs = []
170
+ if t = find_multi_trace
171
+ logs = t.log
172
+ if t.dropped_trace_cnt > 0
173
+ @ui.puts "Return #{logs.size} traces and #{t.dropped_trace_cnt} traces are dropped"
174
+ else
175
+ @ui.puts "Return #{logs.size} traces"
176
+ end
177
+ t.dropped_trace_cnt = 0
178
+ end
179
+ @ui.respond req, logs: logs
180
+ else
181
+ raise "Unknown trace sub command #{cmd}"
182
+ end
183
+ return :retry
184
+ end
185
+
186
+ def find_multi_trace
187
+ @tracers.values.each{|t|
188
+ if t.type == 'multi'
189
+ return t
190
+ end
191
+ }
192
+ return nil
193
+ end
194
+
195
+ def process_record_cmd req
196
+ cmd = req.dig('arguments', 'subCommand')
197
+ case cmd
198
+ when 'enable'
199
+ @tc << [:dap, :rdbgTraceInspector, req]
200
+ when 'disable'
201
+ @tc << [:dap, :rdbgTraceInspector, req]
202
+ when 'step'
203
+ tid = req.dig('arguments', 'threadId')
204
+ count = req.dig('arguments', 'count')
205
+ if tc = find_waiting_tc(tid)
206
+ @ui.respond req, {}
207
+ tc << [:step, :in, count]
208
+ else
209
+ fail_response req
210
+ end
211
+ when 'stepBack'
212
+ tid = req.dig('arguments', 'threadId')
213
+ count = req.dig('arguments', 'count')
214
+ if tc = find_waiting_tc(tid)
215
+ @ui.respond req, {}
216
+ tc << [:step, :back, count]
217
+ else
218
+ fail_response req
219
+ end
220
+ when 'collect'
221
+ tid = req.dig('arguments', 'threadId')
222
+ if tc = find_waiting_tc(tid)
223
+ tc << [:dap, :rdbgTraceInspector, req]
224
+ else
225
+ fail_response req
226
+ end
227
+ else
228
+ raise "Unknown record sub command #{cmd}"
229
+ end
230
+ end
231
+
232
+ def custom_dap_request_rdbgTraceInspector(req)
233
+ cmd = req.dig('arguments', 'command')
234
+ case cmd
235
+ when 'trace'
236
+ process_trace_cmd req
237
+ when 'record'
238
+ process_record_cmd req
239
+ else
240
+ raise "Unknown command #{cmd}"
241
+ end
242
+ end
243
+
244
+ def custom_dap_request_event_rdbgTraceInspector(req, result)
245
+ cmd = req.dig('arguments', 'command')
246
+ case cmd
247
+ when 'record'
248
+ process_event_record_cmd(req, result)
249
+ else
250
+ raise "Unknown command #{cmd}"
251
+ end
252
+ end
253
+
254
+ def process_event_record_cmd(req, result)
255
+ cmd = req.dig('arguments', 'subCommand')
256
+ case cmd
257
+ when 'enable'
258
+ @ui.respond req, {}
259
+ when 'disable'
260
+ @ui.respond req, {}
261
+ when 'collect'
262
+ cnt = result.delete :dropped_trace_cnt
263
+ if cnt > 0
264
+ @ui.puts "Return #{result[:logs].size} traces and #{cnt} traces are dropped"
265
+ else
266
+ @ui.puts "Return #{result[:logs].size} traces"
267
+ end
268
+ @ui.respond req, result
269
+ else
270
+ raise "Unknown command #{cmd}"
271
+ end
272
+ end
273
+ end
274
+
275
+ module Custom_ThreadClient
276
+ def custom_dap_request_rdbgTraceInspector(req)
277
+ cmd = req.dig('arguments', 'command')
278
+ case cmd
279
+ when 'record'
280
+ process_record_cmd(req)
281
+ else
282
+ raise "Unknown command #{cmd}"
283
+ end
284
+ end
285
+
286
+ def process_record_cmd(req)
287
+ cmd = req.dig('arguments', 'subCommand')
288
+ case cmd
289
+ when 'enable'
290
+ size = req.dig('arguments', 'maxLogSize')
291
+ @recorder = Custom_Recorder.new max_log_size: size
292
+ @recorder.enable
293
+ event! :protocol_result, :rdbgTraceInspector, req
294
+ when 'disable'
295
+ if @recorder&.enabled?
296
+ @recorder.disable
297
+ end
298
+ @recorder = nil
299
+ event! :protocol_result, :rdbgTraceInspector, req
300
+ when 'collect'
301
+ logs = []
302
+ log_index = nil
303
+ trace_cnt = 0
304
+ unless @recorder.nil?
305
+ log_index = @recorder.log_index
306
+ @recorder.log.each{|frames|
307
+ crt_frame = frames[0]
308
+ log = {
309
+ name: crt_frame.name,
310
+ location: {
311
+ path: crt_frame.location.path,
312
+ line: crt_frame.location.lineno,
313
+ },
314
+ depth: crt_frame.frame_depth
315
+ }
316
+ if params = crt_frame.iseq_parameters_info
317
+ log[:parameters] = params
318
+ end
319
+ if return_str = crt_frame.return_str
320
+ log[:returnValue] = return_str
321
+ end
322
+ logs << log
323
+ }
324
+ trace_cnt = @recorder.dropped_trace_cnt
325
+ @recorder.dropped_trace_cnt = 0
326
+ end
327
+ event! :protocol_result, :rdbgTraceInspector, req, logs: logs, stoppedIndex: log_index, dropped_trace_cnt: trace_cnt
328
+ else
329
+ raise "Unknown command #{cmd}"
330
+ end
331
+ end
332
+ end
333
+
334
+ ::DEBUGGER__::SESSION.extend_feature session: Custom_Session, thread_client: Custom_ThreadClient, ui: Custom_UI_DAP
335
+ end
336
+ end
@@ -147,6 +147,15 @@ module DEBUGGER__
147
147
  end
148
148
  end
149
149
 
150
+ def iseq_parameters_info
151
+ case frame_type
152
+ when :block, :method
153
+ parameters_info
154
+ else
155
+ nil
156
+ end
157
+ end
158
+
150
159
  def parameters_info
151
160
  vars = iseq.parameters_symbols
152
161
  vars.map{|var|
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'irb'
4
+
5
+ module DEBUGGER__
6
+ module IrbPatch
7
+ def evaluate(line, line_no)
8
+ SESSION.send(:restart_all_threads)
9
+ super
10
+ # This is to communicate with the test framework so it can feed the next input
11
+ puts "INTERNAL_INFO: {}" if ENV['RUBY_DEBUG_TEST_UI'] == 'terminal'
12
+ ensure
13
+ SESSION.send(:stop_all_threads)
14
+ end
15
+ end
16
+
17
+ class ThreadClient
18
+ def activate_irb_integration
19
+ IRB.setup(location, argv: [])
20
+ workspace = IRB::WorkSpace.new(current_frame&.binding || TOPLEVEL_BINDING)
21
+ irb = IRB::Irb.new(workspace)
22
+ IRB.conf[:MAIN_CONTEXT] = irb.context
23
+ IRB::Debug.setup(irb)
24
+ IRB::Context.prepend(IrbPatch)
25
+ end
26
+ end
27
+ end
data/lib/debug/server.rb CHANGED
@@ -133,7 +133,7 @@ module DEBUGGER__
133
133
  require 'etc'
134
134
 
135
135
  check_cookie $1
136
- @sock.puts "PID: #{Process.pid}, $0: #{$0}"
136
+ @sock.puts "PID: #{Process.pid}, $0: #{$0}, session_name: #{CONFIG[:session_name]}"
137
137
  @sock.puts "debug #{VERSION} on #{RUBY_DESCRIPTION}"
138
138
  @sock.puts "uname: #{Etc.uname.inspect}"
139
139
  @sock.close
@@ -149,7 +149,9 @@ module DEBUGGER__
149
149
  end
150
150
  parse_option(params)
151
151
 
152
- puts "DEBUGGER (client): Connected. PID:#{Process.pid}, $0:#{$0}"
152
+ session_name = CONFIG[:session_name]
153
+ session_name_str = ", session_name:#{session_name}" if session_name
154
+ puts "DEBUGGER (client): Connected. PID:#{Process.pid}, $0:#{$0}#{session_name_str}"
153
155
  puts "DEBUGGER (client): Type `Ctrl-C` to enter the debug console." unless @need_pause_at_first
154
156
  puts
155
157
 
@@ -406,14 +408,13 @@ module DEBUGGER__
406
408
  require_relative 'server_cdp'
407
409
 
408
410
  @uuid = SecureRandom.uuid
409
- unless @chrome_pid = UI_CDP.setup_chrome(@local_addr.inspect_sockaddr, @uuid)
410
- DEBUGGER__.warn <<~EOS
411
- With Chrome browser, type the following URL in the address-bar:
411
+ @chrome_pid = UI_CDP.setup_chrome(@local_addr.inspect_sockaddr, @uuid)
412
+ DEBUGGER__.warn <<~EOS
413
+ With Chrome browser, type the following URL in the address-bar:
412
414
 
413
- devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{@local_addr.inspect_sockaddr}/#{@uuid}
415
+ devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&noJavaScriptCompletion=true&ws=#{@local_addr.inspect_sockaddr}/#{@uuid}
414
416
 
415
- EOS
416
- end
417
+ EOS
417
418
  end
418
419
 
419
420
  def accept