debug 1.0.0.alpha1 → 1.0.0.beta5
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/.github/workflows/ruby.yml +34 -0
- data/.gitignore +4 -0
- data/CONTRIBUTING.md +145 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +20 -19
- data/README.md +398 -54
- data/Rakefile +21 -1
- data/TODO.md +33 -0
- data/debug.gemspec +7 -10
- data/exe/rdbg +39 -1
- data/ext/debug/debug.c +119 -0
- data/ext/debug/extconf.rb +2 -0
- data/ext/debug/iseq_collector.c +91 -0
- data/lib/debug.rb +1 -1
- data/lib/debug/breakpoint.rb +362 -34
- data/lib/debug/client.rb +53 -17
- data/lib/debug/color.rb +70 -0
- data/lib/debug/config.rb +169 -7
- data/lib/debug/console.rb +96 -0
- data/lib/debug/frame_info.rb +144 -0
- data/lib/debug/open.rb +12 -0
- data/lib/debug/run.rb +4 -0
- data/lib/debug/server.rb +178 -27
- data/lib/debug/server_dap.rb +605 -0
- data/lib/debug/session.rb +779 -193
- data/lib/debug/source_repository.rb +77 -20
- data/lib/debug/test_console.rb +0 -0
- data/lib/debug/thread_client.rb +390 -179
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +350 -0
- metadata +29 -28
- data/lib/debug/repl.rb +0 -69
- data/lib/debug/tcpserver.rb +0 -22
- data/lib/debug/unixserver.rb +0 -18
data/lib/debug/session.rb
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
require 'iseq_collector'
|
|
1
|
+
|
|
2
|
+
# skip to load debugger for bundle exec
|
|
3
|
+
return if $0.end_with?('bin/bundle') && ARGV.first == 'exec'
|
|
5
4
|
|
|
5
|
+
require_relative 'config'
|
|
6
|
+
require_relative 'thread_client'
|
|
6
7
|
require_relative 'source_repository'
|
|
7
8
|
require_relative 'breakpoint'
|
|
8
|
-
|
|
9
|
+
|
|
10
|
+
require 'json' if ENV['RUBY_DEBUG_TEST_MODE']
|
|
9
11
|
|
|
10
12
|
class RubyVM::InstructionSequence
|
|
11
13
|
def traceable_lines_norec lines
|
|
@@ -39,6 +41,14 @@ class RubyVM::InstructionSequence
|
|
|
39
41
|
def locals
|
|
40
42
|
self.to_a[10]
|
|
41
43
|
end
|
|
44
|
+
|
|
45
|
+
def last_line
|
|
46
|
+
self.to_a[4][:code_location][2]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def first_line
|
|
50
|
+
self.to_a[4][:code_location][0]
|
|
51
|
+
end
|
|
42
52
|
end
|
|
43
53
|
|
|
44
54
|
module DEBUGGER__
|
|
@@ -46,49 +56,132 @@ module DEBUGGER__
|
|
|
46
56
|
def initialize ui
|
|
47
57
|
@ui = ui
|
|
48
58
|
@sr = SourceRepository.new
|
|
49
|
-
@
|
|
50
|
-
|
|
59
|
+
@bps = {} # bp.key => bp
|
|
60
|
+
# [file, line] => LineBreakpoint
|
|
61
|
+
# "Error" => CatchBreakpoint
|
|
62
|
+
# "Foo#bar" => MethodBreakpoint
|
|
63
|
+
# [:watch, ivar] => WatchIVarBreakpoint
|
|
64
|
+
# [:check, expr] => CheckBreakpoint
|
|
51
65
|
@th_clients = {} # {Thread => ThreadClient}
|
|
52
66
|
@q_evt = Queue.new
|
|
53
67
|
@displays = []
|
|
54
68
|
@tc = nil
|
|
69
|
+
@tc_id = 0
|
|
70
|
+
@initial_commands = []
|
|
71
|
+
|
|
72
|
+
@frame_map = {} # {id => [threadId, frame_depth]} for DAP
|
|
73
|
+
@var_map = {1 => [:globals], } # {id => ...} for DAP
|
|
74
|
+
@src_map = {} # {id => src}
|
|
55
75
|
|
|
56
76
|
@tp_load_script = TracePoint.new(:script_compiled){|tp|
|
|
57
|
-
|
|
58
|
-
|
|
77
|
+
unless @management_threads.include? Thread.current
|
|
78
|
+
ThreadClient.current.on_load tp.instruction_sequence, tp.eval_script
|
|
79
|
+
end
|
|
80
|
+
}
|
|
81
|
+
@tp_load_script.enable
|
|
59
82
|
|
|
60
83
|
@session_server = Thread.new do
|
|
61
84
|
Thread.current.abort_on_exception = true
|
|
85
|
+
session_server_main
|
|
86
|
+
end
|
|
62
87
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
@management_threads = [@session_server]
|
|
89
|
+
@management_threads << @ui.reader_thread if @ui.respond_to? :reader_thread
|
|
90
|
+
|
|
91
|
+
setup_threads
|
|
92
|
+
|
|
93
|
+
@tp_thread_begin = TracePoint.new(:thread_begin){|tp|
|
|
94
|
+
unless @management_threads.include?(th = Thread.current)
|
|
95
|
+
ThreadClient.current.on_thread_begin th
|
|
96
|
+
end
|
|
97
|
+
}
|
|
98
|
+
@tp_thread_begin.enable
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def session_server_main
|
|
102
|
+
while evt = @q_evt.pop
|
|
103
|
+
# varible `@internal_info` is only used for test
|
|
104
|
+
tc, output, ev, @internal_info, *ev_args = evt
|
|
105
|
+
output.each{|str| @ui.puts str}
|
|
106
|
+
|
|
107
|
+
case ev
|
|
108
|
+
when :load
|
|
109
|
+
iseq, src = ev_args
|
|
110
|
+
on_load iseq, src
|
|
111
|
+
@ui.event :load
|
|
112
|
+
tc << :continue
|
|
113
|
+
when :thread_begin
|
|
114
|
+
th = ev_args.shift
|
|
115
|
+
on_thread_begin th
|
|
116
|
+
@ui.event :thread_begin, th
|
|
117
|
+
tc << :continue
|
|
118
|
+
when :suspend
|
|
119
|
+
case ev_args.first
|
|
120
|
+
when :breakpoint
|
|
121
|
+
bp, i = bp_index ev_args[1]
|
|
122
|
+
@ui.event :suspend_bp, i, bp
|
|
123
|
+
when :trap
|
|
124
|
+
@ui.event :suspend_trap, ev_args[1]
|
|
125
|
+
else
|
|
126
|
+
@ui.event :suspended
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
if @displays.empty?
|
|
130
|
+
wait_command_loop tc
|
|
131
|
+
else
|
|
132
|
+
tc << [:eval, :display, @displays]
|
|
133
|
+
end
|
|
134
|
+
when :result
|
|
135
|
+
case ev_args.first
|
|
136
|
+
when :watch
|
|
137
|
+
bp = ev_args[1]
|
|
138
|
+
@bps[bp.key] = bp
|
|
139
|
+
show_bps bp
|
|
140
|
+
when :try_display
|
|
141
|
+
failed_results = ev_args[1]
|
|
142
|
+
if failed_results.size > 0
|
|
143
|
+
i, _msg = failed_results.last
|
|
144
|
+
if i+1 == @displays.size
|
|
145
|
+
@ui.puts "canceled: #{@displays.pop}"
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
when :method_breakpoint
|
|
149
|
+
bp = ev_args[1]
|
|
150
|
+
if bp
|
|
151
|
+
@bps[bp.key] = bp
|
|
152
|
+
show_bps bp
|
|
75
153
|
else
|
|
76
|
-
|
|
154
|
+
# can't make a bp
|
|
77
155
|
end
|
|
78
|
-
|
|
79
|
-
|
|
156
|
+
else
|
|
157
|
+
# ignore
|
|
80
158
|
end
|
|
159
|
+
|
|
160
|
+
wait_command_loop tc
|
|
161
|
+
|
|
162
|
+
when :dap_result
|
|
163
|
+
dap_event ev_args # server.rb
|
|
164
|
+
wait_command_loop tc
|
|
81
165
|
end
|
|
82
166
|
end
|
|
167
|
+
ensure
|
|
168
|
+
@bps.each{|k, bp| bp.disable}
|
|
169
|
+
@th_clients.each{|th, thc| thc.close}
|
|
170
|
+
end
|
|
83
171
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
172
|
+
def add_initial_commands cmds
|
|
173
|
+
cmds.each{|c|
|
|
174
|
+
c.gsub('#.*', '').strip!
|
|
175
|
+
@initial_commands << c unless c.empty?
|
|
176
|
+
}
|
|
88
177
|
end
|
|
89
178
|
|
|
90
|
-
def source
|
|
91
|
-
|
|
179
|
+
def source iseq
|
|
180
|
+
if CONFIG[:use_colorize]
|
|
181
|
+
@sr.get_colored(iseq)
|
|
182
|
+
else
|
|
183
|
+
@sr.get(iseq)
|
|
184
|
+
end
|
|
92
185
|
end
|
|
93
186
|
|
|
94
187
|
def inspect
|
|
@@ -109,11 +202,30 @@ module DEBUGGER__
|
|
|
109
202
|
retry
|
|
110
203
|
end
|
|
111
204
|
end
|
|
205
|
+
ensure
|
|
206
|
+
@tc = nil
|
|
112
207
|
end
|
|
113
208
|
|
|
114
209
|
def wait_command
|
|
115
|
-
|
|
210
|
+
if @initial_commands.empty?
|
|
211
|
+
@ui.puts "INTERNAL_INFO: #{JSON.generate(@internal_info)}" if ENV['RUBY_DEBUG_TEST_MODE']
|
|
212
|
+
line = @ui.readline
|
|
213
|
+
else
|
|
214
|
+
line = @initial_commands.shift.strip
|
|
215
|
+
@ui.puts "(rdbg:init) #{line}"
|
|
216
|
+
end
|
|
116
217
|
|
|
218
|
+
case line
|
|
219
|
+
when String
|
|
220
|
+
process_command line
|
|
221
|
+
when Hash
|
|
222
|
+
process_dap_request line # defined in server.rb
|
|
223
|
+
else
|
|
224
|
+
raise "unexpected input: #{line.inspect}"
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def process_command line
|
|
117
229
|
if line.empty?
|
|
118
230
|
if @repl_prev_line
|
|
119
231
|
line = @repl_prev_line
|
|
@@ -130,16 +242,30 @@ module DEBUGGER__
|
|
|
130
242
|
# p cmd: [cmd, *arg]
|
|
131
243
|
|
|
132
244
|
case cmd
|
|
245
|
+
### Control flow
|
|
133
246
|
|
|
134
|
-
#
|
|
247
|
+
# * `s[tep]`
|
|
248
|
+
# * Step in. Resume the program until next breakable point.
|
|
135
249
|
when 's', 'step'
|
|
136
250
|
@tc << [:step, :in]
|
|
251
|
+
|
|
252
|
+
# * `n[ext]`
|
|
253
|
+
# * Step over. Resume the program until next line.
|
|
137
254
|
when 'n', 'next'
|
|
138
255
|
@tc << [:step, :next]
|
|
256
|
+
|
|
257
|
+
# * `fin[ish]`
|
|
258
|
+
# * Finish this frame. Resume the program until the current frame is finished.
|
|
139
259
|
when 'fin', 'finish'
|
|
140
260
|
@tc << [:step, :finish]
|
|
261
|
+
|
|
262
|
+
# * `c[ontinue]`
|
|
263
|
+
# * Resume the program.
|
|
141
264
|
when 'c', 'continue'
|
|
142
265
|
@tc << :continue
|
|
266
|
+
|
|
267
|
+
# * `q[uit]` or `Ctrl-D`
|
|
268
|
+
# * Finish debugger (with the debuggee process on non-remote debugging).
|
|
143
269
|
when 'q', 'quit'
|
|
144
270
|
if ask 'Really quit?'
|
|
145
271
|
@ui.quit arg.to_i
|
|
@@ -147,23 +273,63 @@ module DEBUGGER__
|
|
|
147
273
|
else
|
|
148
274
|
return :retry
|
|
149
275
|
end
|
|
276
|
+
|
|
277
|
+
# * `q[uit]!`
|
|
278
|
+
# * Same as q[uit] but without the confirmation prompt.
|
|
279
|
+
when 'q!', 'quit!'
|
|
280
|
+
@ui.quit arg.to_i
|
|
281
|
+
@tc << :continue
|
|
282
|
+
|
|
283
|
+
# * `kill`
|
|
284
|
+
# * Stop the debuggee process with `Kernal#exit!`.
|
|
150
285
|
when 'kill'
|
|
151
|
-
if ask 'Really
|
|
286
|
+
if ask 'Really kill?'
|
|
152
287
|
exit! (arg || 1).to_i
|
|
153
288
|
else
|
|
154
289
|
return :retry
|
|
155
290
|
end
|
|
156
291
|
|
|
157
|
-
#
|
|
292
|
+
# * `kill!`
|
|
293
|
+
# * Same as kill but without the confirmation prompt.
|
|
294
|
+
when 'kill!'
|
|
295
|
+
exit! (arg || 1).to_i
|
|
296
|
+
|
|
297
|
+
### Breakpoint
|
|
298
|
+
|
|
299
|
+
# * `b[reak]`
|
|
300
|
+
# * Show all breakpoints.
|
|
301
|
+
# * `b[reak] <line>`
|
|
302
|
+
# * Set breakpoint on `<line>` at the current frame's file.
|
|
303
|
+
# * `b[reak] <file>:<line>` or `<file> <line>`
|
|
304
|
+
# * Set breakpoint on `<file>:<line>`.
|
|
305
|
+
# * `b[reak] <class>#<name>`
|
|
306
|
+
# * Set breakpoint on the method `<class>#<name>`.
|
|
307
|
+
# * `b[reak] <expr>.<name>`
|
|
308
|
+
# * Set breakpoint on the method `<expr>.<name>`.
|
|
309
|
+
# * `b[reak] ... if <expr>`
|
|
310
|
+
# * break if `<expr>` is true at specified location.
|
|
311
|
+
# * `b[reak] if <expr>`
|
|
312
|
+
# * break if `<expr>` is true at any lines.
|
|
313
|
+
# * Note that this feature is super slow.
|
|
158
314
|
when 'b', 'break'
|
|
159
315
|
if arg == nil
|
|
160
316
|
show_bps
|
|
317
|
+
return :retry
|
|
161
318
|
else
|
|
162
|
-
bp = repl_add_breakpoint
|
|
163
|
-
|
|
319
|
+
case bp = repl_add_breakpoint(arg)
|
|
320
|
+
when :noretry
|
|
321
|
+
when nil
|
|
322
|
+
return :retry
|
|
323
|
+
else
|
|
324
|
+
show_bps bp
|
|
325
|
+
return :retry
|
|
326
|
+
end
|
|
164
327
|
end
|
|
165
|
-
|
|
328
|
+
|
|
329
|
+
# skip
|
|
166
330
|
when 'bv'
|
|
331
|
+
require 'json'
|
|
332
|
+
|
|
167
333
|
h = Hash.new{|h, k| h[k] = []}
|
|
168
334
|
@bps.each{|key, bp|
|
|
169
335
|
if LineBreakpoint === bp
|
|
@@ -184,12 +350,33 @@ module DEBUGGER__
|
|
|
184
350
|
end
|
|
185
351
|
|
|
186
352
|
return :retry
|
|
353
|
+
|
|
354
|
+
# * `catch <Error>`
|
|
355
|
+
# * Set breakpoint on raising `<Error>`.
|
|
187
356
|
when 'catch'
|
|
188
357
|
if arg
|
|
189
358
|
bp = add_catch_breakpoint arg
|
|
190
359
|
show_bps bp if bp
|
|
360
|
+
else
|
|
361
|
+
show_bps
|
|
191
362
|
end
|
|
192
363
|
return :retry
|
|
364
|
+
|
|
365
|
+
# * `watch @ivar`
|
|
366
|
+
# * Stop the execution when the result of current scope's `@ivar` is changed.
|
|
367
|
+
# * Note that this feature is super slow.
|
|
368
|
+
when 'wat', 'watch'
|
|
369
|
+
if arg
|
|
370
|
+
@tc << [:eval, :watch, arg]
|
|
371
|
+
else
|
|
372
|
+
show_bps
|
|
373
|
+
return :retry
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# * `del[ete]`
|
|
377
|
+
# * delete all breakpoints.
|
|
378
|
+
# * `del[ete] <bpnum>`
|
|
379
|
+
# * delete specified breakpoint.
|
|
193
380
|
when 'del', 'delete'
|
|
194
381
|
bp =
|
|
195
382
|
case arg
|
|
@@ -206,42 +393,90 @@ module DEBUGGER__
|
|
|
206
393
|
@ui.puts "deleted: \##{bp[0]} #{bp[1]}" if bp
|
|
207
394
|
return :retry
|
|
208
395
|
|
|
209
|
-
|
|
210
|
-
when 'p'
|
|
211
|
-
@tc << [:eval, :p, arg.to_s]
|
|
212
|
-
when 'pp'
|
|
213
|
-
@tc << [:eval, :pp, arg.to_s]
|
|
214
|
-
when 'e', 'eval', 'call'
|
|
215
|
-
@tc << [:eval, :call, arg]
|
|
216
|
-
when 'irb'
|
|
217
|
-
@tc << [:eval, :call, 'binding.irb']
|
|
396
|
+
### Information
|
|
218
397
|
|
|
219
|
-
#
|
|
220
|
-
|
|
221
|
-
@tc << [:frame, :up]
|
|
222
|
-
when 'down'
|
|
223
|
-
@tc << [:frame, :down]
|
|
224
|
-
when 'frame', 'f'
|
|
225
|
-
@tc << [:frame, :set, arg]
|
|
226
|
-
|
|
227
|
-
# information
|
|
398
|
+
# * `bt` or `backtrace`
|
|
399
|
+
# * Show backtrace (frame) information.
|
|
228
400
|
when 'bt', 'backtrace'
|
|
229
401
|
@tc << [:show, :backtrace]
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
402
|
+
|
|
403
|
+
# * `l[ist]`
|
|
404
|
+
# * Show current frame's source code.
|
|
405
|
+
# * Next `list` command shows the successor lines.
|
|
406
|
+
# * `l[ist] -`
|
|
407
|
+
# * Show predecessor lines as opposed to the `list` command.
|
|
408
|
+
# * `l[ist] <start>` or `l[ist] <start>-<end>`
|
|
409
|
+
# * Show current frame's source code from the line <start> to <end> if given.
|
|
410
|
+
when 'l', 'list'
|
|
411
|
+
case arg ? arg.strip : nil
|
|
412
|
+
when /\A(\d+)\z/
|
|
413
|
+
@tc << [:show, :list, {start_line: arg.to_i - 1}]
|
|
414
|
+
when /\A-\z/
|
|
415
|
+
@tc << [:show, :list, {dir: -1}]
|
|
416
|
+
when /\A(\d+)-(\d+)\z/
|
|
417
|
+
@tc << [:show, :list, {start_line: $1.to_i - 1, end_line: $2.to_i}]
|
|
418
|
+
when nil
|
|
419
|
+
@tc << [:show, :list]
|
|
420
|
+
else
|
|
421
|
+
@ui.puts "Can not handle list argument: #{arg}"
|
|
422
|
+
return :retry
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# * `edit`
|
|
426
|
+
# * Open the current file on the editor (use `EDITOR` environment variable).
|
|
427
|
+
# * Note that edited file will not be reloaded.
|
|
428
|
+
# * `edit <file>`
|
|
429
|
+
# * Open <file> on the editor.
|
|
430
|
+
when 'edit'
|
|
431
|
+
if @ui.remote?
|
|
432
|
+
@ui.puts "not supported on the remote console."
|
|
433
|
+
return :retry
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
begin
|
|
437
|
+
arg = resolve_path(arg) if arg
|
|
438
|
+
rescue Errno::ENOENT
|
|
439
|
+
@ui.puts "not found: #{arg}"
|
|
440
|
+
return :retry
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
@tc << [:show, :edit, arg]
|
|
444
|
+
|
|
445
|
+
# * `i[nfo]`, `i[nfo] l[ocal[s]]`
|
|
446
|
+
# * Show information about the current frame (local variables)
|
|
447
|
+
# * It includes `self` as `%self` and a return value as `%return`.
|
|
448
|
+
# * `i[nfo] th[read[s]]`
|
|
449
|
+
# * Show all threads (same as `th[read]`).
|
|
450
|
+
when 'i', 'info'
|
|
233
451
|
case arg
|
|
234
|
-
when
|
|
235
|
-
@tc << [:show, :
|
|
236
|
-
when '
|
|
237
|
-
@tc << [:show, :
|
|
452
|
+
when nil
|
|
453
|
+
@tc << [:show, :local]
|
|
454
|
+
when 'l', /locals?/
|
|
455
|
+
@tc << [:show, :local]
|
|
456
|
+
when 'th', /threads?/
|
|
457
|
+
thread_list
|
|
458
|
+
return :retry
|
|
238
459
|
else
|
|
239
|
-
|
|
460
|
+
show_help 'info'
|
|
240
461
|
return :retry
|
|
241
462
|
end
|
|
463
|
+
|
|
464
|
+
# * `display`
|
|
465
|
+
# * Show display setting.
|
|
466
|
+
# * `display <expr>`
|
|
467
|
+
# * Show the result of `<expr>` at every suspended timing.
|
|
242
468
|
when 'display'
|
|
243
|
-
|
|
244
|
-
|
|
469
|
+
if arg && !arg.empty?
|
|
470
|
+
@displays << arg
|
|
471
|
+
@tc << [:eval, :try_display, @displays]
|
|
472
|
+
else
|
|
473
|
+
@tc << [:eval, :display, @displays]
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
# * `undisplay`
|
|
477
|
+
# * Remove all display settings.
|
|
478
|
+
# * `undisplay <displaynum>`
|
|
479
|
+
# * Remove a specified display setting.
|
|
245
480
|
when 'undisplay'
|
|
246
481
|
case arg
|
|
247
482
|
when /(\d+)/
|
|
@@ -258,26 +493,79 @@ module DEBUGGER__
|
|
|
258
493
|
end
|
|
259
494
|
return :retry
|
|
260
495
|
|
|
261
|
-
# trace
|
|
496
|
+
# * `trace [on|off]`
|
|
497
|
+
# * enable or disable line tracer.
|
|
262
498
|
when 'trace'
|
|
263
499
|
case arg
|
|
264
500
|
when 'on'
|
|
265
|
-
|
|
266
|
-
|
|
501
|
+
dir = __dir__
|
|
502
|
+
@tracer ||= TracePoint.new(:call, :return, :b_call, :b_return, :line, :class, :end){|tp|
|
|
503
|
+
next if File.dirname(tp.path) == dir
|
|
267
504
|
next if tp.path == '<internal:trace_point>'
|
|
505
|
+
# Skip when `JSON.generate` is called during tests
|
|
506
|
+
next if tp.binding.eval('self').to_s == 'JSON' and ENV['RUBY_DEBUG_TEST_MODE']
|
|
268
507
|
# next if tp.event != :line
|
|
269
508
|
@ui.puts pretty_tp(tp)
|
|
270
509
|
}
|
|
271
510
|
@tracer.enable
|
|
272
511
|
when 'off'
|
|
273
512
|
@tracer && @tracer.disable
|
|
274
|
-
else
|
|
275
|
-
enabled = (@tracer && @tracer.enabled?) ? true : false
|
|
276
|
-
@ui.puts "Trace #{enabled ? 'on' : 'off'}"
|
|
277
513
|
end
|
|
514
|
+
enabled = (@tracer && @tracer.enabled?) ? true : false
|
|
515
|
+
@ui.puts "Trace #{enabled ? 'on' : 'off'}"
|
|
278
516
|
return :retry
|
|
279
517
|
|
|
280
|
-
|
|
518
|
+
### Frame control
|
|
519
|
+
|
|
520
|
+
# * `f[rame]`
|
|
521
|
+
# * Show the current frame.
|
|
522
|
+
# * `f[rame] <framenum>`
|
|
523
|
+
# * Specify a current frame. Evaluation are run on specified frame.
|
|
524
|
+
when 'frame', 'f'
|
|
525
|
+
@tc << [:frame, :set, arg]
|
|
526
|
+
|
|
527
|
+
# * `up`
|
|
528
|
+
# * Specify the upper frame.
|
|
529
|
+
when 'up'
|
|
530
|
+
@tc << [:frame, :up]
|
|
531
|
+
|
|
532
|
+
# * `down`
|
|
533
|
+
# * Specify the lower frame.
|
|
534
|
+
when 'down'
|
|
535
|
+
@tc << [:frame, :down]
|
|
536
|
+
|
|
537
|
+
### Evaluate
|
|
538
|
+
|
|
539
|
+
# * `p <expr>`
|
|
540
|
+
# * Evaluate like `p <expr>` on the current frame.
|
|
541
|
+
when 'p'
|
|
542
|
+
@tc << [:eval, :p, arg.to_s]
|
|
543
|
+
|
|
544
|
+
# * `pp <expr>`
|
|
545
|
+
# * Evaluate like `pp <expr>` on the current frame.
|
|
546
|
+
when 'pp'
|
|
547
|
+
@tc << [:eval, :pp, arg.to_s]
|
|
548
|
+
|
|
549
|
+
# * `e[val] <expr>`
|
|
550
|
+
# * Evaluate `<expr>` on the current frame.
|
|
551
|
+
when 'e', 'eval', 'call'
|
|
552
|
+
@tc << [:eval, :call, arg]
|
|
553
|
+
|
|
554
|
+
# * `irb`
|
|
555
|
+
# * Invoke `irb` on the current frame.
|
|
556
|
+
when 'irb'
|
|
557
|
+
if @ui.remote?
|
|
558
|
+
@ui.puts "not supported on the remote console."
|
|
559
|
+
return :retry
|
|
560
|
+
end
|
|
561
|
+
@tc << [:eval, :call, 'binding.irb']
|
|
562
|
+
|
|
563
|
+
### Thread control
|
|
564
|
+
|
|
565
|
+
# * `th[read]`
|
|
566
|
+
# * Show all threads.
|
|
567
|
+
# * `th[read] <thnum>`
|
|
568
|
+
# * Switch thread specified by `<thnum>`.
|
|
281
569
|
when 'th', 'thread'
|
|
282
570
|
case arg
|
|
283
571
|
when nil, 'list', 'l'
|
|
@@ -289,6 +577,21 @@ module DEBUGGER__
|
|
|
289
577
|
end
|
|
290
578
|
return :retry
|
|
291
579
|
|
|
580
|
+
### Help
|
|
581
|
+
|
|
582
|
+
# * `h[elp]`
|
|
583
|
+
# * Show help for all commands.
|
|
584
|
+
# * `h[elp] <command>`
|
|
585
|
+
# * Show help for the given command.
|
|
586
|
+
when 'h', 'help'
|
|
587
|
+
if arg
|
|
588
|
+
show_help arg
|
|
589
|
+
else
|
|
590
|
+
@ui.puts DEBUGGER__.help
|
|
591
|
+
end
|
|
592
|
+
return :retry
|
|
593
|
+
|
|
594
|
+
### END
|
|
292
595
|
else
|
|
293
596
|
@ui.puts "unknown command: #{line}"
|
|
294
597
|
@repl_prev_line = nil
|
|
@@ -305,6 +608,18 @@ module DEBUGGER__
|
|
|
305
608
|
return :retry
|
|
306
609
|
end
|
|
307
610
|
|
|
611
|
+
def show_help arg
|
|
612
|
+
DEBUGGER__.helps.each{|cat, cs|
|
|
613
|
+
cs.each{|ws, desc|
|
|
614
|
+
if ws.include? arg
|
|
615
|
+
@ui.puts desc
|
|
616
|
+
return
|
|
617
|
+
end
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
@ui.puts "not found: #{arg}"
|
|
621
|
+
end
|
|
622
|
+
|
|
308
623
|
def ask msg, default = 'Y'
|
|
309
624
|
opts = '[y/n]'.tr(default.downcase, default)
|
|
310
625
|
input = @ui.ask("#{msg} #{opts} ")
|
|
@@ -363,35 +678,84 @@ module DEBUGGER__
|
|
|
363
678
|
exit!
|
|
364
679
|
end
|
|
365
680
|
|
|
366
|
-
def
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
681
|
+
def iterate_bps
|
|
682
|
+
deleted_bps = []
|
|
683
|
+
i = 0
|
|
684
|
+
@bps.each{|key, bp|
|
|
685
|
+
if !bp.deleted?
|
|
686
|
+
yield key, bp, i
|
|
687
|
+
i += 1
|
|
688
|
+
else
|
|
689
|
+
deleted_bps << bp
|
|
370
690
|
end
|
|
371
691
|
}
|
|
692
|
+
ensure
|
|
693
|
+
deleted_bps.each{|bp| @bps.delete bp}
|
|
372
694
|
end
|
|
373
695
|
|
|
374
|
-
def
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
696
|
+
def show_bps specific_bp = nil
|
|
697
|
+
iterate_bps do |key, bp, i|
|
|
698
|
+
@ui.puts "#%d %s" % [i, bp.to_s] if !specific_bp || bp == specific_bp
|
|
699
|
+
end
|
|
700
|
+
end
|
|
379
701
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
702
|
+
def bp_index specific_bp_key
|
|
703
|
+
iterate_bps do |key, bp, i|
|
|
704
|
+
if key == specific_bp_key
|
|
705
|
+
return [bp, i]
|
|
706
|
+
end
|
|
385
707
|
end
|
|
708
|
+
nil
|
|
386
709
|
end
|
|
387
710
|
|
|
388
|
-
def
|
|
389
|
-
|
|
390
|
-
|
|
711
|
+
def delete_breakpoint arg = nil
|
|
712
|
+
case arg
|
|
713
|
+
when nil
|
|
714
|
+
@bps.each{|key, bp| bp.delete}
|
|
715
|
+
@bps.clear
|
|
716
|
+
else
|
|
717
|
+
del_bp = nil
|
|
718
|
+
iterate_bps{|key, bp, i| del_bp = bp if i == arg}
|
|
719
|
+
if del_bp
|
|
720
|
+
del_bp.delete
|
|
721
|
+
@bps.delete del_bp.key
|
|
722
|
+
return [arg, del_bp]
|
|
723
|
+
end
|
|
391
724
|
end
|
|
392
|
-
thread_list
|
|
393
725
|
end
|
|
394
726
|
|
|
727
|
+
def repl_add_breakpoint arg
|
|
728
|
+
arg.strip!
|
|
729
|
+
|
|
730
|
+
case arg
|
|
731
|
+
when /\Aif\s+(.+)\z/
|
|
732
|
+
cond = $1
|
|
733
|
+
when /(.+?)\s+if\s+(.+)\z/
|
|
734
|
+
sig = $1
|
|
735
|
+
cond = $2
|
|
736
|
+
else
|
|
737
|
+
sig = arg
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
case sig
|
|
741
|
+
when /\A(\d+)\z/
|
|
742
|
+
add_line_breakpoint @tc.location.path, $1.to_i, cond: cond
|
|
743
|
+
when /\A(.+)[:\s+](\d+)\z/
|
|
744
|
+
add_line_breakpoint $1, $2.to_i, cond: cond
|
|
745
|
+
when /\A(.+)([\.\#])(.+)\z/
|
|
746
|
+
@tc << [:breakpoint, :method, $1, $2, $3, cond]
|
|
747
|
+
return :noretry
|
|
748
|
+
when nil
|
|
749
|
+
add_check_breakpoint cond
|
|
750
|
+
else
|
|
751
|
+
@ui.puts "Unknown breakpoint format: #{arg}"
|
|
752
|
+
@ui.puts
|
|
753
|
+
show_help 'b'
|
|
754
|
+
end
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
# threads
|
|
758
|
+
|
|
395
759
|
def update_thread_list
|
|
396
760
|
list = Thread.list
|
|
397
761
|
thcs = []
|
|
@@ -401,66 +765,77 @@ module DEBUGGER__
|
|
|
401
765
|
case
|
|
402
766
|
when th == Thread.current
|
|
403
767
|
# ignore
|
|
768
|
+
when @management_threads.include?(th)
|
|
769
|
+
# ignore
|
|
404
770
|
when @th_clients.has_key?(th)
|
|
405
771
|
thcs << @th_clients[th]
|
|
406
772
|
else
|
|
407
773
|
unmanaged << th
|
|
408
774
|
end
|
|
409
775
|
}
|
|
410
|
-
return thcs, unmanaged
|
|
776
|
+
return thcs.sort_by{|thc| thc.id}, unmanaged
|
|
411
777
|
end
|
|
412
778
|
|
|
413
|
-
def
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
@
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
779
|
+
def thread_list
|
|
780
|
+
thcs, unmanaged_ths = update_thread_list
|
|
781
|
+
thcs.each_with_index{|thc, i|
|
|
782
|
+
@ui.puts "#{@tc == thc ? "--> " : " "}\##{i} #{thc}"
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if !unmanaged_ths.empty?
|
|
786
|
+
@ui.puts "The following threads are not managed yet by the debugger:"
|
|
787
|
+
unmanaged_ths.each{|th|
|
|
788
|
+
@ui.puts " " + th.to_s
|
|
789
|
+
}
|
|
424
790
|
end
|
|
425
791
|
end
|
|
426
792
|
|
|
427
|
-
def
|
|
428
|
-
|
|
793
|
+
def managed_thread_clients
|
|
794
|
+
thcs, _unmanaged_ths = update_thread_list
|
|
795
|
+
thcs
|
|
796
|
+
end
|
|
429
797
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
cond = $2
|
|
433
|
-
else
|
|
434
|
-
sig = arg
|
|
435
|
-
end
|
|
798
|
+
def thread_switch n
|
|
799
|
+
thcs, _unmanaged_ths = update_thread_list
|
|
436
800
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
add_method_breakpoint arg, cond
|
|
444
|
-
else
|
|
445
|
-
raise "unknown breakpoint format: #{arg}"
|
|
801
|
+
if tc = thcs[n]
|
|
802
|
+
if tc.mode
|
|
803
|
+
@tc = tc
|
|
804
|
+
else
|
|
805
|
+
@ui.puts "#{tc.thread} is not controllable yet."
|
|
806
|
+
end
|
|
446
807
|
end
|
|
808
|
+
thread_list
|
|
447
809
|
end
|
|
448
810
|
|
|
449
|
-
def
|
|
450
|
-
@
|
|
811
|
+
def thread_client_create th
|
|
812
|
+
@th_clients[th] = ThreadClient.new((@tc_id += 1), @q_evt, Queue.new, th)
|
|
451
813
|
end
|
|
452
814
|
|
|
453
815
|
def setup_threads
|
|
454
816
|
stop_all_threads do
|
|
455
817
|
Thread.list.each{|th|
|
|
456
|
-
|
|
818
|
+
thread_client_create(th)
|
|
457
819
|
}
|
|
458
820
|
end
|
|
459
821
|
end
|
|
460
822
|
|
|
823
|
+
def on_thread_begin th
|
|
824
|
+
if @th_clients.has_key? th
|
|
825
|
+
# OK
|
|
826
|
+
else
|
|
827
|
+
# TODO: NG?
|
|
828
|
+
thread_client_create th
|
|
829
|
+
end
|
|
830
|
+
end
|
|
831
|
+
|
|
461
832
|
def thread_client
|
|
462
833
|
thr = Thread.current
|
|
463
|
-
@th_clients
|
|
834
|
+
if @th_clients.has_key? thr
|
|
835
|
+
@th_clients[thr]
|
|
836
|
+
else
|
|
837
|
+
@th_clients[thr] = thread_client_create(thr)
|
|
838
|
+
end
|
|
464
839
|
end
|
|
465
840
|
|
|
466
841
|
def stop_all_threads
|
|
@@ -492,120 +867,331 @@ module DEBUGGER__
|
|
|
492
867
|
end
|
|
493
868
|
end
|
|
494
869
|
|
|
495
|
-
## event
|
|
870
|
+
## event
|
|
496
871
|
|
|
497
872
|
def on_load iseq, src
|
|
498
873
|
@sr.add iseq, src
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
874
|
+
|
|
875
|
+
pending_line_breakpoints do |bp|
|
|
876
|
+
if bp.path == (iseq.absolute_path || iseq.path)
|
|
877
|
+
bp.try_activate
|
|
502
878
|
end
|
|
879
|
+
end
|
|
880
|
+
end
|
|
881
|
+
|
|
882
|
+
# breakpoint management
|
|
883
|
+
|
|
884
|
+
def add_breakpoint bp
|
|
885
|
+
if @bps.has_key? bp.key
|
|
886
|
+
@ui.puts "duplicated breakpoint: #{bp}"
|
|
887
|
+
else
|
|
888
|
+
@bps[bp.key] = bp
|
|
889
|
+
end
|
|
890
|
+
end
|
|
891
|
+
|
|
892
|
+
def rehash_bps
|
|
893
|
+
bps = @bps.values
|
|
894
|
+
@bps.clear
|
|
895
|
+
bps.each{|bp|
|
|
896
|
+
add_breakpoint bp
|
|
503
897
|
}
|
|
504
898
|
end
|
|
505
899
|
|
|
506
|
-
|
|
900
|
+
def break? file, line
|
|
901
|
+
@bps.has_key? [file, line]
|
|
902
|
+
end
|
|
507
903
|
|
|
508
904
|
def add_catch_breakpoint arg
|
|
509
905
|
bp = CatchBreakpoint.new(arg)
|
|
510
|
-
|
|
511
|
-
bp
|
|
512
|
-
end
|
|
513
|
-
|
|
514
|
-
def add_line_breakpoint_exact iseq, events, file, line, cond
|
|
515
|
-
if @bps[[file, line]]
|
|
516
|
-
return nil # duplicated
|
|
517
|
-
end
|
|
518
|
-
|
|
519
|
-
bp = case
|
|
520
|
-
when events.include?(:RUBY_EVENT_CALL)
|
|
521
|
-
# "def foo" line set bp on the beggining of method foo
|
|
522
|
-
LineBreakpoint.new(:call, iseq, line, cond)
|
|
523
|
-
when events.include?(:RUBY_EVENT_LINE)
|
|
524
|
-
LineBreakpoint.new(:line, iseq, line, cond)
|
|
525
|
-
when events.include?(:RUBY_EVENT_RETURN)
|
|
526
|
-
LineBreakpoint.new(:return, iseq, line, cond)
|
|
527
|
-
when events.include?(:RUBY_EVENT_B_RETURN)
|
|
528
|
-
LineBreakpoint.new(:b_return, iseq, line, cond)
|
|
529
|
-
when events.include?(:RUBY_EVENT_END)
|
|
530
|
-
LineBreakpoint.new(:end, iseq, line, cond)
|
|
531
|
-
else
|
|
532
|
-
nil
|
|
533
|
-
end
|
|
534
|
-
@bps[bp.key] = bp if bp
|
|
906
|
+
add_breakpoint bp
|
|
535
907
|
end
|
|
536
908
|
|
|
537
|
-
|
|
909
|
+
def add_check_breakpoint expr
|
|
910
|
+
bp = CheckBreakpoint.new(expr)
|
|
911
|
+
add_breakpoint bp
|
|
912
|
+
end
|
|
538
913
|
|
|
539
|
-
def
|
|
540
|
-
|
|
914
|
+
def resolve_path file
|
|
915
|
+
File.realpath(File.expand_path(file))
|
|
916
|
+
rescue Errno::ENOENT
|
|
917
|
+
case file
|
|
918
|
+
when '-e', '-'
|
|
919
|
+
return file
|
|
920
|
+
else
|
|
921
|
+
$LOAD_PATH.each do |lp|
|
|
922
|
+
libpath = File.join(lp, file)
|
|
923
|
+
return File.realpath(libpath)
|
|
924
|
+
rescue Errno::ENOENT
|
|
925
|
+
# next
|
|
926
|
+
end
|
|
927
|
+
end
|
|
541
928
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
929
|
+
raise
|
|
930
|
+
end
|
|
931
|
+
|
|
932
|
+
def add_line_breakpoint file, line, **kw
|
|
933
|
+
file = resolve_path(file)
|
|
934
|
+
bp = LineBreakpoint.new(file, line, **kw)
|
|
935
|
+
add_breakpoint bp
|
|
936
|
+
rescue Errno::ENOENT => e
|
|
937
|
+
@ui.puts e.message
|
|
938
|
+
end
|
|
546
939
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
940
|
+
def pending_line_breakpoints
|
|
941
|
+
@bps.find_all do |key, bp|
|
|
942
|
+
LineBreakpoint === bp && !bp.iseq
|
|
943
|
+
end.each do |key, bp|
|
|
944
|
+
yield bp
|
|
945
|
+
end
|
|
946
|
+
end
|
|
550
947
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
948
|
+
def method_added tp
|
|
949
|
+
b = tp.binding
|
|
950
|
+
if var_name = b.local_variables.first
|
|
951
|
+
mid = b.local_variable_get(var_name)
|
|
952
|
+
unresolved = false
|
|
953
|
+
|
|
954
|
+
@bps.each{|k, bp|
|
|
955
|
+
case bp
|
|
956
|
+
when MethodBreakpoint
|
|
957
|
+
if bp.method.nil?
|
|
958
|
+
if bp.sig_method_name == mid.to_s
|
|
959
|
+
bp.try_enable(quiet: true)
|
|
559
960
|
end
|
|
560
961
|
end
|
|
962
|
+
|
|
963
|
+
unresolved = true unless bp.enabled?
|
|
561
964
|
end
|
|
965
|
+
}
|
|
966
|
+
unless unresolved
|
|
967
|
+
METHOD_ADDED_TRACKER.disable
|
|
562
968
|
end
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
if nearest
|
|
566
|
-
add_line_breakpoint_exact nearest.iseq, nearest.events, file, nearest.line, cond
|
|
567
|
-
else
|
|
568
|
-
return nil
|
|
569
969
|
end
|
|
570
970
|
end
|
|
571
971
|
|
|
572
|
-
def
|
|
573
|
-
|
|
574
|
-
rescue Errno::ENOENT
|
|
575
|
-
file
|
|
972
|
+
def width
|
|
973
|
+
@ui.width
|
|
576
974
|
end
|
|
577
975
|
|
|
578
|
-
def
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
976
|
+
def check_forked
|
|
977
|
+
unless @session_server.status
|
|
978
|
+
# TODO: Support it
|
|
979
|
+
raise 'DEBUGGER: stop at forked process is not supported yet.'
|
|
980
|
+
end
|
|
583
981
|
end
|
|
982
|
+
end
|
|
584
983
|
|
|
585
|
-
|
|
586
|
-
|
|
984
|
+
class UI_Base
|
|
985
|
+
def event type, *args
|
|
986
|
+
case type
|
|
987
|
+
when :suspend_bp
|
|
988
|
+
i, bp = *args
|
|
989
|
+
puts "\nStop by \##{i} #{bp}" if bp
|
|
990
|
+
when :suspend_trap
|
|
991
|
+
puts "\nStop by #{args.first}"
|
|
992
|
+
end
|
|
587
993
|
end
|
|
588
994
|
end
|
|
589
995
|
|
|
590
|
-
|
|
591
|
-
|
|
996
|
+
# manual configuration methods
|
|
997
|
+
|
|
998
|
+
def self.add_line_breakpoint file, line, **kw
|
|
999
|
+
::DEBUGGER__::SESSION.add_line_breakpoint file, line, **kw
|
|
592
1000
|
end
|
|
593
1001
|
|
|
594
1002
|
def self.add_catch_breakpoint pat
|
|
595
1003
|
::DEBUGGER__::SESSION.add_catch_breakpoint pat
|
|
596
1004
|
end
|
|
597
1005
|
|
|
1006
|
+
# String for requring location
|
|
1007
|
+
# nil for -r
|
|
1008
|
+
def self.require_location
|
|
1009
|
+
locs = caller_locations
|
|
1010
|
+
dir_prefix = /#{__dir__}/
|
|
1011
|
+
|
|
1012
|
+
locs.each do |loc|
|
|
1013
|
+
case loc.absolute_path
|
|
1014
|
+
when dir_prefix
|
|
1015
|
+
when %r{rubygems/core_ext/kernel_require\.rb}
|
|
1016
|
+
else
|
|
1017
|
+
return loc if loc.absolute_path
|
|
1018
|
+
end
|
|
1019
|
+
end
|
|
1020
|
+
nil
|
|
1021
|
+
end
|
|
1022
|
+
|
|
1023
|
+
# start methods
|
|
1024
|
+
|
|
1025
|
+
def self.console **kw
|
|
1026
|
+
set_config(kw)
|
|
1027
|
+
|
|
1028
|
+
require_relative 'console'
|
|
1029
|
+
|
|
1030
|
+
initialize_session UI_Console.new
|
|
1031
|
+
|
|
1032
|
+
@prev_handler = trap(:SIGINT){
|
|
1033
|
+
ThreadClient.current.on_trap :SIGINT
|
|
1034
|
+
}
|
|
1035
|
+
end
|
|
1036
|
+
|
|
1037
|
+
def self.open host: nil, port: ::DEBUGGER__::CONFIG[:port], sock_path: nil, sock_dir: nil, **kw
|
|
1038
|
+
set_config(kw)
|
|
1039
|
+
|
|
1040
|
+
if port
|
|
1041
|
+
open_tcp host: host, port: port
|
|
1042
|
+
else
|
|
1043
|
+
open_unix sock_path: sock_path, sock_dir: sock_dir
|
|
1044
|
+
end
|
|
1045
|
+
end
|
|
1046
|
+
|
|
1047
|
+
def self.open_tcp host: nil, port:, **kw
|
|
1048
|
+
set_config(kw)
|
|
1049
|
+
require_relative 'server'
|
|
1050
|
+
initialize_session UI_TcpServer.new(host: host, port: port)
|
|
1051
|
+
end
|
|
1052
|
+
|
|
1053
|
+
def self.open_unix sock_path: nil, sock_dir: nil, **kw
|
|
1054
|
+
set_config(kw)
|
|
1055
|
+
require_relative 'server'
|
|
1056
|
+
initialize_session UI_UnixDomainServer.new(sock_dir: sock_dir, sock_path: sock_path)
|
|
1057
|
+
end
|
|
1058
|
+
|
|
1059
|
+
# boot utilities
|
|
1060
|
+
|
|
598
1061
|
class << self
|
|
599
1062
|
define_method :initialize_session do |ui|
|
|
600
1063
|
::DEBUGGER__.const_set(:SESSION, Session.new(ui))
|
|
601
1064
|
|
|
602
1065
|
# default breakpoints
|
|
603
|
-
|
|
1066
|
+
|
|
1067
|
+
# ::DEBUGGER__.add_catch_breakpoint 'RuntimeError'
|
|
604
1068
|
|
|
605
1069
|
Binding.module_eval do
|
|
606
|
-
|
|
607
|
-
|
|
1070
|
+
def bp command: nil
|
|
1071
|
+
if command
|
|
1072
|
+
cmds = command.split(";;")
|
|
1073
|
+
SESSION.add_initial_commands cmds
|
|
1074
|
+
end
|
|
1075
|
+
|
|
1076
|
+
::DEBUGGER__.add_line_breakpoint __FILE__, __LINE__ + 1, oneshot: true
|
|
1077
|
+
true
|
|
1078
|
+
end
|
|
1079
|
+
end
|
|
1080
|
+
|
|
1081
|
+
if !::DEBUGGER__::CONFIG[:nonstop]
|
|
1082
|
+
if loc = ::DEBUGGER__.require_location
|
|
1083
|
+
# require 'debug/console' or 'debug'
|
|
1084
|
+
add_line_breakpoint loc.absolute_path, loc.lineno + 1, oneshot: true, hook_call: false
|
|
1085
|
+
else
|
|
1086
|
+
# -r
|
|
1087
|
+
add_line_breakpoint $0, 1, oneshot: true, hook_call: false
|
|
1088
|
+
end
|
|
1089
|
+
end
|
|
1090
|
+
|
|
1091
|
+
load_rc
|
|
1092
|
+
end
|
|
1093
|
+
end
|
|
1094
|
+
|
|
1095
|
+
def self.load_rc
|
|
1096
|
+
['./rdbgrc.rb', File.expand_path('~/.rdbgrc.rb')].each{|path|
|
|
1097
|
+
if File.file? path
|
|
1098
|
+
load path
|
|
1099
|
+
end
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
# debug commands file
|
|
1103
|
+
[init_script = ::DEBUGGER__::CONFIG[:init_script],
|
|
1104
|
+
'./.rdbgrc',
|
|
1105
|
+
File.expand_path('~/.rdbgrc')].each{|path|
|
|
1106
|
+
next unless path
|
|
1107
|
+
|
|
1108
|
+
if File.file? path
|
|
1109
|
+
::DEBUGGER__::SESSION.add_initial_commands File.readlines(path)
|
|
1110
|
+
elsif path == init_script
|
|
1111
|
+
warn "Not found: #{path}"
|
|
1112
|
+
end
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
# given debug commands
|
|
1116
|
+
if ::DEBUGGER__::CONFIG[:commands]
|
|
1117
|
+
cmds = ::DEBUGGER__::CONFIG[:commands].split(';;')
|
|
1118
|
+
::DEBUGGER__::SESSION.add_initial_commands cmds
|
|
1119
|
+
end
|
|
1120
|
+
end
|
|
1121
|
+
|
|
1122
|
+
def self.parse_help
|
|
1123
|
+
helps = Hash.new{|h, k| h[k] = []}
|
|
1124
|
+
desc = cat = nil
|
|
1125
|
+
cmds = []
|
|
1126
|
+
|
|
1127
|
+
File.read(__FILE__).each_line do |line|
|
|
1128
|
+
case line
|
|
1129
|
+
when /\A\s*### (.+)/
|
|
1130
|
+
cat = $1
|
|
1131
|
+
break if $1 == 'END'
|
|
1132
|
+
when /\A when (.+)/
|
|
1133
|
+
next unless cat
|
|
1134
|
+
next unless desc
|
|
1135
|
+
ws = $1.split(/,\s*/).map{|e| e.gsub('\'', '')}
|
|
1136
|
+
helps[cat] << [ws, desc]
|
|
1137
|
+
desc = nil
|
|
1138
|
+
cmds.concat ws
|
|
1139
|
+
when /\A\s+# (\s*\*.+)/
|
|
1140
|
+
if desc
|
|
1141
|
+
desc << "\n" + $1
|
|
1142
|
+
else
|
|
1143
|
+
desc = $1
|
|
1144
|
+
end
|
|
608
1145
|
end
|
|
609
1146
|
end
|
|
1147
|
+
@commands = cmds
|
|
1148
|
+
@helps = helps
|
|
1149
|
+
end
|
|
1150
|
+
|
|
1151
|
+
def self.helps
|
|
1152
|
+
(defined?(@helps) && @helps) || parse_help
|
|
1153
|
+
end
|
|
1154
|
+
|
|
1155
|
+
def self.commands
|
|
1156
|
+
(defined?(@commands) && @commands) || (parse_help; @commands)
|
|
1157
|
+
end
|
|
1158
|
+
|
|
1159
|
+
def self.help
|
|
1160
|
+
r = []
|
|
1161
|
+
self.helps.each{|cat, cmds|
|
|
1162
|
+
r << "### #{cat}"
|
|
1163
|
+
r << ''
|
|
1164
|
+
cmds.each{|ws, desc|
|
|
1165
|
+
r << desc
|
|
1166
|
+
}
|
|
1167
|
+
r << ''
|
|
1168
|
+
}
|
|
1169
|
+
r.join("\n")
|
|
1170
|
+
end
|
|
1171
|
+
|
|
1172
|
+
class ::Module
|
|
1173
|
+
undef method_added
|
|
1174
|
+
def method_added mid; end
|
|
1175
|
+
def singleton_method_added mid; end
|
|
1176
|
+
end
|
|
1177
|
+
|
|
1178
|
+
def self.method_added tp
|
|
1179
|
+
begin
|
|
1180
|
+
SESSION.method_added tp
|
|
1181
|
+
rescue Exception => e
|
|
1182
|
+
p e
|
|
1183
|
+
end
|
|
1184
|
+
end
|
|
1185
|
+
|
|
1186
|
+
METHOD_ADDED_TRACKER = self.create_method_added_tracker
|
|
1187
|
+
|
|
1188
|
+
SHORT_INSPECT_LENGTH = 40
|
|
1189
|
+
def self.short_inspect obj, use_short = true
|
|
1190
|
+
str = obj.inspect
|
|
1191
|
+
if use_short && str.length > SHORT_INSPECT_LENGTH
|
|
1192
|
+
str[0...SHORT_INSPECT_LENGTH] + '...'
|
|
1193
|
+
else
|
|
1194
|
+
str
|
|
1195
|
+
end
|
|
610
1196
|
end
|
|
611
1197
|
end
|