debug 1.7.1 → 1.9.1

Sign up to get free protection for your applications and to get access to all the features.
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