-
Notifications
You must be signed in to change notification settings - Fork 81
attach to child processes #108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| #!/usr/bin/env ruby | ||
|
|
||
| require 'optparse' | ||
| require 'thread' | ||
| require 'ostruct' | ||
|
|
||
| $stdout.sync = true | ||
|
|
@@ -78,51 +79,64 @@ require 'ruby-debug-ide/attach/util' | |
| require 'ruby-debug-ide/attach/native_debugger' | ||
| require 'ruby-debug-ide/attach/process_thread' | ||
|
|
||
| debugger = choose_debugger(options.ruby_path, options.pid, options.gems_to_include, debugger_loader_path, argv) | ||
| pids = get_child_pids(options.pid.to_s) | ||
| attach_threads = Array.new | ||
|
|
||
| trap('INT') do | ||
| unless debugger.exited? | ||
| $stderr.puts "backtraces for threads:\n\n" | ||
| process_threads = debugger.process_threads | ||
| if process_threads | ||
| process_threads.each do |thread| | ||
| $stderr.puts "#{thread.thread_info}\n#{thread.last_bt}\n\n" | ||
| end | ||
| end | ||
| debugger.exit | ||
| pids.each_with_index do |pid, i| | ||
|
|
||
| if(i == 1) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After the extraction of the logic below into methods this |
||
| argv = reset_port(ARGV) # Only the first process works with the initialized port, the rest need to be recalculated | ||
| end | ||
| exit! | ||
| end | ||
|
|
||
| debugger.attach_to_process | ||
| debugger.set_flags | ||
| attach_threads << Thread.new(argv) do |argv| | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another possible way to do this is to replace |
||
|
|
||
| if options.uid | ||
| DebugPrinter.print_debug("changing current uid from #{Process.uid} to #{options.uid}") | ||
| Process::Sys.setuid(options.uid.to_i) | ||
| end | ||
| debugger = choose_debugger(options.ruby_path, options.gems_to_include, debugger_loader_path, argv) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the purpose of removing |
||
|
|
||
| if debugger.check_already_under_debug | ||
| $stderr.puts "Process #{debugger.pid} is already under debug" | ||
| debugger.exit | ||
| exit! | ||
| end | ||
| trap('INT') do | ||
| unless debugger.exited? | ||
| $stderr.puts "backtraces for threads:\n\n" | ||
| process_threads = debugger.process_threads | ||
| if process_threads | ||
| process_threads.each do |thread| | ||
| $stderr.puts "#{thread.thread_info}\n#{thread.last_bt}\n\n" | ||
| end | ||
| end | ||
| debugger.exit | ||
| end | ||
| exit! | ||
| end | ||
|
|
||
| should_check_threads_state = true | ||
| debugger.attach_to_process(pid) | ||
| debugger.set_flags | ||
|
|
||
| while should_check_threads_state | ||
| should_check_threads_state = false | ||
| debugger.update_threads.each do |thread| | ||
| thread.switch | ||
| while thread.need_finish_frame | ||
| should_check_threads_state = true | ||
| thread.finish | ||
| if debugger.check_already_under_debug | ||
| $stderr.puts "Process #{debugger.pid} is already under debug" | ||
| debugger.exit | ||
| exit! | ||
| end | ||
|
|
||
| should_check_threads_state = true | ||
|
|
||
| while should_check_threads_state | ||
| should_check_threads_state = false | ||
| debugger.update_threads.each do |thread| | ||
| thread.switch | ||
| while thread.need_finish_frame | ||
| should_check_threads_state = true | ||
| thread.finish | ||
| end | ||
| end | ||
| end | ||
|
|
||
| debugger.wait_line_event | ||
| debugger.load_debugger | ||
| debugger.exit | ||
| end | ||
| end | ||
|
|
||
| debugger.wait_line_event | ||
| debugger.load_debugger | ||
| debugger.exit | ||
|
|
||
| attach_threads.each {|thread| thread.join} | ||
| if options.uid | ||
| DebugPrinter.print_debug("changing current uid from #{Process.uid} to #{options.uid}") | ||
| Process::Sys.setuid(options.uid.to_i) | ||
| end | ||
| sleep | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,36 @@ | ||
| require 'ruby-debug-ide/attach/lldb' | ||
| require 'ruby-debug-ide/attach/gdb' | ||
| require 'socket' | ||
|
|
||
| def get_child_pids(pid) | ||
| pids = Array.new | ||
|
|
||
| q = Queue.new | ||
| q.push(pid) | ||
|
|
||
| while(!q.empty?) do | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. spacing (here and after) |
||
| pid = q.pop | ||
| pids << pid | ||
|
|
||
| if(command_exists 'pgrep') | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you're checking for the command presence each time, does it mean that the command may disappear during children collection? If not, process such check beforehand and, ideally, also incapsulate running external processes into a separate method(s) in order to fix possible related bugs in a dedicated place.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the command does not exist, then only one element will be added to the queue and the check will be performed once. But it does not look very nice, I agree |
||
| pipe = IO.popen("pgrep -P #{pid}") | ||
|
|
||
| pipe.readlines.each do |child_pid| | ||
| q.push(child_pid.to_i) | ||
| end | ||
| end | ||
| end | ||
|
|
||
| pids | ||
| end | ||
|
|
||
| def reset_port(argv) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still don't get the purpose of that method. I feel uncomfortable with re-checking argv values once again when you have an appropriate library for that.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In any case argv is transferred to rdebug as a string in load_debugger method |
||
| argv.each_with_index do |val, i| | ||
| argv[i + 1] = -1 if(val == '--port') | ||
| end | ||
|
|
||
| '["' + argv * '", "' + '"]' | ||
| end | ||
|
|
||
| def command_exists(command) | ||
| checking_command = "checking command #{command} for existence\n" | ||
|
|
@@ -12,14 +43,14 @@ def command_exists(command) | |
| $?.exitstatus == 0 | ||
| end | ||
|
|
||
| def choose_debugger(ruby_path, pid, gems_to_include, debugger_loader_path, argv) | ||
| def choose_debugger(ruby_path, gems_to_include, debugger_loader_path, argv) | ||
| if command_exists(LLDB.to_s) | ||
| debugger = LLDB.new(ruby_path, pid, '--no-lldbinit', gems_to_include, debugger_loader_path, argv) | ||
| debugger = LLDB.new(ruby_path, '--no-lldbinit', gems_to_include, debugger_loader_path, argv) | ||
| elsif command_exists(GDB.to_s) | ||
| debugger = GDB.new(ruby_path, pid, '-nh -nx', gems_to_include, debugger_loader_path, argv) | ||
| debugger = GDB.new(ruby_path, '-nh -nx', gems_to_include, debugger_loader_path, argv) | ||
| else | ||
| raise 'Neither gdb nor lldb was found. Aborting.' | ||
| end | ||
|
|
||
| debugger | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,3 @@ | ||
| module Debugger | ||
| IDE_VERSION='0.6.1.beta3' | ||
| IDE_VERSION='0.6.1.beta5' | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lots of nested methods deserve method extraction. Please split the code into sensible methods