debug 1.7.2 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
 
@@ -410,7 +412,7 @@ module DEBUGGER__
410
412
  DEBUGGER__.warn <<~EOS
411
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
417
  EOS
416
418
  end
@@ -59,7 +59,7 @@ module DEBUGGER__
59
59
  ws_client.send sessionId: s_id, id: 5,
60
60
  method: 'Page.navigate',
61
61
  params: {
62
- url: "devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{addr}/#{uuid}",
62
+ url: "devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&noJavaScriptCompletion=true&ws=#{addr}/#{uuid}",
63
63
  frameId: f_id
64
64
  }
65
65
  when 101
@@ -474,7 +474,7 @@ module DEBUGGER__
474
474
  when 'Debugger.getScriptSource'
475
475
  @q_msg << req
476
476
  when 'Debugger.enable'
477
- send_response req
477
+ send_response req, debuggerId: rand.to_s
478
478
  @q_msg << req
479
479
  when 'Runtime.enable'
480
480
  send_response req
@@ -849,27 +849,25 @@ module DEBUGGER__
849
849
  unless s_id = @scr_id_map[path]
850
850
  s_id = (@scr_id_map.size + 1).to_s
851
851
  @scr_id_map[path] = s_id
852
+ lineno = 0
853
+ src = ''
852
854
  if path && File.exist?(path)
853
855
  src = File.read(path)
856
+ @src_map[s_id] = src
857
+ lineno = src.lines.count
854
858
  end
855
- @src_map[s_id] = src
856
- end
857
- if src = @src_map[s_id]
858
- lineno = src.lines.count
859
- else
860
- lineno = 0
861
- end
862
- frame[:location][:scriptId] = s_id
863
- frame[:functionLocation][:scriptId] = s_id
864
- @ui.fire_event 'Debugger.scriptParsed',
859
+ @ui.fire_event 'Debugger.scriptParsed',
865
860
  scriptId: s_id,
866
- url: frame[:url],
861
+ url: path,
867
862
  startLine: 0,
868
863
  startColumn: 0,
869
864
  endLine: lineno,
870
865
  endColumn: 0,
871
866
  executionContextId: 1,
872
867
  hash: src.hash.inspect
868
+ end
869
+ frame[:location][:scriptId] = s_id
870
+ frame[:functionLocation][:scriptId] = s_id
873
871
 
874
872
  frame[:scopeChain].each {|s|
875
873
  oid = s.dig(:object, :objectId)