1# Copyright (C) 2000 Network Applied Communication Laboratory, Inc. 2# Copyright (C) 2000 Information-technology Promotion Agency, Japan 3# Copyright (C) 2000-2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org> 4 5require 'continuation' 6 7if $SAFE > 0 8 STDERR.print "-r debug.rb is not available in safe mode\n" 9 exit 1 10end 11 12require 'tracer' 13require 'pp' 14 15class Tracer # :nodoc: 16 def Tracer.trace_func(*vars) 17 Single.trace_func(*vars) 18 end 19end 20 21SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__ # :nodoc: 22 23## 24# This library provides debugging functionality to Ruby. 25# 26# To add a debugger to your code, start by requiring +debug+ in your 27# program: 28# 29# def say(word) 30# require 'debug' 31# puts word 32# end 33# 34# This will cause Ruby to interrupt execution and show a prompt when the +say+ 35# method is run. 36# 37# Once you're inside the prompt, you can start debugging your program. 38# 39# (rdb:1) p word 40# "hello" 41# 42# == Getting help 43# 44# You can get help at any time by pressing +h+. 45# 46# (rdb:1) h 47# Debugger help v.-0.002b 48# Commands 49# b[reak] [file:|class:]<line|method> 50# b[reak] [class.]<line|method> 51# set breakpoint to some position 52# wat[ch] <expression> set watchpoint to some expression 53# cat[ch] (<exception>|off) set catchpoint to an exception 54# b[reak] list breakpoints 55# cat[ch] show catchpoint 56# del[ete][ nnn] delete some or all breakpoints 57# disp[lay] <expression> add expression into display expression list 58# undisp[lay][ nnn] delete one particular or all display expressions 59# c[ont] run until program ends or hit breakpoint 60# s[tep][ nnn] step (into methods) one line or till line nnn 61# n[ext][ nnn] go over one line or till line nnn 62# w[here] display frames 63# f[rame] alias for where 64# l[ist][ (-|nn-mm)] list program, - lists backwards 65# nn-mm lists given lines 66# up[ nn] move to higher frame 67# down[ nn] move to lower frame 68# fin[ish] return to outer frame 69# tr[ace] (on|off) set trace mode of current thread 70# tr[ace] (on|off) all set trace mode of all threads 71# q[uit] exit from debugger 72# v[ar] g[lobal] show global variables 73# v[ar] l[ocal] show local variables 74# v[ar] i[nstance] <object> show instance variables of object 75# v[ar] c[onst] <object> show constants of object 76# m[ethod] i[nstance] <obj> show methods of object 77# m[ethod] <class|module> show instance methods of class or module 78# th[read] l[ist] list all threads 79# th[read] c[ur[rent]] show current thread 80# th[read] [sw[itch]] <nnn> switch thread context to nnn 81# th[read] stop <nnn> stop thread nnn 82# th[read] resume <nnn> resume thread nnn 83# p expression evaluate expression and print its value 84# h[elp] print this help 85# <everything else> evaluate 86# 87# == Usage 88# 89# The following is a list of common functionalities that the debugger 90# provides. 91# 92# === Navigating through your code 93# 94# In general, a debugger is used to find bugs in your program, which 95# often means pausing execution and inspecting variables at some point 96# in time. 97# 98# Let's look at an example: 99# 100# def my_method(foo) 101# require 'debug' 102# foo = get_foo if foo.nil? 103# raise if foo.nil? 104# end 105# 106# When you run this program, the debugger will kick in just before the 107# +foo+ assignment. 108# 109# (rdb:1) p foo 110# nil 111# 112# In this example, it'd be interesting to move to the next line and 113# inspect the value of +foo+ again. You can do that by pressing +n+: 114# 115# (rdb:1) n # goes to next line 116# (rdb:1) p foo 117# nil 118# 119# You now know that the original value of +foo+ was nil, and that it 120# still was nil after calling +get_foo+. 121# 122# Other useful commands for navigating through your code are: 123# 124# +c+:: 125# Runs the program until it either exists or encounters another breakpoint. 126# You usually press +c+ when you are finished debugging your program and 127# want to resume its execution. 128# +s+:: 129# Steps into method definition. In the previous example, +s+ would take you 130# inside the method definition of +get_foo+. 131# +r+:: 132# Restart the program. 133# +q+:: 134# Quit the program. 135# 136# === Inspecting variables 137# 138# You can use the debugger to easily inspect both local and global variables. 139# We've seen how to inspect local variables before: 140# 141# (rdb:1) p my_arg 142# 42 143# 144# You can also pretty print the result of variables or expressions: 145# 146# (rdb:1) pp %w{a very long long array containing many words} 147# ["a", 148# "very", 149# "long", 150# ... 151# ] 152# 153# You can list all local variables with +v l+: 154# 155# (rdb:1) v l 156# foo => "hello" 157# 158# Similarly, you can show all global variables with +v g+: 159# 160# (rdb:1) v g 161# all global variables 162# 163# Finally, you can omit +p+ if you simply want to evaluate a variable or 164# expression 165# 166# (rdb:1) 5**2 167# 25 168# 169# === Going beyond basics 170# 171# Ruby Debug provides more advanced functionalities like switching 172# between threads, setting breakpoints and watch expressions, and more. 173# The full list of commands is available at any time by pressing +h+. 174# 175# == Staying out of trouble 176# 177# Make sure you remove every instance of +require 'debug'+ before 178# shipping your code. Failing to do so may result in your program 179# hanging unpredictably. 180# 181# Debug is not available in safe mode. 182 183class DEBUGGER__ 184 MUTEX = Mutex.new # :nodoc: 185 186 class Context # :nodoc: 187 DEBUG_LAST_CMD = [] 188 189 begin 190 require 'readline' 191 def readline(prompt, hist) 192 Readline::readline(prompt, hist) 193 end 194 rescue LoadError 195 def readline(prompt, hist) 196 STDOUT.print prompt 197 STDOUT.flush 198 line = STDIN.gets 199 exit unless line 200 line.chomp! 201 line 202 end 203 USE_READLINE = false 204 end 205 206 def initialize 207 if Thread.current == Thread.main 208 @stop_next = 1 209 else 210 @stop_next = 0 211 end 212 @last_file = nil 213 @file = nil 214 @line = nil 215 @no_step = nil 216 @frames = [] 217 @finish_pos = 0 218 @trace = false 219 @catch = "StandardError" 220 @suspend_next = false 221 end 222 223 def stop_next(n=1) 224 @stop_next = n 225 end 226 227 def set_suspend 228 @suspend_next = true 229 end 230 231 def clear_suspend 232 @suspend_next = false 233 end 234 235 def suspend_all 236 DEBUGGER__.suspend 237 end 238 239 def resume_all 240 DEBUGGER__.resume 241 end 242 243 def check_suspend 244 while MUTEX.synchronize { 245 if @suspend_next 246 DEBUGGER__.waiting.push Thread.current 247 @suspend_next = false 248 true 249 end 250 } 251 end 252 end 253 254 def trace? 255 @trace 256 end 257 258 def set_trace(arg) 259 @trace = arg 260 end 261 262 def stdout 263 DEBUGGER__.stdout 264 end 265 266 def break_points 267 DEBUGGER__.break_points 268 end 269 270 def display 271 DEBUGGER__.display 272 end 273 274 def context(th) 275 DEBUGGER__.context(th) 276 end 277 278 def set_trace_all(arg) 279 DEBUGGER__.set_trace(arg) 280 end 281 282 def set_last_thread(th) 283 DEBUGGER__.set_last_thread(th) 284 end 285 286 def debug_eval(str, binding) 287 begin 288 eval(str, binding) 289 rescue StandardError, ScriptError => e 290 at = eval("caller(1)", binding) 291 stdout.printf "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '') 292 for i in at 293 stdout.printf "\tfrom %s\n", i 294 end 295 throw :debug_error 296 end 297 end 298 299 def debug_silent_eval(str, binding) 300 begin 301 eval(str, binding) 302 rescue StandardError, ScriptError 303 nil 304 end 305 end 306 307 def var_list(ary, binding) 308 ary.sort! 309 for v in ary 310 stdout.printf " %s => %s\n", v, eval(v.to_s, binding).inspect 311 end 312 end 313 314 def debug_variable_info(input, binding) 315 case input 316 when /^\s*g(?:lobal)?\s*$/ 317 var_list(global_variables, binding) 318 319 when /^\s*l(?:ocal)?\s*$/ 320 var_list(eval("local_variables", binding), binding) 321 322 when /^\s*i(?:nstance)?\s+/ 323 obj = debug_eval($', binding) 324 var_list(obj.instance_variables, obj.instance_eval{binding()}) 325 326 when /^\s*c(?:onst(?:ant)?)?\s+/ 327 obj = debug_eval($', binding) 328 unless obj.kind_of? Module 329 stdout.print "Should be Class/Module: ", $', "\n" 330 else 331 var_list(obj.constants, obj.module_eval{binding()}) 332 end 333 end 334 end 335 336 def debug_method_info(input, binding) 337 case input 338 when /^i(:?nstance)?\s+/ 339 obj = debug_eval($', binding) 340 341 len = 0 342 for v in obj.methods.sort 343 len += v.size + 1 344 if len > 70 345 len = v.size + 1 346 stdout.print "\n" 347 end 348 stdout.print v, " " 349 end 350 stdout.print "\n" 351 352 else 353 obj = debug_eval(input, binding) 354 unless obj.kind_of? Module 355 stdout.print "Should be Class/Module: ", input, "\n" 356 else 357 len = 0 358 for v in obj.instance_methods(false).sort 359 len += v.size + 1 360 if len > 70 361 len = v.size + 1 362 stdout.print "\n" 363 end 364 stdout.print v, " " 365 end 366 stdout.print "\n" 367 end 368 end 369 end 370 371 def thnum 372 num = DEBUGGER__.instance_eval{@thread_list[Thread.current]} 373 unless num 374 DEBUGGER__.make_thread_list 375 num = DEBUGGER__.instance_eval{@thread_list[Thread.current]} 376 end 377 num 378 end 379 380 def debug_command(file, line, id, binding) 381 MUTEX.lock 382 unless defined?($debugger_restart) and $debugger_restart 383 callcc{|c| $debugger_restart = c} 384 end 385 set_last_thread(Thread.current) 386 frame_pos = 0 387 binding_file = file 388 binding_line = line 389 previous_line = nil 390 if ENV['EMACS'] 391 stdout.printf "\032\032%s:%d:\n", binding_file, binding_line 392 else 393 stdout.printf "%s:%d:%s", binding_file, binding_line, 394 line_at(binding_file, binding_line) 395 end 396 @frames[0] = [binding, file, line, id] 397 display_expressions(binding) 398 prompt = true 399 while prompt and input = readline("(rdb:%d) "%thnum(), true) 400 catch(:debug_error) do 401 if input == "" 402 next unless DEBUG_LAST_CMD[0] 403 input = DEBUG_LAST_CMD[0] 404 stdout.print input, "\n" 405 else 406 DEBUG_LAST_CMD[0] = input 407 end 408 409 case input 410 when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/ 411 if defined?( $2 ) 412 if $1 == 'on' 413 set_trace_all true 414 else 415 set_trace_all false 416 end 417 elsif defined?( $1 ) 418 if $1 == 'on' 419 set_trace true 420 else 421 set_trace false 422 end 423 end 424 if trace? 425 stdout.print "Trace on.\n" 426 else 427 stdout.print "Trace off.\n" 428 end 429 430 when /^\s*b(?:reak)?\s+(?:(.+):)?([^.:]+)$/ 431 pos = $2 432 if $1 433 klass = debug_silent_eval($1, binding) 434 file = $1 435 end 436 if pos =~ /^\d+$/ 437 pname = pos 438 pos = pos.to_i 439 else 440 pname = pos = pos.intern.id2name 441 end 442 break_points.push [true, 0, klass || file, pos] 443 stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, klass || file, pname 444 445 when /^\s*b(?:reak)?\s+(.+)[#.]([^.:]+)$/ 446 pos = $2.intern.id2name 447 klass = debug_eval($1, binding) 448 break_points.push [true, 0, klass, pos] 449 stdout.printf "Set breakpoint %d at %s.%s\n", break_points.size, klass, pos 450 451 when /^\s*wat(?:ch)?\s+(.+)$/ 452 exp = $1 453 break_points.push [true, 1, exp] 454 stdout.printf "Set watchpoint %d:%s\n", break_points.size, exp 455 456 when /^\s*b(?:reak)?$/ 457 if break_points.find{|b| b[1] == 0} 458 n = 1 459 stdout.print "Breakpoints:\n" 460 break_points.each do |b| 461 if b[0] and b[1] == 0 462 stdout.printf " %d %s:%s\n", n, b[2], b[3] 463 end 464 n += 1 465 end 466 end 467 if break_points.find{|b| b[1] == 1} 468 n = 1 469 stdout.print "\n" 470 stdout.print "Watchpoints:\n" 471 for b in break_points 472 if b[0] and b[1] == 1 473 stdout.printf " %d %s\n", n, b[2] 474 end 475 n += 1 476 end 477 end 478 if break_points.size == 0 479 stdout.print "No breakpoints\n" 480 else 481 stdout.print "\n" 482 end 483 484 when /^\s*del(?:ete)?(?:\s+(\d+))?$/ 485 pos = $1 486 unless pos 487 input = readline("Clear all breakpoints? (y/n) ", false) 488 if input == "y" 489 for b in break_points 490 b[0] = false 491 end 492 end 493 else 494 pos = pos.to_i 495 if break_points[pos-1] 496 break_points[pos-1][0] = false 497 else 498 stdout.printf "Breakpoint %d is not defined\n", pos 499 end 500 end 501 502 when /^\s*disp(?:lay)?\s+(.+)$/ 503 exp = $1 504 display.push [true, exp] 505 stdout.printf "%d: ", display.size 506 display_expression(exp, binding) 507 508 when /^\s*disp(?:lay)?$/ 509 display_expressions(binding) 510 511 when /^\s*undisp(?:lay)?(?:\s+(\d+))?$/ 512 pos = $1 513 unless pos 514 input = readline("Clear all expressions? (y/n) ", false) 515 if input == "y" 516 for d in display 517 d[0] = false 518 end 519 end 520 else 521 pos = pos.to_i 522 if display[pos-1] 523 display[pos-1][0] = false 524 else 525 stdout.printf "Display expression %d is not defined\n", pos 526 end 527 end 528 529 when /^\s*c(?:ont)?$/ 530 prompt = false 531 532 when /^\s*s(?:tep)?(?:\s+(\d+))?$/ 533 if $1 534 lev = $1.to_i 535 else 536 lev = 1 537 end 538 @stop_next = lev 539 prompt = false 540 541 when /^\s*n(?:ext)?(?:\s+(\d+))?$/ 542 if $1 543 lev = $1.to_i 544 else 545 lev = 1 546 end 547 @stop_next = lev 548 @no_step = @frames.size - frame_pos 549 prompt = false 550 551 when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/ 552 display_frames(frame_pos) 553 554 when /^\s*l(?:ist)?(?:\s+(.+))?$/ 555 if not $1 556 b = previous_line ? previous_line + 10 : binding_line - 5 557 e = b + 9 558 elsif $1 == '-' 559 b = previous_line ? previous_line - 10 : binding_line - 5 560 e = b + 9 561 else 562 b, e = $1.split(/[-,]/) 563 if e 564 b = b.to_i 565 e = e.to_i 566 else 567 b = b.to_i - 5 568 e = b + 9 569 end 570 end 571 previous_line = b 572 display_list(b, e, binding_file, binding_line) 573 574 when /^\s*up(?:\s+(\d+))?$/ 575 previous_line = nil 576 if $1 577 lev = $1.to_i 578 else 579 lev = 1 580 end 581 frame_pos += lev 582 if frame_pos >= @frames.size 583 frame_pos = @frames.size - 1 584 stdout.print "At toplevel\n" 585 end 586 binding, binding_file, binding_line = @frames[frame_pos] 587 stdout.print format_frame(frame_pos) 588 589 when /^\s*down(?:\s+(\d+))?$/ 590 previous_line = nil 591 if $1 592 lev = $1.to_i 593 else 594 lev = 1 595 end 596 frame_pos -= lev 597 if frame_pos < 0 598 frame_pos = 0 599 stdout.print "At stack bottom\n" 600 end 601 binding, binding_file, binding_line = @frames[frame_pos] 602 stdout.print format_frame(frame_pos) 603 604 when /^\s*fin(?:ish)?$/ 605 if frame_pos == @frames.size 606 stdout.print "\"finish\" not meaningful in the outermost frame.\n" 607 else 608 @finish_pos = @frames.size - frame_pos 609 frame_pos = 0 610 prompt = false 611 end 612 613 when /^\s*cat(?:ch)?(?:\s+(.+))?$/ 614 if $1 615 excn = $1 616 if excn == 'off' 617 @catch = nil 618 stdout.print "Clear catchpoint.\n" 619 else 620 @catch = excn 621 stdout.printf "Set catchpoint %s.\n", @catch 622 end 623 else 624 if @catch 625 stdout.printf "Catchpoint %s.\n", @catch 626 else 627 stdout.print "No catchpoint.\n" 628 end 629 end 630 631 when /^\s*q(?:uit)?$/ 632 input = readline("Really quit? (y/n) ", false) 633 if input == "y" 634 exit! # exit -> exit!: No graceful way to stop threads... 635 end 636 637 when /^\s*v(?:ar)?\s+/ 638 debug_variable_info($', binding) 639 640 when /^\s*m(?:ethod)?\s+/ 641 debug_method_info($', binding) 642 643 when /^\s*th(?:read)?\s+/ 644 if DEBUGGER__.debug_thread_info($', binding) == :cont 645 prompt = false 646 end 647 648 when /^\s*pp\s+/ 649 PP.pp(debug_eval($', binding), stdout) 650 651 when /^\s*p\s+/ 652 stdout.printf "%s\n", debug_eval($', binding).inspect 653 654 when /^\s*r(?:estart)?$/ 655 $debugger_restart.call 656 657 when /^\s*h(?:elp)?$/ 658 debug_print_help() 659 660 else 661 v = debug_eval(input, binding) 662 stdout.printf "%s\n", v.inspect 663 end 664 end 665 end 666 MUTEX.unlock 667 resume_all 668 end 669 670 def debug_print_help 671 stdout.print <<EOHELP 672Debugger help v.-0.002b 673Commands 674 b[reak] [file:|class:]<line|method> 675 b[reak] [class.]<line|method> 676 set breakpoint to some position 677 wat[ch] <expression> set watchpoint to some expression 678 cat[ch] (<exception>|off) set catchpoint to an exception 679 b[reak] list breakpoints 680 cat[ch] show catchpoint 681 del[ete][ nnn] delete some or all breakpoints 682 disp[lay] <expression> add expression into display expression list 683 undisp[lay][ nnn] delete one particular or all display expressions 684 c[ont] run until program ends or hit breakpoint 685 s[tep][ nnn] step (into methods) one line or till line nnn 686 n[ext][ nnn] go over one line or till line nnn 687 w[here] display frames 688 f[rame] alias for where 689 l[ist][ (-|nn-mm)] list program, - lists backwards 690 nn-mm lists given lines 691 up[ nn] move to higher frame 692 down[ nn] move to lower frame 693 fin[ish] return to outer frame 694 tr[ace] (on|off) set trace mode of current thread 695 tr[ace] (on|off) all set trace mode of all threads 696 q[uit] exit from debugger 697 v[ar] g[lobal] show global variables 698 v[ar] l[ocal] show local variables 699 v[ar] i[nstance] <object> show instance variables of object 700 v[ar] c[onst] <object> show constants of object 701 m[ethod] i[nstance] <obj> show methods of object 702 m[ethod] <class|module> show instance methods of class or module 703 th[read] l[ist] list all threads 704 th[read] c[ur[rent]] show current thread 705 th[read] [sw[itch]] <nnn> switch thread context to nnn 706 th[read] stop <nnn> stop thread nnn 707 th[read] resume <nnn> resume thread nnn 708 pp expression evaluate expression and pretty_print its value 709 p expression evaluate expression and print its value 710 r[estart] restart program 711 h[elp] print this help 712 <everything else> evaluate 713EOHELP 714 end 715 716 def display_expressions(binding) 717 n = 1 718 for d in display 719 if d[0] 720 stdout.printf "%d: ", n 721 display_expression(d[1], binding) 722 end 723 n += 1 724 end 725 end 726 727 def display_expression(exp, binding) 728 stdout.printf "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s 729 end 730 731 def frame_set_pos(file, line) 732 if @frames[0] 733 @frames[0][1] = file 734 @frames[0][2] = line 735 end 736 end 737 738 def display_frames(pos) 739 0.upto(@frames.size - 1) do |n| 740 if n == pos 741 stdout.print "--> " 742 else 743 stdout.print " " 744 end 745 stdout.print format_frame(n) 746 end 747 end 748 749 def format_frame(pos) 750 _, file, line, id = @frames[pos] 751 sprintf "#%d %s:%s%s\n", pos + 1, file, line, 752 (id ? ":in `#{id.id2name}'" : "") 753 end 754 755 def display_list(b, e, file, line) 756 stdout.printf "[%d, %d] in %s\n", b, e, file 757 if lines = SCRIPT_LINES__[file] and lines != true 758 b.upto(e) do |n| 759 if n > 0 && lines[n-1] 760 if n == line 761 stdout.printf "=> %d %s\n", n, lines[n-1].chomp 762 else 763 stdout.printf " %d %s\n", n, lines[n-1].chomp 764 end 765 end 766 end 767 else 768 stdout.printf "No sourcefile available for %s\n", file 769 end 770 end 771 772 def line_at(file, line) 773 lines = SCRIPT_LINES__[file] 774 if lines 775 return "\n" if lines == true 776 line = lines[line-1] 777 return "\n" unless line 778 return line 779 end 780 return "\n" 781 end 782 783 def debug_funcname(id) 784 if id.nil? 785 "toplevel" 786 else 787 id.id2name 788 end 789 end 790 791 def check_break_points(file, klass, pos, binding, id) 792 return false if break_points.empty? 793 n = 1 794 for b in break_points 795 if b[0] # valid 796 if b[1] == 0 # breakpoint 797 if (b[2] == file and b[3] == pos) or 798 (klass and b[2] == klass and b[3] == pos) 799 stdout.printf "Breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos 800 return true 801 end 802 elsif b[1] == 1 # watchpoint 803 if debug_silent_eval(b[2], binding) 804 stdout.printf "Watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos 805 return true 806 end 807 end 808 end 809 n += 1 810 end 811 return false 812 end 813 814 def excn_handle(file, line, id, binding) 815 if $!.class <= SystemExit 816 set_trace_func nil 817 exit 818 end 819 820 if @catch and ($!.class.ancestors.find { |e| e.to_s == @catch }) 821 stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.class 822 fs = @frames.size 823 tb = caller(0)[-fs..-1] 824 if tb 825 for i in tb 826 stdout.printf "\tfrom %s\n", i 827 end 828 end 829 suspend_all 830 debug_command(file, line, id, binding) 831 end 832 end 833 834 def trace_func(event, file, line, id, binding, klass) 835 Tracer.trace_func(event, file, line, id, binding, klass) if trace? 836 context(Thread.current).check_suspend 837 @file = file 838 @line = line 839 case event 840 when 'line' 841 frame_set_pos(file, line) 842 if !@no_step or @frames.size == @no_step 843 @stop_next -= 1 844 @stop_next = -1 if @stop_next < 0 845 elsif @frames.size < @no_step 846 @stop_next = 0 # break here before leaving... 847 else 848 # nothing to do. skipped. 849 end 850 if @stop_next == 0 or check_break_points(file, nil, line, binding, id) 851 @no_step = nil 852 suspend_all 853 debug_command(file, line, id, binding) 854 end 855 856 when 'call' 857 @frames.unshift [binding, file, line, id] 858 if check_break_points(file, klass, id.id2name, binding, id) 859 suspend_all 860 debug_command(file, line, id, binding) 861 end 862 863 when 'c-call' 864 frame_set_pos(file, line) 865 866 when 'class' 867 @frames.unshift [binding, file, line, id] 868 869 when 'return', 'end' 870 if @frames.size == @finish_pos 871 @stop_next = 1 872 @finish_pos = 0 873 end 874 @frames.shift 875 876 when 'raise' 877 excn_handle(file, line, id, binding) 878 879 end 880 @last_file = file 881 end 882 end 883 884 trap("INT") { DEBUGGER__.interrupt } 885 @last_thread = Thread::main 886 @max_thread = 1 887 @thread_list = {Thread::main => 1} 888 @break_points = [] 889 @display = [] 890 @waiting = [] 891 @stdout = STDOUT 892 893 class << DEBUGGER__ 894 # Returns the IO used as stdout. Defaults to STDOUT 895 def stdout 896 @stdout 897 end 898 899 # Sets the IO used as stdout. Defaults to STDOUT 900 def stdout=(s) 901 @stdout = s 902 end 903 904 # Returns the display expression list 905 # 906 # See DEBUGGER__ for more usage 907 def display 908 @display 909 end 910 911 # Returns the list of break points where execution will be stopped. 912 # 913 # See DEBUGGER__ for more useage 914 def break_points 915 @break_points 916 end 917 918 # Returns the list of waiting threads. 919 # 920 # When stepping through the traces of a function, thread gets suspended, to 921 # be resumed later. 922 def waiting 923 @waiting 924 end 925 926 def set_trace( arg ) 927 MUTEX.synchronize do 928 make_thread_list 929 for th, in @thread_list 930 context(th).set_trace arg 931 end 932 end 933 arg 934 end 935 936 def set_last_thread(th) 937 @last_thread = th 938 end 939 940 def suspend 941 MUTEX.synchronize do 942 make_thread_list 943 for th, in @thread_list 944 next if th == Thread.current 945 context(th).set_suspend 946 end 947 end 948 # Schedule other threads to suspend as soon as possible. 949 Thread.pass 950 end 951 952 def resume 953 MUTEX.synchronize do 954 make_thread_list 955 @thread_list.each do |th,| 956 next if th == Thread.current 957 context(th).clear_suspend 958 end 959 waiting.each do |th| 960 th.run 961 end 962 waiting.clear 963 end 964 # Schedule other threads to restart as soon as possible. 965 Thread.pass 966 end 967 968 def context(thread=Thread.current) 969 c = thread[:__debugger_data__] 970 unless c 971 thread[:__debugger_data__] = c = Context.new 972 end 973 c 974 end 975 976 def interrupt 977 context(@last_thread).stop_next 978 end 979 980 def get_thread(num) 981 th = @thread_list.key(num) 982 unless th 983 @stdout.print "No thread ##{num}\n" 984 throw :debug_error 985 end 986 th 987 end 988 989 def thread_list(num) 990 th = get_thread(num) 991 if th == Thread.current 992 @stdout.print "+" 993 else 994 @stdout.print " " 995 end 996 @stdout.printf "%d ", num 997 @stdout.print th.inspect, "\t" 998 file = context(th).instance_eval{@file} 999 if file 1000 @stdout.print file,":",context(th).instance_eval{@line} 1001 end 1002 @stdout.print "\n" 1003 end 1004 1005 def thread_list_all 1006 for th in @thread_list.values.sort 1007 thread_list(th) 1008 end 1009 end 1010 1011 def make_thread_list 1012 hash = {} 1013 for th in Thread::list 1014 if @thread_list.key? th 1015 hash[th] = @thread_list[th] 1016 else 1017 @max_thread += 1 1018 hash[th] = @max_thread 1019 end 1020 end 1021 @thread_list = hash 1022 end 1023 1024 def debug_thread_info(input, binding) 1025 case input 1026 when /^l(?:ist)?/ 1027 make_thread_list 1028 thread_list_all 1029 1030 when /^c(?:ur(?:rent)?)?$/ 1031 make_thread_list 1032 thread_list(@thread_list[Thread.current]) 1033 1034 when /^(?:sw(?:itch)?\s+)?(\d+)/ 1035 make_thread_list 1036 th = get_thread($1.to_i) 1037 if th == Thread.current 1038 @stdout.print "It's the current thread.\n" 1039 else 1040 thread_list(@thread_list[th]) 1041 context(th).stop_next 1042 th.run 1043 return :cont 1044 end 1045 1046 when /^stop\s+(\d+)/ 1047 make_thread_list 1048 th = get_thread($1.to_i) 1049 if th == Thread.current 1050 @stdout.print "It's the current thread.\n" 1051 elsif th.stop? 1052 @stdout.print "Already stopped.\n" 1053 else 1054 thread_list(@thread_list[th]) 1055 context(th).suspend 1056 end 1057 1058 when /^resume\s+(\d+)/ 1059 make_thread_list 1060 th = get_thread($1.to_i) 1061 if th == Thread.current 1062 @stdout.print "It's the current thread.\n" 1063 elsif !th.stop? 1064 @stdout.print "Already running." 1065 else 1066 thread_list(@thread_list[th]) 1067 th.run 1068 end 1069 end 1070 end 1071 end 1072 1073 stdout.printf "Debug.rb\n" 1074 stdout.printf "Emacs support available.\n\n" 1075 RubyVM::InstructionSequence.compile_option = { 1076 trace_instruction: true 1077 } 1078 set_trace_func proc { |event, file, line, id, binding, klass, *rest| 1079 DEBUGGER__.context.trace_func event, file, line, id, binding, klass 1080 } 1081end 1082