debug 1.7.2 → 1.8.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1bac91980fb350e0e37a0639612064bba25a8e441661a6d797e0d9f996ce0205
4
- data.tar.gz: 3294757150ec0731ccadd0c26a57c8e920c0479e4a967b4900e999dae4afcf9c
3
+ metadata.gz: 39fceaae982c712433b6bf0bc855328c5d923524658a399926e7a032f5d7cdbf
4
+ data.tar.gz: 4f80f39348e0cd46882bd54e04398342c3d124133aa15edcc57a18f1a20fe449
5
5
  SHA512:
6
- metadata.gz: 7a0259c1f8fa904d9a425be5035e2642de89e69cd81cc61bd81bae2e9e7b17dbd18a78addce9e5ed8e3c7723417c0de1113a26922131760e490e77e91bb8493d
7
- data.tar.gz: fd55333ccf690b4338729a2b84d55a21464ba2de71bdf5edcb0b82f309594477333224eb3be0a89c6b7c477042c4f904f8b743f74bba3931f6db4de7a9ff7dd9
6
+ metadata.gz: 67a369a45e2debb29dbb42fcbc32d36e1c40467249d657f45caa8a04a5fb3521a10c91860a3f3407caaa8f97cf81ad35f8a3e5ac83a124b99811d82363afe2cf
7
+ data.tar.gz: b29458d948196114b7cb5c7c182decca2685495b96707d904674714a2256a46bd0244efdde5d1ada8a460d881dff7ad4acafaa2c433c6e0eddbcf928425f6748
data/README.md CHANGED
@@ -476,6 +476,7 @@ config set no_color true
476
476
  * `RUBY_DEBUG_NO_SIGINT_HOOK` (`no_sigint_hook`): Do not suspend on SIGINT (default: false)
477
477
  * `RUBY_DEBUG_NO_RELINE` (`no_reline`): Do not use Reline library (default: false)
478
478
  * `RUBY_DEBUG_NO_HINT` (`no_hint`): Do not show the hint on the REPL (default: false)
479
+ * `RUBY_DEBUG_NO_LINENO` (`no_lineno`): Do not show line numbers (default: false)
479
480
 
480
481
  * CONTROL
481
482
  * `RUBY_DEBUG_SKIP_PATH` (`skip_path`): Skip showing/entering frames for given paths
@@ -908,6 +909,8 @@ Attach mode:
908
909
  'rdbg -A host port' tries to connect to host:port via TCP/IP.
909
910
 
910
911
  Other options:
912
+ -v Show version number
913
+ --version Show version number and exit
911
914
  -h, --help Print help
912
915
  --util=NAME Utility mode (used by tools)
913
916
  --stop-at-load Stop immediately when the debugging feature is loaded.
data/lib/debug/config.rb CHANGED
@@ -21,6 +21,7 @@ module DEBUGGER__
21
21
  no_sigint_hook: ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT", :bool, "false"],
22
22
  no_reline: ['RUBY_DEBUG_NO_RELINE', "UI: Do not use Reline library", :bool, "false"],
23
23
  no_hint: ['RUBY_DEBUG_NO_HINT', "UI: Do not show the hint on the REPL", :bool, "false"],
24
+ no_lineno: ['RUBY_DEBUG_NO_LINENO', "UI: Do not show line numbers", :bool, "false"],
24
25
 
25
26
  # control setting
26
27
  skip_path: ['RUBY_DEBUG_SKIP_PATH', "CONTROL: Skip showing/entering frames for given paths", :path],
@@ -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|
@@ -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)
@@ -274,198 +274,225 @@ module DEBUGGER__
274
274
  retry
275
275
  end
276
276
 
277
+ def load_extensions req
278
+ if exts = req.dig('arguments', 'rdbgExtensions')
279
+ exts.each{|ext|
280
+ require_relative "dap_custom/#{File.basename(ext)}"
281
+ }
282
+ end
283
+
284
+ if scripts = req.dig('arguments', 'rdbgInitialScripts')
285
+ scripts.each do |script|
286
+ begin
287
+ eval(script)
288
+ rescue Exception => e
289
+ puts e.message
290
+ puts e.backtrace.inspect
291
+ end
292
+ end
293
+ end
294
+ end
295
+
277
296
  def process
278
297
  while req = recv_request
279
- raise "not a request: #{req.inspect}" unless req['type'] == 'request'
280
- args = req.dig('arguments')
298
+ process_request(req)
299
+ end
300
+ ensure
301
+ send_event :terminated unless @sock.closed?
302
+ end
281
303
 
282
- case req['command']
304
+ def process_request req
305
+ raise "not a request: #{req.inspect}" unless req['type'] == 'request'
306
+ args = req.dig('arguments')
283
307
 
284
- ## boot/configuration
285
- when 'launch'
286
- send_response req
287
- # `launch` runs on debuggee on the same file system
288
- UI_DAP.local_fs_map_set req.dig('arguments', 'localfs') || req.dig('arguments', 'localfsMap') || true
289
- @nonstop = true
308
+ case req['command']
290
309
 
291
- when 'attach'
292
- send_response req
293
- UI_DAP.local_fs_map_set req.dig('arguments', 'localfs') || req.dig('arguments', 'localfsMap')
310
+ ## boot/configuration
311
+ when 'launch'
312
+ send_response req
313
+ # `launch` runs on debuggee on the same file system
314
+ UI_DAP.local_fs_map_set req.dig('arguments', 'localfs') || req.dig('arguments', 'localfsMap') || true
315
+ @nonstop = true
294
316
 
295
- if req.dig('arguments', 'nonstop') == true
296
- @nonstop = true
297
- else
298
- @nonstop = false
299
- end
317
+ load_extensions req
300
318
 
301
- when 'configurationDone'
302
- send_response req
319
+ when 'attach'
320
+ send_response req
321
+ UI_DAP.local_fs_map_set req.dig('arguments', 'localfs') || req.dig('arguments', 'localfsMap')
303
322
 
304
- if @nonstop
305
- @q_msg << 'continue'
306
- else
307
- if SESSION.in_subsession?
308
- send_event 'stopped', reason: 'pause',
309
- threadId: 1, # maybe ...
310
- allThreadsStopped: true
311
- end
312
- end
323
+ if req.dig('arguments', 'nonstop') == true
324
+ @nonstop = true
325
+ else
326
+ @nonstop = false
327
+ end
313
328
 
314
- when 'setBreakpoints'
315
- req_path = args.dig('source', 'path')
316
- path = UI_DAP.local_to_remote_path(req_path)
317
- if path
318
- SESSION.clear_line_breakpoints path
319
-
320
- bps = []
321
- args['breakpoints'].each{|bp|
322
- line = bp['line']
323
- if cond = bp['condition']
324
- bps << SESSION.add_line_breakpoint(path, line, cond: cond)
325
- else
326
- bps << SESSION.add_line_breakpoint(path, line)
327
- end
328
- }
329
- send_response req, breakpoints: (bps.map do |bp| {verified: true,} end)
330
- else
331
- send_response req, success: false, message: "#{req_path} is not available"
332
- end
329
+ load_extensions req
333
330
 
334
- when 'setFunctionBreakpoints'
335
- send_response req
331
+ when 'configurationDone'
332
+ send_response req
336
333
 
337
- when 'setExceptionBreakpoints'
338
- process_filter = ->(filter_id, cond = nil) {
339
- bp =
340
- case filter_id
341
- when 'any'
342
- SESSION.add_catch_breakpoint 'Exception', cond: cond
343
- when 'RuntimeError'
344
- SESSION.add_catch_breakpoint 'RuntimeError', cond: cond
345
- else
346
- nil
347
- end
348
- {
349
- verified: !bp.nil?,
350
- message: bp.inspect,
351
- }
352
- }
334
+ if @nonstop
335
+ @q_msg << 'continue'
336
+ else
337
+ if SESSION.in_subsession?
338
+ send_event 'stopped', reason: 'pause',
339
+ threadId: 1, # maybe ...
340
+ allThreadsStopped: true
341
+ end
342
+ end
353
343
 
354
- SESSION.clear_catch_breakpoints 'Exception', 'RuntimeError'
344
+ when 'setBreakpoints'
345
+ req_path = args.dig('source', 'path')
346
+ path = UI_DAP.local_to_remote_path(req_path)
347
+ if path
348
+ SESSION.clear_line_breakpoints path
349
+
350
+ bps = []
351
+ args['breakpoints'].each{|bp|
352
+ line = bp['line']
353
+ if cond = bp['condition']
354
+ bps << SESSION.add_line_breakpoint(path, line, cond: cond)
355
+ else
356
+ bps << SESSION.add_line_breakpoint(path, line)
357
+ end
358
+ }
359
+ send_response req, breakpoints: (bps.map do |bp| {verified: true,} end)
360
+ else
361
+ send_response req, success: false, message: "#{req_path} is not available"
362
+ end
355
363
 
356
- filters = args.fetch('filters').map {|filter_id|
357
- process_filter.call(filter_id)
364
+ when 'setFunctionBreakpoints'
365
+ send_response req
366
+
367
+ when 'setExceptionBreakpoints'
368
+ process_filter = ->(filter_id, cond = nil) {
369
+ bp =
370
+ case filter_id
371
+ when 'any'
372
+ SESSION.add_catch_breakpoint 'Exception', cond: cond
373
+ when 'RuntimeError'
374
+ SESSION.add_catch_breakpoint 'RuntimeError', cond: cond
375
+ else
376
+ nil
377
+ end
378
+ {
379
+ verified: !bp.nil?,
380
+ message: bp.inspect,
358
381
  }
382
+ }
383
+
384
+ SESSION.clear_catch_breakpoints 'Exception', 'RuntimeError'
359
385
 
360
- filters += args.fetch('filterOptions', {}).map{|bp_info|
361
- process_filter.call(bp_info['filterId'], bp_info['condition'])
386
+ filters = args.fetch('filters').map {|filter_id|
387
+ process_filter.call(filter_id)
362
388
  }
363
389
 
364
- send_response req, breakpoints: filters
390
+ filters += args.fetch('filterOptions', {}).map{|bp_info|
391
+ process_filter.call(bp_info['filterId'], bp_info['condition'])
392
+ }
365
393
 
366
- when 'disconnect'
367
- terminate = args.fetch("terminateDebuggee", false)
394
+ send_response req, breakpoints: filters
368
395
 
369
- SESSION.clear_all_breakpoints
370
- send_response req
396
+ when 'disconnect'
397
+ terminate = args.fetch("terminateDebuggee", false)
371
398
 
372
- if SESSION.in_subsession?
373
- if terminate
374
- @q_msg << 'kill!'
375
- else
376
- @q_msg << 'continue'
377
- end
378
- else
379
- if terminate
380
- @q_msg << 'kill!'
381
- pause
382
- end
383
- end
399
+ SESSION.clear_all_breakpoints
400
+ send_response req
384
401
 
385
- ## control
386
- when 'continue'
387
- @q_msg << 'c'
388
- send_response req, allThreadsContinued: true
389
- when 'next'
390
- begin
391
- @session.check_postmortem
392
- @q_msg << 'n'
393
- send_response req
394
- rescue PostmortemError
395
- send_response req,
396
- success: false, message: 'postmortem mode',
397
- result: "'Next' is not supported while postmortem mode"
398
- end
399
- when 'stepIn'
400
- begin
401
- @session.check_postmortem
402
- @q_msg << 's'
403
- send_response req
404
- rescue PostmortemError
405
- send_response req,
406
- success: false, message: 'postmortem mode',
407
- result: "'stepIn' is not supported while postmortem mode"
402
+ if SESSION.in_subsession?
403
+ if terminate
404
+ @q_msg << 'kill!'
405
+ else
406
+ @q_msg << 'continue'
408
407
  end
409
- when 'stepOut'
410
- begin
411
- @session.check_postmortem
412
- @q_msg << 'fin'
413
- send_response req
414
- rescue PostmortemError
415
- send_response req,
416
- success: false, message: 'postmortem mode',
417
- result: "'stepOut' is not supported while postmortem mode"
408
+ else
409
+ if terminate
410
+ @q_msg << 'kill!'
411
+ pause
418
412
  end
419
- when 'terminate'
413
+ end
414
+
415
+ ## control
416
+ when 'continue'
417
+ @q_msg << 'c'
418
+ send_response req, allThreadsContinued: true
419
+ when 'next'
420
+ begin
421
+ @session.check_postmortem
422
+ @q_msg << 'n'
420
423
  send_response req
421
- exit
422
- when 'pause'
424
+ rescue PostmortemError
425
+ send_response req,
426
+ success: false, message: 'postmortem mode',
427
+ result: "'Next' is not supported while postmortem mode"
428
+ end
429
+ when 'stepIn'
430
+ begin
431
+ @session.check_postmortem
432
+ @q_msg << 's'
423
433
  send_response req
424
- Process.kill(UI_ServerBase::TRAP_SIGNAL, Process.pid)
425
- when 'reverseContinue'
434
+ rescue PostmortemError
426
435
  send_response req,
427
- success: false, message: 'cancelled',
428
- result: "Reverse Continue is not supported. Only \"Step back\" is supported."
429
- when 'stepBack'
430
- @q_msg << req
436
+ success: false, message: 'postmortem mode',
437
+ result: "'stepIn' is not supported while postmortem mode"
438
+ end
439
+ when 'stepOut'
440
+ begin
441
+ @session.check_postmortem
442
+ @q_msg << 'fin'
443
+ send_response req
444
+ rescue PostmortemError
445
+ send_response req,
446
+ success: false, message: 'postmortem mode',
447
+ result: "'stepOut' is not supported while postmortem mode"
448
+ end
449
+ when 'terminate'
450
+ send_response req
451
+ exit
452
+ when 'pause'
453
+ send_response req
454
+ Process.kill(UI_ServerBase::TRAP_SIGNAL, Process.pid)
455
+ when 'reverseContinue'
456
+ send_response req,
457
+ success: false, message: 'cancelled',
458
+ result: "Reverse Continue is not supported. Only \"Step back\" is supported."
459
+ when 'stepBack'
460
+ @q_msg << req
431
461
 
432
- ## query
433
- when 'threads'
434
- send_response req, threads: SESSION.managed_thread_clients.map{|tc|
435
- { id: tc.id,
436
- name: tc.name,
437
- }
462
+ ## query
463
+ when 'threads'
464
+ send_response req, threads: SESSION.managed_thread_clients.map{|tc|
465
+ { id: tc.id,
466
+ name: tc.name,
438
467
  }
468
+ }
439
469
 
440
- when 'evaluate'
441
- expr = req.dig('arguments', 'expression')
442
- if /\A\s*,(.+)\z/ =~ expr
443
- dbg_expr = $1.strip
444
- dbg_expr.split(';;') { |cmd| @q_msg << cmd }
470
+ when 'evaluate'
471
+ expr = req.dig('arguments', 'expression')
472
+ if /\A\s*,(.+)\z/ =~ expr
473
+ dbg_expr = $1.strip
474
+ dbg_expr.split(';;') { |cmd| @q_msg << cmd }
445
475
 
446
- send_response req,
447
- result: "(rdbg:command) #{dbg_expr}",
448
- variablesReference: 0
449
- else
450
- @q_msg << req
451
- end
452
- when 'stackTrace',
453
- 'scopes',
454
- 'variables',
455
- 'source',
456
- 'completions'
476
+ send_response req,
477
+ result: "(rdbg:command) #{dbg_expr}",
478
+ variablesReference: 0
479
+ else
457
480
  @q_msg << req
481
+ end
482
+ when 'stackTrace',
483
+ 'scopes',
484
+ 'variables',
485
+ 'source',
486
+ 'completions'
487
+ @q_msg << req
458
488
 
489
+ else
490
+ if respond_to? mid = "custom_dap_request_#{req['command']}"
491
+ __send__ mid, req
459
492
  else
460
- if respond_to? mid = "custom_dap_request_#{req['command']}"
461
- __send__ mid, req
462
- else
463
- raise "Unknown request: #{req.inspect}"
464
- end
493
+ raise "Unknown request: #{req.inspect}"
465
494
  end
466
495
  end
467
- ensure
468
- send_event :terminated unless @sock.closed?
469
496
  end
470
497
 
471
498
  ## called by the SESSION thread
@@ -625,6 +652,7 @@ module DEBUGGER__
625
652
  expr = req.dig('arguments', 'expression')
626
653
 
627
654
  if find_waiting_tc(tid)
655
+ restart_all_threads
628
656
  request_tc [:dap, :evaluate, req, fid, expr, context]
629
657
  else
630
658
  fail_response req
@@ -701,6 +729,7 @@ module DEBUGGER__
701
729
  register_vars result[:variables], tid
702
730
  @ui.respond req, result
703
731
  when :evaluate
732
+ stop_all_threads
704
733
  message = result.delete :message
705
734
  if message
706
735
  @ui.respond req, success: false, message: message
@@ -867,7 +896,7 @@ module DEBUGGER__
867
896
  }
868
897
  when String
869
898
  vars = [
870
- variable('#lengthddsfsd', obj.length),
899
+ variable('#length', obj.length),
871
900
  variable('#encoding', obj.encoding),
872
901
  ]
873
902
  printed_str = value_inspect(obj)
@@ -1014,7 +1043,7 @@ module DEBUGGER__
1014
1043
  klass = M_CLASS.bind_call(obj)
1015
1044
 
1016
1045
  begin
1017
- klass.name || klass.to_s
1046
+ M_NAME.bind_call(klass) || klass.to_s
1018
1047
  rescue Exception => e
1019
1048
  "<Error: #{e.message} (#{e.backtrace.first}>"
1020
1049
  end
data/lib/debug/session.rb CHANGED
@@ -1993,6 +1993,13 @@ module DEBUGGER__
1993
1993
  def after_fork_parent
1994
1994
  @ui.after_fork_parent
1995
1995
  end
1996
+
1997
+ # experimental API
1998
+ def extend_feature session: nil, thread_client: nil, ui: nil
1999
+ Session.include session if session
2000
+ ThreadClient.include thread_client if thread_client
2001
+ @ui.extend ui if ui
2002
+ end
1996
2003
  end
1997
2004
 
1998
2005
  class ProcessGroup
@@ -2517,7 +2524,17 @@ module DEBUGGER__
2517
2524
 
2518
2525
  module TrapInterceptor
2519
2526
  def trap sig, *command, &command_proc
2520
- case sig&.to_sym
2527
+ sym =
2528
+ case sig
2529
+ when String
2530
+ sig.to_sym
2531
+ when Integer
2532
+ Signal.signame(sig)&.to_sym
2533
+ else
2534
+ sig
2535
+ end
2536
+
2537
+ case sig&.to_s&.to_sym
2521
2538
  when :INT, :SIGINT
2522
2539
  if defined?(SESSION) && SESSION.active? && SESSION.intercept_trap_sigint?
2523
2540
  return SESSION.save_int_trap(command.empty? ? command_proc : command.first)
@@ -14,6 +14,7 @@ module DEBUGGER__
14
14
  M_RESPOND_TO_P = method(:respond_to?).unbind
15
15
  M_METHOD = method(:method).unbind
16
16
  M_OBJECT_ID = method(:object_id).unbind
17
+ M_NAME = method(:name).unbind
17
18
 
18
19
  module SkipPathHelper
19
20
  def skip_path?(path)
@@ -468,10 +469,14 @@ module DEBUGGER__
468
469
  if file_lines = frame.file_lines
469
470
  frame_line = frame.location.lineno - 1
470
471
 
471
- lines = file_lines.map.with_index do |e, i|
472
- cur = i == frame_line ? '=>' : ' '
473
- line = colorize_dim('%4d|' % (i+1))
474
- "#{cur}#{line} #{e}"
472
+ if CONFIG[:no_lineno]
473
+ lines = file_lines
474
+ else
475
+ lines = file_lines.map.with_index do |e, i|
476
+ cur = i == frame_line ? '=>' : ' '
477
+ line = colorize_dim('%4d|' % (i+1))
478
+ "#{cur}#{line} #{e}"
479
+ end
475
480
  end
476
481
 
477
482
  unless start_line
@@ -607,12 +612,17 @@ module DEBUGGER__
607
612
 
608
613
  def get_consts expr = nil, only_self: false, &block
609
614
  if expr && !expr.empty?
610
- _self = frame_eval(expr)
611
- if M_KIND_OF_P.bind_call(_self, Module)
612
- iter_consts _self, &block
613
- return
615
+ begin
616
+ _self = frame_eval(expr, re_raise: true)
617
+ rescue Exception => e
618
+ # ignore
614
619
  else
615
- raise "#{_self.inspect} (by #{expr}) is not a Module."
620
+ if M_KIND_OF_P.bind_call(_self, Module)
621
+ iter_consts _self, &block
622
+ return
623
+ else
624
+ puts "#{_self.inspect} (by #{expr}) is not a Module."
625
+ end
616
626
  end
617
627
  elsif _self = current_frame&.self
618
628
  cs = {}
@@ -1300,10 +1310,14 @@ module DEBUGGER__
1300
1310
  frame._callee = b.eval('__callee__')
1301
1311
  end
1302
1312
  }
1303
- @log << frames
1313
+ append(frames)
1304
1314
  }
1305
1315
  end
1306
1316
 
1317
+ def append frames
1318
+ @log << frames
1319
+ end
1320
+
1307
1321
  def enable
1308
1322
  unless @tp_recorder.enabled?
1309
1323
  @log.clear
data/lib/debug/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DEBUGGER__
4
- VERSION = "1.7.2"
4
+ VERSION = "1.8.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: debug
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.2
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Koichi Sasada
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-03-28 00:00:00.000000000 Z
11
+ date: 2023-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: irb
@@ -66,6 +66,7 @@ files:
66
66
  - lib/debug/color.rb
67
67
  - lib/debug/config.rb
68
68
  - lib/debug/console.rb
69
+ - lib/debug/dap_custom/traceInspector.rb
69
70
  - lib/debug/frame_info.rb
70
71
  - lib/debug/local.rb
71
72
  - lib/debug/open.rb