5959require 'ruby-debug-ide/greeter'
6060Debugger ::print_greeting_msg ( nil , nil )
6161
62- # this class is some kind of "interface"
6362class NativeDebugger
6463
65- attr_reader :pid , :last_bt
64+ attr_reader :pid , :main_thread , :process_threads
6665
6766 # @param executable -- path to ruby interpreter
6867 # @param pid -- pid of process you want to debug
6968 # @param options -- flags you want to specify to your debugger as a string (e.g. "-nx -nh" for gdb to disable .gdbinit)
7069 def initialize ( executable , pid , options )
7170 @pid = pid
72- @main_thread = nil
7371 @delimiter = '__OUTPUT_FINISHED__' # for getting response
74- @last_bt = '' # to shaw it to user
72+ @tbreak = '__func_to_set_breakpoint_at'
73+ @main_thread = nil
74+ @process_threads = nil
7575
7676 launch_string = "#{ self } #{ executable } #{ options } "
7777 @pipe = IO . popen ( launch_string , 'r+' )
@@ -90,9 +90,6 @@ class NativeDebugger
9090 return ''
9191 end
9292 response = get_response
93- if command == 'bt'
94- @last_bt = response
95- end
9693 $stdout. puts "response for #{ command } :\n #{ response } \n \n \n \n "
9794 response
9895 end
@@ -106,7 +103,7 @@ class NativeDebugger
106103 line = @pipe . readline
107104 $stderr. puts line
108105 next if line =~ /\( lldb\) / # lldb repeats your input to its output
109- break if line =~ /\$ \d +\s =\s "__OUTPUT_FINISHED__ "/
106+ break if line =~ /\$ \d +\s =\s "#{ @delimiter } "/
110107 content += line
111108 end
112109 content
@@ -129,10 +126,12 @@ class NativeDebugger
129126 end
130127
131128 def continue
129+ $stdout. puts 'continuing'
132130 @pipe . puts 'c'
133131 loop do
134132 line = @pipe . readline
135- break if line =~ /__func_to_set_breakpoint_at/
133+ $stderr. puts line
134+ break if line =~ /#{ Regexp . escape ( @tbreak ) } /
136135 end
137136 get_response
138137 end
@@ -142,6 +141,15 @@ class NativeDebugger
142141 @main_thread . switch
143142 end
144143
144+ def wait_line_event
145+ call_start_attach
146+ continue
147+ end
148+
149+ def load_debugger
150+ execute "call rb_eval_string_protect(\" require '#{ $path_to_debugger_loader} '; load_debugger(#{ $gems_to_include. gsub ( "\" " , "'" ) } , #{ $argv. gsub ( "\" " , "'" ) } )\" , (int *)0)"
151+ end
152+
145153 def exit
146154 execute 'q'
147155 end
@@ -163,20 +171,20 @@ class LLDB < NativeDebugger
163171 end
164172
165173 def update_threads
166- process_threads = [ ]
174+ @ process_threads = [ ]
167175 info_threads = ( execute 'thread list' ) . split ( "\n " )
168176 info_threads . each do |thread_info |
169177 next unless thread_info =~ /[\s *]*thread\s #\d +.*/
170178 $stdout. puts "thread_info: #{ thread_info } "
171179 is_main = thread_info [ 0 ] == '*'
172180 thread_num = thread_info . sub ( /[\s *]*thread\s #/ , '' ) . sub ( /:\s .*$/ , '' ) . to_i
173- thread = ProcessThread . new ( thread_num , is_main , self )
181+ thread = ProcessThread . new ( thread_num , is_main , thread_info , self )
174182 if thread . is_main
175183 @main_thread = thread
176184 end
177- process_threads << thread
185+ @ process_threads << thread
178186 end
179- process_threads
187+ @ process_threads
180188 end
181189
182190 def check_already_under_debug
@@ -191,11 +199,12 @@ class LLDB < NativeDebugger
191199 def call_start_attach
192200 super ( )
193201 execute "expr (void *) dlopen(\" /home/equi/Job/JetBrains/Internship_2016/ruby-debug-ide/ext/libAttach.so\" , 2)"
194- execute "call start_attach(\" require '#{ $path_to_debugger_loader} '; load_debugger(#{ $gems_to_include. gsub ( "\" " , "'" ) } , #{ $argv. gsub ( "\" " , "'" ) } )\" )"
202+ execute 'call start_attach()'
203+ set_tbreak ( @tbreak )
195204 end
196205
197206 def to_s
198- 'lldb-3.8 '
207+ 'lldb'
199208 end
200209
201210end
@@ -212,22 +221,20 @@ class GDB < NativeDebugger
212221 end
213222
214223 def update_threads
215- process_threads = [ ]
224+ @ process_threads = [ ]
216225 info_threads = ( execute 'info threads' ) . split ( "\n " )
217- # each thread info looks like this:
218- # 3 Thread 0x7ff535405700 (LWP 8291) "ruby-timer-thr" 0x00007ff534a15fdd in poll () at ../sysdeps/unix/syscall-template.S:81
219226 info_threads . each do |thread_info |
220227 next unless thread_info =~ /[\s *]*\d +\s +Thread.*/
221228 $stdout. puts "thread_info: #{ thread_info } "
222229 is_main = thread_info [ 0 ] == '*'
223230 thread_num = thread_info . sub ( /[\s *]*/ , '' ) . sub ( /\s .*$/ , '' ) . to_i
224- thread = ProcessThread . new ( thread_num , is_main , self )
231+ thread = ProcessThread . new ( thread_num , is_main , thread_info , self )
225232 if thread . is_main
226233 @main_thread = thread
227234 end
228- process_threads << thread
235+ @ process_threads << thread
229236 end
230- process_threads
237+ @ process_threads
231238 end
232239
233240 def check_already_under_debug
@@ -242,7 +249,8 @@ class GDB < NativeDebugger
242249 def call_start_attach
243250 super ( )
244251 execute "call dlopen(\" /home/equi/Job/JetBrains/Internship_2016/ruby-debug-ide/ext/libAttach.so\" , 2)"
245- execute "call start_attach(\" require '#{ $path_to_debugger_loader} '; load_debugger(#{ $gems_to_include. gsub ( "\" " , "'" ) } , #{ $argv. gsub ( "\" " , "'" ) } )\" )"
252+ execute 'call start_attach()'
253+ set_tbreak ( @tbreak )
246254 end
247255
248256 def to_s
@@ -253,12 +261,14 @@ end
253261
254262class ProcessThread
255263
256- attr_reader :thread_num , :is_main
264+ attr_reader :thread_num , :is_main , :thread_info , :last_bt
257265
258- def initialize ( thread_num , is_main , native_debugger )
266+ def initialize ( thread_num , is_main , thread_info , native_debugger )
259267 @thread_num = thread_num
260268 @is_main = is_main
261269 @native_debugger = native_debugger
270+ @thread_info = thread_info
271+ @last_bt = nil
262272 end
263273
264274 def switch
@@ -270,7 +280,7 @@ class ProcessThread
270280 end
271281
272282 def get_bt
273- @native_debugger . execute 'bt'
283+ @last_bt = @ native_debugger. execute 'bt'
274284 end
275285
276286 def any_caller_match ( bt , pattern )
@@ -302,11 +312,19 @@ class ProcessThread
302312
303313end
304314
315+ def exists_command ( command )
316+ `command -v #{ command } >/dev/null 2>&1 || { exit 1; }`
317+ $?. exitstatus == 0
318+ end
319+
305320def choose_debugger
306- if true
321+ $stderr. puts "exists: #{ exists_command ( 'gdb' ) } , #{ exists_command ( 'lldb' ) } "
322+ if exists_command ( 'gdb' )
307323 GDB . new ( @options . ruby_path , @options . pid , '-nh -nx' )
308- else
324+ elsif exists_command ( 'lldb' )
309325 LLDB . new ( @options . ruby_path , @options . pid , '--no-lldbinit' )
326+ else
327+ raise 'Neither gdb nor lldb was found. Aborting.'
310328 end
311329end
312330
@@ -333,14 +351,14 @@ while should_check_threads_state
333351 end
334352end
335353
336- gdb . call_start_attach
337- gdb . set_tbreak ( '__func_to_set_breakpoint_at' )
338- gdb . continue
339- gdb . execute "call rb_eval_string_protect(\" require '#{ $path_to_debugger_loader} '; load_debugger(#{ $gems_to_include. gsub ( "\" " , "'" ) } , #{ $argv. gsub ( "\" " , "'" ) } )\" , (int *)0)"
354+ gdb . wait_line_event
355+ gdb . load_debugger
340356gdb . exit
341357
342358trap ( 'INT' ) do
343- $stderr. puts "Last backtrace:\n #{ gdb . last_bt } "
359+ gdb . process_threads . each do |thread |
360+ $stderr. puts "Last backtrace for thread #{ thread . thread_info } :\n #{ thread . last_bt } "
361+ end
344362 exit!
345363end
346364
0 commit comments