debug 1.6.3 → 1.7.1
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 +4 -4
- data/CONTRIBUTING.md +19 -7
- data/Gemfile +0 -0
- data/LICENSE.txt +0 -0
- data/README.md +37 -14
- data/Rakefile +0 -0
- data/TODO.md +8 -8
- data/debug.gemspec +1 -1
- data/exe/rdbg +1 -1
- data/ext/debug/debug.c +15 -1
- data/ext/debug/extconf.rb +0 -0
- data/ext/debug/iseq_collector.c +0 -0
- data/lib/debug/abbrev_command.rb +77 -0
- data/lib/debug/breakpoint.rb +17 -11
- data/lib/debug/client.rb +26 -9
- data/lib/debug/color.rb +0 -0
- data/lib/debug/config.rb +34 -16
- data/lib/debug/console.rb +0 -0
- data/lib/debug/frame_info.rb +0 -0
- data/lib/debug/local.rb +16 -10
- data/lib/debug/open.rb +0 -0
- data/lib/debug/open_nonstop.rb +0 -0
- data/lib/debug/prelude.rb +1 -1
- data/lib/debug/server.rb +25 -21
- data/lib/debug/server_cdp.rb +292 -86
- data/lib/debug/server_dap.rb +132 -47
- data/lib/debug/session.rb +384 -204
- data/lib/debug/source_repository.rb +39 -19
- data/lib/debug/start.rb +1 -1
- data/lib/debug/thread_client.rb +196 -63
- data/lib/debug/tracer.rb +0 -0
- data/lib/debug/version.rb +1 -1
- data/lib/debug.rb +7 -3
- data/misc/README.md.erb +9 -3
- metadata +5 -4
data/lib/debug/server_dap.rb
CHANGED
@@ -18,7 +18,7 @@ module DEBUGGER__
|
|
18
18
|
end
|
19
19
|
|
20
20
|
at_exit do
|
21
|
-
|
21
|
+
DEBUGGER__.skip_all
|
22
22
|
FileUtils.rm_rf dir if tempdir
|
23
23
|
end
|
24
24
|
|
@@ -128,7 +128,8 @@ module DEBUGGER__
|
|
128
128
|
|
129
129
|
case self
|
130
130
|
when UI_UnixDomainServer
|
131
|
-
|
131
|
+
# If the user specified a mapping, respect it, otherwise, make sure that no mapping is used
|
132
|
+
UI_DAP.local_fs_map_set CONFIG[:local_fs_map] || true
|
132
133
|
when UI_TcpServer
|
133
134
|
# TODO: loopback address can be used to connect other FS env, like Docker containers
|
134
135
|
# UI_DAP.local_fs_set if @local_addr.ipv4_loopback? || @local_addr.ipv6_loopback?
|
@@ -198,6 +199,13 @@ module DEBUGGER__
|
|
198
199
|
# supportsInstructionBreakpoints:
|
199
200
|
)
|
200
201
|
send_event 'initialized'
|
202
|
+
puts <<~WELCOME
|
203
|
+
Ruby REPL: You can run any Ruby expression here.
|
204
|
+
Note that output to the STDOUT/ERR printed on the TERMINAL.
|
205
|
+
[experimental]
|
206
|
+
`,COMMAND` runs `COMMAND` debug command (ex: `,info`).
|
207
|
+
`,help` to list all debug commands.
|
208
|
+
WELCOME
|
201
209
|
end
|
202
210
|
|
203
211
|
def send **kw
|
@@ -271,18 +279,24 @@ module DEBUGGER__
|
|
271
279
|
## boot/configuration
|
272
280
|
when 'launch'
|
273
281
|
send_response req
|
274
|
-
|
275
|
-
|
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
|
276
285
|
|
277
286
|
when 'attach'
|
278
287
|
send_response req
|
279
288
|
UI_DAP.local_fs_map_set req.dig('arguments', 'localfs') || req.dig('arguments', 'localfsMap')
|
280
|
-
|
289
|
+
|
290
|
+
if req.dig('arguments', 'nonstop') == true
|
291
|
+
@nonstop = true
|
292
|
+
else
|
293
|
+
@nonstop = false
|
294
|
+
end
|
281
295
|
|
282
296
|
when 'configurationDone'
|
283
297
|
send_response req
|
284
298
|
|
285
|
-
if @
|
299
|
+
if @nonstop
|
286
300
|
@q_msg << 'continue'
|
287
301
|
else
|
288
302
|
if SESSION.in_subsession?
|
@@ -419,16 +433,30 @@ module DEBUGGER__
|
|
419
433
|
}
|
420
434
|
}
|
421
435
|
|
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
|
422
447
|
when 'stackTrace',
|
423
448
|
'scopes',
|
424
449
|
'variables',
|
425
|
-
'evaluate',
|
426
450
|
'source',
|
427
451
|
'completions'
|
428
452
|
@q_msg << req
|
429
453
|
|
430
454
|
else
|
431
|
-
|
455
|
+
if respond_to? mid = "request_#{req['command']}"
|
456
|
+
send mid, req
|
457
|
+
else
|
458
|
+
raise "Unknown request: #{req.inspect}"
|
459
|
+
end
|
432
460
|
end
|
433
461
|
end
|
434
462
|
ensure
|
@@ -443,7 +471,11 @@ module DEBUGGER__
|
|
443
471
|
|
444
472
|
def puts result
|
445
473
|
# STDERR.puts "puts: #{result}"
|
446
|
-
|
474
|
+
send_event 'output', category: 'console', output: "#{result&.chomp}\n"
|
475
|
+
end
|
476
|
+
|
477
|
+
def ignore_output_on_suspend?
|
478
|
+
true
|
447
479
|
end
|
448
480
|
|
449
481
|
def event type, *args
|
@@ -488,6 +520,8 @@ module DEBUGGER__
|
|
488
520
|
end
|
489
521
|
|
490
522
|
class Session
|
523
|
+
include GlobalVariablesHelper
|
524
|
+
|
491
525
|
def find_waiting_tc id
|
492
526
|
@th_clients.each{|th, tc|
|
493
527
|
return tc if tc.id == id && tc.waiting?
|
@@ -534,8 +568,12 @@ module DEBUGGER__
|
|
534
568
|
if ref = @var_map[varid]
|
535
569
|
case ref[0]
|
536
570
|
when :globals
|
537
|
-
vars =
|
538
|
-
|
571
|
+
vars = safe_global_variables.sort.map do |name|
|
572
|
+
begin
|
573
|
+
gv = eval(name.to_s)
|
574
|
+
rescue Exception => e
|
575
|
+
gv = e.inspect
|
576
|
+
end
|
539
577
|
{
|
540
578
|
name: name,
|
541
579
|
value: gv.inspect,
|
@@ -580,6 +618,7 @@ module DEBUGGER__
|
|
580
618
|
if @frame_map[frame_id]
|
581
619
|
tid, fid = @frame_map[frame_id]
|
582
620
|
expr = req.dig('arguments', 'expression')
|
621
|
+
|
583
622
|
if tc = find_waiting_tc(tid)
|
584
623
|
request_tc [:dap, :evaluate, req, fid, expr, context]
|
585
624
|
else
|
@@ -684,10 +723,33 @@ module DEBUGGER__
|
|
684
723
|
end
|
685
724
|
end
|
686
725
|
|
726
|
+
class NaiveString
|
727
|
+
attr_reader :str
|
728
|
+
def initialize str
|
729
|
+
@str = str
|
730
|
+
end
|
731
|
+
end
|
732
|
+
|
687
733
|
class ThreadClient
|
688
|
-
|
734
|
+
MAX_LENGTH = 180
|
735
|
+
|
736
|
+
def value_inspect obj, short: true
|
689
737
|
# TODO: max length should be configuarable?
|
690
|
-
DEBUGGER__.safe_inspect obj, short:
|
738
|
+
str = DEBUGGER__.safe_inspect obj, short: short, max_length: MAX_LENGTH
|
739
|
+
|
740
|
+
if str.encoding == Encoding::UTF_8
|
741
|
+
str.scrub
|
742
|
+
else
|
743
|
+
str.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
|
744
|
+
end
|
745
|
+
end
|
746
|
+
|
747
|
+
def dap_eval b, expr, _context, prompt: '(repl_eval)'
|
748
|
+
begin
|
749
|
+
b.eval(expr.to_s, prompt)
|
750
|
+
rescue Exception => e
|
751
|
+
e
|
752
|
+
end
|
691
753
|
end
|
692
754
|
|
693
755
|
def process_dap args
|
@@ -702,9 +764,10 @@ module DEBUGGER__
|
|
702
764
|
frames = []
|
703
765
|
@target_frames.each_with_index do |frame, i|
|
704
766
|
next if i < start_frame
|
705
|
-
break if (levels -= 1) < 0
|
706
767
|
|
707
768
|
path = frame.realpath || frame.path
|
769
|
+
next if skip_path?(path) && !SESSION.stop_stepping?(path, frame.location.lineno)
|
770
|
+
break if (levels -= 1) < 0
|
708
771
|
source_name = path ? File.basename(path) : frame.location.to_s
|
709
772
|
|
710
773
|
if (path && File.exist?(path)) && (local_path = UI_DAP.remote_to_local_path(path))
|
@@ -754,7 +817,7 @@ module DEBUGGER__
|
|
754
817
|
name: 'Global variables',
|
755
818
|
presentationHint: 'globals',
|
756
819
|
variablesReference: 1, # GLOBAL
|
757
|
-
namedVariables:
|
820
|
+
namedVariables: safe_global_variables.size,
|
758
821
|
indexedVariables: 0,
|
759
822
|
expensive: false,
|
760
823
|
}]
|
@@ -791,14 +854,13 @@ module DEBUGGER__
|
|
791
854
|
}
|
792
855
|
when String
|
793
856
|
vars = [
|
794
|
-
variable('#
|
795
|
-
variable('#encoding', obj.encoding)
|
857
|
+
variable('#lengthddsfsd', obj.length),
|
858
|
+
variable('#encoding', obj.encoding),
|
796
859
|
]
|
860
|
+
printed_str = value_inspect(obj)
|
861
|
+
vars << variable('#dump', NaiveString.new(obj)) if printed_str.end_with?('...')
|
797
862
|
when Class, Module
|
798
|
-
vars
|
799
|
-
variable(iv, obj.instance_variable_get(iv))
|
800
|
-
}
|
801
|
-
vars.unshift variable('%ancestors', obj.ancestors[1..])
|
863
|
+
vars << variable('%ancestors', obj.ancestors[1..])
|
802
864
|
when Range
|
803
865
|
vars = [
|
804
866
|
variable('#begin', obj.begin),
|
@@ -806,10 +868,12 @@ module DEBUGGER__
|
|
806
868
|
]
|
807
869
|
end
|
808
870
|
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
871
|
+
unless NaiveString === obj
|
872
|
+
vars += M_INSTANCE_VARIABLES.bind_call(obj).sort.map{|iv|
|
873
|
+
variable(iv, M_INSTANCE_VARIABLE_GET.bind_call(obj, iv))
|
874
|
+
}
|
875
|
+
vars.unshift variable('#class', M_CLASS.bind_call(obj))
|
876
|
+
end
|
813
877
|
end
|
814
878
|
end
|
815
879
|
event! :dap_result, :variable, req, variables: (vars || []), tid: self.id
|
@@ -826,12 +890,7 @@ module DEBUGGER__
|
|
826
890
|
|
827
891
|
case context
|
828
892
|
when 'repl', 'watch'
|
829
|
-
|
830
|
-
result = b.eval(expr.to_s, '(DEBUG CONSOLE)')
|
831
|
-
rescue Exception => e
|
832
|
-
result = e
|
833
|
-
end
|
834
|
-
|
893
|
+
result = dap_eval b, expr, context, prompt: '(DEBUG CONSOLE)'
|
835
894
|
when 'hover'
|
836
895
|
case expr
|
837
896
|
when /\A\@\S/
|
@@ -841,7 +900,7 @@ module DEBUGGER__
|
|
841
900
|
message = "Error: Not defined instance variable: #{expr.inspect}"
|
842
901
|
end
|
843
902
|
when /\A\$\S/
|
844
|
-
|
903
|
+
safe_global_variables.each{|gvar|
|
845
904
|
if gvar.to_s == expr
|
846
905
|
result = eval(gvar.to_s)
|
847
906
|
break false
|
@@ -914,7 +973,11 @@ module DEBUGGER__
|
|
914
973
|
[Object, *b.eval('::Module.nesting')].reverse_each{|mod|
|
915
974
|
if cs.all?{|c|
|
916
975
|
if mod.const_defined?(c)
|
917
|
-
|
976
|
+
begin
|
977
|
+
mod = mod.const_get(c)
|
978
|
+
rescue Exception
|
979
|
+
false
|
980
|
+
end
|
918
981
|
else
|
919
982
|
false
|
920
983
|
end
|
@@ -927,11 +990,17 @@ module DEBUGGER__
|
|
927
990
|
end
|
928
991
|
|
929
992
|
def evaluate_result r
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
993
|
+
variable nil, r
|
994
|
+
end
|
995
|
+
|
996
|
+
def type_name obj
|
997
|
+
klass = M_CLASS.bind_call(obj)
|
998
|
+
|
999
|
+
begin
|
1000
|
+
klass.name || klass.to_s
|
1001
|
+
rescue Exception => e
|
1002
|
+
"<Error: #{e.message} (#{e.backtrace.first}>"
|
1003
|
+
end
|
935
1004
|
end
|
936
1005
|
|
937
1006
|
def variable_ name, obj, indexedVariables: 0, namedVariables: 0
|
@@ -942,15 +1011,31 @@ module DEBUGGER__
|
|
942
1011
|
vid = 0
|
943
1012
|
end
|
944
1013
|
|
945
|
-
|
1014
|
+
namedVariables += M_INSTANCE_VARIABLES.bind_call(obj).size
|
946
1015
|
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
1016
|
+
if NaiveString === obj
|
1017
|
+
str = obj.str.dump
|
1018
|
+
vid = indexedVariables = namedVariables = 0
|
1019
|
+
else
|
1020
|
+
str = value_inspect(obj)
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
if name
|
1024
|
+
{ name: name,
|
1025
|
+
value: str,
|
1026
|
+
type: type_name(obj),
|
1027
|
+
variablesReference: vid,
|
1028
|
+
indexedVariables: indexedVariables,
|
1029
|
+
namedVariables: namedVariables,
|
1030
|
+
}
|
1031
|
+
else
|
1032
|
+
{ result: str,
|
1033
|
+
type: type_name(obj),
|
1034
|
+
variablesReference: vid,
|
1035
|
+
indexedVariables: indexedVariables,
|
1036
|
+
namedVariables: namedVariables,
|
1037
|
+
}
|
1038
|
+
end
|
954
1039
|
end
|
955
1040
|
|
956
1041
|
def variable name, obj
|
@@ -960,7 +1045,7 @@ module DEBUGGER__
|
|
960
1045
|
when Hash
|
961
1046
|
variable_ name, obj, namedVariables: obj.size
|
962
1047
|
when String
|
963
|
-
variable_ name, obj, namedVariables: 3 # #
|
1048
|
+
variable_ name, obj, namedVariables: 3 # #length, #encoding, #to_str
|
964
1049
|
when Struct
|
965
1050
|
variable_ name, obj, namedVariables: obj.size
|
966
1051
|
when Class, Module
|