debug 1.7.2 → 1.8.0

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