debug 1.4.0 → 1.9.2

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +210 -6
  3. data/Gemfile +2 -0
  4. data/LICENSE.txt +0 -0
  5. data/README.md +161 -85
  6. data/Rakefile +33 -10
  7. data/TODO.md +8 -8
  8. data/debug.gemspec +9 -7
  9. data/exe/rdbg +23 -4
  10. data/ext/debug/debug.c +111 -21
  11. data/ext/debug/extconf.rb +23 -0
  12. data/ext/debug/iseq_collector.c +2 -0
  13. data/lib/debug/abbrev_command.rb +77 -0
  14. data/lib/debug/breakpoint.rb +102 -74
  15. data/lib/debug/client.rb +46 -12
  16. data/lib/debug/color.rb +0 -0
  17. data/lib/debug/config.rb +129 -36
  18. data/lib/debug/console.rb +46 -40
  19. data/lib/debug/dap_custom/traceInspector.rb +336 -0
  20. data/lib/debug/frame_info.rb +40 -25
  21. data/lib/debug/irb_integration.rb +37 -0
  22. data/lib/debug/local.rb +17 -11
  23. data/lib/debug/open.rb +0 -0
  24. data/lib/debug/open_nonstop.rb +0 -0
  25. data/lib/debug/prelude.rb +3 -2
  26. data/lib/debug/server.rb +126 -56
  27. data/lib/debug/server_cdp.rb +673 -248
  28. data/lib/debug/server_dap.rb +497 -261
  29. data/lib/debug/session.rb +899 -441
  30. data/lib/debug/source_repository.rb +122 -49
  31. data/lib/debug/start.rb +1 -1
  32. data/lib/debug/thread_client.rb +460 -155
  33. data/lib/debug/tracer.rb +10 -16
  34. data/lib/debug/version.rb +1 -1
  35. data/lib/debug.rb +7 -2
  36. data/misc/README.md.erb +106 -56
  37. metadata +14 -24
  38. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -24
  39. data/.github/ISSUE_TEMPLATE/custom.md +0 -10
  40. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -14
  41. data/.github/pull_request_template.md +0 -9
  42. data/.github/workflows/ruby.yml +0 -34
  43. data/.gitignore +0 -12
  44. data/bin/console +0 -14
  45. data/bin/gentest +0 -30
  46. data/bin/setup +0 -8
  47. 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.1 features (TP:ivar_set)
23
- * Faster `next`/`finish` command by specifying target code.
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 encient Ruby versions.}
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.6.0")
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 = Dir.chdir(File.expand_path('..', __FILE__)) do
21
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
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{^exe/}) { |f| File.basename(f) }
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", ">= 1.3.6" # for its color_printer class, which was added after 1.3
29
- spec.add_dependency "reline", ">= 0.2.7"
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
- case config[:mode]
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[:remote] ? "open" : 'start'
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['RUBYOPT'] = "-r #{libpath}/#{start_mode}"
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.update 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
- rb_iseq_t *iseq = DATA_PTR(iseqw);
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 loc, e;
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
- VALUE path = iseq_realpath(iseq);
70
- if (!NIL_P(path) && !NIL_P(skip_path_prefix) && str_start_with(path, skip_path_prefix)) continue;
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
- INT2FIX(len - i));
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
- static void
101
- method_added_tracker(VALUE tpval, void *ptr)
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
- rb_trace_arg_t *arg = rb_tracearg_from_tracepoint(tpval);
104
- VALUE mid = rb_tracearg_callee_id(arg);
105
-
106
- if (RB_UNLIKELY(mid == ID2SYM(rb_intern("method_added")) ||
107
- mid == ID2SYM(rb_intern("singleton_method_added")))) {
108
- VALUE args[] = {
109
- tpval,
110
- };
111
- rb_funcallv(rb_mDebugger, rb_intern("method_added"), 1, args);
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
- create_method_added_tracker(VALUE self)
182
+ iseq_last_line(VALUE iseqw)
117
183
  {
118
- return rb_tracepoint_new(0, RUBY_EVENT_CALL, method_added_tracker, NULL);
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'
@@ -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