Permalink
Browse files

Trying a new approach that doesn't use breakpoints. Instead it ensure…

…s the process hasn't been interrupted during garbage collection and attaches again if it is.
  • Loading branch information...
1 parent 4210c2b commit 1140eccd8e0178969bcc8a77a86e630db829dae9 @ileitch committed Nov 26, 2010
Showing with 38 additions and 21 deletions.
  1. +36 −19 lib/hijack/gdb.rb
  2. +1 −1 lib/hijack/payload.rb
  3. +1 −1 test/gc.rb
View
@@ -6,16 +6,16 @@ def initialize(pid)
@pid = pid
@verbose = Hijack.options[:debug]
@exec_path = File.join(Config::CONFIG['bindir'], Config::CONFIG['RUBY_INSTALL_NAME'] + Config::CONFIG['EXEEXT'])
- attach
+ attach_outside_gc
end
def eval(cmd)
call("(void)rb_eval_string(#{cmd.strip.gsub(/"/, '\"').inspect})")
end
- def detach
+ def quit
return unless @gdb
- exec('detach')
+ detach
exec('quit')
@backtrace = nil
@gdb.close
@@ -24,35 +24,52 @@ def detach
protected
- def attach
- loop do
- @gdb = IO.popen("gdb -q #{@exec_path} #{@pid} 2>&1", 'r+')
- wait
- if backtrace.first =~ /previous frame inner to this frame/i
+ def previous_frame_inner_to_this_frame?
+ backtrace.first =~ /previous frame inner to this frame/i
+ end
+
+ def attach_outside_gc
+ @gdb = IO.popen("gdb -q #{@exec_path} #{@pid} 2>&1", 'r+')
+ wait
+ ensure_attached_to_ruby_process
+ attached = false
+
+ 3.times do |i|
+ attach unless i == 0
+
+ if previous_frame_inner_to_this_frame? || during_gc?
detach
sleep 0.1
else
+ attached = true
break
end
end
-
+
+ unless attached
+ puts "=> Tried 3 times to attach to #{@pid} whilst GC wasn't running but failed."
+ puts "=> This means either the process calls GC.start frequently or its GC runs are slow - try hijacking again."
+ exit 1
+ end
+
# TODO: Check for "Unable to attach to process-id 44528: No child processes (10)"
ensure_main_thread_not_blocked_by_join
- set_breakpoint
- continue
- delete_breakpoint
end
- def set_breakpoint
- exec("break rb_call")
+ def during_gc?
+ backtrace.any? { |line| line =~ /garbage_collect/i }
end
-
- def delete_breakpoint
- exec("delete breakpoints")
+
+ def detach
+ exec("detach")
end
-
+
+ def attach
+ exec("attach #{@pid}")
+ end
+
def ensure_main_thread_not_blocked_by_join
- if backtrace.any? {|line| line =~ /rb_thread_join/}
+ if backtrace.any? { |line| line =~ /rb_thread_join/ }
puts "\n=> Unable to hijack #{@pid} because the main thread is blocked waiting for another thread to join."
puts "=> Check that you are using the most recent version of hijack, a newer version may have solved this shortcoming."
detach
View
@@ -8,7 +8,7 @@ def self.inject(pid)
end
gdb = GDB.new(pid)
gdb.eval(payload(pid))
- gdb.detach
+ gdb.quit
exit if @received_sigint
end
View
@@ -7,7 +7,7 @@ def initialize(i)
end
puts Process.pid
-n = 0
+n = 10000
loop do
(n+=1).times {|i| MyObject.new(i)}
GC.start

0 comments on commit 1140ecc

Please sign in to comment.