debug 1.4.0 → 1.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +210 -6
- data/Gemfile +2 -0
- data/LICENSE.txt +0 -0
- data/README.md +161 -85
- data/Rakefile +33 -10
- data/TODO.md +8 -8
- data/debug.gemspec +9 -7
- data/exe/rdbg +23 -4
- data/ext/debug/debug.c +111 -21
- data/ext/debug/extconf.rb +23 -0
- data/ext/debug/iseq_collector.c +2 -0
- data/lib/debug/abbrev_command.rb +77 -0
- data/lib/debug/breakpoint.rb +102 -74
- data/lib/debug/client.rb +46 -12
- data/lib/debug/color.rb +0 -0
- data/lib/debug/config.rb +129 -36
- data/lib/debug/console.rb +46 -40
- data/lib/debug/dap_custom/traceInspector.rb +336 -0
- data/lib/debug/frame_info.rb +40 -25
- data/lib/debug/irb_integration.rb +37 -0
- data/lib/debug/local.rb +17 -11
- data/lib/debug/open.rb +0 -0
- data/lib/debug/open_nonstop.rb +0 -0
- data/lib/debug/prelude.rb +3 -2
- data/lib/debug/server.rb +126 -56
- data/lib/debug/server_cdp.rb +673 -248
- data/lib/debug/server_dap.rb +497 -261
- data/lib/debug/session.rb +899 -441
- data/lib/debug/source_repository.rb +122 -49
- data/lib/debug/start.rb +1 -1
- data/lib/debug/thread_client.rb +460 -155
- data/lib/debug/tracer.rb +10 -16
- data/lib/debug/version.rb +1 -1
- data/lib/debug.rb +7 -2
- data/misc/README.md.erb +106 -56
- metadata +14 -24
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -24
- data/.github/ISSUE_TEMPLATE/custom.md +0 -10
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -14
- data/.github/pull_request_template.md +0 -9
- data/.github/workflows/ruby.yml +0 -34
- data/.gitignore +0 -12
- data/bin/console +0 -14
- data/bin/gentest +0 -30
- data/bin/setup +0 -8
- data/lib/debug/bp.vim +0 -68
data/TODO.md
CHANGED
@@ -2,22 +2,22 @@
|
|
2
2
|
|
3
3
|
## Basic functionality
|
4
4
|
|
5
|
-
* Support Ractors
|
6
|
-
* Signal (SIGINT) trap handling
|
5
|
+
* Support Fibers and Ractors
|
7
6
|
|
8
7
|
## UI
|
9
8
|
|
9
|
+
* Multi-line support
|
10
10
|
* Completion for Ruby's code
|
11
11
|
* Interactive breakpoint setting
|
12
12
|
* Interactive record & play debugging
|
13
13
|
* irb integration
|
14
|
-
* Web browser integrated UI
|
15
|
-
* History file
|
16
14
|
|
17
15
|
## Debug command
|
18
16
|
|
19
|
-
* Breakpoints
|
20
|
-
* Lightweight pending method break points with Ruby 3.1 feature (TP:method_added)
|
21
17
|
* Watch points
|
22
|
-
* Lightweight watchpoints for instance variables with Ruby 3.
|
23
|
-
*
|
18
|
+
* Lightweight watchpoints for instance variables with Ruby 3.3 features (TP:ivar_set)
|
19
|
+
* Alias
|
20
|
+
|
21
|
+
## Debug port
|
22
|
+
|
23
|
+
* Debug port for monitoring
|
data/debug.gemspec
CHANGED
@@ -7,24 +7,26 @@ Gem::Specification.new do |spec|
|
|
7
7
|
spec.email = ["ko1@atdot.net"]
|
8
8
|
|
9
9
|
spec.summary = %q{Debugging functionality for Ruby}
|
10
|
-
spec.description = %q{Debugging functionality for Ruby. This is completely rewritten debug.rb which was contained by the
|
10
|
+
spec.description = %q{Debugging functionality for Ruby. This is completely rewritten debug.rb which was contained by the ancient Ruby versions.}
|
11
11
|
spec.homepage = "https://github.com/ruby/debug"
|
12
12
|
spec.licenses = ["Ruby", "BSD-2-Clause"]
|
13
|
-
spec.required_ruby_version = Gem::Requirement.new(">= 2.
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
|
14
14
|
|
15
15
|
spec.metadata["homepage_uri"] = spec.homepage
|
16
16
|
spec.metadata["source_code_uri"] = spec.homepage
|
17
17
|
|
18
18
|
# Specify which files should be added to the gem when it is released.
|
19
19
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
20
|
-
spec.files
|
21
|
-
`git ls-files -z`.split("\x0").reject
|
20
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
21
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
22
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
23
|
+
end
|
22
24
|
end
|
23
25
|
spec.bindir = "exe"
|
24
|
-
spec.executables = spec.files.grep(%r{
|
26
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
25
27
|
spec.require_paths = ["lib"]
|
26
28
|
spec.extensions = ['ext/debug/extconf.rb']
|
27
29
|
|
28
|
-
spec.add_dependency "irb", "
|
29
|
-
spec.add_dependency "reline", ">= 0.
|
30
|
+
spec.add_dependency "irb", "~> 1.10" # for irb:debug integration
|
31
|
+
spec.add_dependency "reline", ">= 0.3.8"
|
30
32
|
end
|
data/exe/rdbg
CHANGED
@@ -3,22 +3,41 @@
|
|
3
3
|
require_relative '../lib/debug/config'
|
4
4
|
config = DEBUGGER__::Config::parse_argv(ARGV)
|
5
5
|
|
6
|
-
|
6
|
+
# mode is not an actual configuration option
|
7
|
+
# it's only used to carry the result of parse_argv here
|
8
|
+
case config.delete(:mode)
|
7
9
|
when :start
|
8
10
|
require 'rbconfig'
|
9
11
|
|
10
12
|
libpath = File.join(File.expand_path(File.dirname(__dir__)), 'lib/debug')
|
11
|
-
start_mode = config[:
|
13
|
+
start_mode = config[:open] ? "open" : 'start'
|
12
14
|
cmd = config[:command] ? ARGV.shift : (ENV['RUBY'] || RbConfig.ruby)
|
13
15
|
|
16
|
+
if defined?($:.resolve_feature_path)
|
17
|
+
begin
|
18
|
+
_, sopath = $:.resolve_feature_path('debug/debug.so')
|
19
|
+
rescue LoadError
|
20
|
+
# raises LoadError before 3.1 (2.7 and 3.0)
|
21
|
+
else
|
22
|
+
sopath = File.dirname(File.dirname(sopath)) if sopath
|
23
|
+
end
|
24
|
+
else
|
25
|
+
# `$:.resolve_feature_path` is not defined in 2.6 or earlier.
|
26
|
+
so = "debug/debug.#{RbConfig::CONFIG['DLEXT']}"
|
27
|
+
sopath = $:.find {|dir| File.exist?(File.join(dir, so))}
|
28
|
+
end
|
29
|
+
added = "-r #{libpath}/#{start_mode}"
|
30
|
+
added = "-I #{sopath} #{added}" if sopath
|
31
|
+
rubyopt = ENV['RUBYOPT']
|
14
32
|
env = ::DEBUGGER__::Config.config_to_env_hash(config)
|
15
|
-
env['
|
33
|
+
env['RUBY_DEBUG_ADDED_RUBYOPT'] = added
|
34
|
+
env['RUBYOPT'] = "#{rubyopt} #{added}"
|
16
35
|
|
17
36
|
exec(env, cmd, *ARGV)
|
18
37
|
|
19
38
|
when :attach
|
20
39
|
require_relative "../lib/debug/client"
|
21
|
-
::DEBUGGER__::CONFIG.
|
40
|
+
::DEBUGGER__::CONFIG.set_config(**config)
|
22
41
|
|
23
42
|
begin
|
24
43
|
if ARGV.empty? && config[:port]
|
data/ext/debug/debug.c
CHANGED
@@ -8,13 +8,13 @@ static VALUE rb_mDebugger;
|
|
8
8
|
|
9
9
|
// iseq
|
10
10
|
typedef struct rb_iseq_struct rb_iseq_t;
|
11
|
+
const rb_iseq_t *rb_iseqw_to_iseq(VALUE iseqw);
|
11
12
|
VALUE rb_iseq_realpath(const rb_iseq_t *iseq);
|
12
13
|
|
13
14
|
static VALUE
|
14
15
|
iseq_realpath(VALUE iseqw)
|
15
16
|
{
|
16
|
-
|
17
|
-
return rb_iseq_realpath(iseq);
|
17
|
+
return rb_iseq_realpath(rb_iseqw_to_iseq(iseqw));
|
18
18
|
}
|
19
19
|
|
20
20
|
static VALUE rb_cFrameInfo;
|
@@ -62,21 +62,34 @@ di_body(const rb_debug_inspector_t *dc, void *ptr)
|
|
62
62
|
long i;
|
63
63
|
|
64
64
|
for (i=1; i<len; i++) {
|
65
|
-
VALUE
|
65
|
+
VALUE e;
|
66
66
|
VALUE iseq = rb_debug_inspector_frame_iseq_get(dc, i);
|
67
|
+
VALUE loc = RARRAY_AREF(locs, i);
|
68
|
+
VALUE path;
|
67
69
|
|
68
70
|
if (!NIL_P(iseq)) {
|
69
|
-
|
70
|
-
|
71
|
+
path = iseq_realpath(iseq);
|
72
|
+
}
|
73
|
+
else {
|
74
|
+
// C frame
|
75
|
+
path = rb_funcall(loc, rb_intern("path"), 0);
|
76
|
+
}
|
77
|
+
|
78
|
+
if (!NIL_P(path) && !NIL_P(skip_path_prefix) && str_start_with(path, skip_path_prefix)) {
|
79
|
+
continue;
|
71
80
|
}
|
72
81
|
|
73
|
-
loc = RARRAY_AREF(locs, i);
|
74
82
|
e = di_entry(loc,
|
75
83
|
rb_debug_inspector_frame_self_get(dc, i),
|
76
84
|
rb_debug_inspector_frame_binding_get(dc, i),
|
77
85
|
iseq,
|
78
86
|
rb_debug_inspector_frame_class_get(dc, i),
|
79
|
-
|
87
|
+
#ifdef RB_DEBUG_INSPECTOR_FRAME_DEPTH
|
88
|
+
rb_debug_inspector_frame_depth(dc, i)
|
89
|
+
#else
|
90
|
+
INT2FIX(len - i)
|
91
|
+
#endif
|
92
|
+
);
|
80
93
|
rb_ary_push(ary, e);
|
81
94
|
}
|
82
95
|
|
@@ -89,6 +102,13 @@ capture_frames(VALUE self, VALUE skip_path_prefix)
|
|
89
102
|
return rb_debug_inspector_open(di_body, (void *)skip_path_prefix);
|
90
103
|
}
|
91
104
|
|
105
|
+
#ifdef RB_DEBUG_INSPECTOR_FRAME_DEPTH
|
106
|
+
static VALUE
|
107
|
+
frame_depth(VALUE self)
|
108
|
+
{
|
109
|
+
return rb_debug_inspector_current_depth();
|
110
|
+
}
|
111
|
+
#else
|
92
112
|
static VALUE
|
93
113
|
frame_depth(VALUE self)
|
94
114
|
{
|
@@ -96,33 +116,89 @@ frame_depth(VALUE self)
|
|
96
116
|
VALUE bt = rb_make_backtrace();
|
97
117
|
return INT2FIX(RARRAY_LEN(bt));
|
98
118
|
}
|
119
|
+
#endif
|
120
|
+
|
121
|
+
|
122
|
+
// iseq
|
123
|
+
|
124
|
+
const rb_iseq_t *rb_iseqw_to_iseq(VALUE iseqw);
|
125
|
+
|
126
|
+
#ifdef HAVE_RB_ISEQ_TYPE
|
127
|
+
VALUE rb_iseq_type(const rb_iseq_t *);
|
128
|
+
|
129
|
+
static VALUE
|
130
|
+
iseq_type(VALUE iseqw)
|
131
|
+
{
|
132
|
+
const rb_iseq_t *iseq = rb_iseqw_to_iseq(iseqw);
|
133
|
+
return rb_iseq_type(iseq);
|
134
|
+
}
|
135
|
+
#endif
|
99
136
|
|
100
|
-
|
101
|
-
|
137
|
+
#ifdef HAVE_RB_ISEQ_PARAMETERS
|
138
|
+
VALUE rb_iseq_parameters(const rb_iseq_t *, int is_proc);
|
139
|
+
|
140
|
+
static VALUE
|
141
|
+
iseq_parameters_symbols(VALUE iseqw)
|
102
142
|
{
|
103
|
-
|
104
|
-
VALUE
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
143
|
+
const rb_iseq_t *iseq = rb_iseqw_to_iseq(iseqw);
|
144
|
+
VALUE params = rb_iseq_parameters(iseq, 0);
|
145
|
+
VALUE ary = rb_ary_new();
|
146
|
+
|
147
|
+
static VALUE sym_ast, sym_astast, sym_amp;
|
148
|
+
|
149
|
+
if (sym_ast == 0) {
|
150
|
+
sym_ast = ID2SYM(rb_intern("*"));
|
151
|
+
sym_astast = ID2SYM(rb_intern("**"));
|
152
|
+
sym_amp = ID2SYM(rb_intern("&"));
|
153
|
+
}
|
154
|
+
|
155
|
+
for (long i=0; i<RARRAY_LEN(params); i++) {
|
156
|
+
VALUE e = RARRAY_AREF(params, i);
|
157
|
+
if (RARRAY_LEN(e) == 2) {
|
158
|
+
VALUE sym = RARRAY_AREF(e, 1);
|
159
|
+
if (sym != sym_ast &&
|
160
|
+
sym != sym_astast &&
|
161
|
+
sym != sym_amp) rb_ary_push(ary, RARRAY_AREF(e, 1));
|
162
|
+
}
|
112
163
|
}
|
164
|
+
|
165
|
+
return ary;
|
166
|
+
}
|
167
|
+
#endif
|
168
|
+
|
169
|
+
#ifdef HAVE_RB_ISEQ_CODE_LOCATION
|
170
|
+
void rb_iseq_code_location(const rb_iseq_t *, int *first_lineno, int *first_column, int *last_lineno, int *last_column);
|
171
|
+
|
172
|
+
static VALUE
|
173
|
+
iseq_first_line(VALUE iseqw)
|
174
|
+
{
|
175
|
+
const rb_iseq_t *iseq = rb_iseqw_to_iseq(iseqw);
|
176
|
+
int line;
|
177
|
+
rb_iseq_code_location(iseq, &line, NULL, NULL, NULL);
|
178
|
+
return INT2NUM(line);
|
113
179
|
}
|
114
180
|
|
115
181
|
static VALUE
|
116
|
-
|
182
|
+
iseq_last_line(VALUE iseqw)
|
117
183
|
{
|
118
|
-
|
184
|
+
const rb_iseq_t *iseq = rb_iseqw_to_iseq(iseqw);
|
185
|
+
int line;
|
186
|
+
rb_iseq_code_location(iseq, NULL, NULL, &line, NULL);
|
187
|
+
return INT2NUM(line);
|
119
188
|
}
|
189
|
+
#endif
|
120
190
|
|
191
|
+
#ifdef HAVE_RB_ISEQ
|
121
192
|
void Init_iseq_collector(void);
|
193
|
+
#endif
|
122
194
|
|
123
195
|
void
|
124
196
|
Init_debug(void)
|
125
197
|
{
|
198
|
+
#ifdef HAVE_RB_ISEQ
|
199
|
+
VALUE rb_mRubyVM = rb_const_get(rb_cObject, rb_intern("RubyVM"));
|
200
|
+
VALUE rb_cISeq = rb_const_get(rb_mRubyVM, rb_intern("InstructionSequence"));
|
201
|
+
#endif
|
126
202
|
rb_mDebugger = rb_const_get(rb_cObject, rb_intern("DEBUGGER__"));
|
127
203
|
rb_cFrameInfo = rb_const_get(rb_mDebugger, rb_intern("FrameInfo"));
|
128
204
|
|
@@ -132,7 +208,21 @@ Init_debug(void)
|
|
132
208
|
rb_gc_register_mark_object(rb_cFrameInfo);
|
133
209
|
rb_define_singleton_method(rb_mDebugger, "capture_frames", capture_frames, 1);
|
134
210
|
rb_define_singleton_method(rb_mDebugger, "frame_depth", frame_depth, 0);
|
135
|
-
rb_define_singleton_method(rb_mDebugger, "create_method_added_tracker", create_method_added_tracker, 0);
|
136
211
|
rb_define_const(rb_mDebugger, "SO_VERSION", rb_str_new2(RUBY_DEBUG_VERSION));
|
212
|
+
|
213
|
+
// iseq
|
214
|
+
#ifdef HAVE_RB_ISEQ_TYPE
|
215
|
+
rb_define_method(rb_cISeq, "type", iseq_type, 0);
|
216
|
+
#endif
|
217
|
+
#ifdef HAVE_RB_ISEQ_PARAMETERS
|
218
|
+
rb_define_method(rb_cISeq, "parameters_symbols", iseq_parameters_symbols, 0);
|
219
|
+
#endif
|
220
|
+
#ifdef HAVE_RB_ISEQ_CODE_LOCATION
|
221
|
+
rb_define_method(rb_cISeq, "first_line", iseq_first_line, 0);
|
222
|
+
rb_define_method(rb_cISeq, "last_line", iseq_last_line, 0);
|
223
|
+
#endif
|
224
|
+
|
225
|
+
#ifdef HAVE_RB_ISEQ
|
137
226
|
Init_iseq_collector();
|
227
|
+
#endif
|
138
228
|
}
|
data/ext/debug/extconf.rb
CHANGED
@@ -1,4 +1,27 @@
|
|
1
1
|
require 'mkmf'
|
2
2
|
require_relative '../../lib/debug/version'
|
3
3
|
File.write("debug_version.h", "#define RUBY_DEBUG_VERSION \"#{DEBUGGER__::VERSION}\"\n")
|
4
|
+
$distcleanfiles << "debug_version.h"
|
5
|
+
|
6
|
+
if defined? RubyVM
|
7
|
+
$defs << '-DHAVE_RB_ISEQ'
|
8
|
+
$defs << '-DHAVE_RB_ISEQ_PARAMETERS'
|
9
|
+
$defs << '-DHAVE_RB_ISEQ_CODE_LOCATION'
|
10
|
+
|
11
|
+
if RUBY_VERSION >= '3.1.0'
|
12
|
+
$defs << '-DHAVE_RB_ISEQ_TYPE'
|
13
|
+
end
|
14
|
+
else
|
15
|
+
# not on MRI
|
16
|
+
|
17
|
+
have_func "rb_iseq_parameters(NULL, 0)",
|
18
|
+
[["VALUE rb_iseq_parameters(void *, int is_proc);"]]
|
19
|
+
|
20
|
+
have_func "rb_iseq_code_location(NULL, NULL, NULL, NULL, NULL)",
|
21
|
+
[["void rb_iseq_code_location(void *, int *first_lineno, int *first_column, int *last_lineno, int *last_column);"]]
|
22
|
+
# from Ruby 3.1
|
23
|
+
have_func "rb_iseq_type(NULL)",
|
24
|
+
[["VALUE rb_iseq_type(void *);"]]
|
25
|
+
end
|
26
|
+
|
4
27
|
create_makefile 'debug/debug'
|
data/ext/debug/iseq_collector.c
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
#include <ruby/ruby.h>
|
2
2
|
|
3
|
+
#ifdef HAVE_RB_ISEQ
|
3
4
|
VALUE rb_iseqw_new(VALUE v);
|
4
5
|
void rb_objspace_each_objects(
|
5
6
|
int (*callback)(void *start, void *end, size_t stride, void *data),
|
@@ -89,3 +90,4 @@ Init_iseq_collector(void)
|
|
89
90
|
rb_define_singleton_method(rb_mObjSpace, "each_iseq", each_iseq, 0);
|
90
91
|
rb_define_singleton_method(rb_mObjSpace, "count_iseq", count_iseq, 0);
|
91
92
|
}
|
93
|
+
#endif
|
@@ -0,0 +1,77 @@
|
|
1
|
+
|
2
|
+
module DEBUGGER__
|
3
|
+
class AbbrevCommand
|
4
|
+
class TrieNode
|
5
|
+
def initialize
|
6
|
+
@children = {}
|
7
|
+
@types = {} # set
|
8
|
+
end
|
9
|
+
|
10
|
+
def append c, type
|
11
|
+
trie = (@children[c] ||= TrieNode.new)
|
12
|
+
trie.add_type type
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](c)
|
16
|
+
@children[c]
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_type type
|
20
|
+
@types[type] = true
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def types
|
25
|
+
@types.keys
|
26
|
+
end
|
27
|
+
|
28
|
+
def type
|
29
|
+
if @types.size == 1
|
30
|
+
@types.keys.first
|
31
|
+
else
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def candidates
|
37
|
+
@children.map{|c, n|
|
38
|
+
ss = n.candidates
|
39
|
+
ss.empty? ? c :
|
40
|
+
ss.map{|s|
|
41
|
+
c+s
|
42
|
+
}
|
43
|
+
}.flatten
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# config: { type: [commands...], ... }
|
48
|
+
def initialize config
|
49
|
+
@trie = TrieNode.new
|
50
|
+
build config
|
51
|
+
end
|
52
|
+
|
53
|
+
private def build config
|
54
|
+
config.each do |type, commands|
|
55
|
+
commands.each do |command|
|
56
|
+
trie = @trie
|
57
|
+
command.each_char do |c|
|
58
|
+
trie = trie.append(c, type)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def search str, if_none = nil
|
65
|
+
trie = @trie
|
66
|
+
str.each_char do |c|
|
67
|
+
if trie = trie[c]
|
68
|
+
return trie.type if trie.type
|
69
|
+
else
|
70
|
+
return if_none
|
71
|
+
end
|
72
|
+
end
|
73
|
+
yield trie.candidates.map{|s| str + s} if block_given?
|
74
|
+
if_none
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|