debug 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5ef5033a78a878b42dd6850ee75552d58a0a0e02bfb890b3bf8970d9b5076387
4
+ data.tar.gz: a20c0455d990c8d74975ee9d67c327cfd9165f50a6ae32e6a69d79d6334bed56
5
+ SHA512:
6
+ metadata.gz: 8421e8a499c15e797ccced0f4f6ee2e315c9a18332db39beac30978fdcc87ac541c765fbf1c6e31b577020949421a928139dc81d3bf0ed0fcc97f7d7e2158487
7
+ data.tar.gz: bcab6869de9e3522bfa139a610ad5964121da2fd69678966fc0ab2c10d0b544d9692bbb2614e794b2df44ab7469d556fb86983be8dc6971c402c33ba816a6dc3
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in debug.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem "rake"
8
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
+ SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # Debug
2
+
3
+ This library provides debugging functionality to Ruby.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'debug'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install debug
20
+
21
+ ## Usage
22
+
23
+ To add a debugger to your code, start by requiring +debug+ in your
24
+ program:
25
+
26
+ ```ruby
27
+ def say(word)
28
+ require 'debug'
29
+ puts word
30
+ end
31
+ ```
32
+
33
+ This will cause Ruby to interrupt execution and show a prompt when the +say+
34
+ method is run.
35
+
36
+ Once you're inside the prompt, you can start debugging your program.
37
+
38
+ ```
39
+ (rdb:1) p word
40
+ "hello"
41
+ ```
42
+
43
+ ## Development
44
+
45
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
46
+
47
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
48
+
49
+ ## Contributing
50
+
51
+ Bug reports and pull requests are welcome on GitHub at https://github.com/hsbt/debug.
52
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "debug"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/debug.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "debug"
3
+ spec.version = "0.2.0"
4
+ spec.authors = ["Yukihiro Matsumoto"]
5
+ spec.email = ["matz@ruby-lang.org"]
6
+
7
+ spec.summary = %q{Debugging functionality for Ruby}
8
+ spec.description = %q{Debugging functionality for Ruby}
9
+ spec.homepage = "https://github.com/ruby/debug"
10
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
11
+ spec.licenses = ["Ruby", "BSD-2-Clause"]
12
+
13
+ spec.metadata["homepage_uri"] = spec.homepage
14
+ spec.metadata["source_code_uri"] = spec.homepage
15
+
16
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
17
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+ end
data/lib/debug.rb ADDED
@@ -0,0 +1,1111 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
3
+ # Copyright (C) 2000 Information-technology Promotion Agency, Japan
4
+ # Copyright (C) 2000-2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>
5
+
6
+ require 'continuation'
7
+
8
+ if $SAFE > 0
9
+ STDERR.print "-r debug.rb is not available in safe mode\n"
10
+ exit 1
11
+ end
12
+
13
+ require 'tracer'
14
+ require 'pp'
15
+
16
+ class Tracer # :nodoc:
17
+ def Tracer.trace_func(*vars)
18
+ Single.trace_func(*vars)
19
+ end
20
+ end
21
+
22
+ SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__ # :nodoc:
23
+
24
+ ##
25
+ # This library provides debugging functionality to Ruby.
26
+ #
27
+ # To add a debugger to your code, start by requiring +debug+ in your
28
+ # program:
29
+ #
30
+ # def say(word)
31
+ # require 'debug'
32
+ # puts word
33
+ # end
34
+ #
35
+ # This will cause Ruby to interrupt execution and show a prompt when the +say+
36
+ # method is run.
37
+ #
38
+ # Once you're inside the prompt, you can start debugging your program.
39
+ #
40
+ # (rdb:1) p word
41
+ # "hello"
42
+ #
43
+ # == Getting help
44
+ #
45
+ # You can get help at any time by pressing +h+.
46
+ #
47
+ # (rdb:1) h
48
+ # Debugger help v.-0.002b
49
+ # Commands
50
+ # b[reak] [file:|class:]<line|method>
51
+ # b[reak] [class.]<line|method>
52
+ # set breakpoint to some position
53
+ # wat[ch] <expression> set watchpoint to some expression
54
+ # cat[ch] (<exception>|off) set catchpoint to an exception
55
+ # b[reak] list breakpoints
56
+ # cat[ch] show catchpoint
57
+ # del[ete][ nnn] delete some or all breakpoints
58
+ # disp[lay] <expression> add expression into display expression list
59
+ # undisp[lay][ nnn] delete one particular or all display expressions
60
+ # c[ont] run until program ends or hit breakpoint
61
+ # s[tep][ nnn] step (into methods) one line or till line nnn
62
+ # n[ext][ nnn] go over one line or till line nnn
63
+ # w[here] display frames
64
+ # f[rame] alias for where
65
+ # l[ist][ (-|nn-mm)] list program, - lists backwards
66
+ # nn-mm lists given lines
67
+ # up[ nn] move to higher frame
68
+ # down[ nn] move to lower frame
69
+ # fin[ish] return to outer frame
70
+ # tr[ace] (on|off) set trace mode of current thread
71
+ # tr[ace] (on|off) all set trace mode of all threads
72
+ # q[uit] exit from debugger
73
+ # v[ar] g[lobal] show global variables
74
+ # v[ar] l[ocal] show local variables
75
+ # v[ar] i[nstance] <object> show instance variables of object
76
+ # v[ar] c[onst] <object> show constants of object
77
+ # m[ethod] i[nstance] <obj> show methods of object
78
+ # m[ethod] <class|module> show instance methods of class or module
79
+ # th[read] l[ist] list all threads
80
+ # th[read] c[ur[rent]] show current thread
81
+ # th[read] [sw[itch]] <nnn> switch thread context to nnn
82
+ # th[read] stop <nnn> stop thread nnn
83
+ # th[read] resume <nnn> resume thread nnn
84
+ # p expression evaluate expression and print its value
85
+ # h[elp] print this help
86
+ # <everything else> evaluate
87
+ #
88
+ # == Usage
89
+ #
90
+ # The following is a list of common functionalities that the debugger
91
+ # provides.
92
+ #
93
+ # === Navigating through your code
94
+ #
95
+ # In general, a debugger is used to find bugs in your program, which
96
+ # often means pausing execution and inspecting variables at some point
97
+ # in time.
98
+ #
99
+ # Let's look at an example:
100
+ #
101
+ # def my_method(foo)
102
+ # require 'debug'
103
+ # foo = get_foo if foo.nil?
104
+ # raise if foo.nil?
105
+ # end
106
+ #
107
+ # When you run this program, the debugger will kick in just before the
108
+ # +foo+ assignment.
109
+ #
110
+ # (rdb:1) p foo
111
+ # nil
112
+ #
113
+ # In this example, it'd be interesting to move to the next line and
114
+ # inspect the value of +foo+ again. You can do that by pressing +n+:
115
+ #
116
+ # (rdb:1) n # goes to next line
117
+ # (rdb:1) p foo
118
+ # nil
119
+ #
120
+ # You now know that the original value of +foo+ was nil, and that it
121
+ # still was nil after calling +get_foo+.
122
+ #
123
+ # Other useful commands for navigating through your code are:
124
+ #
125
+ # +c+::
126
+ # Runs the program until it either exists or encounters another breakpoint.
127
+ # You usually press +c+ when you are finished debugging your program and
128
+ # want to resume its execution.
129
+ # +s+::
130
+ # Steps into method definition. In the previous example, +s+ would take you
131
+ # inside the method definition of +get_foo+.
132
+ # +r+::
133
+ # Restart the program.
134
+ # +q+::
135
+ # Quit the program.
136
+ #
137
+ # === Inspecting variables
138
+ #
139
+ # You can use the debugger to easily inspect both local and global variables.
140
+ # We've seen how to inspect local variables before:
141
+ #
142
+ # (rdb:1) p my_arg
143
+ # 42
144
+ #
145
+ # You can also pretty print the result of variables or expressions:
146
+ #
147
+ # (rdb:1) pp %w{a very long long array containing many words}
148
+ # ["a",
149
+ # "very",
150
+ # "long",
151
+ # ...
152
+ # ]
153
+ #
154
+ # You can list all local variables with +v l+:
155
+ #
156
+ # (rdb:1) v l
157
+ # foo => "hello"
158
+ #
159
+ # Similarly, you can show all global variables with +v g+:
160
+ #
161
+ # (rdb:1) v g
162
+ # all global variables
163
+ #
164
+ # Finally, you can omit +p+ if you simply want to evaluate a variable or
165
+ # expression
166
+ #
167
+ # (rdb:1) 5**2
168
+ # 25
169
+ #
170
+ # === Going beyond basics
171
+ #
172
+ # Ruby Debug provides more advanced functionalities like switching
173
+ # between threads, setting breakpoints and watch expressions, and more.
174
+ # The full list of commands is available at any time by pressing +h+.
175
+ #
176
+ # == Staying out of trouble
177
+ #
178
+ # Make sure you remove every instance of +require 'debug'+ before
179
+ # shipping your code. Failing to do so may result in your program
180
+ # hanging unpredictably.
181
+ #
182
+ # Debug is not available in safe mode.
183
+
184
+ class DEBUGGER__
185
+ MUTEX = Thread::Mutex.new # :nodoc:
186
+
187
+ class Context # :nodoc:
188
+ DEBUG_LAST_CMD = []
189
+
190
+ begin
191
+ require 'readline'
192
+ def readline(prompt, hist)
193
+ Readline::readline(prompt, hist)
194
+ end
195
+ rescue LoadError
196
+ def readline(prompt, hist)
197
+ STDOUT.print prompt
198
+ STDOUT.flush
199
+ line = STDIN.gets
200
+ exit unless line
201
+ line.chomp!
202
+ line
203
+ end
204
+ USE_READLINE = false
205
+ end
206
+
207
+ def initialize
208
+ if Thread.current == Thread.main
209
+ @stop_next = 1
210
+ else
211
+ @stop_next = 0
212
+ end
213
+ @last_file = nil
214
+ @file = nil
215
+ @line = nil
216
+ @no_step = nil
217
+ @frames = []
218
+ @finish_pos = 0
219
+ @trace = false
220
+ @catch = "StandardError"
221
+ @suspend_next = false
222
+ end
223
+
224
+ def stop_next(n=1)
225
+ @stop_next = n
226
+ end
227
+
228
+ def set_suspend
229
+ @suspend_next = true
230
+ end
231
+
232
+ def clear_suspend
233
+ @suspend_next = false
234
+ end
235
+
236
+ def suspend_all
237
+ DEBUGGER__.suspend
238
+ end
239
+
240
+ def resume_all
241
+ DEBUGGER__.resume
242
+ end
243
+
244
+ def check_suspend
245
+ while MUTEX.synchronize {
246
+ if @suspend_next
247
+ DEBUGGER__.waiting.push Thread.current
248
+ @suspend_next = false
249
+ true
250
+ end
251
+ }
252
+ end
253
+ end
254
+
255
+ def trace?
256
+ @trace
257
+ end
258
+
259
+ def set_trace(arg)
260
+ @trace = arg
261
+ end
262
+
263
+ def stdout
264
+ DEBUGGER__.stdout
265
+ end
266
+
267
+ def break_points
268
+ DEBUGGER__.break_points
269
+ end
270
+
271
+ def display
272
+ DEBUGGER__.display
273
+ end
274
+
275
+ def context(th)
276
+ DEBUGGER__.context(th)
277
+ end
278
+
279
+ def set_trace_all(arg)
280
+ DEBUGGER__.set_trace(arg)
281
+ end
282
+
283
+ def set_last_thread(th)
284
+ DEBUGGER__.set_last_thread(th)
285
+ end
286
+
287
+ def debug_eval(str, binding)
288
+ begin
289
+ eval(str, binding)
290
+ rescue StandardError, ScriptError => e
291
+ at = eval("caller(1)", binding)
292
+ stdout.printf "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '')
293
+ for i in at
294
+ stdout.printf "\tfrom %s\n", i
295
+ end
296
+ throw :debug_error
297
+ end
298
+ end
299
+
300
+ def debug_silent_eval(str, binding)
301
+ begin
302
+ eval(str, binding)
303
+ rescue StandardError, ScriptError
304
+ nil
305
+ end
306
+ end
307
+
308
+ def var_list(ary, binding)
309
+ ary.sort!
310
+ for v in ary
311
+ stdout.printf " %s => %s\n", v, eval(v.to_s, binding).inspect
312
+ end
313
+ end
314
+
315
+ def debug_variable_info(input, binding)
316
+ case input
317
+ when /^\s*g(?:lobal)?\s*$/
318
+ var_list(global_variables, binding)
319
+
320
+ when /^\s*l(?:ocal)?\s*$/
321
+ var_list(eval("local_variables", binding), binding)
322
+
323
+ when /^\s*i(?:nstance)?\s+/
324
+ obj = debug_eval($', binding)
325
+ var_list(obj.instance_variables, obj.instance_eval{binding()})
326
+
327
+ when /^\s*c(?:onst(?:ant)?)?\s+/
328
+ obj = debug_eval($', binding)
329
+ unless obj.kind_of? Module
330
+ stdout.print "Should be Class/Module: ", $', "\n"
331
+ else
332
+ var_list(obj.constants, obj.module_eval{binding()})
333
+ end
334
+ end
335
+ end
336
+
337
+ def debug_method_info(input, binding)
338
+ case input
339
+ when /^i(:?nstance)?\s+/
340
+ obj = debug_eval($', binding)
341
+
342
+ len = 0
343
+ for v in obj.methods.sort
344
+ len += v.size + 1
345
+ if len > 70
346
+ len = v.size + 1
347
+ stdout.print "\n"
348
+ end
349
+ stdout.print v, " "
350
+ end
351
+ stdout.print "\n"
352
+
353
+ else
354
+ obj = debug_eval(input, binding)
355
+ unless obj.kind_of? Module
356
+ stdout.print "Should be Class/Module: ", input, "\n"
357
+ else
358
+ len = 0
359
+ for v in obj.instance_methods(false).sort
360
+ len += v.size + 1
361
+ if len > 70
362
+ len = v.size + 1
363
+ stdout.print "\n"
364
+ end
365
+ stdout.print v, " "
366
+ end
367
+ stdout.print "\n"
368
+ end
369
+ end
370
+ end
371
+
372
+ def thnum
373
+ num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
374
+ unless num
375
+ DEBUGGER__.make_thread_list
376
+ num = DEBUGGER__.instance_eval{@thread_list[Thread.current]}
377
+ end
378
+ num
379
+ end
380
+
381
+ def debug_command(file, line, id, binding)
382
+ MUTEX.lock
383
+ unless defined?($debugger_restart) and $debugger_restart
384
+ callcc{|c| $debugger_restart = c}
385
+ end
386
+ set_last_thread(Thread.current)
387
+ frame_pos = 0
388
+ binding_file = file
389
+ binding_line = line
390
+ previous_line = nil
391
+ if ENV['EMACS']
392
+ stdout.printf "\032\032%s:%d:\n", binding_file, binding_line
393
+ else
394
+ stdout.printf "%s:%d:%s", binding_file, binding_line,
395
+ line_at(binding_file, binding_line)
396
+ end
397
+ @frames[0] = [binding, file, line, id]
398
+ display_expressions(binding)
399
+ prompt = true
400
+ while prompt and input = readline("(rdb:%d) "%thnum(), true)
401
+ catch(:debug_error) do
402
+ if input == ""
403
+ next unless DEBUG_LAST_CMD[0]
404
+ input = DEBUG_LAST_CMD[0]
405
+ stdout.print input, "\n"
406
+ else
407
+ DEBUG_LAST_CMD[0] = input
408
+ end
409
+
410
+ case input
411
+ when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/
412
+ if defined?( $2 )
413
+ if $1 == 'on'
414
+ set_trace_all true
415
+ else
416
+ set_trace_all false
417
+ end
418
+ elsif defined?( $1 )
419
+ if $1 == 'on'
420
+ set_trace true
421
+ else
422
+ set_trace false
423
+ end
424
+ end
425
+ if trace?
426
+ stdout.print "Trace on.\n"
427
+ else
428
+ stdout.print "Trace off.\n"
429
+ end
430
+
431
+ when /^\s*b(?:reak)?\s+(?:(.+):)?([^.:]+)$/
432
+ pos = $2
433
+ if $1
434
+ klass = debug_silent_eval($1, binding)
435
+ file = $1
436
+ end
437
+ if pos =~ /^\d+$/
438
+ pname = pos
439
+ pos = pos.to_i
440
+ else
441
+ pname = pos = pos.intern.id2name
442
+ end
443
+ break_points.push [true, 0, klass || file, pos]
444
+ stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, klass || file, pname
445
+
446
+ when /^\s*b(?:reak)?\s+(.+)[#.]([^.:]+)$/
447
+ pos = $2.intern.id2name
448
+ klass = debug_eval($1, binding)
449
+ break_points.push [true, 0, klass, pos]
450
+ stdout.printf "Set breakpoint %d at %s.%s\n", break_points.size, klass, pos
451
+
452
+ when /^\s*wat(?:ch)?\s+(.+)$/
453
+ exp = $1
454
+ break_points.push [true, 1, exp]
455
+ stdout.printf "Set watchpoint %d:%s\n", break_points.size, exp
456
+
457
+ when /^\s*b(?:reak)?$/
458
+ if break_points.find{|b| b[1] == 0}
459
+ n = 1
460
+ stdout.print "Breakpoints:\n"
461
+ break_points.each do |b|
462
+ if b[0] and b[1] == 0
463
+ stdout.printf " %d %s:%s\n", n, b[2], b[3]
464
+ end
465
+ n += 1
466
+ end
467
+ end
468
+ if break_points.find{|b| b[1] == 1}
469
+ n = 1
470
+ stdout.print "\n"
471
+ stdout.print "Watchpoints:\n"
472
+ for b in break_points
473
+ if b[0] and b[1] == 1
474
+ stdout.printf " %d %s\n", n, b[2]
475
+ end
476
+ n += 1
477
+ end
478
+ end
479
+ if break_points.size == 0
480
+ stdout.print "No breakpoints\n"
481
+ else
482
+ stdout.print "\n"
483
+ end
484
+
485
+ when /^\s*del(?:ete)?(?:\s+(\d+))?$/
486
+ pos = $1
487
+ unless pos
488
+ input = readline("Clear all breakpoints? (y/n) ", false)
489
+ if input == "y"
490
+ for b in break_points
491
+ b[0] = false
492
+ end
493
+ end
494
+ else
495
+ pos = pos.to_i
496
+ if break_points[pos-1]
497
+ break_points[pos-1][0] = false
498
+ else
499
+ stdout.printf "Breakpoint %d is not defined\n", pos
500
+ end
501
+ end
502
+
503
+ when /^\s*disp(?:lay)?\s+(.+)$/
504
+ exp = $1
505
+ display.push [true, exp]
506
+ stdout.printf "%d: ", display.size
507
+ display_expression(exp, binding)
508
+
509
+ when /^\s*disp(?:lay)?$/
510
+ display_expressions(binding)
511
+
512
+ when /^\s*undisp(?:lay)?(?:\s+(\d+))?$/
513
+ pos = $1
514
+ unless pos
515
+ input = readline("Clear all expressions? (y/n) ", false)
516
+ if input == "y"
517
+ for d in display
518
+ d[0] = false
519
+ end
520
+ end
521
+ else
522
+ pos = pos.to_i
523
+ if display[pos-1]
524
+ display[pos-1][0] = false
525
+ else
526
+ stdout.printf "Display expression %d is not defined\n", pos
527
+ end
528
+ end
529
+
530
+ when /^\s*c(?:ont)?$/
531
+ prompt = false
532
+
533
+ when /^\s*s(?:tep)?(?:\s+(\d+))?$/
534
+ if $1
535
+ lev = $1.to_i
536
+ else
537
+ lev = 1
538
+ end
539
+ @stop_next = lev
540
+ prompt = false
541
+
542
+ when /^\s*n(?:ext)?(?:\s+(\d+))?$/
543
+ if $1
544
+ lev = $1.to_i
545
+ else
546
+ lev = 1
547
+ end
548
+ @stop_next = lev
549
+ @no_step = @frames.size - frame_pos
550
+ prompt = false
551
+
552
+ when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/
553
+ display_frames(frame_pos)
554
+
555
+ when /^\s*l(?:ist)?(?:\s+(.+))?$/
556
+ if not $1
557
+ b = previous_line ? previous_line + 10 : binding_line - 5
558
+ e = b + 9
559
+ elsif $1 == '-'
560
+ b = previous_line ? previous_line - 10 : binding_line - 5
561
+ e = b + 9
562
+ else
563
+ b, e = $1.split(/[-,]/)
564
+ if e
565
+ b = b.to_i
566
+ e = e.to_i
567
+ else
568
+ b = b.to_i - 5
569
+ e = b + 9
570
+ end
571
+ end
572
+ previous_line = b
573
+ display_list(b, e, binding_file, binding_line)
574
+
575
+ when /^\s*up(?:\s+(\d+))?$/
576
+ previous_line = nil
577
+ if $1
578
+ lev = $1.to_i
579
+ else
580
+ lev = 1
581
+ end
582
+ frame_pos += lev
583
+ if frame_pos >= @frames.size
584
+ frame_pos = @frames.size - 1
585
+ stdout.print "At toplevel\n"
586
+ end
587
+ binding, binding_file, binding_line = @frames[frame_pos]
588
+ stdout.print format_frame(frame_pos)
589
+
590
+ when /^\s*down(?:\s+(\d+))?$/
591
+ previous_line = nil
592
+ if $1
593
+ lev = $1.to_i
594
+ else
595
+ lev = 1
596
+ end
597
+ frame_pos -= lev
598
+ if frame_pos < 0
599
+ frame_pos = 0
600
+ stdout.print "At stack bottom\n"
601
+ end
602
+ binding, binding_file, binding_line = @frames[frame_pos]
603
+ stdout.print format_frame(frame_pos)
604
+
605
+ when /^\s*fin(?:ish)?$/
606
+ if frame_pos == @frames.size
607
+ stdout.print "\"finish\" not meaningful in the outermost frame.\n"
608
+ else
609
+ @finish_pos = @frames.size - frame_pos
610
+ frame_pos = 0
611
+ prompt = false
612
+ end
613
+
614
+ when /^\s*cat(?:ch)?(?:\s+(.+))?$/
615
+ if $1
616
+ excn = $1
617
+ if excn == 'off'
618
+ @catch = nil
619
+ stdout.print "Clear catchpoint.\n"
620
+ else
621
+ @catch = excn
622
+ stdout.printf "Set catchpoint %s.\n", @catch
623
+ end
624
+ else
625
+ if @catch
626
+ stdout.printf "Catchpoint %s.\n", @catch
627
+ else
628
+ stdout.print "No catchpoint.\n"
629
+ end
630
+ end
631
+
632
+ when /^\s*q(?:uit)?$/
633
+ input = readline("Really quit? (y/n) ", false)
634
+ if input == "y"
635
+ exit! # exit -> exit!: No graceful way to stop threads...
636
+ end
637
+
638
+ when /^\s*v(?:ar)?\s+/
639
+ debug_variable_info($', binding)
640
+
641
+ when /^\s*m(?:ethod)?\s+/
642
+ debug_method_info($', binding)
643
+
644
+ when /^\s*th(?:read)?\s+/
645
+ if DEBUGGER__.debug_thread_info($', binding) == :cont
646
+ prompt = false
647
+ end
648
+
649
+ when /^\s*pp\s+/
650
+ PP.pp(debug_eval($', binding), stdout)
651
+
652
+ when /^\s*p\s+/
653
+ stdout.printf "%s\n", debug_eval($', binding).inspect
654
+
655
+ when /^\s*r(?:estart)?$/
656
+ $debugger_restart.call
657
+
658
+ when /^\s*h(?:elp)?$/
659
+ debug_print_help()
660
+
661
+ else
662
+ v = debug_eval(input, binding)
663
+ stdout.printf "%s\n", v.inspect
664
+ end
665
+ end
666
+ end
667
+ MUTEX.unlock
668
+ resume_all
669
+ end
670
+
671
+ def debug_print_help
672
+ stdout.print <<EOHELP
673
+ Debugger help v.-0.002b
674
+ Commands
675
+ b[reak] [file:|class:]<line|method>
676
+ b[reak] [class.]<line|method>
677
+ set breakpoint to some position
678
+ wat[ch] <expression> set watchpoint to some expression
679
+ cat[ch] (<exception>|off) set catchpoint to an exception
680
+ b[reak] list breakpoints
681
+ cat[ch] show catchpoint
682
+ del[ete][ nnn] delete some or all breakpoints
683
+ disp[lay] <expression> add expression into display expression list
684
+ undisp[lay][ nnn] delete one particular or all display expressions
685
+ c[ont] run until program ends or hit breakpoint
686
+ s[tep][ nnn] step (into methods) one line or till line nnn
687
+ n[ext][ nnn] go over one line or till line nnn
688
+ w[here] display frames
689
+ f[rame] alias for where
690
+ l[ist][ (-|nn-mm)] list program, - lists backwards
691
+ nn-mm lists given lines
692
+ up[ nn] move to higher frame
693
+ down[ nn] move to lower frame
694
+ fin[ish] return to outer frame
695
+ tr[ace] (on|off) set trace mode of current thread
696
+ tr[ace] (on|off) all set trace mode of all threads
697
+ q[uit] exit from debugger
698
+ v[ar] g[lobal] show global variables
699
+ v[ar] l[ocal] show local variables
700
+ v[ar] i[nstance] <object> show instance variables of object
701
+ v[ar] c[onst] <object> show constants of object
702
+ m[ethod] i[nstance] <obj> show methods of object
703
+ m[ethod] <class|module> show instance methods of class or module
704
+ th[read] l[ist] list all threads
705
+ th[read] c[ur[rent]] show current thread
706
+ th[read] [sw[itch]] <nnn> switch thread context to nnn
707
+ th[read] stop <nnn> stop thread nnn
708
+ th[read] resume <nnn> resume thread nnn
709
+ pp expression evaluate expression and pretty_print its value
710
+ p expression evaluate expression and print its value
711
+ r[estart] restart program
712
+ h[elp] print this help
713
+ <everything else> evaluate
714
+ EOHELP
715
+ end
716
+
717
+ def display_expressions(binding)
718
+ n = 1
719
+ for d in display
720
+ if d[0]
721
+ stdout.printf "%d: ", n
722
+ display_expression(d[1], binding)
723
+ end
724
+ n += 1
725
+ end
726
+ end
727
+
728
+ def display_expression(exp, binding)
729
+ stdout.printf "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s
730
+ end
731
+
732
+ def frame_set_pos(file, line)
733
+ if @frames[0]
734
+ @frames[0][1] = file
735
+ @frames[0][2] = line
736
+ end
737
+ end
738
+
739
+ def display_frames(pos)
740
+ 0.upto(@frames.size - 1) do |n|
741
+ if n == pos
742
+ stdout.print "--> "
743
+ else
744
+ stdout.print " "
745
+ end
746
+ stdout.print format_frame(n)
747
+ end
748
+ end
749
+
750
+ def format_frame(pos)
751
+ _, file, line, id = @frames[pos]
752
+ sprintf "#%d %s:%s%s\n", pos + 1, file, line,
753
+ (id ? ":in `#{id.id2name}'" : "")
754
+ end
755
+
756
+ def script_lines(file, line)
757
+ unless (lines = SCRIPT_LINES__[file]) and lines != true
758
+ Tracer::Single.get_line(file, line) if File.exist?(file)
759
+ lines = SCRIPT_LINES__[file]
760
+ lines = nil if lines == true
761
+ end
762
+ lines
763
+ end
764
+
765
+ def display_list(b, e, file, line)
766
+ if lines = script_lines(file, line)
767
+ stdout.printf "[%d, %d] in %s\n", b, e, file
768
+ b.upto(e) do |n|
769
+ if n > 0 && lines[n-1]
770
+ if n == line
771
+ stdout.printf "=> %d %s\n", n, lines[n-1].chomp
772
+ else
773
+ stdout.printf " %d %s\n", n, lines[n-1].chomp
774
+ end
775
+ end
776
+ end
777
+ else
778
+ stdout.printf "No sourcefile available for %s\n", file
779
+ end
780
+ end
781
+
782
+ def line_at(file, line)
783
+ lines = script_lines(file, line)
784
+ if lines and line = lines[line-1]
785
+ return line
786
+ end
787
+ return "\n"
788
+ end
789
+
790
+ def debug_funcname(id)
791
+ if id.nil?
792
+ "toplevel"
793
+ else
794
+ id.id2name
795
+ end
796
+ end
797
+
798
+ def check_break_points(file, klass, pos, binding, id)
799
+ return false if break_points.empty?
800
+ n = 1
801
+ for b in break_points
802
+ if b[0] # valid
803
+ if b[1] == 0 # breakpoint
804
+ if (b[2] == file and b[3] == pos) or
805
+ (klass and b[2] == klass and b[3] == pos)
806
+ stdout.printf "Breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
807
+ return true
808
+ end
809
+ elsif b[1] == 1 # watchpoint
810
+ if debug_silent_eval(b[2], binding)
811
+ stdout.printf "Watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos
812
+ return true
813
+ end
814
+ end
815
+ end
816
+ n += 1
817
+ end
818
+ return false
819
+ end
820
+
821
+ def excn_handle(file, line, id, binding)
822
+ if $!.class <= SystemExit
823
+ set_trace_func nil
824
+ exit
825
+ end
826
+
827
+ if @catch and ($!.class.ancestors.find { |e| e.to_s == @catch })
828
+ stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.class
829
+ fs = @frames.size
830
+ tb = caller(0)[-fs..-1]
831
+ if tb
832
+ for i in tb
833
+ stdout.printf "\tfrom %s\n", i
834
+ end
835
+ end
836
+ suspend_all
837
+ debug_command(file, line, id, binding)
838
+ end
839
+ end
840
+
841
+ def trace_func(event, file, line, id, binding, klass)
842
+ Tracer.trace_func(event, file, line, id, binding, klass) if trace?
843
+ context(Thread.current).check_suspend
844
+ @file = file
845
+ @line = line
846
+ case event
847
+ when 'line'
848
+ frame_set_pos(file, line)
849
+ if !@no_step or @frames.size == @no_step
850
+ @stop_next -= 1
851
+ @stop_next = -1 if @stop_next < 0
852
+ elsif @frames.size < @no_step
853
+ @stop_next = 0 # break here before leaving...
854
+ else
855
+ # nothing to do. skipped.
856
+ end
857
+ if @stop_next == 0 or check_break_points(file, nil, line, binding, id)
858
+ @no_step = nil
859
+ suspend_all
860
+ debug_command(file, line, id, binding)
861
+ end
862
+
863
+ when 'call'
864
+ @frames.unshift [binding, file, line, id]
865
+ if check_break_points(file, klass, id.id2name, binding, id)
866
+ suspend_all
867
+ debug_command(file, line, id, binding)
868
+ end
869
+
870
+ when 'c-call'
871
+ frame_set_pos(file, line)
872
+
873
+ when 'class'
874
+ @frames.unshift [binding, file, line, id]
875
+
876
+ when 'return', 'end'
877
+ if @frames.size == @finish_pos
878
+ @stop_next = 1
879
+ @finish_pos = 0
880
+ end
881
+ @frames.shift
882
+
883
+ when 'raise'
884
+ excn_handle(file, line, id, binding)
885
+
886
+ end
887
+ @last_file = file
888
+ end
889
+ end
890
+
891
+ trap("INT") { DEBUGGER__.interrupt }
892
+ @last_thread = Thread::main
893
+ @max_thread = 1
894
+ @thread_list = {Thread::main => 1}
895
+ @break_points = []
896
+ @display = []
897
+ @waiting = []
898
+ @stdout = STDOUT
899
+
900
+ class << DEBUGGER__
901
+ # Returns the IO used as stdout. Defaults to STDOUT
902
+ def stdout
903
+ @stdout
904
+ end
905
+
906
+ # Sets the IO used as stdout. Defaults to STDOUT
907
+ def stdout=(s)
908
+ @stdout = s
909
+ end
910
+
911
+ # Returns the display expression list
912
+ #
913
+ # See DEBUGGER__ for more usage
914
+ def display
915
+ @display
916
+ end
917
+
918
+ # Returns the list of break points where execution will be stopped.
919
+ #
920
+ # See DEBUGGER__ for more usage
921
+ def break_points
922
+ @break_points
923
+ end
924
+
925
+ # Returns the list of waiting threads.
926
+ #
927
+ # When stepping through the traces of a function, thread gets suspended, to
928
+ # be resumed later.
929
+ def waiting
930
+ @waiting
931
+ end
932
+
933
+ def set_trace( arg )
934
+ MUTEX.synchronize do
935
+ make_thread_list
936
+ for th, in @thread_list
937
+ context(th).set_trace arg
938
+ end
939
+ end
940
+ arg
941
+ end
942
+
943
+ def set_last_thread(th)
944
+ @last_thread = th
945
+ end
946
+
947
+ def suspend
948
+ MUTEX.synchronize do
949
+ make_thread_list
950
+ for th, in @thread_list
951
+ next if th == Thread.current
952
+ context(th).set_suspend
953
+ end
954
+ end
955
+ # Schedule other threads to suspend as soon as possible.
956
+ Thread.pass
957
+ end
958
+
959
+ def resume
960
+ MUTEX.synchronize do
961
+ make_thread_list
962
+ @thread_list.each do |th,|
963
+ next if th == Thread.current
964
+ context(th).clear_suspend
965
+ end
966
+ waiting.each do |th|
967
+ th.run
968
+ end
969
+ waiting.clear
970
+ end
971
+ # Schedule other threads to restart as soon as possible.
972
+ Thread.pass
973
+ end
974
+
975
+ def context(thread=Thread.current)
976
+ c = thread[:__debugger_data__]
977
+ unless c
978
+ thread[:__debugger_data__] = c = Context.new
979
+ end
980
+ c
981
+ end
982
+
983
+ def interrupt
984
+ context(@last_thread).stop_next
985
+ end
986
+
987
+ def get_thread(num)
988
+ th = @thread_list.key(num)
989
+ unless th
990
+ @stdout.print "No thread ##{num}\n"
991
+ throw :debug_error
992
+ end
993
+ th
994
+ end
995
+
996
+ def thread_list(num)
997
+ th = get_thread(num)
998
+ if th == Thread.current
999
+ @stdout.print "+"
1000
+ else
1001
+ @stdout.print " "
1002
+ end
1003
+ @stdout.printf "%d ", num
1004
+ @stdout.print th.inspect, "\t"
1005
+ file = context(th).instance_eval{@file}
1006
+ if file
1007
+ @stdout.print file,":",context(th).instance_eval{@line}
1008
+ end
1009
+ @stdout.print "\n"
1010
+ end
1011
+
1012
+ # Prints all threads in @thread_list to @stdout. Returns a sorted array of
1013
+ # values from the @thread_list hash.
1014
+ #
1015
+ # While in the debugger you can list all of
1016
+ # the threads with: <b>DEBUGGER__.thread_list_all</b>
1017
+ #
1018
+ # (rdb:1) DEBUGGER__.thread_list_all
1019
+ # +1 #<Thread:0x007fb2320c03f0 run> debug_me.rb.rb:3
1020
+ # 2 #<Thread:0x007fb23218a538@debug_me.rb.rb:3 sleep>
1021
+ # 3 #<Thread:0x007fb23218b0f0@debug_me.rb.rb:3 sleep>
1022
+ # [1, 2, 3]
1023
+ #
1024
+ # Your current thread is indicated by a <b>+</b>
1025
+ #
1026
+ # Additionally you can list all threads with <b>th l</b>
1027
+ #
1028
+ # (rdb:1) th l
1029
+ # +1 #<Thread:0x007f99328c0410 run> debug_me.rb:3
1030
+ # 2 #<Thread:0x007f9932938230@debug_me.rb:3 sleep> debug_me.rb:3
1031
+ # 3 #<Thread:0x007f9932938e10@debug_me.rb:3 sleep> debug_me.rb:3
1032
+ #
1033
+ # See DEBUGGER__ for more usage.
1034
+
1035
+ def thread_list_all
1036
+ for th in @thread_list.values.sort
1037
+ thread_list(th)
1038
+ end
1039
+ end
1040
+
1041
+ def make_thread_list
1042
+ hash = {}
1043
+ for th in Thread::list
1044
+ if @thread_list.key? th
1045
+ hash[th] = @thread_list[th]
1046
+ else
1047
+ @max_thread += 1
1048
+ hash[th] = @max_thread
1049
+ end
1050
+ end
1051
+ @thread_list = hash
1052
+ end
1053
+
1054
+ def debug_thread_info(input, binding)
1055
+ case input
1056
+ when /^l(?:ist)?/
1057
+ make_thread_list
1058
+ thread_list_all
1059
+
1060
+ when /^c(?:ur(?:rent)?)?$/
1061
+ make_thread_list
1062
+ thread_list(@thread_list[Thread.current])
1063
+
1064
+ when /^(?:sw(?:itch)?\s+)?(\d+)/
1065
+ make_thread_list
1066
+ th = get_thread($1.to_i)
1067
+ if th == Thread.current
1068
+ @stdout.print "It's the current thread.\n"
1069
+ else
1070
+ thread_list(@thread_list[th])
1071
+ context(th).stop_next
1072
+ th.run
1073
+ return :cont
1074
+ end
1075
+
1076
+ when /^stop\s+(\d+)/
1077
+ make_thread_list
1078
+ th = get_thread($1.to_i)
1079
+ if th == Thread.current
1080
+ @stdout.print "It's the current thread.\n"
1081
+ elsif th.stop?
1082
+ @stdout.print "Already stopped.\n"
1083
+ else
1084
+ thread_list(@thread_list[th])
1085
+ context(th).suspend
1086
+ end
1087
+
1088
+ when /^resume\s+(\d+)/
1089
+ make_thread_list
1090
+ th = get_thread($1.to_i)
1091
+ if th == Thread.current
1092
+ @stdout.print "It's the current thread.\n"
1093
+ elsif !th.stop?
1094
+ @stdout.print "Already running."
1095
+ else
1096
+ thread_list(@thread_list[th])
1097
+ th.run
1098
+ end
1099
+ end
1100
+ end
1101
+ end
1102
+
1103
+ stdout.printf "Debug.rb\n"
1104
+ stdout.printf "Emacs support available.\n\n"
1105
+ RubyVM::InstructionSequence.compile_option = {
1106
+ trace_instruction: true
1107
+ }
1108
+ set_trace_func proc { |event, file, line, id, binding, klass, *rest|
1109
+ DEBUGGER__.context.trace_func event, file, line, id, binding, klass
1110
+ }
1111
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: debug
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Yukihiro Matsumoto
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-03-17 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Debugging functionality for Ruby
14
+ email:
15
+ - matz@ruby-lang.org
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".gitignore"
21
+ - Gemfile
22
+ - LICENSE.txt
23
+ - README.md
24
+ - Rakefile
25
+ - bin/console
26
+ - bin/setup
27
+ - debug.gemspec
28
+ - lib/debug.rb
29
+ homepage: https://github.com/ruby/debug
30
+ licenses:
31
+ - Ruby
32
+ - BSD-2-Clause
33
+ metadata:
34
+ homepage_uri: https://github.com/ruby/debug
35
+ source_code_uri: https://github.com/ruby/debug
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 2.3.0
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubygems_version: 3.2.14
52
+ signing_key:
53
+ specification_version: 4
54
+ summary: Debugging functionality for Ruby
55
+ test_files: []