trepanning 1.93.35 → 2.15.33
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 +7 -0
- data/ChangeLog +491 -55
- data/LICENSE +1 -1
- data/NEWS +18 -14
- data/README.md +5 -22
- data/Rakefile +22 -1
- data/app/breakpoint.rb +5 -3
- data/app/core.rb +147 -179
- data/app/default.rb +47 -46
- data/app/file.rb +6 -7
- data/app/frame.rb +183 -176
- data/app/markdown.rb +2 -9
- data/app/options.rb +1 -1
- data/app/run.rb +71 -37
- data/interface/script.rb +8 -8
- data/io.rb +19 -20
- data/lib/trepanning.rb +292 -297
- data/processor.rb +332 -344
- data/processor/breakpoint.rb +98 -96
- data/processor/command/base/submgr.rb +9 -9
- data/processor/command/break.rb +40 -38
- data/processor/command/continue.rb +15 -10
- data/processor/command/debug.rb +6 -25
- data/processor/command/delete.rb +21 -12
- data/processor/command/directory.rb +15 -13
- data/processor/command/disable.rb +12 -9
- data/processor/command/disassemble.rb +80 -74
- data/processor/command/display.rb +15 -12
- data/processor/command/down.rb +8 -3
- data/processor/command/edit.rb +37 -23
- data/processor/command/enable.rb +11 -8
- data/processor/command/eval.rb +24 -22
- data/processor/command/finish.rb +50 -48
- data/processor/command/help.rb +1 -1
- data/processor/command/info_subcmd/breakpoints.rb +7 -7
- data/processor/command/info_subcmd/files.rb +195 -196
- data/processor/command/info_subcmd/frame.rb +7 -4
- data/processor/command/info_subcmd/locals.rb +29 -12
- data/processor/command/info_subcmd/program.rb +48 -39
- data/processor/command/info_subcmd/registers_subcmd/ep.rb +46 -0
- data/processor/command/info_subcmd/registers_subcmd/helper.rb +32 -35
- data/processor/command/info_subcmd/registers_subcmd/sp.rb +29 -23
- data/processor/command/info_subcmd/return.rb +28 -10
- data/processor/command/info_subcmd/variables_subcmd/class.rb +3 -3
- data/processor/command/info_subcmd/variables_subcmd/constants.rb +77 -0
- data/processor/command/info_subcmd/variables_subcmd/globals.rb +7 -7
- data/processor/command/info_subcmd/variables_subcmd/instance.rb +68 -22
- data/processor/command/info_subcmd/variables_subcmd/locals.rb +148 -67
- data/processor/command/list.rb +14 -8
- data/processor/command/macro.rb +1 -1
- data/processor/command/next.rb +1 -0
- data/processor/command/set_subcmd/auto.rb +3 -3
- data/processor/command/set_subcmd/different.rb +30 -29
- data/processor/command/set_subcmd/events.rb +74 -48
- data/processor/command/set_subcmd/max_subcmd/list.rb +12 -5
- data/processor/command/set_subcmd/max_subcmd/width.rb +28 -19
- data/processor/command/set_subcmd/register.rb +37 -0
- data/processor/command/set_subcmd/register_subcmd/pc.rb +67 -0
- data/processor/command/set_subcmd/register_subcmd/sp.rb +75 -0
- data/processor/command/set_subcmd/reload.rb +12 -10
- data/processor/command/set_subcmd/return.rb +68 -44
- data/processor/command/shell.rb +3 -2
- data/processor/command/show_subcmd/different.rb +17 -14
- data/processor/command/show_subcmd/events.rb +25 -25
- data/processor/default.rb +1 -1
- data/processor/eval.rb +14 -15
- data/processor/frame.rb +43 -36
- data/processor/help.rb +5 -5
- data/processor/hook.rb +26 -29
- data/processor/location.rb +54 -51
- data/processor/mock.rb +4 -3
- data/processor/running.rb +113 -103
- data/processor/validate.rb +401 -373
- data/test/data/debug.cmd +8 -0
- data/test/data/debug.right +13 -0
- data/test/data/debugger-stop.right +6 -4
- data/test/data/fname-with-blank.cmd +1 -1
- data/test/data/fname-with-blank.right +5 -0
- data/test/data/pc.cmd +8 -0
- data/test/data/pc.right +10 -0
- data/test/data/quit.right +3 -1
- data/test/data/trace.cmd +2 -2
- data/test/data/trace.right +41 -20
- data/test/example/assign.rb +6 -0
- data/test/functional/fn_helper.rb +11 -17
- data/test/functional/test-break-long.rb +15 -16
- data/test/functional/test-break.rb +6 -8
- data/test/functional/test-condition.rb +8 -10
- data/test/functional/test-debugger-call-bug.rb +21 -22
- data/test/functional/test-delete.rb +57 -59
- data/test/functional/test-eval.rb +101 -103
- data/test/functional/test-finish.rb +24 -33
- data/test/functional/test-immediate-step-bug.rb +6 -10
- data/test/functional/test-next.rb +64 -65
- data/test/functional/test-raise.rb +63 -64
- data/test/functional/test-recursive-bt.rb +81 -76
- data/test/functional/test-remap.rb +6 -7
- data/test/functional/test-return.rb +44 -38
- data/test/functional/test-step.rb +55 -53
- data/test/functional/test-stepbug.rb +6 -9
- data/test/functional/test-watchg.rb +40 -39
- data/test/integration/test-debug.rb +12 -0
- data/test/integration/test-debugger-stop.rb +7 -7
- data/test/integration/test-pc.rb +24 -0
- data/test/integration/test-trace.rb +1 -1
- data/test/unit/cmd-helper.rb +0 -1
- data/test/unit/test-app-brkpt.rb +21 -21
- data/test/unit/test-app-brkptmgr.rb +7 -8
- data/test/unit/test-app-display.rb +3 -4
- data/test/unit/test-app-frame.rb +4 -5
- data/test/unit/test-base-subsubcmd.rb +2 -2
- data/test/unit/test-cmd-break.rb +6 -6
- data/test/unit/test-cmd-endisable.rb +7 -6
- data/test/unit/test-cmd-parse_list_cmd.rb +24 -24
- data/test/unit/test-io-tcpserver.rb +39 -35
- data/test/unit/test-proc-default.rb +23 -22
- data/test/unit/test-proc-eval.rb +1 -2
- data/test/unit/test-proc-frame.rb +8 -9
- data/test/unit/test-proc-list.rb +1 -1
- data/test/unit/test-proc-location.rb +2 -2
- data/test/unit/test-proc-main.rb +10 -10
- data/test/unit/test-proc-validate.rb +11 -13
- data/test/unit/test-subcmd-help.rb +1 -2
- data/trepanning.gemspec +8 -13
- metadata +44 -95
- data/COPYING +0 -57
- data/data/custom_require.rb +0 -44
- data/data/perldb.bindings +0 -17
- data/data/prelude.rb +0 -38
- data/processor/command/info_subcmd/variables_subcmd/constant.rb +0 -41
- data/processor/command/raise.rb +0 -48
- data/processor/command/set_subcmd/pc.rb +0 -62
- data/processor/command/set_subcmd/sp.rb +0 -67
- data/processor/eventbuf.rb +0 -133
data/app/markdown.rb
CHANGED
@@ -14,11 +14,6 @@ module Redcarpet
|
|
14
14
|
attr_accessor :width
|
15
15
|
attr_accessor :try_ansi
|
16
16
|
|
17
|
-
def strip_term_sequence(text)
|
18
|
-
# to be completed...
|
19
|
-
text
|
20
|
-
end
|
21
|
-
|
22
17
|
# reflow text so lines are not
|
23
18
|
# longer than @width. prefix
|
24
19
|
# is used only after the first line
|
@@ -29,7 +24,7 @@ module Redcarpet
|
|
29
24
|
line = ''
|
30
25
|
width = @width - prefix.size
|
31
26
|
text.split.each do |word|
|
32
|
-
word_size =
|
27
|
+
word_size = Term::ANSIColor::uncolor(word).size
|
33
28
|
if (line_len + word_size) >= width
|
34
29
|
lines << line
|
35
30
|
line = prefix + word + ' '
|
@@ -58,7 +53,7 @@ module Redcarpet
|
|
58
53
|
|
59
54
|
def codespan(text)
|
60
55
|
if ansi?
|
61
|
-
Term::ANSIColor.underline + text + Term::ANSIColor.reset
|
56
|
+
Term::ANSIColor.underline + text + Term::ANSIColor.reset + "\n"
|
62
57
|
else
|
63
58
|
"'" + text + "'"
|
64
59
|
end
|
@@ -162,8 +157,6 @@ if __FILE__ == $0
|
|
162
157
|
# HI
|
163
158
|
This is a paragraph
|
164
159
|
|
165
|
-
A `b`, `c`, d
|
166
|
-
|
167
160
|
**This** is another *paragraph*.
|
168
161
|
EOF
|
169
162
|
[[80, true],
|
data/app/options.rb
CHANGED
data/app/run.rb
CHANGED
@@ -1,9 +1,32 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
# Copyright (C) 2010-2013 Rocky Bernstein <rockyb@rubyforge.net>
|
2
|
+
# Copyright (C) 2010-2013, 2015 Rocky Bernstein <rockyb@rubyforge.net>
|
3
3
|
require 'rbconfig'
|
4
4
|
module Trepanning
|
5
5
|
|
6
|
-
|
6
|
+
class Termination < RuntimeError
|
7
|
+
end
|
8
|
+
|
9
|
+
module_function # All functions below are easily publically accessible
|
10
|
+
|
11
|
+
def run_program(dbgr, program_to_debug)
|
12
|
+
RubyVM::Frame::get.trace_off = false
|
13
|
+
RubyVM::Frame::get.trace_off1 = true
|
14
|
+
dbgr.core.processor.hidelevels[Thread.current] =
|
15
|
+
RubyVM::Frame.stack_size
|
16
|
+
dbgr.trace_point.enable
|
17
|
+
|
18
|
+
# FIXME: the magic skip count 4 below is to skip over
|
19
|
+
# the following calls triggered by Kernel::load
|
20
|
+
# c_call - IO#set_encoding(1)
|
21
|
+
# c_return - IO#set_encoding -> *debugged program*
|
22
|
+
# call - IO#set_encoding(1)
|
23
|
+
# c_return - IO#set_encoding -> *debugged program*
|
24
|
+
# This is not very robust. Figure out how to
|
25
|
+
# address this.
|
26
|
+
dbgr.core.step_count = 4
|
27
|
+
Kernel::load program_to_debug
|
28
|
+
end
|
29
|
+
|
7
30
|
|
8
31
|
# Given a Ruby interpreter and program we are to debug, debug it.
|
9
32
|
# The caller must ensure that ARGV is set up to remove any debugger
|
@@ -11,50 +34,61 @@ module Trepanning
|
|
11
34
|
# see. FIXME: Should we make ARGV an explicit parameter?
|
12
35
|
def debug_program(dbgr, program_to_debug)
|
13
36
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
37
|
+
# Make sure Ruby script syntax checks okay.
|
38
|
+
# Otherwise we get a load message that looks like trepanning has
|
39
|
+
# a problem.
|
40
|
+
output = ruby_syntax_errors(program_to_debug)
|
41
|
+
if output
|
42
|
+
puts output
|
43
|
+
exit $?.exitstatus
|
44
|
+
end
|
22
45
|
|
23
|
-
|
24
|
-
|
46
|
+
# dbgr.trace_filter << self.method(:debug_program)
|
47
|
+
# dbgr.trace_filter << Kernel.method(:load)
|
48
|
+
dbgr.trace_filter << 'debug_program'
|
49
|
+
dbgr.trace_filter << 'load'
|
25
50
|
|
26
|
-
|
51
|
+
old_dollar_0 = $0
|
27
52
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
53
|
+
# Without the dance below to set $0, setting it to a signifcantly
|
54
|
+
# longer value will truncate it in some OS's. See
|
55
|
+
# http://www.ruby-forum.com/topic/187083
|
56
|
+
$progname = program_to_debug
|
57
|
+
alias $0 $progname
|
58
|
+
dollar_0_tracker = lambda {|val| $program_name = val}
|
59
|
+
trace_var(:$0, dollar_0_tracker)
|
35
60
|
|
36
|
-
dbgr.debugger(:hide_stack=>true) do
|
37
|
-
dbgr.core.processor.hidelevels[Thread.current] =
|
38
|
-
RubyVM::Frame.current.stack_size + 1
|
39
61
|
begin
|
40
|
-
|
62
|
+
dbgr.start(false)
|
63
|
+
frame = RubyVM::Frame.get
|
64
|
+
while frame do
|
65
|
+
frame.trace_off = true
|
66
|
+
frame = frame.prev
|
67
|
+
end
|
68
|
+
run_program(dbgr, program_to_debug)
|
69
|
+
raise Termination
|
70
|
+
rescue Termination
|
71
|
+
if dbgr.settings[:cmdloop_on_exit]
|
72
|
+
dbgr.stop
|
73
|
+
puts "Program terminated, type q to quit"
|
74
|
+
dbgr.core.processor.process_commands(nil, 0)
|
75
|
+
end
|
41
76
|
rescue Interrupt
|
42
77
|
end
|
43
|
-
end
|
44
78
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
79
|
+
# The dance we have to undo to restore $0 and undo the mess created
|
80
|
+
# above.
|
81
|
+
$0 = old_dollar_0
|
82
|
+
untrace_var(:$0, dollar_0_tracker)
|
49
83
|
rescue
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
84
|
+
if dbgr.settings[:post_mortem]
|
85
|
+
frame = RubyVM::Frame.get
|
86
|
+
dbgr.core.step_count = 0 # Make event processor stop
|
87
|
+
dbgr.core.processor.settings[:debugstack] = 0 # Make event processor stop
|
88
|
+
dbgr.core.event_processor('post-mortem', frame, $!)
|
89
|
+
else
|
56
90
|
raise
|
57
|
-
|
91
|
+
end
|
58
92
|
end
|
59
93
|
|
60
94
|
# Do a shell-like path lookup for prog_script and return the results.
|
@@ -97,7 +131,7 @@ if __FILE__ == $0
|
|
97
131
|
puts whence_file('irb')
|
98
132
|
puts whence_file('probably-does-not-exist')
|
99
133
|
puts RbConfig.ruby
|
100
|
-
puts "#{__FILE__} is syntactically correct" unless
|
134
|
+
puts "#{__FILE__} is syntactically correct" unless
|
101
135
|
ruby_syntax_errors(__FILE__)
|
102
136
|
readme = File.join(File.dirname(__FILE__), '..', 'README.textile')
|
103
137
|
puts "#{readme} is not syntactically correct" if
|
data/interface/script.rb
CHANGED
@@ -16,7 +16,7 @@ class Trepan::ScriptInterface < Trepan::Interface
|
|
16
16
|
:confirm_val => false,
|
17
17
|
:verbose => false
|
18
18
|
} unless defined?(DEFAULT_OPTS)
|
19
|
-
|
19
|
+
|
20
20
|
def initialize(script_name, out=nil, opts={})
|
21
21
|
|
22
22
|
@opts = DEFAULT_OPTS.merge(opts)
|
@@ -24,7 +24,7 @@ class Trepan::ScriptInterface < Trepan::Interface
|
|
24
24
|
at_exit { finalize }
|
25
25
|
@script_name = script_name
|
26
26
|
@input_lineno = 0
|
27
|
-
@input = opts[:input] ||
|
27
|
+
@input = opts[:input] ||
|
28
28
|
Trepan::UserInput.open(script_name, :line_edit => false)
|
29
29
|
@buffer_output = []
|
30
30
|
unless @opts[:verbose] or out
|
@@ -35,7 +35,7 @@ class Trepan::ScriptInterface < Trepan::Interface
|
|
35
35
|
|
36
36
|
# Closes input only.
|
37
37
|
def close
|
38
|
-
@input.close
|
38
|
+
# @input.close unless @input.closed?
|
39
39
|
end
|
40
40
|
|
41
41
|
# Called when a dangerous action is about to be done, to make
|
@@ -48,7 +48,7 @@ class Trepan::ScriptInterface < Trepan::Interface
|
|
48
48
|
end
|
49
49
|
|
50
50
|
# Common routine for reporting debugger error messages.
|
51
|
-
#
|
51
|
+
#
|
52
52
|
def errmsg(msg, prefix="*** ")
|
53
53
|
# self.verbose shows lines so we don't have to duplicate info
|
54
54
|
# here. Perhaps there should be a 'terse' mode to never show
|
@@ -67,8 +67,8 @@ class Trepan::ScriptInterface < Trepan::Interface
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def interactive? ; false end
|
70
|
-
|
71
|
-
# Script interface to read a command. `prompt' is a parameter for
|
70
|
+
|
71
|
+
# Script interface to read a command. `prompt' is a parameter for
|
72
72
|
# compatibilty and is ignored.
|
73
73
|
def read_command(prompt='')
|
74
74
|
@input_lineno += 1
|
@@ -81,12 +81,12 @@ class Trepan::ScriptInterface < Trepan::Interface
|
|
81
81
|
return line
|
82
82
|
end
|
83
83
|
|
84
|
-
# Script interface to read a line. `prompt' is a parameter for
|
84
|
+
# Script interface to read a line. `prompt' is a parameter for
|
85
85
|
# compatibilty and is ignored.
|
86
86
|
#
|
87
87
|
# Could decide make this look for interactive input?
|
88
88
|
def readline(prompt='')
|
89
|
-
begin
|
89
|
+
begin
|
90
90
|
return input.readline().chomp
|
91
91
|
rescue EOFError
|
92
92
|
@eof = true
|
data/io.rb
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
# classes to support communication to and from the debugger. This
|
3
3
|
# communcation might be to/from another process or another computer.
|
4
4
|
# And reading may be from a debugger command script.
|
5
|
-
#
|
5
|
+
#
|
6
6
|
# For example, we'd like to support Sockets, and serial lines and file
|
7
7
|
# reading, as well a readline-type input. Encryption and Authentication
|
8
8
|
# methods might decorate some of the communication channels.
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# Some ideas originiated as part of Matt Fleming's 2006 Google Summer of
|
11
11
|
# Code project.
|
12
12
|
|
@@ -14,7 +14,7 @@ class Trepan
|
|
14
14
|
|
15
15
|
NotImplementedMessage = 'This method must be overriden in a subclass' unless
|
16
16
|
defined?(NotImplementedMessage)
|
17
|
-
|
17
|
+
|
18
18
|
class InputBase
|
19
19
|
attr_reader :input
|
20
20
|
attr_reader :line_edit
|
@@ -30,10 +30,10 @@ class Trepan
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def close
|
33
|
-
|
33
|
+
@input.close unless @input.closed?
|
34
34
|
end
|
35
35
|
|
36
|
-
def eof?
|
36
|
+
def eof?
|
37
37
|
begin
|
38
38
|
@input.eof?
|
39
39
|
rescue IOError
|
@@ -41,7 +41,7 @@ class Trepan
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
# Read a line of input. EOFError will be raised on EOF.
|
44
|
+
# Read a line of input. EOFError will be raised on EOF.
|
45
45
|
#
|
46
46
|
# Note that we don't support prompting first. Instead, arrange
|
47
47
|
# to call Trepan::Output.write() first with the prompt. If
|
@@ -69,7 +69,7 @@ class Trepan
|
|
69
69
|
@eof = true
|
70
70
|
end
|
71
71
|
|
72
|
-
def eof?
|
72
|
+
def eof?
|
73
73
|
@eof
|
74
74
|
end
|
75
75
|
|
@@ -77,7 +77,7 @@ class Trepan
|
|
77
77
|
@output.flush
|
78
78
|
end
|
79
79
|
|
80
|
-
# Use this to set where to write to. output can be a
|
80
|
+
# Use this to set where to write to. output can be a
|
81
81
|
# file object or a string. This code raises IOError on error.
|
82
82
|
def write(*args)
|
83
83
|
@output.print(*args)
|
@@ -95,17 +95,17 @@ class Trepan
|
|
95
95
|
# handled by the same channel, e.g. a socket or tty.
|
96
96
|
#
|
97
97
|
class InOutBase
|
98
|
-
|
98
|
+
|
99
99
|
def initialize(inout, opts={})
|
100
100
|
@opts = DEFAULT_OPTS.merge(opts)
|
101
101
|
@inout = inout
|
102
102
|
end
|
103
|
-
|
103
|
+
|
104
104
|
def close
|
105
105
|
@inout.close() if @inout
|
106
106
|
end
|
107
|
-
|
108
|
-
def eof?
|
107
|
+
|
108
|
+
def eof?
|
109
109
|
begin
|
110
110
|
@input.eof?
|
111
111
|
rescue IOError
|
@@ -116,9 +116,9 @@ class Trepan
|
|
116
116
|
def flush
|
117
117
|
@inout.flush
|
118
118
|
end
|
119
|
-
|
120
|
-
# Read a line of input. EOFError will be raised on EOF.
|
121
|
-
#
|
119
|
+
|
120
|
+
# Read a line of input. EOFError will be raised on EOF.
|
121
|
+
#
|
122
122
|
# Note that we don't support prompting first. Instead, arrange to
|
123
123
|
# call DebuggerOutput.write() first with the prompt. If `use_raw'
|
124
124
|
# is set raw_input() will be used in that is supported by the
|
@@ -128,15 +128,15 @@ class Trepan
|
|
128
128
|
@input.readline
|
129
129
|
end
|
130
130
|
|
131
|
-
# Use this to set where to write to. output can be a
|
131
|
+
# Use this to set where to write to. output can be a
|
132
132
|
# file object or a string. This code raises IOError on error.
|
133
|
-
#
|
134
|
-
# Use this to set where to write to. output can be a
|
133
|
+
#
|
134
|
+
# Use this to set where to write to. output can be a
|
135
135
|
# file object or a string. This code raises IOError on error.
|
136
136
|
def write(*args)
|
137
137
|
@inout.write(*args)
|
138
138
|
end
|
139
|
-
|
139
|
+
|
140
140
|
# used to write to a debugger that is connected to this
|
141
141
|
# server; `str' written will have a newline added to it
|
142
142
|
def writeline( msg)
|
@@ -145,4 +145,3 @@ class Trepan
|
|
145
145
|
end
|
146
146
|
|
147
147
|
end
|
148
|
-
|
data/lib/trepanning.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# -*- coding: utf-8 -*-
|
3
|
-
# Copyright (C) 2010-2012 Rocky Bernstein <rockyb@rubyforge.net>
|
4
|
-
require '
|
5
|
-
require 'thread_frame' # Stack frame introspection and more.
|
3
|
+
# Copyright (C) 2010-2012, 2015 Rocky Bernstein <rockyb@rubyforge.net>
|
4
|
+
require 'set'
|
6
5
|
require_relative '../app/complete' # command completion
|
7
6
|
require_relative '../app/core' # core event-handling mechanism
|
8
7
|
require_relative '../app/default' # default debugger settings
|
@@ -11,13 +10,13 @@ require_relative '../interface/script' # --command interface (includes I/O)
|
|
11
10
|
require_relative '../interface/client' # client interface (remote debugging)
|
12
11
|
require_relative '../interface/server' # server interface (remote debugging)
|
13
12
|
|
14
|
-
# SCRIPT_ISEQS__ is like SCRIPT_LINES__ in a patched Ruby
|
15
|
-
# this variable to a hash causes instruction
|
16
|
-
# this has under their "filename" as a
|
17
|
-
# the "filename" is instruction sequence
|
18
|
-
# "filename" parameter when the
|
19
|
-
# generated. Each value is an array of
|
20
|
-
# share that name.
|
13
|
+
# SCRIPT_ISEQS__ is like SCRIPT_LINES__ in a patched Ruby
|
14
|
+
# 1.9/2.1. Setting this variable to a hash causes instruction
|
15
|
+
# sequences to be added in this has under their "filename" as a
|
16
|
+
# key. More accurately though, the "filename" is instruction sequence
|
17
|
+
# name that was given as in the "filename" parameter when the
|
18
|
+
# instruction sequence was generated. Each value is an array of
|
19
|
+
# instruction sequences that share that name.
|
21
20
|
SCRIPT_ISEQS__ = {} unless
|
22
21
|
defined?(SCRIPT_ISEQS__) && SCRIPT_ISEQS__.is_a?(Hash)
|
23
22
|
ISEQS__ = {} unless
|
@@ -25,318 +24,314 @@ ISEQS__ = {} unless
|
|
25
24
|
|
26
25
|
class Trepan
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
opts = Trepan::ServerInterface::DEFAULT_INIT_CONNECTION_OPTS.dup
|
57
|
-
opts[:port] = @settings[:port] if @settings[:port]
|
58
|
-
opts[:host] = @settings[:host] if @settings[:host]
|
27
|
+
attr_reader :completion_proc # GNU Readline completion proc
|
28
|
+
attr_accessor :core # access to Trepan::Core instance
|
29
|
+
attr_accessor :intf # Array. The way the outside world
|
30
|
+
# interfaces with us. An array, so that
|
31
|
+
# interfaces can be stacked.
|
32
|
+
attr_accessor :restart_argv # How to restart us, empty or nil.
|
33
|
+
# Note: restart_argv is typically C's
|
34
|
+
# **argv, not Ruby's ARGV. So
|
35
|
+
# restart_argv[0] is $0.
|
36
|
+
attr_reader :settings # Hash[:symbol] of things you can configure
|
37
|
+
attr_accessor :trace_filter # Procs/Methods we ignore.
|
38
|
+
attr_accessor :trace_point # Trace point object
|
39
|
+
|
40
|
+
def initialize(settings={})
|
41
|
+
@trace_point = nil
|
42
|
+
@trace_filter = Set.new()
|
43
|
+
@settings = Trepan::DEFAULT_SETTINGS.merge(settings)
|
44
|
+
@input = @settings[:input] || STDIN
|
45
|
+
@output = @settings[:output] || STDOUT
|
46
|
+
|
47
|
+
@completion_proc = method(:completion_method)
|
48
|
+
|
49
|
+
@intf =
|
50
|
+
if @settings[:server]
|
51
|
+
@completion_proc = nil
|
52
|
+
opts = Trepan::ServerInterface::DEFAULT_INIT_CONNECTION_OPTS.dup
|
53
|
+
opts[:port] = @settings[:port] if @settings[:port]
|
54
|
+
opts[:host] = @settings[:host] if @settings[:host]
|
59
55
|
opts[:readline] = false
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
56
|
+
puts("starting debugger in out-of-process mode port at " +
|
57
|
+
"#{opts[:host]}:#{opts[:port]}")
|
58
|
+
[Trepan::ServerInterface.new(nil, nil, opts)]
|
59
|
+
elsif @settings[:client]
|
60
|
+
opts = Trepan::ClientInterface::DEFAULT_INIT_CONNECTION_OPTS.dup
|
61
|
+
opts[:port] = @settings[:port] if @settings[:port]
|
62
|
+
opts[:host] = @settings[:host] if @settings[:host]
|
63
|
+
opts[:complete] = @completion_proc
|
64
|
+
user_opts = @settings[:user_opts] || {}
|
65
|
+
[Trepan::ClientInterface.new(nil, nil, nil, user_opts, opts)]
|
66
|
+
else
|
67
|
+
opts = {:complete => @completion_proc,
|
72
68
|
:readline => @settings[:readline]}
|
73
|
-
|
74
|
-
|
69
|
+
[Trepan::UserInterface.new(@input, @output, opts)]
|
70
|
+
end
|
75
71
|
|
76
|
-
|
72
|
+
process_cmdfile_setting(@settings)
|
77
73
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
74
|
+
# FIXME: The below option settings is a big crock.
|
75
|
+
@settings[:core_opts][:cmdproc_opts] ||= {}
|
76
|
+
@settings[:core_opts][:cmdproc_opts][:basename] = settings[:basename]
|
77
|
+
@settings[:core_opts][:cmdproc_opts][:highlight] ||= settings[:highlight]
|
78
|
+
@settings[:core_opts][:cmdproc_opts][:traceprint] = settings[:traceprint]
|
83
79
|
|
84
|
-
|
80
|
+
if settings[:traceprint]
|
81
|
+
@settings[:cmdloop_on_exit] = false
|
82
|
+
end
|
85
83
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
84
|
+
@core = Core.new(self, @settings[:core_opts])
|
85
|
+
|
86
|
+
if @settings[:initial_dir]
|
87
|
+
Dir.chdir(@settings[:initial_dir])
|
88
|
+
else
|
89
|
+
@settings[:initial_dir] = Dir.pwd
|
90
|
+
end
|
91
|
+
@initial_dir = @settings[:initial_dir]
|
92
|
+
@restart_argv = @settings[:restart_argv]
|
93
|
+
|
94
|
+
unless @settings[:client]
|
95
|
+
%w(debugger stop).each do |m|
|
96
|
+
@trace_filter << self.method(m.to_sym)
|
97
|
+
end
|
98
|
+
%w(debugger event_processor trace_var_processor).each do
|
99
|
+
|m|
|
100
|
+
# FIXME
|
101
|
+
# @trace_filter << @core.method(m)
|
102
|
+
@trace_filter << m
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Run user debugger command startup files.
|
107
|
+
add_startup_files unless @settings[:nx]
|
107
108
|
|
108
|
-
# Run user debugger command startup files.
|
109
|
-
add_startup_files unless @settings[:nx]
|
110
109
|
|
111
|
-
|
112
|
-
|
113
|
-
|
110
|
+
at_exit do
|
111
|
+
RubyVM::Frame::get.trace_off = true
|
112
|
+
stop
|
113
|
+
@intf[-1].close
|
114
|
+
end
|
114
115
|
end
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
116
|
+
|
117
|
+
# The method is called when we want to do debugger command completion
|
118
|
+
# such as called from GNU Readline with <TAB>.
|
119
|
+
def completion_method(last_token, leading=Readline.line_buffer)
|
120
|
+
completion = @core.processor.complete(leading, last_token)
|
121
|
+
if 1 == completion.size
|
122
|
+
completion_token = completion[0]
|
123
|
+
if last_token.end_with?(' ')
|
124
|
+
if last_token.rstrip == completion_token
|
125
|
+
# There is nothing more to complete
|
126
|
+
[]
|
127
|
+
else
|
128
|
+
[]
|
129
|
+
end
|
130
|
+
else
|
131
|
+
[completion_token]
|
132
|
+
end
|
133
|
+
else
|
134
|
+
# We have multiple completions. Get the last token so that will
|
135
|
+
# be presented as a list of completions.
|
136
|
+
completion
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# To call from inside a Ruby program, there is one-time setup that
|
141
|
+
# needs to be done first:
|
142
|
+
# require 'trepanning'
|
143
|
+
# mydbg = Trepan.new()
|
144
|
+
# which will tell the debugger how to "restart" the program.
|
145
|
+
#
|
146
|
+
# If you want a synchronous stop in your program call to the
|
147
|
+
# debugger at the point of the call, set opts[:immediate]
|
148
|
+
# true. Example:
|
149
|
+
#
|
150
|
+
# ... work, work, work
|
151
|
+
# mydbg.debugger(:immediate=>true) # enter debugger here
|
152
|
+
# ... work, work, work
|
153
|
+
#
|
154
|
+
# However to enter the debugger on the next event after the
|
155
|
+
# debugger() call:
|
156
|
+
#
|
157
|
+
# ... work, work, work
|
158
|
+
# mydbg.debugger # Don't stop here...
|
159
|
+
# work # but stop here.
|
160
|
+
#
|
161
|
+
# And finally, if you want to debug just a block:
|
162
|
+
# mydbg.debugger {
|
163
|
+
# ... code you want to debug.
|
164
|
+
# }
|
165
|
+
#
|
166
|
+
# Some options
|
167
|
+
|
168
|
+
# :immediate - boolean. If true, immediate stop rather than wait
|
169
|
+
# for an event
|
170
|
+
#
|
171
|
+
# :hide_stack - boolean. If true, omit stack frames before the
|
172
|
+
# debugger call
|
173
|
+
#
|
174
|
+
# :debugme - boolean. Allow tracing into this routine. You
|
175
|
+
# generally won't want this. It slows things
|
176
|
+
# down horribly.
|
177
|
+
|
178
|
+
def debugger(opts={}, &block)
|
179
|
+
# FIXME: one option we may want to pass is the initial trace
|
180
|
+
# filter.
|
181
|
+
if opts[:hide_stack]
|
182
|
+
@core.processor.hidelevels[Thread.current] =
|
183
|
+
RubyVM::Frame.get.stack_size
|
184
|
+
end
|
185
|
+
# unless defined?(PROG_UNRESOLVED_SCRIPT)
|
186
|
+
# # We may later do more sophisticated things...
|
187
|
+
# Trepan.const_set('PROG_UNRESOLVED_SCRIPT', RubyVM::OS_ARGV.index($0) ? $0 : nil)
|
188
|
+
# end
|
189
|
+
th = Thread.current
|
190
|
+
if block
|
191
|
+
@trace_point = TracePoint.new() do |tp|
|
192
|
+
@core.event_processor_tp(tp)
|
193
|
+
end
|
194
|
+
RubyVM::Frame::get.trace_off1 = true
|
195
|
+
@trace_point.enable
|
196
|
+
yield block
|
197
|
+
RubyVM::Frame::get.trace_off = true
|
198
|
+
@trace_point.disable
|
199
|
+
@trace_point = nil
|
128
200
|
else
|
129
|
-
|
201
|
+
@trace_point = TracePoint.new() do |tp|
|
202
|
+
@core.event_processor_tp(tp)
|
203
|
+
end
|
204
|
+
RubyVM::Frame::get.trace_off1 = true
|
205
|
+
@trace_point.enable
|
130
206
|
end
|
131
|
-
else
|
132
|
-
[completion_token]
|
133
|
-
end
|
134
|
-
else
|
135
|
-
# We have multiple completions. Get the last token so that will
|
136
|
-
# be presented as a list of completions.
|
137
|
-
completion
|
138
207
|
end
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
# If you want a synchronous stop in your program call to the
|
148
|
-
# debugger at the point of the call, set opts[:immediate]
|
149
|
-
# true. Example:
|
150
|
-
#
|
151
|
-
# ... work, work, work
|
152
|
-
# mydbg.debugger(:immediate=>true) # enter debugger here
|
153
|
-
# ... work, work, work
|
154
|
-
#
|
155
|
-
# However to enter the debugger on the next event after the
|
156
|
-
# debugger() call:
|
157
|
-
#
|
158
|
-
# ... work, work, work
|
159
|
-
# mydbg.debugger # Don't stop here...
|
160
|
-
# work # but stop here.
|
161
|
-
#
|
162
|
-
# And finally, if you want to debug just a block:
|
163
|
-
# mydbg.debugger {
|
164
|
-
# ... code you want to debug.
|
165
|
-
# }
|
166
|
-
#
|
167
|
-
# Some options
|
168
|
-
|
169
|
-
# :immediate - boolean. If true, immediate stop rather than wait
|
170
|
-
# for an event
|
171
|
-
#
|
172
|
-
# :hide_stack - boolean. If true, omit stack frames before the
|
173
|
-
# debugger call
|
174
|
-
#
|
175
|
-
# :debugme - boolean. Allow tracing into this routine. You
|
176
|
-
# generally won't want this. It slows things
|
177
|
-
# down horribly.
|
178
|
-
|
179
|
-
def debugger(opts={}, &block)
|
180
|
-
# FIXME: one option we may want to pass is the initial trace filter.
|
181
|
-
if opts[:hide_stack]
|
182
|
-
@core.processor.hidelevels[Thread.current] =
|
183
|
-
RubyVM::Frame.current.stack_size
|
208
|
+
|
209
|
+
# Set core's trace-event processor to run
|
210
|
+
def start(enable = false)
|
211
|
+
@trace_point = TracePoint.new() do |tp|
|
212
|
+
@core.event_processor_tp(tp)
|
213
|
+
end
|
214
|
+
RubyVM::Frame::get.trace_off1 = true
|
215
|
+
@trace_point.enable if enable
|
184
216
|
end
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
if block
|
191
|
-
start
|
192
|
-
ret = block.call
|
193
|
-
stop
|
194
|
-
return ret
|
195
|
-
elsif opts[:immediate]
|
196
|
-
# Stop immediately after this method returns. But if opts[:debugme]
|
197
|
-
# is set, we can stop in this method.
|
198
|
-
RubyVM::Frame::current.trace_off = true unless opts[:debugme]
|
199
|
-
@trace_filter.set_trace_func(@core.event_proc)
|
200
|
-
Trace.event_masks[0] |= @core.step_events
|
201
|
-
@core.debugger(1)
|
202
|
-
else
|
203
|
-
RubyVM::Frame::current.trace_off = true unless opts[:debugme]
|
204
|
-
|
205
|
-
@trace_filter.set_trace_func(@core.event_proc)
|
206
|
-
Trace.event_masks[0] |= @core.step_events
|
207
|
-
|
208
|
-
# Set to stop on the next event after this returns.
|
209
|
-
@core.step_count = opts[:step_count] || 0
|
217
|
+
|
218
|
+
def stop
|
219
|
+
RubyVM::Frame::get.trace_off = true
|
220
|
+
@trace_point.disable if @trace_point and
|
221
|
+
@trace_point.respond_to?(:disable)
|
210
222
|
end
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
# @trace_filter.remove_trace_func
|
224
|
-
clear_trace_func
|
225
|
-
end
|
226
|
-
|
227
|
-
def add_command_file(cmdfile, stderr=$stderr)
|
228
|
-
unless File.readable?(cmdfile)
|
229
|
-
if File.exists?(cmdfile)
|
230
|
-
stderr.puts "Command file '#{cmdfile}' is not readable."
|
231
|
-
return
|
232
|
-
else
|
233
|
-
stderr.puts "Command file '#{cmdfile}' does not exist."
|
234
|
-
return
|
235
|
-
end
|
223
|
+
|
224
|
+
def add_command_file(cmdfile, opts={}, stderr=$stderr)
|
225
|
+
unless File.readable?(cmdfile)
|
226
|
+
if File.exists?(cmdfile)
|
227
|
+
stderr.puts "Command file '#{cmdfile}' is not readable."
|
228
|
+
return
|
229
|
+
else
|
230
|
+
stderr.puts "Command file '#{cmdfile}' does not exist."
|
231
|
+
return
|
232
|
+
end
|
233
|
+
end
|
234
|
+
@intf << Trepan::ScriptInterface.new(cmdfile, opts[:out])
|
236
235
|
end
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
seen[full_initfile_path] = true
|
236
|
+
|
237
|
+
def add_startup_files()
|
238
|
+
seen = {}
|
239
|
+
cwd_initfile = File.join('.', Trepan::CMD_INITFILE_BASE)
|
240
|
+
[cwd_initfile, Trepan::CMD_INITFILE].each do |initfile|
|
241
|
+
full_initfile_path = File.expand_path(initfile)
|
242
|
+
next if seen[full_initfile_path]
|
243
|
+
add_command_file(full_initfile_path) if File.readable?(full_initfile_path)
|
244
|
+
seen[full_initfile_path] = true
|
245
|
+
end
|
248
246
|
end
|
249
|
-
end
|
250
247
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
248
|
+
def process_cmdfile_setting(settings)
|
249
|
+
settings[:cmdfiles].each do |item|
|
250
|
+
cmdfile, opts =
|
251
|
+
if item.kind_of?(Array)
|
252
|
+
item
|
253
|
+
else
|
254
|
+
[item, {:out => @output}]
|
255
|
+
end
|
256
|
+
add_command_file(cmdfile, opts)
|
257
|
+
end if settings.member?(:cmdfiles)
|
258
|
+
end
|
259
|
+
|
260
|
+
# As a simplification for creating a debugger object, and then
|
261
|
+
# calling using the object to invoke the debugger, we allow this
|
262
|
+
# two-step process in one step. That is, instead of
|
263
|
+
#
|
264
|
+
# require 'trepanning'
|
265
|
+
# mydbg = Trepan.new()
|
266
|
+
# ...
|
267
|
+
# mydbg.debugger
|
268
|
+
|
269
|
+
# You can run:
|
270
|
+
# require 'trepanning'
|
271
|
+
# ...
|
272
|
+
# Trepan.debug
|
273
|
+
#
|
274
|
+
# See debugger for options that can be passed. By default :hide_stack is
|
275
|
+
# set.
|
276
|
+
#
|
277
|
+
# Likewise for mydbg.debugger{ ... }
|
278
|
+
|
279
|
+
def self.debug(opts={}, &block)
|
280
|
+
opts = {:hide_stack => false}.merge(opts)
|
281
|
+
unless defined?($trepanning) && $trepanning.is_a?(Trepan)
|
282
|
+
$trepanning = Trepan.new(opts)
|
258
283
|
end
|
259
|
-
|
260
|
-
end
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
# mydbg = Trepan.new()
|
269
|
-
# ...
|
270
|
-
# mydbg.debugger
|
271
|
-
|
272
|
-
# You can run:
|
273
|
-
# require 'trepanning'
|
274
|
-
# ...
|
275
|
-
# Trepan.debug
|
276
|
-
#
|
277
|
-
# See debugger for options that can be passed. By default :hide_stack is
|
278
|
-
# set.
|
279
|
-
#
|
280
|
-
# Likewise for mydbg.debugger{ ... }
|
281
|
-
|
282
|
-
def self.debug(opts={}, &block)
|
283
|
-
opts = {:hide_stack => false}.merge(opts)
|
284
|
-
unless defined?($trepanning) && $trepanning.is_a?(Trepan)
|
285
|
-
$trepanning = Trepan.new(opts)
|
284
|
+
$trepanning.debugger(opts, &block)
|
285
|
+
end
|
286
|
+
|
287
|
+
def self.debug_str(string, opts = DEFAULT_DEBUG_STR_SETTINGS)
|
288
|
+
$trepanning = Trepan.new(opts) unless
|
289
|
+
$trepanning && $trepanning.is_a?(Trepan)
|
290
|
+
$trepanning.core.processor.settings[:different] = false
|
291
|
+
# Perhaps we should do a remap file to string right here?
|
292
|
+
$trepanning.debugger(opts) { eval(string) }
|
286
293
|
end
|
287
|
-
tf = $trepanning.trace_filter
|
288
|
-
tf << self.method(:debugger) unless tf.member? self.method(:debugger)
|
289
|
-
$trepanning.debugger(opts, &block)
|
290
|
-
end
|
291
|
-
|
292
|
-
def self.debug_str(string, opts = DEFAULT_DEBUG_STR_SETTINGS)
|
293
|
-
$trepanning = Trepan.new(opts) unless
|
294
|
-
$trepanning && $trepanning.is_a?(Trepan)
|
295
|
-
$trepanning.core.processor.settings[:different] = false
|
296
|
-
# Perhaps we should do a remap file to string right here?
|
297
|
-
$trepanning.debugger(opts) { eval(string) }
|
298
|
-
end
|
299
294
|
end
|
300
295
|
|
301
296
|
module Kernel
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
297
|
+
# Same as Trepan.debug.
|
298
|
+
# FIXME figure out a way to remove duplication.
|
299
|
+
def debugger(opts={}, &block)
|
300
|
+
opts = {:hide_stack => false}.merge(opts)
|
301
|
+
unless defined?($trepanning) && $trepanning.is_a?(Trepan)
|
302
|
+
$trepanning = Trepan.new(opts)
|
303
|
+
end
|
304
|
+
tf = $trepanning.trace_filter
|
305
|
+
tf << self.method(:debugger) unless tf.member? self.method(:debugger)
|
306
|
+
$trepanning.debugger(opts)
|
308
307
|
end
|
309
|
-
tf = $trepanning.trace_filter
|
310
|
-
tf << self.method(:debugger) unless tf.member? self.method(:debugger)
|
311
|
-
$trepanning.debugger(opts)
|
312
|
-
end
|
313
308
|
end
|
314
309
|
|
315
310
|
if __FILE__ == $0
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
311
|
+
def square(x) # :nodoc
|
312
|
+
x * x
|
313
|
+
end
|
314
|
+
puts 'block debugging...'
|
315
|
+
# It is imagined that there are all sorts of command-line options here.
|
316
|
+
# (I have a good imagination.)
|
317
|
+
Trepan.debug {
|
318
|
+
a = 2
|
319
|
+
b = square(a)
|
320
|
+
p "square of #{a} is #{b}"
|
321
|
+
}
|
322
|
+
|
323
|
+
puts 'immediate debugging (not)...'
|
324
|
+
$trepanning.debugger # (:immediate => true) # no immediate for now
|
325
|
+
puts 'line after immediate'
|
326
|
+
a = 3
|
327
|
+
square(a)
|
328
|
+
|
329
|
+
class MyClass
|
330
|
+
def initialize(x)
|
331
|
+
@x = x
|
332
|
+
end
|
337
333
|
end
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
raise RuntimeError # To see how we handle post-mortem debugging.
|
334
|
+
$trepanning.debugger
|
335
|
+
m = MyClass.new(5)
|
336
|
+
raise RuntimeError # To see how we handle post-mortem debugging.
|
342
337
|
end
|