debug 1.4.0 → 1.9.2

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