debug 1.4.0 → 1.6.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 +195 -3
- data/Gemfile +1 -0
- data/README.md +55 -32
- data/Rakefile +28 -10
- data/debug.gemspec +7 -5
- data/exe/rdbg +7 -3
- data/ext/debug/debug.c +76 -14
- data/ext/debug/extconf.rb +22 -0
- data/lib/debug/breakpoint.rb +90 -66
- data/lib/debug/client.rb +21 -5
- data/lib/debug/config.rb +55 -24
- data/lib/debug/console.rb +43 -16
- data/lib/debug/frame_info.rb +32 -26
- data/lib/debug/local.rb +1 -1
- data/lib/debug/server.rb +105 -40
- data/lib/debug/server_cdp.rb +373 -152
- data/lib/debug/server_dap.rb +273 -170
- data/lib/debug/session.rb +450 -236
- data/lib/debug/source_repository.rb +104 -51
- data/lib/debug/thread_client.rb +252 -103
- data/lib/debug/tracer.rb +7 -12
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +34 -14
- metadata +6 -16
- 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/ext/debug/debug.c
    CHANGED
    
    | @@ -97,32 +97,82 @@ frame_depth(VALUE self) | |
| 97 97 | 
             
                return INT2FIX(RARRAY_LEN(bt));
         | 
| 98 98 | 
             
            }
         | 
| 99 99 |  | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 100 | 
            +
            // iseq
         | 
| 101 | 
            +
             | 
| 102 | 
            +
            const struct rb_iseq *rb_iseqw_to_iseq(VALUE iseqw);
         | 
| 103 | 
            +
             | 
| 104 | 
            +
            #ifdef HAVE_RB_ISEQ_TYPE
         | 
| 105 | 
            +
            VALUE rb_iseq_type(const struct rb_iseq *);
         | 
| 106 | 
            +
             | 
| 107 | 
            +
            static VALUE
         | 
| 108 | 
            +
            iseq_type(VALUE iseqw)
         | 
| 102 109 | 
             
            {
         | 
| 103 | 
            -
                 | 
| 104 | 
            -
                 | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 110 | 
            +
                const struct rb_iseq *iseq = rb_iseqw_to_iseq(iseqw);
         | 
| 111 | 
            +
                return rb_iseq_type(iseq);
         | 
| 112 | 
            +
            }
         | 
| 113 | 
            +
            #endif
         | 
| 114 | 
            +
             | 
| 115 | 
            +
            #ifdef HAVE_RB_ISEQ_PARAMETERS
         | 
| 116 | 
            +
            VALUE rb_iseq_parameters(const struct rb_iseq *, int is_proc);
         | 
| 117 | 
            +
             | 
| 118 | 
            +
            static VALUE
         | 
| 119 | 
            +
            iseq_parameters_symbols(VALUE iseqw)
         | 
| 120 | 
            +
            {
         | 
| 121 | 
            +
                const struct rb_iseq *iseq = rb_iseqw_to_iseq(iseqw);
         | 
| 122 | 
            +
                VALUE params = rb_iseq_parameters(iseq, 0);
         | 
| 123 | 
            +
                VALUE ary = rb_ary_new();
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                static VALUE sym_ast, sym_astast, sym_amp;
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                if (sym_ast == 0) {
         | 
| 128 | 
            +
                    sym_ast = ID2SYM(rb_intern("*"));
         | 
| 129 | 
            +
                    sym_astast = ID2SYM(rb_intern("**"));
         | 
| 130 | 
            +
                    sym_amp = ID2SYM(rb_intern("&"));
         | 
| 112 131 | 
             
                }
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                for (long i=0; i<RARRAY_LEN(params); i++) {
         | 
| 134 | 
            +
                    VALUE e = RARRAY_AREF(params, i);
         | 
| 135 | 
            +
                    if (RARRAY_LEN(e) == 2) {
         | 
| 136 | 
            +
                        VALUE sym = RARRAY_AREF(e, 1);
         | 
| 137 | 
            +
                        if (sym != sym_ast &&
         | 
| 138 | 
            +
                            sym != sym_astast &&
         | 
| 139 | 
            +
                            sym != sym_amp) rb_ary_push(ary, RARRAY_AREF(e, 1));
         | 
| 140 | 
            +
                    }
         | 
| 141 | 
            +
                }
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                return ary;
         | 
| 144 | 
            +
            }
         | 
| 145 | 
            +
            #endif
         | 
| 146 | 
            +
             | 
| 147 | 
            +
            #ifdef HAVE_RB_ISEQ_CODE_LOCATION
         | 
| 148 | 
            +
            void rb_iseq_code_location(const struct rb_iseq *, int *first_lineno, int *first_column, int *last_lineno, int *last_column);
         | 
| 149 | 
            +
             | 
| 150 | 
            +
            static VALUE
         | 
| 151 | 
            +
            iseq_first_line(VALUE iseqw)
         | 
| 152 | 
            +
            {
         | 
| 153 | 
            +
                const struct rb_iseq *iseq = rb_iseqw_to_iseq(iseqw);
         | 
| 154 | 
            +
                int line;
         | 
| 155 | 
            +
                rb_iseq_code_location(iseq, &line, NULL, NULL, NULL);
         | 
| 156 | 
            +
                return INT2NUM(line);
         | 
| 113 157 | 
             
            }
         | 
| 114 158 |  | 
| 115 159 | 
             
            static VALUE
         | 
| 116 | 
            -
             | 
| 160 | 
            +
            iseq_last_line(VALUE iseqw)
         | 
| 117 161 | 
             
            {
         | 
| 118 | 
            -
                 | 
| 162 | 
            +
                const struct rb_iseq *iseq = rb_iseqw_to_iseq(iseqw);
         | 
| 163 | 
            +
                int line;
         | 
| 164 | 
            +
                rb_iseq_code_location(iseq, NULL, NULL, &line, NULL);
         | 
| 165 | 
            +
                return INT2NUM(line);
         | 
| 119 166 | 
             
            }
         | 
| 167 | 
            +
            #endif
         | 
| 120 168 |  | 
| 121 169 | 
             
            void Init_iseq_collector(void);
         | 
| 122 170 |  | 
| 123 171 | 
             
            void
         | 
| 124 172 | 
             
            Init_debug(void)
         | 
| 125 173 | 
             
            {
         | 
| 174 | 
            +
                VALUE rb_mRubyVM = rb_const_get(rb_cObject, rb_intern("RubyVM"));
         | 
| 175 | 
            +
                VALUE rb_cISeq = rb_const_get(rb_mRubyVM, rb_intern("InstructionSequence"));
         | 
| 126 176 | 
             
                rb_mDebugger = rb_const_get(rb_cObject, rb_intern("DEBUGGER__"));
         | 
| 127 177 | 
             
                rb_cFrameInfo = rb_const_get(rb_mDebugger, rb_intern("FrameInfo"));
         | 
| 128 178 |  | 
| @@ -132,7 +182,19 @@ Init_debug(void) | |
| 132 182 | 
             
                rb_gc_register_mark_object(rb_cFrameInfo);
         | 
| 133 183 | 
             
                rb_define_singleton_method(rb_mDebugger, "capture_frames", capture_frames, 1);
         | 
| 134 184 | 
             
                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 185 | 
             
                rb_define_const(rb_mDebugger, "SO_VERSION", rb_str_new2(RUBY_DEBUG_VERSION));
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                // iseq
         | 
| 188 | 
            +
            #ifdef HAVE_RB_ISEQ_TYPE
         | 
| 189 | 
            +
                rb_define_method(rb_cISeq, "type", iseq_type, 0);
         | 
| 190 | 
            +
            #endif
         | 
| 191 | 
            +
            #ifdef HAVE_RB_ISEQ_PARAMETERS
         | 
| 192 | 
            +
                rb_define_method(rb_cISeq, "parameters_symbols", iseq_parameters_symbols, 0);
         | 
| 193 | 
            +
            #endif
         | 
| 194 | 
            +
            #ifdef HAVE_RB_ISEQ_CODE_LOCATION
         | 
| 195 | 
            +
                rb_define_method(rb_cISeq, "first_line", iseq_first_line, 0);
         | 
| 196 | 
            +
                rb_define_method(rb_cISeq, "last_line", iseq_last_line, 0);
         | 
| 197 | 
            +
            #endif
         | 
| 198 | 
            +
             | 
| 137 199 | 
             
                Init_iseq_collector();
         | 
| 138 200 | 
             
            }
         | 
    
        data/ext/debug/extconf.rb
    CHANGED
    
    | @@ -1,4 +1,26 @@ | |
| 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_PARAMETERS'
         | 
| 8 | 
            +
              $defs << '-DHAVE_RB_ISEQ_CODE_LOCATION'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              if RUBY_VERSION >= '3.1.0'
         | 
| 11 | 
            +
                $defs << '-DHAVE_RB_ISEQ_TYPE'
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            else
         | 
| 14 | 
            +
              # not on MRI
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              have_func "rb_iseq_parameters(NULL, 0)",
         | 
| 17 | 
            +
                         [["VALUE rb_iseq_parameters(void *, int is_proc);"]]
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              have_func "rb_iseq_code_location(NULL, NULL, NULL, NULL, NULL)",
         | 
| 20 | 
            +
                        [["void rb_iseq_code_location(void *, int *first_lineno, int *first_column, int *last_lineno, int *last_column);"]]
         | 
| 21 | 
            +
              # from Ruby 3.1
         | 
| 22 | 
            +
              have_func "rb_iseq_type(NULL)",
         | 
| 23 | 
            +
                        [["VALUE rb_iseq_type(void *);"]]
         | 
| 24 | 
            +
            end
         | 
| 25 | 
            +
             | 
| 4 26 | 
             
            create_makefile 'debug/debug'
         | 
    
        data/lib/debug/breakpoint.rb
    CHANGED
    
    | @@ -8,9 +8,13 @@ module DEBUGGER__ | |
| 8 8 |  | 
| 9 9 | 
             
                attr_reader :key
         | 
| 10 10 |  | 
| 11 | 
            -
                def initialize do_enable  | 
| 11 | 
            +
                def initialize cond, command, path, do_enable: true
         | 
| 12 12 | 
             
                  @deleted = false
         | 
| 13 13 |  | 
| 14 | 
            +
                  @cond = cond
         | 
| 15 | 
            +
                  @command = command
         | 
| 16 | 
            +
                  @path = path
         | 
| 17 | 
            +
             | 
| 14 18 | 
             
                  setup
         | 
| 15 19 | 
             
                  enable if do_enable
         | 
| 16 20 | 
             
                end
         | 
| @@ -82,8 +86,11 @@ module DEBUGGER__ | |
| 82 86 | 
             
                end
         | 
| 83 87 |  | 
| 84 88 | 
             
                def skip_path?(path)
         | 
| 85 | 
            -
                   | 
| 89 | 
            +
                  case @path
         | 
| 90 | 
            +
                  when Regexp
         | 
| 86 91 | 
             
                    !path.match?(@path)
         | 
| 92 | 
            +
                  when String
         | 
| 93 | 
            +
                    !path.include?(@path)
         | 
| 87 94 | 
             
                  else
         | 
| 88 95 | 
             
                    super
         | 
| 89 96 | 
             
                  end
         | 
| @@ -108,7 +115,7 @@ module DEBUGGER__ | |
| 108 115 | 
             
                  @oneshot = oneshot
         | 
| 109 116 | 
             
                  @key = [:iseq, @iseq.path, @iseq.first_lineno].freeze
         | 
| 110 117 |  | 
| 111 | 
            -
                  super()
         | 
| 118 | 
            +
                  super(nil, nil, nil)
         | 
| 112 119 | 
             
                end
         | 
| 113 120 |  | 
| 114 121 | 
             
                def setup
         | 
| @@ -124,25 +131,30 @@ module DEBUGGER__ | |
| 124 131 | 
             
              end
         | 
| 125 132 |  | 
| 126 133 | 
             
              class LineBreakpoint < Breakpoint
         | 
| 127 | 
            -
                attr_reader :path, :line, :iseq
         | 
| 134 | 
            +
                attr_reader :path, :line, :iseq, :cond, :oneshot, :hook_call, :command
         | 
| 128 135 |  | 
| 129 | 
            -
                def  | 
| 130 | 
            -
                   | 
| 136 | 
            +
                def self.copy bp, root_iseq
         | 
| 137 | 
            +
                  nbp = LineBreakpoint.new bp.path, bp.line,
         | 
| 138 | 
            +
                                           cond: bp.cond, oneshot: bp.oneshot, hook_call: bp.hook_call,
         | 
| 139 | 
            +
                                           command: bp.command, skip_activate: true
         | 
| 140 | 
            +
                  nbp.try_activate root_iseq
         | 
| 141 | 
            +
                  nbp
         | 
| 142 | 
            +
                end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                def initialize path, line, cond: nil, oneshot: false, hook_call: true, command: nil, skip_activate: false
         | 
| 131 145 | 
             
                  @line = line
         | 
| 132 | 
            -
                  @cond = cond
         | 
| 133 146 | 
             
                  @oneshot = oneshot
         | 
| 134 147 | 
             
                  @hook_call = hook_call
         | 
| 135 | 
            -
                  @command = command
         | 
| 136 148 | 
             
                  @pending = false
         | 
| 137 149 |  | 
| 138 150 | 
             
                  @iseq = nil
         | 
| 139 151 | 
             
                  @type = nil
         | 
| 140 152 |  | 
| 141 | 
            -
                  @key = [ | 
| 153 | 
            +
                  @key = [path, @line].freeze
         | 
| 142 154 |  | 
| 143 | 
            -
                  super()
         | 
| 155 | 
            +
                  super(cond, command, path)
         | 
| 144 156 |  | 
| 145 | 
            -
                  try_activate
         | 
| 157 | 
            +
                  try_activate unless skip_activate
         | 
| 146 158 | 
             
                  @pending = !@iseq
         | 
| 147 159 | 
             
                end
         | 
| 148 160 |  | 
| @@ -202,7 +214,7 @@ module DEBUGGER__ | |
| 202 214 | 
             
                  when events.include?(:RUBY_EVENT_END)
         | 
| 203 215 | 
             
                    activate(iseq, :end, line)
         | 
| 204 216 | 
             
                  else
         | 
| 205 | 
            -
                    # not  | 
| 217 | 
            +
                    # not activated
         | 
| 206 218 | 
             
                  end
         | 
| 207 219 | 
             
                end
         | 
| 208 220 |  | 
| @@ -212,42 +224,56 @@ module DEBUGGER__ | |
| 212 224 |  | 
| 213 225 | 
             
                NearestISeq = Struct.new(:iseq, :line, :events)
         | 
| 214 226 |  | 
| 215 | 
            -
                def  | 
| 216 | 
            -
                   | 
| 217 | 
            -
             | 
| 218 | 
            -
             | 
| 219 | 
            -
             | 
| 220 | 
            -
             | 
| 221 | 
            -
                         | 
| 227 | 
            +
                def iterate_iseq root_iseq
         | 
| 228 | 
            +
                  if root_iseq
         | 
| 229 | 
            +
                    is = [root_iseq]
         | 
| 230 | 
            +
                    while iseq = is.pop
         | 
| 231 | 
            +
                      yield iseq
         | 
| 232 | 
            +
                      iseq.each_child do |child_iseq|
         | 
| 233 | 
            +
                        is << child_iseq
         | 
| 234 | 
            +
                      end
         | 
| 235 | 
            +
                    end
         | 
| 236 | 
            +
                  else
         | 
| 237 | 
            +
                    ObjectSpace.each_iseq do |iseq|
         | 
| 238 | 
            +
                      if DEBUGGER__.compare_path((iseq.absolute_path || iseq.path), self.path) &&
         | 
| 239 | 
            +
                         iseq.first_lineno <= self.line &&
         | 
| 240 | 
            +
                         iseq.type != :ensure # ensure iseq is copied (duplicated)
         | 
| 241 | 
            +
                        yield iseq
         | 
| 242 | 
            +
                      end
         | 
| 243 | 
            +
                    end
         | 
| 244 | 
            +
                  end
         | 
| 245 | 
            +
                end
         | 
| 222 246 |  | 
| 223 | 
            -
             | 
| 224 | 
            -
             | 
| 247 | 
            +
                def try_activate root_iseq = nil
         | 
| 248 | 
            +
                  nearest = nil # NearestISeq
         | 
| 249 | 
            +
                  iterate_iseq root_iseq do |iseq|
         | 
| 250 | 
            +
                    iseq.traceable_lines_norec(line_events = {})
         | 
| 251 | 
            +
                    lines = line_events.keys.sort
         | 
| 225 252 |  | 
| 226 | 
            -
             | 
| 227 | 
            -
             | 
| 228 | 
            -
             | 
| 253 | 
            +
                    if !lines.empty? && lines.last >= line
         | 
| 254 | 
            +
                      nline = lines.bsearch{|l| line <= l}
         | 
| 255 | 
            +
                      events = line_events[nline]
         | 
| 229 256 |  | 
| 230 | 
            -
             | 
| 257 | 
            +
                      next if events == [:RUBY_EVENT_B_CALL]
         | 
| 231 258 |  | 
| 232 | 
            -
             | 
| 233 | 
            -
             | 
| 234 | 
            -
             | 
| 235 | 
            -
             | 
| 236 | 
            -
             | 
| 259 | 
            +
                      if @hook_call &&
         | 
| 260 | 
            +
                        events.include?(:RUBY_EVENT_CALL) &&
         | 
| 261 | 
            +
                        self.line == iseq.first_lineno
         | 
| 262 | 
            +
                        nline = iseq.first_lineno
         | 
| 263 | 
            +
                      end
         | 
| 237 264 |  | 
| 238 | 
            -
             | 
| 239 | 
            -
             | 
| 240 | 
            -
             | 
| 241 | 
            -
             | 
| 242 | 
            -
             | 
| 243 | 
            -
             | 
| 244 | 
            -
             | 
| 245 | 
            -
                            end
         | 
| 265 | 
            +
                      if !nearest || ((line - nline).abs < (line - nearest.line).abs)
         | 
| 266 | 
            +
                        nearest = NearestISeq.new(iseq, nline, events)
         | 
| 267 | 
            +
                      else
         | 
| 268 | 
            +
                        if @hook_call && nearest.iseq.first_lineno <= iseq.first_lineno
         | 
| 269 | 
            +
                          if (nearest.line > line && !nearest.events.include?(:RUBY_EVENT_CALL)) ||
         | 
| 270 | 
            +
                            (events.include?(:RUBY_EVENT_CALL))
         | 
| 271 | 
            +
                            nearest = NearestISeq.new(iseq, nline, events)
         | 
| 246 272 | 
             
                          end
         | 
| 247 273 | 
             
                        end
         | 
| 248 274 | 
             
                      end
         | 
| 249 275 | 
             
                    end
         | 
| 250 | 
            -
                   | 
| 276 | 
            +
                  end
         | 
| 251 277 |  | 
| 252 278 | 
             
                  if nearest
         | 
| 253 279 | 
             
                    activate_exact nearest.iseq, nearest.events, nearest.line
         | 
| @@ -277,11 +303,7 @@ module DEBUGGER__ | |
| 277 303 | 
             
                  @key = [:catch, @pat].freeze
         | 
| 278 304 | 
             
                  @last_exc = nil
         | 
| 279 305 |  | 
| 280 | 
            -
                   | 
| 281 | 
            -
                  @command = command
         | 
| 282 | 
            -
                  @path = path
         | 
| 283 | 
            -
             | 
| 284 | 
            -
                  super()
         | 
| 306 | 
            +
                  super(cond, command, path)
         | 
| 285 307 | 
             
                end
         | 
| 286 308 |  | 
| 287 309 | 
             
                def setup
         | 
| @@ -314,29 +336,41 @@ module DEBUGGER__ | |
| 314 336 | 
             
              end
         | 
| 315 337 |  | 
| 316 338 | 
             
              class CheckBreakpoint < Breakpoint
         | 
| 317 | 
            -
                def initialize  | 
| 318 | 
            -
                  @ | 
| 319 | 
            -
                  @key = [:check, @expr].freeze
         | 
| 320 | 
            -
                  @path = path
         | 
| 339 | 
            +
                def initialize cond:, command: nil, path: nil
         | 
| 340 | 
            +
                  @key = [:check, cond].freeze
         | 
| 321 341 |  | 
| 322 | 
            -
                  super()
         | 
| 342 | 
            +
                  super(cond, command, path)
         | 
| 323 343 | 
             
                end
         | 
| 324 344 |  | 
| 325 345 | 
             
                def setup
         | 
| 326 346 | 
             
                  @tp = TracePoint.new(:line){|tp|
         | 
| 327 | 
            -
                    next if  | 
| 328 | 
            -
                    next if tp.path.start_with? '<internal:'
         | 
| 347 | 
            +
                    next if SESSION.in_subsession? # TODO: Ractor support
         | 
| 329 348 | 
             
                    next if ThreadClient.current.management?
         | 
| 330 349 | 
             
                    next if skip_path?(tp.path)
         | 
| 331 350 |  | 
| 332 | 
            -
                    if safe_eval | 
| 351 | 
            +
                    if need_suspend? safe_eval(tp.binding, @cond)
         | 
| 333 352 | 
             
                      suspend
         | 
| 334 353 | 
             
                    end
         | 
| 335 354 | 
             
                  }
         | 
| 336 355 | 
             
                end
         | 
| 337 356 |  | 
| 357 | 
            +
                private def need_suspend? cond_result
         | 
| 358 | 
            +
                  map = ThreadClient.current.check_bp_fulfillment_map
         | 
| 359 | 
            +
                  if cond_result
         | 
| 360 | 
            +
                    if map[self]
         | 
| 361 | 
            +
                      false
         | 
| 362 | 
            +
                    else
         | 
| 363 | 
            +
                      map[self] = true
         | 
| 364 | 
            +
                    end
         | 
| 365 | 
            +
                  else
         | 
| 366 | 
            +
                    map[self] = false
         | 
| 367 | 
            +
                  end
         | 
| 368 | 
            +
                end
         | 
| 369 | 
            +
             | 
| 338 370 | 
             
                def to_s
         | 
| 339 | 
            -
                  "#{generate_label("Check")} | 
| 371 | 
            +
                  s = "#{generate_label("Check")}"
         | 
| 372 | 
            +
                  s += super
         | 
| 373 | 
            +
                  s
         | 
| 340 374 | 
             
                end
         | 
| 341 375 | 
             
              end
         | 
| 342 376 |  | 
| @@ -348,10 +382,7 @@ module DEBUGGER__ | |
| 348 382 |  | 
| 349 383 | 
             
                  @current = current
         | 
| 350 384 |  | 
| 351 | 
            -
                   | 
| 352 | 
            -
                  @command = command
         | 
| 353 | 
            -
                  @path = path
         | 
| 354 | 
            -
                  super()
         | 
| 385 | 
            +
                  super(cond, command, path)
         | 
| 355 386 | 
             
                end
         | 
| 356 387 |  | 
| 357 388 | 
             
                def watch_eval(tp)
         | 
| @@ -374,9 +405,6 @@ module DEBUGGER__ | |
| 374 405 |  | 
| 375 406 | 
             
                def setup
         | 
| 376 407 | 
             
                  @tp = TracePoint.new(:line, :return, :b_return){|tp|
         | 
| 377 | 
            -
                    next if tp.path.start_with? __dir__
         | 
| 378 | 
            -
                    next if tp.path.start_with? '<internal:'
         | 
| 379 | 
            -
             | 
| 380 408 | 
             
                    watch_eval(tp)
         | 
| 381 409 | 
             
                  }
         | 
| 382 410 | 
             
                end
         | 
| @@ -393,7 +421,7 @@ module DEBUGGER__ | |
| 393 421 | 
             
              end
         | 
| 394 422 |  | 
| 395 423 | 
             
              class MethodBreakpoint < Breakpoint
         | 
| 396 | 
            -
                attr_reader :sig_method_name, :method
         | 
| 424 | 
            +
                attr_reader :sig_method_name, :method, :klass
         | 
| 397 425 |  | 
| 398 426 | 
             
                def initialize b, klass_name, op, method_name, cond: nil, command: nil, path: nil
         | 
| 399 427 | 
             
                  @sig_klass_name = klass_name
         | 
| @@ -404,13 +432,10 @@ module DEBUGGER__ | |
| 404 432 |  | 
| 405 433 | 
             
                  @klass = nil
         | 
| 406 434 | 
             
                  @method = nil
         | 
| 407 | 
            -
                  @cond = cond
         | 
| 408 435 | 
             
                  @cond_class = nil
         | 
| 409 | 
            -
                  @command = command
         | 
| 410 | 
            -
                  @path = path
         | 
| 411 436 | 
             
                  @key = "#{klass_name}#{op}#{method_name}".freeze
         | 
| 412 437 |  | 
| 413 | 
            -
                  super(false)
         | 
| 438 | 
            +
                  super(cond, command, path, do_enable: false)
         | 
| 414 439 | 
             
                end
         | 
| 415 440 |  | 
| 416 441 | 
             
                def setup
         | 
| @@ -419,7 +444,6 @@ module DEBUGGER__ | |
| 419 444 | 
             
                    next if @cond_class && !tp.self.kind_of?(@cond_class)
         | 
| 420 445 |  | 
| 421 446 | 
             
                    caller_location = caller_locations(2, 1).first.to_s
         | 
| 422 | 
            -
                    next if caller_location.start_with?(__dir__)
         | 
| 423 447 | 
             
                    next if skip_path?(caller_location)
         | 
| 424 448 |  | 
| 425 449 | 
             
                    suspend
         | 
    
        data/lib/debug/client.rb
    CHANGED
    
    | @@ -18,6 +18,10 @@ module DEBUGGER__ | |
| 18 18 | 
             
                    case name
         | 
| 19 19 | 
             
                    when 'gen-sockpath'
         | 
| 20 20 | 
             
                      puts DEBUGGER__.create_unix_domain_socket_name
         | 
| 21 | 
            +
                    when 'gen-portpath'
         | 
| 22 | 
            +
                      port_path = File.join(DEBUGGER__.unix_domain_socket_dir, 'tcp_port')
         | 
| 23 | 
            +
                      File.unlink port_path if File.exist?(port_path)
         | 
| 24 | 
            +
                      puts port_path
         | 
| 21 25 | 
             
                    when 'list-socks'
         | 
| 22 26 | 
             
                      cleanup_unix_domain_sockets
         | 
| 23 27 | 
             
                      puts list_connections
         | 
| @@ -76,7 +80,7 @@ module DEBUGGER__ | |
| 76 80 |  | 
| 77 81 | 
             
                  def cleanup_unix_domain_sockets
         | 
| 78 82 | 
             
                    Dir.glob(DEBUGGER__.create_unix_domain_socket_name_prefix + '*') do |file|
         | 
| 79 | 
            -
                      if  | 
| 83 | 
            +
                      if File.socket?(file) && (/-(\d+)-\d+$/ =~ file || /-(\d+)$/ =~ file)
         | 
| 80 84 | 
             
                        begin
         | 
| 81 85 | 
             
                          Process.kill(0, $1.to_i)
         | 
| 82 86 | 
             
                        rescue Errno::EPERM
         | 
| @@ -88,7 +92,9 @@ module DEBUGGER__ | |
| 88 92 | 
             
                  end
         | 
| 89 93 |  | 
| 90 94 | 
             
                  def list_connections
         | 
| 91 | 
            -
                    Dir.glob(DEBUGGER__.create_unix_domain_socket_name_prefix + '*')
         | 
| 95 | 
            +
                    Dir.glob(DEBUGGER__.create_unix_domain_socket_name_prefix + '*').find_all do |path|
         | 
| 96 | 
            +
                      File.socket?(path)
         | 
| 97 | 
            +
                    end
         | 
| 92 98 | 
             
                  end
         | 
| 93 99 | 
             
                end
         | 
| 94 100 |  | 
| @@ -115,7 +121,10 @@ module DEBUGGER__ | |
| 115 121 | 
             
                  @width = IO.console_size[1]
         | 
| 116 122 | 
             
                  @width = 80 if @width == 0
         | 
| 117 123 |  | 
| 118 | 
            -
                  send "version: #{VERSION}  | 
| 124 | 
            +
                  send "version: #{VERSION} " +
         | 
| 125 | 
            +
                       "width: #{@width} " +
         | 
| 126 | 
            +
                       "cookie: #{CONFIG[:cookie] || '-'} " +
         | 
| 127 | 
            +
                       "nonstop: #{CONFIG[:nonstop] ? 'true' : 'false'}"
         | 
| 119 128 | 
             
                end
         | 
| 120 129 |  | 
| 121 130 | 
             
                def deactivate
         | 
| @@ -167,6 +176,8 @@ module DEBUGGER__ | |
| 167 176 | 
             
                end
         | 
| 168 177 |  | 
| 169 178 | 
             
                def connect
         | 
| 179 | 
            +
                  pre_commands = (CONFIG[:commands] || '').split(';;')
         | 
| 180 | 
            +
             | 
| 170 181 | 
             
                  trap(:SIGINT){
         | 
| 171 182 | 
             
                    send "pause"
         | 
| 172 183 | 
             
                  }
         | 
| @@ -175,7 +186,7 @@ module DEBUGGER__ | |
| 175 186 | 
             
                    trap(:SIGWINCH){
         | 
| 176 187 | 
             
                      @width = IO.console_size[1]
         | 
| 177 188 | 
             
                    }
         | 
| 178 | 
            -
                  rescue ArgumentError | 
| 189 | 
            +
                  rescue ArgumentError
         | 
| 179 190 | 
             
                    @width = 80
         | 
| 180 191 | 
             
                  end
         | 
| 181 192 |  | 
| @@ -193,7 +204,12 @@ module DEBUGGER__ | |
| 193 204 | 
             
                      prev_trap = trap(:SIGINT, 'DEFAULT')
         | 
| 194 205 |  | 
| 195 206 | 
             
                      begin
         | 
| 196 | 
            -
                         | 
| 207 | 
            +
                        if pre_commands.empty?
         | 
| 208 | 
            +
                          line = readline
         | 
| 209 | 
            +
                        else
         | 
| 210 | 
            +
                          line = pre_commands.shift
         | 
| 211 | 
            +
                          puts "(rdbg:remote:command) #{line}"
         | 
| 212 | 
            +
                        end
         | 
| 197 213 | 
             
                      rescue Interrupt
         | 
| 198 214 | 
             
                        retry
         | 
| 199 215 | 
             
                      ensure
         | 
    
        data/lib/debug/config.rb
    CHANGED
    
    | @@ -7,46 +7,50 @@ module DEBUGGER__ | |
| 7 7 | 
             
                ERROR:   2,
         | 
| 8 8 | 
             
                WARN:    3,
         | 
| 9 9 | 
             
                INFO:    4,
         | 
| 10 | 
            +
                DEBUG:   5
         | 
| 10 11 | 
             
              }.freeze
         | 
| 11 12 |  | 
| 12 13 | 
             
              CONFIG_SET = {
         | 
| 13 14 | 
             
                # UI setting
         | 
| 14 | 
            -
                log_level:      ['RUBY_DEBUG_LOG_LEVEL',      "UI: Log level same as Logger | 
| 15 | 
            -
                show_src_lines: ['RUBY_DEBUG_SHOW_SRC_LINES', "UI: Show n lines source code on breakpoint  | 
| 16 | 
            -
                show_frames:    ['RUBY_DEBUG_SHOW_FRAMES',    "UI: Show n frames on breakpoint | 
| 17 | 
            -
                use_short_path: ['RUBY_DEBUG_USE_SHORT_PATH', "UI: Show shorten PATH (like $(Gem)/foo.rb)", | 
| 18 | 
            -
                no_color:       ['RUBY_DEBUG_NO_COLOR',       "UI: Do not use colorize | 
| 19 | 
            -
                no_sigint_hook: ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT | 
| 20 | 
            -
                no_reline:      ['RUBY_DEBUG_NO_RELINE',      "UI: Do not use Reline library | 
| 15 | 
            +
                log_level:      ['RUBY_DEBUG_LOG_LEVEL',      "UI: Log level same as Logger",               :loglevel, "WARN"],
         | 
| 16 | 
            +
                show_src_lines: ['RUBY_DEBUG_SHOW_SRC_LINES', "UI: Show n lines source code on breakpoint", :int, "10"],
         | 
| 17 | 
            +
                show_frames:    ['RUBY_DEBUG_SHOW_FRAMES',    "UI: Show n frames on breakpoint",            :int, "2"],
         | 
| 18 | 
            +
                use_short_path: ['RUBY_DEBUG_USE_SHORT_PATH', "UI: Show shorten PATH (like $(Gem)/foo.rb)", :bool, "false"],
         | 
| 19 | 
            +
                no_color:       ['RUBY_DEBUG_NO_COLOR',       "UI: Do not use colorize",                    :bool, "false"],
         | 
| 20 | 
            +
                no_sigint_hook: ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT",               :bool, "false"],
         | 
| 21 | 
            +
                no_reline:      ['RUBY_DEBUG_NO_RELINE',      "UI: Do not use Reline library",              :bool, "false"],
         | 
| 22 | 
            +
                no_hint:        ['RUBY_DEBUG_NO_HINT',        "UI: Do not show the hint on the REPL",       :bool, "false"],
         | 
| 21 23 |  | 
| 22 24 | 
             
                # control setting
         | 
| 23 | 
            -
                skip_path:      ['RUBY_DEBUG_SKIP_PATH',      "CONTROL: Skip showing/entering frames for given paths | 
| 24 | 
            -
                skip_nosrc:     ['RUBY_DEBUG_SKIP_NOSRC',     "CONTROL: Skip on no source code lines | 
| 25 | 
            -
                keep_alloc_site:['RUBY_DEBUG_KEEP_ALLOC_SITE',"CONTROL: Keep allocation site and p, pp shows it | 
| 26 | 
            -
                postmortem:     ['RUBY_DEBUG_POSTMORTEM',     "CONTROL: Enable postmortem debug | 
| 27 | 
            -
                fork_mode:      ['RUBY_DEBUG_FORK_MODE',      "CONTROL: Control which process activates a debugger after fork (both/parent/child)  | 
| 28 | 
            -
                sigdump_sig:    ['RUBY_DEBUG_SIGDUMP_SIG',    "CONTROL: Sigdump signal  | 
| 25 | 
            +
                skip_path:      ['RUBY_DEBUG_SKIP_PATH',      "CONTROL: Skip showing/entering frames for given paths", :path],
         | 
| 26 | 
            +
                skip_nosrc:     ['RUBY_DEBUG_SKIP_NOSRC',     "CONTROL: Skip on no source code lines",              :bool, "false"],
         | 
| 27 | 
            +
                keep_alloc_site:['RUBY_DEBUG_KEEP_ALLOC_SITE',"CONTROL: Keep allocation site and p, pp shows it",   :bool, "false"],
         | 
| 28 | 
            +
                postmortem:     ['RUBY_DEBUG_POSTMORTEM',     "CONTROL: Enable postmortem debug",                   :bool, "false"],
         | 
| 29 | 
            +
                fork_mode:      ['RUBY_DEBUG_FORK_MODE',      "CONTROL: Control which process activates a debugger after fork (both/parent/child)", :forkmode, "both"],
         | 
| 30 | 
            +
                sigdump_sig:    ['RUBY_DEBUG_SIGDUMP_SIG',    "CONTROL: Sigdump signal", :bool, "false"],
         | 
| 29 31 |  | 
| 30 32 | 
             
                # boot setting
         | 
| 31 | 
            -
                nonstop:        ['RUBY_DEBUG_NONSTOP',     "BOOT: Nonstop mode",                                                :bool],
         | 
| 32 | 
            -
                stop_at_load:   ['RUBY_DEBUG_STOP_AT_LOAD',"BOOT: Stop at just loading location",                               :bool],
         | 
| 33 | 
            +
                nonstop:        ['RUBY_DEBUG_NONSTOP',     "BOOT: Nonstop mode",                                                :bool, "false"],
         | 
| 34 | 
            +
                stop_at_load:   ['RUBY_DEBUG_STOP_AT_LOAD',"BOOT: Stop at just loading location",                               :bool, "false"],
         | 
| 33 35 | 
             
                init_script:    ['RUBY_DEBUG_INIT_SCRIPT', "BOOT: debug command script path loaded at first stop"],
         | 
| 34 36 | 
             
                commands:       ['RUBY_DEBUG_COMMANDS',    "BOOT: debug commands invoked at first stop. commands should be separated by ';;'"],
         | 
| 35 | 
            -
                no_rc:          ['RUBY_DEBUG_NO_RC',       "BOOT: ignore loading ~/.rdbgrc(.rb)",                               :bool],
         | 
| 36 | 
            -
                history_file:   ['RUBY_DEBUG_HISTORY_FILE',"BOOT: history file | 
| 37 | 
            -
                save_history:   ['RUBY_DEBUG_SAVE_HISTORY',"BOOT: maximum save history lines  | 
| 37 | 
            +
                no_rc:          ['RUBY_DEBUG_NO_RC',       "BOOT: ignore loading ~/.rdbgrc(.rb)",                               :bool, "false"],
         | 
| 38 | 
            +
                history_file:   ['RUBY_DEBUG_HISTORY_FILE',"BOOT: history file",               :string, "~/.rdbg_history"],
         | 
| 39 | 
            +
                save_history:   ['RUBY_DEBUG_SAVE_HISTORY',"BOOT: maximum save history lines", :int, "10000"],
         | 
| 38 40 |  | 
| 39 41 | 
             
                # remote setting
         | 
| 40 42 | 
             
                port:           ['RUBY_DEBUG_PORT',         "REMOTE: TCP/IP remote debugging: port"],
         | 
| 41 | 
            -
                host:           ['RUBY_DEBUG_HOST',         "REMOTE: TCP/IP remote debugging: host  | 
| 43 | 
            +
                host:           ['RUBY_DEBUG_HOST',         "REMOTE: TCP/IP remote debugging: host", :string, "127.0.0.1"],
         | 
| 42 44 | 
             
                sock_path:      ['RUBY_DEBUG_SOCK_PATH',    "REMOTE: UNIX Domain Socket remote debugging: socket path"],
         | 
| 43 45 | 
             
                sock_dir:       ['RUBY_DEBUG_SOCK_DIR',     "REMOTE: UNIX Domain Socket remote debugging: socket directory"],
         | 
| 46 | 
            +
                local_fs_map:   ['RUBY_DEBUG_LOCAL_FS_MAP', "REMOTE: Specify local fs map", :path_map],
         | 
| 47 | 
            +
                skip_bp:        ['RUBY_DEBUG_SKIP_BP',      "REMOTE: Skip breakpoints if no clients are attached", :bool, 'false'],
         | 
| 44 48 | 
             
                cookie:         ['RUBY_DEBUG_COOKIE',       "REMOTE: Cookie for negotiation"],
         | 
| 45 49 | 
             
                open_frontend:  ['RUBY_DEBUG_OPEN_FRONTEND',"REMOTE: frontend used by open command (vscode, chrome, default: rdbg)."],
         | 
| 46 50 | 
             
                chrome_path:    ['RUBY_DEBUG_CHROME_PATH',  "REMOTE: Platform dependent path of Chrome (For more information, See [here](https://github.com/ruby/debug/pull/334/files#diff-5fc3d0a901379a95bc111b86cf0090b03f857edfd0b99a0c1537e26735698453R55-R64))"],
         | 
| 47 51 |  | 
| 48 52 | 
             
                # obsolete
         | 
| 49 | 
            -
                parent_on_fork: ['RUBY_DEBUG_PARENT_ON_FORK', "OBSOLETE: Keep debugging parent process on fork | 
| 53 | 
            +
                parent_on_fork: ['RUBY_DEBUG_PARENT_ON_FORK', "OBSOLETE: Keep debugging parent process on fork",     :bool, "false"],
         | 
| 50 54 | 
             
              }.freeze
         | 
| 51 55 |  | 
| 52 56 | 
             
              CONFIG_MAP = CONFIG_SET.map{|k, (ev, _)| [k, ev]}.to_h.freeze
         | 
| @@ -59,11 +63,23 @@ module DEBUGGER__ | |
| 59 63 | 
             
                end
         | 
| 60 64 |  | 
| 61 65 | 
             
                def initialize argv
         | 
| 66 | 
            +
                  @skip_all = false
         | 
| 67 | 
            +
             | 
| 62 68 | 
             
                  if self.class.config
         | 
| 63 69 | 
             
                    raise 'Can not make multiple configurations in one process'
         | 
| 64 70 | 
             
                  end
         | 
| 65 71 |  | 
| 66 | 
            -
                   | 
| 72 | 
            +
                  config = self.class.parse_argv(argv)
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  # apply defaults
         | 
| 75 | 
            +
                  CONFIG_SET.each do |k, config_detail|
         | 
| 76 | 
            +
                    unless config.key?(k)
         | 
| 77 | 
            +
                      default_value = config_detail[3]
         | 
| 78 | 
            +
                      config[k] = parse_config_value(k, default_value)
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  update config
         | 
| 67 83 | 
             
                end
         | 
| 68 84 |  | 
| 69 85 | 
             
                def inspect
         | 
| @@ -78,6 +94,14 @@ module DEBUGGER__ | |
| 78 94 | 
             
                  set_config(key => val)
         | 
| 79 95 | 
             
                end
         | 
| 80 96 |  | 
| 97 | 
            +
                def skip_all
         | 
| 98 | 
            +
                  @skip_all = true
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                def skip?
         | 
| 102 | 
            +
                  @skip_all
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
             | 
| 81 105 | 
             
                def set_config(**kw)
         | 
| 82 106 | 
             
                  conf = config.dup
         | 
| 83 107 | 
             
                  kw.each{|k, v|
         | 
| @@ -114,11 +138,13 @@ module DEBUGGER__ | |
| 114 138 | 
             
                  self.class.instance_variable_set(:@config, conf.freeze)
         | 
| 115 139 |  | 
| 116 140 | 
             
                  # Post process
         | 
| 117 | 
            -
                  if_updated old_conf, conf, :keep_alloc_site do | | 
| 141 | 
            +
                  if_updated old_conf, conf, :keep_alloc_site do |old, new|
         | 
| 118 142 | 
             
                    if new
         | 
| 119 143 | 
             
                      require 'objspace'
         | 
| 120 144 | 
             
                      ObjectSpace.trace_object_allocations_start
         | 
| 121 | 
            -
                     | 
| 145 | 
            +
                    end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                    if old && !new
         | 
| 122 148 | 
             
                      ObjectSpace.trace_object_allocations_stop
         | 
| 123 149 | 
             
                    end
         | 
| 124 150 | 
             
                  end
         | 
| @@ -215,6 +241,8 @@ module DEBUGGER__ | |
| 215 241 | 
             
                        e
         | 
| 216 242 | 
             
                      end
         | 
| 217 243 | 
             
                    }
         | 
| 244 | 
            +
                  when :path_map
         | 
| 245 | 
            +
                    valstr.split(',').map{|e| e.split(':')}
         | 
| 218 246 | 
             
                  else
         | 
| 219 247 | 
             
                    valstr
         | 
| 220 248 | 
             
                  end
         | 
| @@ -223,6 +251,7 @@ module DEBUGGER__ | |
| 223 251 | 
             
                def self.parse_argv argv
         | 
| 224 252 | 
             
                  config = {
         | 
| 225 253 | 
             
                    mode: :start,
         | 
| 254 | 
            +
                    no_color: (nc = ENV['NO_COLOR']) && !nc.empty?,
         | 
| 226 255 | 
             
                  }
         | 
| 227 256 | 
             
                  CONFIG_MAP.each{|key, evname|
         | 
| 228 257 | 
             
                    if val = ENV[evname]
         | 
| @@ -361,6 +390,8 @@ module DEBUGGER__ | |
| 361 390 | 
             
                      case CONFIG_SET[key][2]
         | 
| 362 391 | 
             
                      when :path
         | 
| 363 392 | 
             
                        valstr = config[key].map{|e| e.kind_of?(Regexp) ? e.inspect : e}.join(':')
         | 
| 393 | 
            +
                      when :path_map
         | 
| 394 | 
            +
                        valstr = config[key].map{|e| e.join(':')}.join(',')
         | 
| 364 395 | 
             
                      else
         | 
| 365 396 | 
             
                        valstr = config[key].to_s
         | 
| 366 397 | 
             
                      end
         | 
| @@ -428,7 +459,7 @@ module DEBUGGER__ | |
| 428 459 | 
             
              end
         | 
| 429 460 |  | 
| 430 461 | 
             
              def self.create_unix_domain_socket_name_prefix(base_dir = unix_domain_socket_dir)
         | 
| 431 | 
            -
                user = ENV['USER'] || ' | 
| 462 | 
            +
                user = ENV['USER'] || 'UnknownUser'
         | 
| 432 463 | 
             
                File.join(base_dir, "ruby-debug-#{user}")
         | 
| 433 464 | 
             
              end
         | 
| 434 465 |  |