Skip to content

Commit 3c1b52d

Browse files
committed
run context commands on stopped thread to prevent segfaults
1 parent 4197e90 commit 3c1b52d

File tree

3 files changed

+56
-52
lines changed

3 files changed

+56
-52
lines changed

lib/ruby-debug/event_processor.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class EventProcessor
1111

1212
def initialize(interface)
1313
@printer = XmlPrinter.new(interface)
14+
@interface = interface
1415
@line = nil
1516
@file = nil
1617
@last_breakpoint = nil
@@ -57,9 +58,7 @@ def line_event(context, file, line)
5758
raise "DebuggerThread are not supposed to be traced (#{context.thread})" if context.thread.is_a?(Debugger::DebugThread)
5859
@printer.print_debug("Stopping Thread %s", context.thread.to_s)
5960
@printer.print_debug("Threads equal: %s", Thread.current == context.thread)
60-
# will be resumed by commands like `step', `next', `continue', `finish'
61-
# from `control thread'
62-
Thread.stop
61+
CommandProcessor.new(@interface).process_commands
6362
@printer.print_debug("Resumed Thread %s", context.thread.to_s)
6463
@line = nil
6564
@file = nil

lib/ruby-debug/interface.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ def non_blocking_gets
1414
module Debugger
1515

1616
class RemoteInterface # :nodoc:
17+
attr_accessor :command_queue
1718

1819
def initialize(socket)
1920
@socket = socket
21+
@command_queue = []
2022
end
2123

2224
def read_command

lib/ruby-debug/processor.rb

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -10,84 +10,57 @@ module Debugger
1010
# this class is added to resolve problems, with ruby-debug gem incompatibility see
1111
# http://rubyforge.org/tracker/index.php?func=detail&aid=27055&group_id=3085&atid=11903
1212
class CommandProcessor
13-
end
14-
15-
class ControlCommandProcessor # :nodoc:
1613
def initialize(interface)
1714
@interface = interface
1815
@printer = XmlPrinter.new(@interface)
1916
end
20-
17+
2118
def print(*args)
2219
@interface.print(*args)
2320
end
24-
21+
2522
def process_commands
26-
@printer.print_debug("Starting command read loop")
27-
ctrl_cmd_classes = Command.commands.select{|cmd| cmd.control}
28-
state = ControlState.new(@interface)
29-
ctrl_cmds = ctrl_cmd_classes.map{|cmd| cmd.new(state, @printer)}
30-
31-
while input = @interface.read_command
32-
# escape % since print_debug might use printf
33-
@printer.print_debug "Processing: #{input.gsub('%', '%%')}"
34-
# sleep 0.3
35-
catch(:debug_error) do
36-
if cmd = ctrl_cmds.find{|c| c.match(input) }
37-
cmd.execute
38-
else
39-
process_context_commands(input)
40-
end
41-
end
42-
end
43-
rescue IOError, Errno::EPIPE
44-
@printer.print_error "INTERNAL ERROR!!! #{$!}\n" rescue nil
45-
@printer.print_error $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
46-
rescue Exception
47-
@printer.print_error "INTERNAL ERROR!!! #{$!}\n" rescue nil
48-
@printer.print_error $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
49-
ensure
50-
@interface.close
51-
end
52-
53-
def process_context_commands(input)
5423
unless Debugger.event_processor.at_line?
5524
@printer.print_error "There is no thread suspended at the time and therefore no context to execute '#{input.gsub('%', '%%')}'"
5625
return
5726
end
5827
context = Debugger.event_processor.context
5928
file = Debugger.event_processor.file
6029
line = Debugger.event_processor.line
61-
event_cmds_classes = Command.commands.select{|cmd| cmd.event}
6230
state = State.new do |s|
6331
s.context = context
6432
s.file = file
6533
s.line = line
6634
s.binding = context.frame_binding(0)
6735
s.interface = @interface
6836
end
69-
event_cmds = event_cmds_classes.map{|cmd| cmd.new(state, @printer) }
70-
catch(:debug_error) do
71-
splitter[input].each do |input|
72-
# escape % since print_debug might use printf
73-
@printer.print_debug "Processing context: #{input.gsub('%', '%%')}"
74-
if cmd = event_cmds.find{ |c| c.match(input) }
75-
if context.dead? && cmd.class.need_context
76-
@printer.print_msg "Command is unavailable\n"
37+
event_cmds = Command.commands.map{|cmd| cmd.new(state, @printer) }
38+
while !state.proceed? do
39+
input = @interface.command_queue.empty? ? nil : @interface.command_queue.shift
40+
unless input
41+
sleep 0.1
42+
next
43+
end
44+
catch(:debug_error) do
45+
splitter[input].each do |input|
46+
# escape % since print_debug might use printf
47+
@printer.print_debug "Processing in context: #{input.gsub('%', '%%')}"
48+
if cmd = event_cmds.find { |c| c.match(input) }
49+
if context.dead? && cmd.class.need_context
50+
@printer.print_msg "Command is unavailable\n"
51+
else
52+
cmd.execute
53+
end
7754
else
78-
cmd.execute
55+
@printer.print_msg "Unknown command: #{input}"
7956
end
80-
else
81-
@printer.print_msg "Unknown command: #{input}"
8257
end
8358
end
8459
end
85-
86-
context.thread.run if state.proceed?
8760
end
88-
61+
8962
def splitter
90-
return lambda do |str|
63+
lambda do |str|
9164
str.split(/;/).inject([]) do |m, v|
9265
if m.empty?
9366
m << v
@@ -102,6 +75,36 @@ def splitter
10275
m
10376
end
10477
end
78+
end
79+
end
80+
81+
class ControlCommandProcessor < CommandProcessor# :nodoc:
82+
def process_commands
83+
@printer.print_debug("Starting control thread")
84+
ctrl_cmd_classes = Command.commands.select{|cmd| cmd.control}
85+
state = ControlState.new(@interface)
86+
ctrl_cmds = ctrl_cmd_classes.map{|cmd| cmd.new(state, @printer)}
87+
88+
while input = @interface.read_command
89+
# escape % since print_debug might use printf
90+
@printer.print_debug "Processing in control: #{input.gsub('%', '%%')}"
91+
# sleep 0.3
92+
catch(:debug_error) do
93+
if cmd = ctrl_cmds.find{|c| c.match(input) }
94+
cmd.execute
95+
else
96+
@interface.command_queue << input
97+
end
98+
end
99+
end
100+
rescue IOError, Errno::EPIPE
101+
@printer.print_error "INTERNAL ERROR!!! #{$!}\n" rescue nil
102+
@printer.print_error $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
103+
rescue Exception
104+
@printer.print_error "INTERNAL ERROR!!! #{$!}\n" rescue nil
105+
@printer.print_error $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
106+
ensure
107+
@interface.close
105108
end
106109
end
107110

0 commit comments

Comments
 (0)