debug 1.1.0 → 1.2.0
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/README.md +5 -0
- data/ext/debug/debug.c +5 -0
- data/lib/debug/breakpoint.rb +14 -5
- data/lib/debug/color.rb +2 -2
- data/lib/debug/config.rb +8 -1
- data/lib/debug/console.rb +10 -2
- data/lib/debug/frame_info.rb +2 -2
- data/lib/debug/local.rb +21 -24
- data/lib/debug/server.rb +8 -7
- data/lib/debug/session.rb +204 -78
- data/lib/debug/thread_client.rb +22 -13
- data/lib/debug/tracer.rb +7 -2
- data/lib/debug/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6eba8a00eaa711138e43036c5adb97834f1a87e586f8a5a3f580875e4d78e5c
|
4
|
+
data.tar.gz: 26ed8ac77bb93ab2a9f67fac6c4ef4481fbcdd227f116aaff78e808a2343b13b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b459267a95928388d1bf53bab8bf51fc8d5a6d0db0bc2fbaa55f740b1b446aad2294e45cc2b70471ccf8bb1daada458f239f08c7ceb7588774f08796a7838620
|
7
|
+
data.tar.gz: 86d06a4ec76943e26af39a26120c8a3a906d3fcecca195a662e63b9be2b7b9a1ba82bd2fba177205b436cde89be9214988e0f2cccc0f7fb2dcbf7226e92886e9
|
data/README.md
CHANGED
@@ -372,6 +372,7 @@ config set no_color true
|
|
372
372
|
|
373
373
|
* BOOT
|
374
374
|
* `RUBY_DEBUG_NONSTOP` (`nonstop`): Nonstop mode
|
375
|
+
* `RUBY_DEBUG_STOP_AT_LOAD` (`stop_at_load`): Stop at just loading location
|
375
376
|
* `RUBY_DEBUG_INIT_SCRIPT` (`init_script`): debug command script path loaded at first stop
|
376
377
|
* `RUBY_DEBUG_COMMANDS` (`commands`): debug commands invoked at first stop. commands should be separated by ';;'
|
377
378
|
* `RUBY_DEBUG_NO_RC` (`no_rc`): ignore loading ~/.rdbgrc(.rb)
|
@@ -436,6 +437,9 @@ The `<...>` notation means the argument.
|
|
436
437
|
* Stop the debuggee process with `Kernal#exit!`.
|
437
438
|
* `kill!`
|
438
439
|
* Same as kill but without the confirmation prompt.
|
440
|
+
* `sigint`
|
441
|
+
* Execute SIGINT handler registerred by the debuggee.
|
442
|
+
* Note that this command should be used just after stop by `SIGINT`.
|
439
443
|
|
440
444
|
### Breakpoint
|
441
445
|
|
@@ -728,6 +732,7 @@ Attach mode:
|
|
728
732
|
Other options:
|
729
733
|
-h, --help Print help
|
730
734
|
--util=NAME Utility mode (used by tools)
|
735
|
+
--stop-at-load Stop immediately when the debugging feature is loaded.
|
731
736
|
|
732
737
|
NOTE
|
733
738
|
All messages communicated between a debugger and a debuggee are *NOT* encrypted.
|
data/ext/debug/debug.c
CHANGED
@@ -122,6 +122,11 @@ Init_debug(void)
|
|
122
122
|
{
|
123
123
|
rb_mDebugger = rb_const_get(rb_cObject, rb_intern("DEBUGGER__"));
|
124
124
|
rb_cFrameInfo = rb_const_get(rb_mDebugger, rb_intern("FrameInfo"));
|
125
|
+
|
126
|
+
// Debugger and FrameInfo were defined in Ruby. We need to register them
|
127
|
+
// as mark objects so they are automatically pinned.
|
128
|
+
rb_gc_register_mark_object(rb_mDebugger);
|
129
|
+
rb_gc_register_mark_object(rb_cFrameInfo);
|
125
130
|
rb_define_singleton_method(rb_mDebugger, "capture_frames", capture_frames, 1);
|
126
131
|
rb_define_singleton_method(rb_mDebugger, "frame_depth", frame_depth, 0);
|
127
132
|
rb_define_singleton_method(rb_mDebugger, "create_method_added_tracker", create_method_added_tracker, 0);
|
data/lib/debug/breakpoint.rb
CHANGED
@@ -288,6 +288,7 @@ module DEBUGGER__
|
|
288
288
|
@tp = TracePoint.new(:line){|tp|
|
289
289
|
next if tp.path.start_with? __dir__
|
290
290
|
next if tp.path.start_with? '<internal:'
|
291
|
+
next if ThreadClient.current.management?
|
291
292
|
|
292
293
|
if safe_eval tp.binding, @expr
|
293
294
|
suspend
|
@@ -430,17 +431,24 @@ module DEBUGGER__
|
|
430
431
|
if @sig_op == '#'
|
431
432
|
@cond_class = @klass if @method.owner != @klass
|
432
433
|
else # '.'
|
433
|
-
|
434
|
+
begin
|
435
|
+
@cond_class = @klass.singleton_class if @method.owner != @klass.singleton_class
|
436
|
+
rescue TypeError
|
437
|
+
end
|
434
438
|
end
|
435
439
|
|
436
|
-
rescue ArgumentError
|
440
|
+
rescue ArgumentError => e
|
437
441
|
raise if retried
|
438
442
|
retried = true
|
439
443
|
|
440
444
|
# maybe C method
|
441
445
|
case @sig_op
|
442
446
|
when '.'
|
443
|
-
|
447
|
+
begin
|
448
|
+
override @klass.singleton_class
|
449
|
+
rescue TypeError
|
450
|
+
override @klass.class
|
451
|
+
end
|
444
452
|
when '#'
|
445
453
|
override @klass
|
446
454
|
end
|
@@ -450,7 +458,7 @@ module DEBUGGER__
|
|
450
458
|
@override_method = true if @method
|
451
459
|
retry
|
452
460
|
end
|
453
|
-
rescue Exception
|
461
|
+
rescue Exception => e
|
454
462
|
raise unless added
|
455
463
|
end
|
456
464
|
|
@@ -460,7 +468,8 @@ module DEBUGGER__
|
|
460
468
|
|
461
469
|
def to_s
|
462
470
|
if @method
|
463
|
-
|
471
|
+
loc = @method.source_location || []
|
472
|
+
"#{generate_label("Method")} #{sig} at #{loc.join(':')}"
|
464
473
|
else
|
465
474
|
"#{generate_label("Method (pending)")} #{sig}"
|
466
475
|
end + super
|
data/lib/debug/color.rb
CHANGED
@@ -19,7 +19,7 @@ module DEBUGGER__
|
|
19
19
|
if defined? IRB::Color.colorize
|
20
20
|
def colorize str, color
|
21
21
|
if !CONFIG[:no_color]
|
22
|
-
IRB::Color.colorize str, color
|
22
|
+
IRB::Color.colorize str, color, colorable: true
|
23
23
|
else
|
24
24
|
str
|
25
25
|
end
|
@@ -64,7 +64,7 @@ module DEBUGGER__
|
|
64
64
|
|
65
65
|
if defined? IRB::Color.colorize_code
|
66
66
|
def colorize_code code
|
67
|
-
IRB::Color.colorize_code(code)
|
67
|
+
IRB::Color.colorize_code(code, colorable: true)
|
68
68
|
end
|
69
69
|
else
|
70
70
|
def colorize_code code
|
data/lib/debug/config.rb
CHANGED
@@ -29,6 +29,7 @@ module DEBUGGER__
|
|
29
29
|
|
30
30
|
# boot setting
|
31
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],
|
32
33
|
init_script: ['RUBY_DEBUG_INIT_SCRIPT', "BOOT: debug command script path loaded at first stop"],
|
33
34
|
commands: ['RUBY_DEBUG_COMMANDS', "BOOT: debug commands invoked at first stop. commands should be separated by ';;'"],
|
34
35
|
no_rc: ['RUBY_DEBUG_NO_RC', "BOOT: ignore loading ~/.rdbgrc(.rb)", :bool],
|
@@ -110,7 +111,9 @@ module DEBUGGER__
|
|
110
111
|
end
|
111
112
|
|
112
113
|
if_updated old_conf, conf, :postmortem do |_, new_p|
|
113
|
-
SESSION
|
114
|
+
if defined?(SESSION)
|
115
|
+
SESSION.postmortem = new_p
|
116
|
+
end
|
114
117
|
end
|
115
118
|
|
116
119
|
if_updated old_conf, conf, :sigdump_sig do |old_sig, new_sig|
|
@@ -314,6 +317,10 @@ module DEBUGGER__
|
|
314
317
|
exit
|
315
318
|
end
|
316
319
|
|
320
|
+
o.on('--stop-at-load', 'Stop immediately when the debugging feature is loaded.') do
|
321
|
+
config[:stop_at_load] = true
|
322
|
+
end
|
323
|
+
|
317
324
|
o.separator ''
|
318
325
|
o.separator 'NOTE'
|
319
326
|
o.separator ' All messages communicated between a debugger and a debuggee are *NOT* encrypted.'
|
data/lib/debug/console.rb
CHANGED
@@ -6,11 +6,19 @@ module DEBUGGER__
|
|
6
6
|
require 'reline'
|
7
7
|
|
8
8
|
# reline 0.2.7 or later is required.
|
9
|
-
raise LoadError if Reline::VERSION < '0.2.
|
9
|
+
raise LoadError if Reline::VERSION < '0.2.7'
|
10
10
|
|
11
11
|
require_relative 'color'
|
12
12
|
include Color
|
13
13
|
|
14
|
+
begin
|
15
|
+
prev = trap(:SIGWINCH, nil)
|
16
|
+
trap(:SIGWINCH, prev)
|
17
|
+
SIGWINCH_SUPPORTED = true
|
18
|
+
rescue ArgumentError
|
19
|
+
SIGWINCH_SUPPORTED = false
|
20
|
+
end
|
21
|
+
|
14
22
|
# 0.2.7 has SIGWINCH issue on non-main thread
|
15
23
|
class ::Reline::LineEditor
|
16
24
|
m = Module.new do
|
@@ -20,7 +28,7 @@ module DEBUGGER__
|
|
20
28
|
end
|
21
29
|
end
|
22
30
|
prepend m
|
23
|
-
end
|
31
|
+
end if SIGWINCH_SUPPORTED
|
24
32
|
|
25
33
|
def readline_setup prompt
|
26
34
|
commands = DEBUGGER__.commands
|
data/lib/debug/frame_info.rb
CHANGED
@@ -27,6 +27,7 @@ module DEBUGGER__
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def pretty_path
|
30
|
+
return '#<none>' unless path = self.path
|
30
31
|
use_short_path = CONFIG[:use_short_path]
|
31
32
|
|
32
33
|
case
|
@@ -137,10 +138,9 @@ module DEBUGGER__
|
|
137
138
|
if lvars = self._local_variables
|
138
139
|
lvars
|
139
140
|
elsif b = self.binding
|
140
|
-
|
141
|
+
b.local_variables.map{|var|
|
141
142
|
[var, b.local_variable_get(var)]
|
142
143
|
}.to_h
|
143
|
-
self._local_variables = lvars
|
144
144
|
end
|
145
145
|
end
|
146
146
|
|
data/lib/debug/local.rb
CHANGED
@@ -7,32 +7,27 @@ module DEBUGGER__
|
|
7
7
|
class UI_LocalConsole < UI_Base
|
8
8
|
def initialize
|
9
9
|
@console = Console.new
|
10
|
-
|
11
|
-
unless CONFIG[:no_sigint_hook]
|
12
|
-
@prev_handler = trap(:SIGINT){
|
13
|
-
if SESSION.active?
|
14
|
-
ThreadClient.current.on_trap :SIGINT
|
15
|
-
end
|
16
|
-
}
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def close
|
21
|
-
if @prev_handler
|
22
|
-
trap(:SIGINT, @prev_handler)
|
23
|
-
end
|
24
10
|
end
|
25
11
|
|
26
12
|
def remote?
|
27
13
|
false
|
28
14
|
end
|
29
15
|
|
30
|
-
def activate on_fork: false
|
31
|
-
|
16
|
+
def activate session, on_fork: false
|
17
|
+
unless CONFIG[:no_sigint_hook]
|
18
|
+
prev_handler = trap(:SIGINT){
|
19
|
+
if session.active?
|
20
|
+
ThreadClient.current.on_trap :SIGINT
|
21
|
+
end
|
22
|
+
}
|
23
|
+
session.intercept_trap_sigint_start prev_handler
|
24
|
+
end
|
32
25
|
end
|
33
26
|
|
34
27
|
def deactivate
|
35
|
-
|
28
|
+
if SESSION.intercept_trap_sigint?
|
29
|
+
trap(:SIGINT, SESSION.intercepted_sigint_cmd)
|
30
|
+
end
|
36
31
|
end
|
37
32
|
|
38
33
|
def width
|
@@ -76,15 +71,17 @@ module DEBUGGER__
|
|
76
71
|
end
|
77
72
|
|
78
73
|
def setup_interrupt
|
79
|
-
|
74
|
+
SESSION.intercept_trap_sigint false do
|
75
|
+
current_thread = Thread.current # should be session_server thread
|
80
76
|
|
81
|
-
|
82
|
-
|
83
|
-
|
77
|
+
prev_handler = trap(:INT){
|
78
|
+
current_thread.raise Interrupt
|
79
|
+
}
|
84
80
|
|
85
|
-
|
86
|
-
|
87
|
-
|
81
|
+
yield
|
82
|
+
ensure
|
83
|
+
trap(:INT, prev_handler)
|
84
|
+
end
|
88
85
|
end
|
89
86
|
end
|
90
87
|
end
|
data/lib/debug/server.rb
CHANGED
@@ -15,8 +15,6 @@ module DEBUGGER__
|
|
15
15
|
@q_ans = nil
|
16
16
|
@unsent_messages = []
|
17
17
|
@width = 80
|
18
|
-
|
19
|
-
activate
|
20
18
|
end
|
21
19
|
|
22
20
|
class Terminate < StandardError
|
@@ -37,7 +35,7 @@ module DEBUGGER__
|
|
37
35
|
end
|
38
36
|
end
|
39
37
|
|
40
|
-
def activate on_fork: false
|
38
|
+
def activate session, on_fork: false
|
41
39
|
@reader_thread = Thread.new do
|
42
40
|
# An error on this thread should break the system.
|
43
41
|
Thread.current.abort_on_exception = true
|
@@ -138,9 +136,9 @@ module DEBUGGER__
|
|
138
136
|
end
|
139
137
|
|
140
138
|
def setup_interrupt
|
141
|
-
prev_handler = trap(:
|
139
|
+
prev_handler = trap(:SIGURG) do
|
142
140
|
# $stderr.puts "trapped SIGINT"
|
143
|
-
ThreadClient.current.on_trap :
|
141
|
+
ThreadClient.current.on_trap :SIGURG
|
144
142
|
|
145
143
|
case prev_handler
|
146
144
|
when Proc
|
@@ -150,9 +148,12 @@ module DEBUGGER__
|
|
150
148
|
end
|
151
149
|
end
|
152
150
|
|
151
|
+
if prev_handler != "SYSTEM_DEFAULT"
|
152
|
+
DEBUGGER__.warn "SIGURG handler is overriddend by the debugger."
|
153
|
+
end
|
153
154
|
yield
|
154
155
|
ensure
|
155
|
-
trap(:
|
156
|
+
trap(:SIGURG, prev_handler)
|
156
157
|
end
|
157
158
|
|
158
159
|
attr_reader :reader_thread
|
@@ -230,7 +231,7 @@ module DEBUGGER__
|
|
230
231
|
|
231
232
|
def pause
|
232
233
|
# $stderr.puts "DEBUG: pause request"
|
233
|
-
Process.kill(:
|
234
|
+
Process.kill(:SIGURG, Process.pid)
|
234
235
|
end
|
235
236
|
|
236
237
|
def quit n
|
data/lib/debug/session.rb
CHANGED
@@ -86,7 +86,7 @@ module DEBUGGER__
|
|
86
86
|
# [:watch, ivar] => WatchIVarBreakpoint
|
87
87
|
# [:check, expr] => CheckBreakpoint
|
88
88
|
#
|
89
|
-
@tracers =
|
89
|
+
@tracers = {}
|
90
90
|
@th_clients = nil # {Thread => ThreadClient}
|
91
91
|
@q_evt = Queue.new
|
92
92
|
@displays = []
|
@@ -96,17 +96,22 @@ module DEBUGGER__
|
|
96
96
|
@postmortem_hook = nil
|
97
97
|
@postmortem = false
|
98
98
|
@thread_stopper = nil
|
99
|
+
@intercept_trap_sigint = false
|
100
|
+
@intercepted_sigint_cmd = 'DEFAULT'
|
99
101
|
|
100
102
|
@frame_map = {} # {id => [threadId, frame_depth]} for DAP
|
101
103
|
@var_map = {1 => [:globals], } # {id => ...} for DAP
|
102
104
|
@src_map = {} # {id => src}
|
103
105
|
|
106
|
+
@tp_thread_begin = nil
|
104
107
|
@tp_load_script = TracePoint.new(:script_compiled){|tp|
|
105
108
|
ThreadClient.current.on_load tp.instruction_sequence, tp.eval_script
|
106
109
|
}
|
107
110
|
@tp_load_script.enable
|
108
111
|
|
109
112
|
activate
|
113
|
+
|
114
|
+
self.postmortem = CONFIG[:postmortem]
|
110
115
|
end
|
111
116
|
|
112
117
|
def active?
|
@@ -117,40 +122,41 @@ module DEBUGGER__
|
|
117
122
|
@bps.has_key? [file, line]
|
118
123
|
end
|
119
124
|
|
120
|
-
def
|
121
|
-
|
122
|
-
|
123
|
-
|
125
|
+
def activate on_fork: false
|
126
|
+
@tp_thread_begin&.disable
|
127
|
+
@tp_thread_begin = nil
|
128
|
+
|
129
|
+
if on_fork
|
130
|
+
@ui.activate self, on_fork: true
|
131
|
+
else
|
132
|
+
@ui.activate self, on_fork: false
|
124
133
|
end
|
125
|
-
end
|
126
134
|
|
127
|
-
|
135
|
+
q = Queue.new
|
128
136
|
@session_server = Thread.new do
|
129
137
|
Thread.current.name = 'DEBUGGER__::SESSION@server'
|
130
138
|
Thread.current.abort_on_exception = true
|
131
|
-
session_server_main
|
132
|
-
end
|
133
139
|
|
134
|
-
|
140
|
+
# Thread management
|
141
|
+
setup_threads
|
142
|
+
thc = thread_client Thread.current
|
143
|
+
thc.is_management
|
135
144
|
|
136
|
-
|
137
|
-
|
145
|
+
if @ui.respond_to?(:reader_thread) && thc = thread_client(@ui.reader_thread)
|
146
|
+
thc.is_management
|
147
|
+
end
|
138
148
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
@
|
143
|
-
end
|
149
|
+
@tp_thread_begin = TracePoint.new(:thread_begin) do |tp|
|
150
|
+
thread_client
|
151
|
+
end
|
152
|
+
@tp_thread_begin.enable
|
144
153
|
|
145
|
-
|
146
|
-
|
154
|
+
# session start
|
155
|
+
q << true
|
156
|
+
session_server_main
|
147
157
|
end
|
148
158
|
|
149
|
-
|
150
|
-
th = Thread.current
|
151
|
-
ThreadClient.current.on_thread_begin th
|
152
|
-
}
|
153
|
-
@tp_thread_begin.enable
|
159
|
+
q.pop
|
154
160
|
end
|
155
161
|
|
156
162
|
def deactivate
|
@@ -160,14 +166,14 @@ module DEBUGGER__
|
|
160
166
|
@tp_thread_begin.disable
|
161
167
|
@bps.each{|k, bp| bp.disable}
|
162
168
|
@th_clients.each{|th, thc| thc.close}
|
163
|
-
@tracers.each{|t| t.disable}
|
169
|
+
@tracers.values.each{|t| t.disable}
|
164
170
|
@q_evt.close
|
165
171
|
@ui&.deactivate
|
166
172
|
@ui = nil
|
167
173
|
end
|
168
174
|
|
169
175
|
def reset_ui ui
|
170
|
-
@ui.
|
176
|
+
@ui.deactivate
|
171
177
|
@ui = ui
|
172
178
|
end
|
173
179
|
|
@@ -182,6 +188,13 @@ module DEBUGGER__
|
|
182
188
|
output.each{|str| @ui.puts str}
|
183
189
|
|
184
190
|
case ev
|
191
|
+
|
192
|
+
when :thread_begin # special event, tc is nil
|
193
|
+
th = ev_args.shift
|
194
|
+
q = ev_args.shift
|
195
|
+
on_thread_begin th
|
196
|
+
q << true
|
197
|
+
|
185
198
|
when :init
|
186
199
|
wait_command_loop tc
|
187
200
|
|
@@ -193,24 +206,23 @@ module DEBUGGER__
|
|
193
206
|
|
194
207
|
when :trace
|
195
208
|
trace_id, msg = ev_args
|
196
|
-
if t = @tracers.find{|t| t.object_id == trace_id}
|
209
|
+
if t = @tracers.values.find{|t| t.object_id == trace_id}
|
197
210
|
t.puts msg
|
198
211
|
end
|
199
212
|
tc << :continue
|
200
213
|
|
201
|
-
when :thread_begin
|
202
|
-
th = ev_args.shift
|
203
|
-
on_thread_begin th
|
204
|
-
@ui.event :thread_begin, th
|
205
|
-
tc << :continue
|
206
|
-
|
207
214
|
when :suspend
|
208
215
|
case ev_args.first
|
209
216
|
when :breakpoint
|
210
217
|
bp, i = bp_index ev_args[1]
|
211
218
|
@ui.event :suspend_bp, i, bp
|
212
219
|
when :trap
|
213
|
-
@ui.event :suspend_trap, ev_args[1]
|
220
|
+
@ui.event :suspend_trap, sig = ev_args[1]
|
221
|
+
|
222
|
+
if sig == :SIGINT && (@intercepted_sigint_cmd.kind_of?(Proc) || @intercepted_sigint_cmd.kind_of?(String))
|
223
|
+
@ui.puts "#{@intercepted_sigint_cmd.inspect} is registerred as SIGINT handler."
|
224
|
+
@ui.puts "`sigint` command execute it."
|
225
|
+
end
|
214
226
|
else
|
215
227
|
@ui.event :suspended
|
216
228
|
end
|
@@ -246,8 +258,7 @@ module DEBUGGER__
|
|
246
258
|
obj_id = ev_args[1]
|
247
259
|
obj_inspect = ev_args[2]
|
248
260
|
opt = ev_args[3]
|
249
|
-
|
250
|
-
@ui.puts "Enable #{t.to_s}"
|
261
|
+
add_tracer ObjectTracer.new(@ui, obj_id, obj_inspect, **opt)
|
251
262
|
else
|
252
263
|
# ignore
|
253
264
|
end
|
@@ -432,6 +443,29 @@ module DEBUGGER__
|
|
432
443
|
when 'kill!'
|
433
444
|
exit! (arg || 1).to_i
|
434
445
|
|
446
|
+
# * `sigint`
|
447
|
+
# * Execute SIGINT handler registerred by the debuggee.
|
448
|
+
# * Note that this command should be used just after stop by `SIGINT`.
|
449
|
+
when 'sigint'
|
450
|
+
begin
|
451
|
+
case cmd = @intercepted_sigint_cmd
|
452
|
+
when nil, 'IGNORE', :IGNORE, 'DEFAULT', :DEFAULT
|
453
|
+
# ignore
|
454
|
+
when String
|
455
|
+
eval(cmd)
|
456
|
+
when Proc
|
457
|
+
cmd.call
|
458
|
+
end
|
459
|
+
|
460
|
+
@tc << :continue
|
461
|
+
restart_all_threads
|
462
|
+
|
463
|
+
rescue Exception => e
|
464
|
+
@ui.puts "Exception: #{e}"
|
465
|
+
@ui.puts e.backtrace.map{|line| " #{e}"}
|
466
|
+
return :retry
|
467
|
+
end
|
468
|
+
|
435
469
|
### Breakpoint
|
436
470
|
|
437
471
|
# * `b[reak]`
|
@@ -740,7 +774,7 @@ module DEBUGGER__
|
|
740
774
|
@ui.puts "not supported on the remote console."
|
741
775
|
return :retry
|
742
776
|
end
|
743
|
-
@tc << [:eval, :
|
777
|
+
@tc << [:eval, :irb]
|
744
778
|
|
745
779
|
# don't repeat irb command
|
746
780
|
@repl_prev_line = nil
|
@@ -778,32 +812,29 @@ module DEBUGGER__
|
|
778
812
|
case arg
|
779
813
|
when nil
|
780
814
|
@ui.puts 'Tracers:'
|
781
|
-
@tracers.each_with_index{|t, i|
|
815
|
+
@tracers.values.each_with_index{|t, i|
|
782
816
|
@ui.puts "* \##{i} #{t}"
|
783
817
|
}
|
784
818
|
@ui.puts
|
785
819
|
return :retry
|
786
820
|
|
787
821
|
when /\Aline\z/
|
788
|
-
|
789
|
-
@ui.puts "Enable #{t.to_s}"
|
822
|
+
add_tracer LineTracer.new(@ui, pattern: pattern, into: into)
|
790
823
|
return :retry
|
791
824
|
|
792
825
|
when /\Acall\z/
|
793
|
-
|
794
|
-
@ui.puts "Enable #{t.to_s}"
|
826
|
+
add_tracer CallTracer.new(@ui, pattern: pattern, into: into)
|
795
827
|
return :retry
|
796
828
|
|
797
829
|
when /\Aexception\z/
|
798
|
-
|
799
|
-
@ui.puts "Enable #{t.to_s}"
|
830
|
+
add_tracer ExceptionTracer.new(@ui, pattern: pattern, into: into)
|
800
831
|
return :retry
|
801
832
|
|
802
833
|
when /\Aobject\s+(.+)/
|
803
834
|
@tc << [:trace, :object, $1.strip, {pattern: pattern, into: into}]
|
804
835
|
|
805
836
|
when /\Aoff\s+(\d+)\z/
|
806
|
-
if t = @tracers[$1.to_i]
|
837
|
+
if t = @tracers.values[$1.to_i]
|
807
838
|
t.disable
|
808
839
|
@ui.puts "Disable #{t.to_s}"
|
809
840
|
else
|
@@ -812,7 +843,7 @@ module DEBUGGER__
|
|
812
843
|
return :retry
|
813
844
|
|
814
845
|
when /\Aoff(\s+(line|call|exception|object))?\z/
|
815
|
-
@tracers.each{|t|
|
846
|
+
@tracers.values.each{|t|
|
816
847
|
if $2.nil? || t.type == $2
|
817
848
|
t.disable
|
818
849
|
@ui.puts "Disable #{t.to_s}"
|
@@ -1190,6 +1221,20 @@ module DEBUGGER__
|
|
1190
1221
|
@ui.puts e.message
|
1191
1222
|
end
|
1192
1223
|
|
1224
|
+
# tracers
|
1225
|
+
|
1226
|
+
def add_tracer tracer
|
1227
|
+
# don't repeat commands that add tracers
|
1228
|
+
@repl_prev_line = nil
|
1229
|
+
if @tracers.has_key? tracer.key
|
1230
|
+
tracer.disable
|
1231
|
+
@ui.puts "Duplicated tracer: #{tracer}"
|
1232
|
+
else
|
1233
|
+
@tracers[tracer.key] = tracer
|
1234
|
+
@ui.puts "Enable #{tracer}"
|
1235
|
+
end
|
1236
|
+
end
|
1237
|
+
|
1193
1238
|
# threads
|
1194
1239
|
|
1195
1240
|
def update_thread_list
|
@@ -1242,10 +1287,6 @@ module DEBUGGER__
|
|
1242
1287
|
thread_list
|
1243
1288
|
end
|
1244
1289
|
|
1245
|
-
def thread_client_create th
|
1246
|
-
@th_clients[th] = ThreadClient.new((@tc_id += 1), @q_evt, Queue.new, th)
|
1247
|
-
end
|
1248
|
-
|
1249
1290
|
def setup_threads
|
1250
1291
|
@th_clients = {}
|
1251
1292
|
|
@@ -1256,18 +1297,38 @@ module DEBUGGER__
|
|
1256
1297
|
|
1257
1298
|
def on_thread_begin th
|
1258
1299
|
if @th_clients.has_key? th
|
1259
|
-
# OK
|
1260
|
-
else
|
1261
1300
|
# TODO: NG?
|
1301
|
+
else
|
1262
1302
|
thread_client_create th
|
1263
1303
|
end
|
1264
1304
|
end
|
1265
1305
|
|
1266
|
-
def
|
1267
|
-
|
1268
|
-
|
1306
|
+
private def thread_client_create th
|
1307
|
+
# TODO: Ractor support
|
1308
|
+
raise "Only session_server can create thread_client" unless Thread.current == @session_server
|
1309
|
+
@th_clients[th] = ThreadClient.new((@tc_id += 1), @q_evt, Queue.new, th)
|
1310
|
+
end
|
1311
|
+
|
1312
|
+
private def ask_thread_client th = Thread.current
|
1313
|
+
# TODO: Ractor support
|
1314
|
+
q2 = Queue.new
|
1315
|
+
# tc, output, ev, @internal_info, *ev_args = evt
|
1316
|
+
@q_evt << [nil, [], :thread_begin, nil, th, q2]
|
1317
|
+
q2.pop
|
1318
|
+
|
1319
|
+
@th_clients[th] or raise "unexpected error"
|
1320
|
+
end
|
1321
|
+
|
1322
|
+
# can be called by other threads
|
1323
|
+
def thread_client th = Thread.current
|
1324
|
+
if @th_clients.has_key? th
|
1325
|
+
@th_clients[th]
|
1269
1326
|
else
|
1270
|
-
|
1327
|
+
if Thread.current == @session_server
|
1328
|
+
thread_client_create th
|
1329
|
+
else
|
1330
|
+
ask_thread_client th
|
1331
|
+
end
|
1271
1332
|
end
|
1272
1333
|
end
|
1273
1334
|
|
@@ -1386,7 +1447,10 @@ module DEBUGGER__
|
|
1386
1447
|
end
|
1387
1448
|
end
|
1388
1449
|
|
1389
|
-
def enter_postmortem_session
|
1450
|
+
def enter_postmortem_session exc
|
1451
|
+
return unless exc.instance_variable_defined? :@__debugger_postmortem_frames
|
1452
|
+
|
1453
|
+
frames = exc.instance_variable_get(:@__debugger_postmortem_frames)
|
1390
1454
|
@postmortem = true
|
1391
1455
|
ThreadClient.current.suspend :postmortem, postmortem_frames: frames
|
1392
1456
|
ensure
|
@@ -1399,7 +1463,7 @@ module DEBUGGER__
|
|
1399
1463
|
@postmortem_hook = TracePoint.new(:raise){|tp|
|
1400
1464
|
exc = tp.raised_exception
|
1401
1465
|
frames = DEBUGGER__.capture_frames(__dir__)
|
1402
|
-
exc.instance_variable_set(:@
|
1466
|
+
exc.instance_variable_set(:@__debugger_postmortem_frames, frames)
|
1403
1467
|
}
|
1404
1468
|
at_exit{
|
1405
1469
|
@postmortem_hook.disable
|
@@ -1411,7 +1475,7 @@ module DEBUGGER__
|
|
1411
1475
|
@ui.puts exc.backtrace.map{|e| ' ' + e}
|
1412
1476
|
@ui.puts "\n"
|
1413
1477
|
|
1414
|
-
enter_postmortem_session exc
|
1478
|
+
enter_postmortem_session exc
|
1415
1479
|
rescue SystemExit
|
1416
1480
|
exit!
|
1417
1481
|
rescue Exception => e
|
@@ -1431,6 +1495,30 @@ module DEBUGGER__
|
|
1431
1495
|
end
|
1432
1496
|
end
|
1433
1497
|
end
|
1498
|
+
|
1499
|
+
def save_int_trap cmd
|
1500
|
+
prev, @intercepted_sigint_cmd = @intercepted_sigint_cmd, cmd
|
1501
|
+
prev
|
1502
|
+
end
|
1503
|
+
|
1504
|
+
attr_reader :intercepted_sigint_cmd
|
1505
|
+
|
1506
|
+
def intercept_trap_sigint?
|
1507
|
+
@intercept_trap_sigint
|
1508
|
+
end
|
1509
|
+
|
1510
|
+
def intercept_trap_sigint flag, &b
|
1511
|
+
prev = @intercept_trap_sigint
|
1512
|
+
@intercept_trap_sigint = flag
|
1513
|
+
yield
|
1514
|
+
ensure
|
1515
|
+
@intercept_trap_sigint = prev
|
1516
|
+
end
|
1517
|
+
|
1518
|
+
def intercept_trap_sigint_start prev
|
1519
|
+
@intercept_trap_sigint = true
|
1520
|
+
@intercepted_sigint_cmd = prev
|
1521
|
+
end
|
1434
1522
|
end
|
1435
1523
|
|
1436
1524
|
class UI_Base
|
@@ -1526,6 +1614,9 @@ module DEBUGGER__
|
|
1526
1614
|
def self.setup_initial_suspend
|
1527
1615
|
if !CONFIG[:nonstop]
|
1528
1616
|
case
|
1617
|
+
when CONFIG[:stop_at_load]
|
1618
|
+
add_line_breakpoint __FILE__, __LINE__ + 1, oneshot: true, hook_call: false
|
1619
|
+
nil # stop here
|
1529
1620
|
when path = ENV['RUBY_DEBUG_INITIAL_SUSPEND_PATH']
|
1530
1621
|
add_line_breakpoint path, 0, oneshot: true, hook_call: false
|
1531
1622
|
when loc = ::DEBUGGER__.require_location
|
@@ -1643,7 +1734,18 @@ module DEBUGGER__
|
|
1643
1734
|
|
1644
1735
|
at_exit{
|
1645
1736
|
trap(:SIGINT, :IGNORE)
|
1646
|
-
|
1737
|
+
|
1738
|
+
# only check child process from its parent
|
1739
|
+
if Process.pid == parent_pid
|
1740
|
+
begin
|
1741
|
+
# sending a null signal to see if the child is still alive
|
1742
|
+
Process.kill(0, child_pid)
|
1743
|
+
# if the child is still alive, wait for it
|
1744
|
+
Process.waitpid(child_pid)
|
1745
|
+
rescue Errno::ESRCH
|
1746
|
+
# if the child process has died, do nothing
|
1747
|
+
end
|
1748
|
+
end
|
1647
1749
|
}
|
1648
1750
|
}
|
1649
1751
|
child_hook = -> {
|
@@ -1675,8 +1777,36 @@ module DEBUGGER__
|
|
1675
1777
|
end
|
1676
1778
|
end
|
1677
1779
|
|
1678
|
-
|
1679
|
-
|
1780
|
+
module TrapInterceptor
|
1781
|
+
def trap sig, *command, &command_proc
|
1782
|
+
case sig&.to_sym
|
1783
|
+
when :INT, :SIGINT
|
1784
|
+
if defined?(SESSION) && SESSION.active? && SESSION.intercept_trap_sigint?
|
1785
|
+
return SESSION.save_int_trap(command.empty? ? command_proc : command.first)
|
1786
|
+
end
|
1787
|
+
end
|
1788
|
+
|
1789
|
+
super
|
1790
|
+
end
|
1791
|
+
end
|
1792
|
+
|
1793
|
+
if RUBY_VERSION >= '3.0.0'
|
1794
|
+
module ::Kernel
|
1795
|
+
prepend ForkInterceptor
|
1796
|
+
prepend TrapInterceptor
|
1797
|
+
end
|
1798
|
+
else
|
1799
|
+
class ::Object
|
1800
|
+
include ForkInterceptor
|
1801
|
+
include TrapInterceptor
|
1802
|
+
end
|
1803
|
+
end
|
1804
|
+
|
1805
|
+
module ::Kernel
|
1806
|
+
class << self
|
1807
|
+
prepend ForkInterceptor
|
1808
|
+
prepend TrapInterceptor
|
1809
|
+
end
|
1680
1810
|
end
|
1681
1811
|
|
1682
1812
|
module ::Process
|
@@ -1684,10 +1814,16 @@ module DEBUGGER__
|
|
1684
1814
|
prepend ForkInterceptor
|
1685
1815
|
end
|
1686
1816
|
end
|
1817
|
+
|
1818
|
+
module ::Signal
|
1819
|
+
class << self
|
1820
|
+
prepend TrapInterceptor
|
1821
|
+
end
|
1822
|
+
end
|
1687
1823
|
end
|
1688
1824
|
|
1689
|
-
|
1690
|
-
def
|
1825
|
+
module Kernel
|
1826
|
+
def debugger pre: nil, do: nil
|
1691
1827
|
return if !defined?(::DEBUGGER__::SESSION) || !::DEBUGGER__::SESSION.active?
|
1692
1828
|
|
1693
1829
|
if pre || (do_expr = binding.local_variable_get(:do))
|
@@ -1697,20 +1833,10 @@ class Binding
|
|
1697
1833
|
::DEBUGGER__.add_line_breakpoint __FILE__, __LINE__ + 1, oneshot: true, command: cmds
|
1698
1834
|
self
|
1699
1835
|
end
|
1700
|
-
alias b break
|
1701
1836
|
end
|
1702
1837
|
|
1703
|
-
|
1704
|
-
|
1705
|
-
|
1706
|
-
def debugger(...)
|
1707
|
-
binding.break(...)
|
1708
|
-
end
|
1709
|
-
RUBY
|
1710
|
-
else
|
1711
|
-
def debugger pre: nil, do: nil
|
1712
|
-
b = binding
|
1713
|
-
b.break pre: pre, do: b.local_variable_get(:do)
|
1714
|
-
end
|
1715
|
-
end
|
1838
|
+
class Binding
|
1839
|
+
alias break debugger
|
1840
|
+
alias b debugger
|
1716
1841
|
end
|
1842
|
+
|
data/lib/debug/thread_client.rb
CHANGED
@@ -20,9 +20,11 @@ module DEBUGGER__
|
|
20
20
|
|
21
21
|
class ThreadClient
|
22
22
|
def self.current
|
23
|
-
Thread.current[:DEBUGGER__ThreadClient]
|
24
|
-
|
25
|
-
|
23
|
+
if thc = Thread.current[:DEBUGGER__ThreadClient]
|
24
|
+
thc
|
25
|
+
else
|
26
|
+
thc = SESSION.thread_client
|
27
|
+
Thread.current[:DEBUGGER__ThreadClient] = thc
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
@@ -115,7 +117,7 @@ module DEBUGGER__
|
|
115
117
|
# TODO: there is waiting -> waiting
|
116
118
|
# raise "#{mode} is given, but #{mode}" unless self.running?
|
117
119
|
else
|
118
|
-
raise
|
120
|
+
raise "unknown mode: #{mode}"
|
119
121
|
end
|
120
122
|
|
121
123
|
@mode = mode
|
@@ -138,7 +140,11 @@ module DEBUGGER__
|
|
138
140
|
end
|
139
141
|
|
140
142
|
def inspect
|
141
|
-
|
143
|
+
if bt = @thread.backtrace
|
144
|
+
"#<DBG:TC #{self.id}:#{@mode}@#{bt[-1]}>"
|
145
|
+
else # bt can be nil
|
146
|
+
"#<DBG:TC #{self.id}:#{@mode}>"
|
147
|
+
end
|
142
148
|
end
|
143
149
|
|
144
150
|
def to_s
|
@@ -194,10 +200,6 @@ module DEBUGGER__
|
|
194
200
|
wait_next_action
|
195
201
|
end
|
196
202
|
|
197
|
-
def on_thread_begin th
|
198
|
-
wait_reply [:thread_begin, th]
|
199
|
-
end
|
200
|
-
|
201
203
|
def on_load iseq, eval_src
|
202
204
|
wait_reply [:load, iseq, eval_src]
|
203
205
|
end
|
@@ -340,7 +342,8 @@ module DEBUGGER__
|
|
340
342
|
begin
|
341
343
|
@success_last_eval = false
|
342
344
|
|
343
|
-
b = current_frame
|
345
|
+
b = current_frame&.eval_binding || TOPLEVEL_BINDING
|
346
|
+
|
344
347
|
result = if b
|
345
348
|
f, _l = b.source_location
|
346
349
|
b.eval(src, "(rdbg)/#{f}")
|
@@ -659,18 +662,17 @@ module DEBUGGER__
|
|
659
662
|
|
660
663
|
def wait_next_action_
|
661
664
|
# assertions
|
662
|
-
raise "@mode is #{@mode}"
|
665
|
+
raise "@mode is #{@mode}" if !waiting?
|
663
666
|
|
664
667
|
unless SESSION.active?
|
665
668
|
pp caller
|
666
669
|
set_mode :running
|
667
670
|
return
|
668
671
|
end
|
669
|
-
# SESSION.check_forked
|
670
672
|
|
671
673
|
while true
|
672
674
|
begin
|
673
|
-
set_mode :waiting if
|
675
|
+
set_mode :waiting if !waiting?
|
674
676
|
cmds = @q_cmd.pop
|
675
677
|
# pp [self, cmds: cmds]
|
676
678
|
break unless cmds
|
@@ -779,6 +781,13 @@ module DEBUGGER__
|
|
779
781
|
end
|
780
782
|
when :call
|
781
783
|
result = frame_eval(eval_src)
|
784
|
+
when :irb
|
785
|
+
begin
|
786
|
+
result = frame_eval('binding.irb')
|
787
|
+
ensure
|
788
|
+
# workaround: https://github.com/ruby/debug/issues/308
|
789
|
+
Reline.prompt_proc = nil if defined? Reline
|
790
|
+
end
|
782
791
|
when :display, :try_display
|
783
792
|
failed_results = []
|
784
793
|
eval_src.each_with_index{|src, i|
|
data/lib/debug/tracer.rb
CHANGED
@@ -14,7 +14,7 @@ module DEBUGGER__
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
attr_reader :type
|
17
|
+
attr_reader :type, :key
|
18
18
|
|
19
19
|
def initialize ui, pattern: nil, into: nil
|
20
20
|
if /\ADEBUGGER__::(([A-Z][a-z]+?)[A-Z][a-z]+)/ =~ self.class.name
|
@@ -37,6 +37,8 @@ module DEBUGGER__
|
|
37
37
|
@output = ui
|
38
38
|
end
|
39
39
|
|
40
|
+
@key = [@type, @pattern, @into].freeze
|
41
|
+
|
40
42
|
enable
|
41
43
|
end
|
42
44
|
|
@@ -95,6 +97,8 @@ module DEBUGGER__
|
|
95
97
|
end
|
96
98
|
|
97
99
|
def minfo tp
|
100
|
+
return "block{}" if tp.event == :b_call
|
101
|
+
|
98
102
|
klass = tp.defined_class
|
99
103
|
|
100
104
|
if klass.singleton_class?
|
@@ -172,6 +176,7 @@ module DEBUGGER__
|
|
172
176
|
@obj_id = obj_id
|
173
177
|
@obj_inspect = obj_inspect
|
174
178
|
super(ui, **kw)
|
179
|
+
@key = [@type, @obj_id, @pattern, @into].freeze
|
175
180
|
end
|
176
181
|
|
177
182
|
def description
|
@@ -201,7 +206,7 @@ module DEBUGGER__
|
|
201
206
|
end
|
202
207
|
|
203
208
|
out tp, " #{colorized_obj_inspect} receives #{colorize_blue(method_info)}"
|
204
|
-
|
209
|
+
elsif !tp.parameters.empty?
|
205
210
|
b = tp.binding
|
206
211
|
method_info = colorize_blue(minfo(tp))
|
207
212
|
|
data/lib/debug/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: debug
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Koichi Sasada
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: irb
|