debug 1.7.1 → 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.
@@ -125,6 +125,7 @@ module DEBUGGER__
125
125
  def dap_setup bytes
126
126
  CONFIG.set_config no_color: true
127
127
  @seq = 0
128
+ @send_lock = Mutex.new
128
129
 
129
130
  case self
130
131
  when UI_UnixDomainServer
@@ -212,9 +213,13 @@ module DEBUGGER__
212
213
  if sock = @sock
213
214
  kw[:seq] = @seq += 1
214
215
  str = JSON.dump(kw)
215
- sock.write "Content-Length: #{str.bytesize}\r\n\r\n#{str}"
216
+ @send_lock.synchronize do
217
+ sock.write "Content-Length: #{str.bytesize}\r\n\r\n#{str}"
218
+ end
216
219
  show_protocol '<', str
217
220
  end
221
+ rescue Errno::EPIPE => e
222
+ $stderr.puts "#{e.inspect} rescued during sending message"
218
223
  end
219
224
 
220
225
  def send_response req, success: true, message: nil, **kw
@@ -246,17 +251,17 @@ module DEBUGGER__
246
251
  end
247
252
 
248
253
  def recv_request
249
- r = IO.select([@sock])
254
+ IO.select([@sock])
250
255
 
251
256
  @session.process_group.sync do
252
257
  raise RetryBecauseCantRead unless IO.select([@sock], nil, nil, 0)
253
258
 
254
- case header = @sock.gets
259
+ case @sock.gets
255
260
  when /Content-Length: (\d+)/
256
261
  b = @sock.read(2)
257
262
  raise b.inspect unless b == "\r\n"
258
263
 
259
- l = @sock.read(s = $1.to_i)
264
+ l = @sock.read($1.to_i)
260
265
  show_protocol :>, l
261
266
  JSON.load(l)
262
267
  when nil
@@ -269,198 +274,225 @@ module DEBUGGER__
269
274
  retry
270
275
  end
271
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
+
272
296
  def process
273
297
  while req = recv_request
274
- raise "not a request: #{req.inspect}" unless req['type'] == 'request'
275
- args = req.dig('arguments')
298
+ process_request(req)
299
+ end
300
+ ensure
301
+ send_event :terminated unless @sock.closed?
302
+ end
276
303
 
277
- case req['command']
304
+ def process_request req
305
+ raise "not a request: #{req.inspect}" unless req['type'] == 'request'
306
+ args = req.dig('arguments')
278
307
 
279
- ## boot/configuration
280
- when 'launch'
281
- send_response req
282
- # `launch` runs on debuggee on the same file system
283
- UI_DAP.local_fs_map_set req.dig('arguments', 'localfs') || req.dig('arguments', 'localfsMap') || true
284
- @nonstop = true
308
+ case req['command']
285
309
 
286
- when 'attach'
287
- send_response req
288
- 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
289
316
 
290
- if req.dig('arguments', 'nonstop') == true
291
- @nonstop = true
292
- else
293
- @nonstop = false
294
- end
317
+ load_extensions req
295
318
 
296
- when 'configurationDone'
297
- 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')
298
322
 
299
- if @nonstop
300
- @q_msg << 'continue'
301
- else
302
- if SESSION.in_subsession?
303
- send_event 'stopped', reason: 'pause',
304
- threadId: 1, # maybe ...
305
- allThreadsStopped: true
306
- end
307
- end
323
+ if req.dig('arguments', 'nonstop') == true
324
+ @nonstop = true
325
+ else
326
+ @nonstop = false
327
+ end
308
328
 
309
- when 'setBreakpoints'
310
- req_path = args.dig('source', 'path')
311
- path = UI_DAP.local_to_remote_path(req_path)
329
+ load_extensions req
312
330
 
313
- if path
314
- SESSION.clear_line_breakpoints path
331
+ when 'configurationDone'
332
+ send_response req
315
333
 
316
- bps = []
317
- args['breakpoints'].each{|bp|
318
- line = bp['line']
319
- if cond = bp['condition']
320
- bps << SESSION.add_line_breakpoint(path, line, cond: cond)
321
- else
322
- bps << SESSION.add_line_breakpoint(path, line)
323
- end
324
- }
325
- send_response req, breakpoints: (bps.map do |bp| {verified: true,} end)
326
- else
327
- send_response req, success: false, message: "#{req_path} is not available"
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
328
341
  end
342
+ end
329
343
 
330
- when 'setFunctionBreakpoints'
331
- send_response req
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
332
363
 
333
- when 'setExceptionBreakpoints'
334
- process_filter = ->(filter_id, cond = nil) {
335
- bp =
336
- case filter_id
337
- when 'any'
338
- SESSION.add_catch_breakpoint 'Exception', cond: cond
339
- when 'RuntimeError'
340
- SESSION.add_catch_breakpoint 'RuntimeError', cond: cond
341
- else
342
- nil
343
- end
344
- {
345
- verified: !bp.nil?,
346
- message: bp.inspect,
347
- }
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,
348
381
  }
382
+ }
349
383
 
350
- SESSION.clear_catch_breakpoints 'Exception', 'RuntimeError'
351
-
352
- filters = args.fetch('filters').map {|filter_id|
353
- process_filter.call(filter_id)
354
- }
384
+ SESSION.clear_catch_breakpoints 'Exception', 'RuntimeError'
355
385
 
356
- filters += args.fetch('filterOptions', {}).map{|bp_info|
357
- process_filter.call(bp_info['filterId'], bp_info['condition'])
386
+ filters = args.fetch('filters').map {|filter_id|
387
+ process_filter.call(filter_id)
358
388
  }
359
389
 
360
- send_response req, breakpoints: filters
390
+ filters += args.fetch('filterOptions', {}).map{|bp_info|
391
+ process_filter.call(bp_info['filterId'], bp_info['condition'])
392
+ }
361
393
 
362
- when 'disconnect'
363
- terminate = args.fetch("terminateDebuggee", false)
394
+ send_response req, breakpoints: filters
364
395
 
365
- SESSION.clear_all_breakpoints
366
- send_response req
396
+ when 'disconnect'
397
+ terminate = args.fetch("terminateDebuggee", false)
367
398
 
368
- if SESSION.in_subsession?
369
- if terminate
370
- @q_msg << 'kill!'
371
- else
372
- @q_msg << 'continue'
373
- end
374
- else
375
- if terminate
376
- @q_msg << 'kill!'
377
- pause
378
- end
379
- end
399
+ SESSION.clear_all_breakpoints
400
+ send_response req
380
401
 
381
- ## control
382
- when 'continue'
383
- @q_msg << 'c'
384
- send_response req, allThreadsContinued: true
385
- when 'next'
386
- begin
387
- @session.check_postmortem
388
- @q_msg << 'n'
389
- send_response req
390
- rescue PostmortemError
391
- send_response req,
392
- success: false, message: 'postmortem mode',
393
- result: "'Next' is not supported while postmortem mode"
394
- end
395
- when 'stepIn'
396
- begin
397
- @session.check_postmortem
398
- @q_msg << 's'
399
- send_response req
400
- rescue PostmortemError
401
- send_response req,
402
- success: false, message: 'postmortem mode',
403
- 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'
404
407
  end
405
- when 'stepOut'
406
- begin
407
- @session.check_postmortem
408
- @q_msg << 'fin'
409
- send_response req
410
- rescue PostmortemError
411
- send_response req,
412
- success: false, message: 'postmortem mode',
413
- result: "'stepOut' is not supported while postmortem mode"
408
+ else
409
+ if terminate
410
+ @q_msg << 'kill!'
411
+ pause
414
412
  end
415
- 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'
416
423
  send_response req
417
- exit
418
- 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'
419
433
  send_response req
420
- Process.kill(UI_ServerBase::TRAP_SIGNAL, Process.pid)
421
- when 'reverseContinue'
434
+ rescue PostmortemError
422
435
  send_response req,
423
- success: false, message: 'cancelled',
424
- result: "Reverse Continue is not supported. Only \"Step back\" is supported."
425
- when 'stepBack'
426
- @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
427
461
 
428
- ## query
429
- when 'threads'
430
- send_response req, threads: SESSION.managed_thread_clients.map{|tc|
431
- { id: tc.id,
432
- name: tc.name,
433
- }
462
+ ## query
463
+ when 'threads'
464
+ send_response req, threads: SESSION.managed_thread_clients.map{|tc|
465
+ { id: tc.id,
466
+ name: tc.name,
434
467
  }
468
+ }
435
469
 
436
- when 'evaluate'
437
- expr = req.dig('arguments', 'expression')
438
- if /\A\s*,(.+)\z/ =~ expr
439
- dbg_expr = $1
440
- send_response req,
441
- result: "",
442
- variablesReference: 0
443
- debugger do: dbg_expr
444
- else
445
- @q_msg << req
446
- end
447
- when 'stackTrace',
448
- 'scopes',
449
- 'variables',
450
- 'source',
451
- 'completions'
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 }
475
+
476
+ send_response req,
477
+ result: "(rdbg:command) #{dbg_expr}",
478
+ variablesReference: 0
479
+ else
452
480
  @q_msg << req
481
+ end
482
+ when 'stackTrace',
483
+ 'scopes',
484
+ 'variables',
485
+ 'source',
486
+ 'completions'
487
+ @q_msg << req
453
488
 
489
+ else
490
+ if respond_to? mid = "custom_dap_request_#{req['command']}"
491
+ __send__ mid, req
454
492
  else
455
- if respond_to? mid = "request_#{req['command']}"
456
- send mid, req
457
- else
458
- raise "Unknown request: #{req.inspect}"
459
- end
493
+ raise "Unknown request: #{req.inspect}"
460
494
  end
461
495
  end
462
- ensure
463
- send_event :terminated unless @sock.closed?
464
496
  end
465
497
 
466
498
  ## called by the SESSION thread
@@ -546,7 +578,7 @@ module DEBUGGER__
546
578
  when 'stackTrace'
547
579
  tid = req.dig('arguments', 'threadId')
548
580
 
549
- if tc = find_waiting_tc(tid)
581
+ if find_waiting_tc(tid)
550
582
  request_tc [:dap, :backtrace, req]
551
583
  else
552
584
  fail_response req
@@ -555,7 +587,7 @@ module DEBUGGER__
555
587
  frame_id = req.dig('arguments', 'frameId')
556
588
  if @frame_map[frame_id]
557
589
  tid, fid = @frame_map[frame_id]
558
- if tc = find_waiting_tc(tid)
590
+ if find_waiting_tc(tid)
559
591
  request_tc [:dap, :scopes, req, fid]
560
592
  else
561
593
  fail_response req
@@ -591,7 +623,7 @@ module DEBUGGER__
591
623
  frame_id = ref[1]
592
624
  tid, fid = @frame_map[frame_id]
593
625
 
594
- if tc = find_waiting_tc(tid)
626
+ if find_waiting_tc(tid)
595
627
  request_tc [:dap, :scope, req, fid]
596
628
  else
597
629
  fail_response req
@@ -600,7 +632,7 @@ module DEBUGGER__
600
632
  when :variable
601
633
  tid, vid = ref[1], ref[2]
602
634
 
603
- if tc = find_waiting_tc(tid)
635
+ if find_waiting_tc(tid)
604
636
  request_tc [:dap, :variable, req, vid]
605
637
  else
606
638
  fail_response req
@@ -619,7 +651,8 @@ module DEBUGGER__
619
651
  tid, fid = @frame_map[frame_id]
620
652
  expr = req.dig('arguments', 'expression')
621
653
 
622
- if tc = find_waiting_tc(tid)
654
+ if find_waiting_tc(tid)
655
+ restart_all_threads
623
656
  request_tc [:dap, :evaluate, req, fid, expr, context]
624
657
  else
625
658
  fail_response req
@@ -640,7 +673,7 @@ module DEBUGGER__
640
673
  frame_id = req.dig('arguments', 'frameId')
641
674
  tid, fid = @frame_map[frame_id]
642
675
 
643
- if tc = find_waiting_tc(tid)
676
+ if find_waiting_tc(tid)
644
677
  text = req.dig('arguments', 'text')
645
678
  line = req.dig('arguments', 'line')
646
679
  if col = req.dig('arguments', 'column')
@@ -651,11 +684,15 @@ module DEBUGGER__
651
684
  fail_response req
652
685
  end
653
686
  else
654
- raise "Unknown DAP request: #{req.inspect}"
687
+ if respond_to? mid = "custom_dap_request_#{req['command']}"
688
+ __send__ mid, req
689
+ else
690
+ raise "Unknown request: #{req.inspect}"
691
+ end
655
692
  end
656
693
  end
657
694
 
658
- def dap_event args
695
+ def process_protocol_result args
659
696
  # puts({dap_event: args}.inspect)
660
697
  type, req, result = args
661
698
 
@@ -692,6 +729,7 @@ module DEBUGGER__
692
729
  register_vars result[:variables], tid
693
730
  @ui.respond req, result
694
731
  when :evaluate
732
+ stop_all_threads
695
733
  message = result.delete :message
696
734
  if message
697
735
  @ui.respond req, success: false, message: message
@@ -703,7 +741,11 @@ module DEBUGGER__
703
741
  when :completions
704
742
  @ui.respond req, result
705
743
  else
706
- raise "unsupported: #{args.inspect}"
744
+ if respond_to? mid = "custom_dap_request_event_#{type}"
745
+ __send__ mid, req, result
746
+ else
747
+ raise "unsupported: #{args.inspect}"
748
+ end
707
749
  end
708
750
  end
709
751
 
@@ -789,7 +831,7 @@ module DEBUGGER__
789
831
  }
790
832
  end
791
833
 
792
- event! :dap_result, :backtrace, req, {
834
+ event! :protocol_result, :backtrace, req, {
793
835
  stackFrames: frames,
794
836
  totalFrames: @target_frames.size,
795
837
  }
@@ -806,7 +848,7 @@ module DEBUGGER__
806
848
  0
807
849
  end
808
850
 
809
- event! :dap_result, :scopes, req, scopes: [{
851
+ event! :protocol_result, :scopes, req, scopes: [{
810
852
  name: 'Local variables',
811
853
  presentationHint: 'locals',
812
854
  # variablesReference: N, # filled by SESSION
@@ -828,7 +870,7 @@ module DEBUGGER__
828
870
  variable(var, val)
829
871
  end
830
872
 
831
- event! :dap_result, :scope, req, variables: vars, tid: self.id
873
+ event! :protocol_result, :scope, req, variables: vars, tid: self.id
832
874
  when :variable
833
875
  vid = args.shift
834
876
  obj = @var_map[vid]
@@ -854,7 +896,7 @@ module DEBUGGER__
854
896
  }
855
897
  when String
856
898
  vars = [
857
- variable('#lengthddsfsd', obj.length),
899
+ variable('#length', obj.length),
858
900
  variable('#encoding', obj.encoding),
859
901
  ]
860
902
  printed_str = value_inspect(obj)
@@ -876,7 +918,7 @@ module DEBUGGER__
876
918
  end
877
919
  end
878
920
  end
879
- event! :dap_result, :variable, req, variables: (vars || []), tid: self.id
921
+ event! :protocol_result, :variable, req, variables: (vars || []), tid: self.id
880
922
 
881
923
  when :evaluate
882
924
  fid, expr, context = args
@@ -931,7 +973,7 @@ module DEBUGGER__
931
973
  result = 'Error: Can not evaluate on this frame'
932
974
  end
933
975
 
934
- event! :dap_result, :evaluate, req, message: message, tid: self.id, **evaluate_result(result)
976
+ event! :protocol_result, :evaluate, req, message: message, tid: self.id, **evaluate_result(result)
935
977
 
936
978
  when :completions
937
979
  fid, text = args
@@ -941,7 +983,7 @@ module DEBUGGER__
941
983
  words = IRB::InputCompletor::retrieve_completion_data(word, bind: b).compact
942
984
  end
943
985
 
944
- event! :dap_result, :completions, req, targets: (words || []).map{|phrase|
986
+ event! :protocol_result, :completions, req, targets: (words || []).map{|phrase|
945
987
  detail = nil
946
988
 
947
989
  if /\b([_a-zA-Z]\w*[!\?]?)\z/ =~ phrase
@@ -964,7 +1006,11 @@ module DEBUGGER__
964
1006
  }
965
1007
 
966
1008
  else
967
- raise "Unknown req: #{args.inspect}"
1009
+ if respond_to? mid = "custom_dap_request_#{type}"
1010
+ __send__ mid, req
1011
+ else
1012
+ raise "Unknown request: #{args.inspect}"
1013
+ end
968
1014
  end
969
1015
  end
970
1016
 
@@ -997,7 +1043,7 @@ module DEBUGGER__
997
1043
  klass = M_CLASS.bind_call(obj)
998
1044
 
999
1045
  begin
1000
- klass.name || klass.to_s
1046
+ M_NAME.bind_call(klass) || klass.to_s
1001
1047
  rescue Exception => e
1002
1048
  "<Error: #{e.message} (#{e.backtrace.first}>"
1003
1049
  end