debug 1.7.1 → 1.8.0

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