Module: DEBUGGER__
- Defined in:
- lib/debug/client.rb,
lib/debug/color.rb,
lib/debug/local.rb,
lib/debug/config.rb,
lib/debug/server.rb,
lib/debug/tracer.rb,
lib/debug/console.rb,
lib/debug/version.rb,
lib/debug/breakpoint.rb,
lib/debug/frame_info.rb,
lib/debug/server_cdp.rb,
lib/debug/server_dap.rb,
lib/debug/thread_client.rb,
lib/debug/abbrev_command.rb,
lib/debug/irb_integration.rb,
lib/debug/source_repository.rb,
lib/debug/dap_custom/traceInspector.rb,
lib/debug/session.rb
Overview
$VERBOSE = true
Defined Under Namespace
Modules: Color, DAP_TraceInspector, ForkInterceptor, GlobalVariablesHelper, IrbPatch, MultiProcessGroup, SkipPathHelper, TrapInterceptor, UI_CDP, UI_DAP Classes: AbbrevCommand, Breakpoint, CallTracer, CatchBreakpoint, CheckBreakpoint, Client, CommandLineOptionError, Config, Console, ExceptionTracer, FrameInfo, ISeqBreakpoint, LimitedPP, LineBreakpoint, LineTracer, MethodBreakpoint, NaiveString, ObjectTracer, PostmortemError, PresetCommands, ProcessGroup, Session, SessionCommand, SourceRepository, ThreadClient, Tracer, UI_Base, UI_LocalConsole, UI_ServerBase, UI_TcpServer, UI_UnixDomainServer, WatchIVarBreakpoint
Constant Summary collapse
- LOG_LEVELS =
{ UNKNOWN: 0, FATAL: 1, ERROR: 2, WARN: 3, INFO: 4, DEBUG: 5 }.freeze
- CONFIG_SET =
{ # UI setting log_level: ['RUBY_DEBUG_LOG_LEVEL', "UI: Log level same as Logger", :loglevel, "WARN"], show_src_lines: ['RUBY_DEBUG_SHOW_SRC_LINES', "UI: Show n lines source code on breakpoint", :int, "10"], show_evaledsrc: ['RUBY_DEBUG_SHOW_EVALEDSRC', "UI: Show actually evaluated source", :bool, "false"], show_frames: ['RUBY_DEBUG_SHOW_FRAMES', "UI: Show n frames on breakpoint", :int, "2"], use_short_path: ['RUBY_DEBUG_USE_SHORT_PATH', "UI: Show shorten PATH (like $(Gem)/foo.rb)", :bool, "false"], no_color: ['RUBY_DEBUG_NO_COLOR', "UI: Do not use colorize", :bool, "false"], no_sigint_hook: ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT", :bool, "false"], no_reline: ['RUBY_DEBUG_NO_RELINE', "UI: Do not use Reline library", :bool, "false"], no_hint: ['RUBY_DEBUG_NO_HINT', "UI: Do not show the hint on the REPL", :bool, "false"], no_lineno: ['RUBY_DEBUG_NO_LINENO', "UI: Do not show line numbers", :bool, "false"], irb_console: ["RUBY_DEBUG_IRB_CONSOLE", "UI: Use IRB as the console", :bool, "false"], # control setting skip_path: ['RUBY_DEBUG_SKIP_PATH', "CONTROL: Skip showing/entering frames for given paths", :path], skip_nosrc: ['RUBY_DEBUG_SKIP_NOSRC', "CONTROL: Skip on no source code lines", :bool, "false"], keep_alloc_site:['RUBY_DEBUG_KEEP_ALLOC_SITE',"CONTROL: Keep allocation site and p, pp shows it", :bool, "false"], postmortem: ['RUBY_DEBUG_POSTMORTEM', "CONTROL: Enable postmortem debug", :bool, "false"], fork_mode: ['RUBY_DEBUG_FORK_MODE', "CONTROL: Control which process activates a debugger after fork (both/parent/child)", :forkmode, "both"], sigdump_sig: ['RUBY_DEBUG_SIGDUMP_SIG', "CONTROL: Sigdump signal", :bool, "false"], # boot setting nonstop: ['RUBY_DEBUG_NONSTOP', "BOOT: Nonstop mode", :bool, "false"], stop_at_load: ['RUBY_DEBUG_STOP_AT_LOAD',"BOOT: Stop at just loading location", :bool, "false"], init_script: ['RUBY_DEBUG_INIT_SCRIPT', "BOOT: debug command script path loaded at first stop"], commands: ['RUBY_DEBUG_COMMANDS', "BOOT: debug commands invoked at first stop. Commands should be separated by `;;`"], no_rc: ['RUBY_DEBUG_NO_RC', "BOOT: ignore loading ~/.rdbgrc(.rb)", :bool, "false"], history_file: ['RUBY_DEBUG_HISTORY_FILE',"BOOT: history file", :string, "~/.rdbg_history"], save_history: ['RUBY_DEBUG_SAVE_HISTORY',"BOOT: maximum save history lines", :int, "10000"], # remote setting open: ['RUBY_DEBUG_OPEN', "REMOTE: Open remote port (same as `rdbg --open` option)"], port: ['RUBY_DEBUG_PORT', "REMOTE: TCP/IP remote debugging: port"], host: ['RUBY_DEBUG_HOST', "REMOTE: TCP/IP remote debugging: host", :string, "127.0.0.1"], sock_path: ['RUBY_DEBUG_SOCK_PATH', "REMOTE: UNIX Domain Socket remote debugging: socket path"], sock_dir: ['RUBY_DEBUG_SOCK_DIR', "REMOTE: UNIX Domain Socket remote debugging: socket directory"], local_fs_map: ['RUBY_DEBUG_LOCAL_FS_MAP', "REMOTE: Specify local fs map", :path_map], skip_bp: ['RUBY_DEBUG_SKIP_BP', "REMOTE: Skip breakpoints if no clients are attached", :bool, 'false'], cookie: ['RUBY_DEBUG_COOKIE', "REMOTE: Cookie for negotiation"], session_name: ['RUBY_DEBUG_SESSION_NAME', "REMOTE: Session name for differentiating multiple sessions"], chrome_path: ['RUBY_DEBUG_CHROME_PATH', "REMOTE: Platform dependent path of Chrome (For more information, See [here](https://github.com/ruby/debug/pull/334/files#diff-5fc3d0a901379a95bc111b86cf0090b03f857edfd0b99a0c1537e26735698453R55-R64))"], # obsolete parent_on_fork: ['RUBY_DEBUG_PARENT_ON_FORK', "OBSOLETE: Keep debugging parent process on fork", :bool, "false"], }.freeze
- CONFIG_MAP =
CONFIG_SET.map{|k, (ev, _)| [k, ev]}.to_h.freeze
- CONFIG =
- VERSION =
"1.9.2"
- M_INSTANCE_VARIABLES =
method(:instance_variables).unbind
- M_INSTANCE_VARIABLE_GET =
method(:instance_variable_get).unbind
- M_CLASS =
method(:class).unbind
- M_SINGLETON_CLASS =
method(:singleton_class).unbind
- M_KIND_OF_P =
method(:kind_of?).unbind
- M_RESPOND_TO_P =
method(:respond_to?).unbind
- M_METHOD =
method(:method).unbind
- M_OBJECT_ID =
method(:object_id).unbind
- M_NAME =
method(:name).unbind
- SHORT_INSPECT_LENGTH =
Inspector
40
Class Method Summary collapse
- .add_catch_breakpoint(pat) ⇒ Object
-
.add_line_breakpoint(file, line, **kw) ⇒ Object
manual configuration methods.
-
.check_dir_authority(path) ⇒ Object
Unix domain socket configuration.
- .check_loglevel(level) ⇒ Object
- .commands ⇒ Object
-
.compare_path(a, b) ⇒ Object
For case insensitive file system (like Windows) Note that this check is not enough because case sensitive/insensitive is depend on the file system.
- .create_unix_domain_socket_name(base_dir = unix_domain_socket_dir) ⇒ Object
- .create_unix_domain_socket_name_prefix(base_dir = unix_domain_socket_dir) ⇒ Object
- .debug(&b) ⇒ Object
- .help ⇒ Object
- .helps ⇒ Object
- .info(msg) ⇒ Object
- .load_rc ⇒ Object
- .log(level, msg) ⇒ Object
- .open(host: nil, port: , sock_path: nil, sock_dir: nil, nonstop: false, **kw) ⇒ Object
- .open_tcp(host: nil, port:, nonstop: false, **kw) ⇒ Object
- .open_unix(sock_path: nil, sock_dir: nil, nonstop: false, **kw) ⇒ Object
-
.parse_help ⇒ Object
Help.
-
.require_location ⇒ Object
String for requiring location nil for -r.
- .safe_inspect(obj, max_length: SHORT_INSPECT_LENGTH, short: false) ⇒ Object
-
.setup_initial_suspend ⇒ Object
boot utilities.
- .skip? ⇒ Boolean
- .skip_all ⇒ Object
-
.start(nonstop: false, **kw) ⇒ Object
start methods.
- .step_in(&b) ⇒ Object
- .unix_domain_socket_dir ⇒ Object
- .unix_domain_socket_homedir ⇒ Object
- .unix_domain_socket_tmpdir ⇒ Object
- .warn(msg) ⇒ Object
Class Method Details
.add_catch_breakpoint(pat) ⇒ Object
2187 2188 2189 |
# File 'lib/debug/session.rb', line 2187 def self.add_catch_breakpoint pat ::DEBUGGER__::SESSION.add_catch_breakpoint pat end |
.add_line_breakpoint(file, line, **kw) ⇒ Object
manual configuration methods
2183 2184 2185 |
# File 'lib/debug/session.rb', line 2183 def self.add_line_breakpoint file, line, **kw ::DEBUGGER__::SESSION.add_line_breakpoint file, line, **kw end |
.check_dir_authority(path) ⇒ Object
Unix domain socket configuration
463 464 465 466 467 468 469 470 471 472 473 474 475 |
# File 'lib/debug/config.rb', line 463 def self. path fs = File.stat(path) unless (dir_uid = fs.uid) == (uid = Process.uid) raise "#{path} uid is #{dir_uid}, but Process.uid is #{uid}" end if fs.world_writable? && !fs.sticky? raise "#{path} is world writable but not sticky" end path end |
.check_loglevel(level) ⇒ Object
2387 2388 2389 2390 2391 |
# File 'lib/debug/session.rb', line 2387 def self.check_loglevel level lv = LOG_LEVELS[level] config_lv = LOG_LEVELS[CONFIG[:log_level]] lv <= config_lv end |
.commands ⇒ Object
570 571 572 |
# File 'lib/debug/config.rb', line 570 def self.commands (defined?(@commands) && @commands) || (parse_help; @commands) end |
.compare_path(a, b) ⇒ Object
For case insensitive file system (like Windows) Note that this check is not enough because case sensitive/insensitive is depend on the file system. So this check is only roughly estimation.
2433 2434 2435 |
# File 'lib/debug/session.rb', line 2433 def self.compare_path(a, b) a&.downcase == b&.downcase end |
.create_unix_domain_socket_name(base_dir = unix_domain_socket_dir) ⇒ Object
521 522 523 524 525 526 |
# File 'lib/debug/config.rb', line 521 def self.create_unix_domain_socket_name(base_dir = unix_domain_socket_dir) suffix = "-#{Process.pid}" name = CONFIG[:session_name] suffix << "-#{name}" if name create_unix_domain_socket_name_prefix(base_dir) + suffix end |
.create_unix_domain_socket_name_prefix(base_dir = unix_domain_socket_dir) ⇒ Object
517 518 519 |
# File 'lib/debug/config.rb', line 517 def self.create_unix_domain_socket_name_prefix(base_dir = unix_domain_socket_dir) File.join(base_dir, "rdbg") end |
.debug(&b) ⇒ Object
2393 2394 2395 2396 2397 |
# File 'lib/debug/session.rb', line 2393 def self.debug(&b) if check_loglevel :DEBUG log :DEBUG, b.call end end |
.help ⇒ Object
574 575 576 577 578 579 580 581 582 583 584 585 |
# File 'lib/debug/config.rb', line 574 def self.help r = [] self.helps.each{|cat, cmds| r << "### #{cat}" r << '' cmds.each{|_, desc| r << desc } r << '' } r.join("\n") end |
.helps ⇒ Object
566 567 568 |
# File 'lib/debug/config.rb', line 566 def self.helps (defined?(@helps) && @helps) || parse_help end |
.info(msg) ⇒ Object
2383 2384 2385 |
# File 'lib/debug/session.rb', line 2383 def self.info msg log :INFO, msg end |
.load_rc ⇒ Object
2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 |
# File 'lib/debug/session.rb', line 2303 def self.load_rc [[File.('~/.rdbgrc'), true], [File.('~/.rdbgrc.rb'), true], # ['./.rdbgrc', true], # disable because of security concern [CONFIG[:init_script], false], ].each{|(path, rc)| next unless path next if rc && CONFIG[:no_rc] # ignore rc if File.file? path if path.end_with?('.rb') load path else ::DEBUGGER__::SESSION.add_preset_commands path, File.readlines(path) end elsif !rc warn "Not found: #{path}" end } # given debug commands if CONFIG[:commands] cmds = CONFIG[:commands].split(';;') ::DEBUGGER__::SESSION.add_preset_commands "commands", cmds, kick: false, continue: false end end |
.log(level, msg) ⇒ Object
2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 |
# File 'lib/debug/session.rb', line 2399 def self.log level, msg if check_loglevel level @logfile = STDERR unless defined? @logfile return if @logfile.closed? if defined? SESSION pi = SESSION.process_info process_info = pi ? "[#{pi}]" : nil end if level == :WARN # :WARN on debugger is general information @logfile.puts "DEBUGGER#{process_info}: #{msg}" @logfile.flush else @logfile.puts "DEBUGGER#{process_info} (#{level}): #{msg}" @logfile.flush end end end |
.open(host: nil, port: , sock_path: nil, sock_dir: nil, nonstop: false, **kw) ⇒ Object
2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 |
# File 'lib/debug/session.rb', line 2225 def self.open host: nil, port: CONFIG[:port], sock_path: nil, sock_dir: nil, nonstop: false, **kw CONFIG.set_config(**kw) require_relative 'server' if port || CONFIG[:open] == 'chrome' || (!::Addrinfo.respond_to?(:unix)) open_tcp host: host, port: (port || 0), nonstop: nonstop else open_unix sock_path: sock_path, sock_dir: sock_dir, nonstop: nonstop end end |
.open_tcp(host: nil, port:, nonstop: false, **kw) ⇒ Object
2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 |
# File 'lib/debug/session.rb', line 2236 def self.open_tcp host: nil, port:, nonstop: false, **kw CONFIG.set_config(**kw) require_relative 'server' if defined? SESSION SESSION.reset_ui UI_TcpServer.new(host: host, port: port) else initialize_session{ UI_TcpServer.new(host: host, port: port) } end setup_initial_suspend unless nonstop end |
.open_unix(sock_path: nil, sock_dir: nil, nonstop: false, **kw) ⇒ Object
2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 |
# File 'lib/debug/session.rb', line 2249 def self.open_unix sock_path: nil, sock_dir: nil, nonstop: false, **kw CONFIG.set_config(**kw) require_relative 'server' if defined? SESSION SESSION.reset_ui UI_UnixDomainServer.new(sock_dir: sock_dir, sock_path: sock_path) else initialize_session{ UI_UnixDomainServer.new(sock_dir: sock_dir, sock_path: sock_path) } end setup_initial_suspend unless nonstop end |
.parse_help ⇒ Object
Help
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 |
# File 'lib/debug/config.rb', line 530 def self.parse_help helps = Hash.new{|h, k| h[k] = []} desc = cat = nil cmds = Hash.new File.read(File.join(__dir__, 'session.rb'), encoding: Encoding::UTF_8).each_line do |line| case line when /\A\s*### (.+)/ cat = $1 break if $1 == 'END' when /\A register_command (.+)/ next unless cat next unless desc ws = [] $1.gsub(/'([a-z]+)'/){|w| ws << $1 } helps[cat] << [ws, desc] desc = nil max_w = ws.max_by{|w| w.length} ws.each{|w| cmds[w] = max_w } when /\A\s+# (\s*\*.+)/ if desc desc << "\n" + $1 else desc = $1 end end end @commands = cmds @helps = helps end |
.require_location ⇒ Object
String for requiring location nil for -r
2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 |
# File 'lib/debug/session.rb', line 2193 def self.require_location locs = caller_locations dir_prefix = /#{Regexp.escape(__dir__)}/ locs.each do |loc| case loc.absolute_path when dir_prefix when %r{rubygems/core_ext/kernel_require\.rb} when %r{bundled_gems\.rb} else return loc if loc.absolute_path end end nil end |
.safe_inspect(obj, max_length: SHORT_INSPECT_LENGTH, short: false) ⇒ Object
2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 |
# File 'lib/debug/session.rb', line 2361 def self.safe_inspect obj, max_length: SHORT_INSPECT_LENGTH, short: false if short LimitedPP.pp(obj, max_length) else obj.inspect end rescue NoMethodError => e klass, oid = M_CLASS.bind_call(obj), M_OBJECT_ID.bind_call(obj) if obj == (r = e.receiver) "<\##{klass.name}#{oid} does not have \#inspect>" else rklass, roid = M_CLASS.bind_call(r), M_OBJECT_ID.bind_call(r) "<\##{klass.name}:#{roid} contains <\##{rklass}:#{roid} and it does not have #inspect>" end rescue Exception => e "<#inspect raises #{e.inspect}>" end |
.setup_initial_suspend ⇒ Object
boot utilities
2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 |
# File 'lib/debug/session.rb', line 2264 def self.setup_initial_suspend if !CONFIG[:nonstop] case when CONFIG[:stop_at_load] add_line_breakpoint __FILE__, __LINE__ + 1, oneshot: true, hook_call: false nil # stop here when path = ENV['RUBY_DEBUG_INITIAL_SUSPEND_PATH'] add_line_breakpoint path, 0, oneshot: true, hook_call: false when loc = ::DEBUGGER__.require_location # require 'debug/start' or 'debug' add_line_breakpoint loc.absolute_path, loc.lineno + 1, oneshot: true, hook_call: false else # -r add_line_breakpoint $0, 0, oneshot: true, hook_call: false end end end |
.skip? ⇒ Boolean
2298 2299 2300 |
# File 'lib/debug/session.rb', line 2298 def skip? @skip_all end |
.skip_all ⇒ Object
2294 2295 2296 |
# File 'lib/debug/session.rb', line 2294 def skip_all @skip_all = true end |
.start(nonstop: false, **kw) ⇒ Object
start methods
2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 |
# File 'lib/debug/session.rb', line 2211 def self.start nonstop: false, **kw CONFIG.set_config(**kw) if CONFIG[:open] open nonstop: nonstop, **kw else unless defined? SESSION require_relative 'local' initialize_session{ UI_LocalConsole.new } end setup_initial_suspend unless nonstop end end |
.step_in(&b) ⇒ Object
2420 2421 2422 2423 2424 2425 2426 |
# File 'lib/debug/session.rb', line 2420 def self.step_in &b if defined?(SESSION) && SESSION.active? SESSION.add_iseq_breakpoint RubyVM::InstructionSequence.of(b), oneshot: true end yield end |
.unix_domain_socket_dir ⇒ Object
504 505 506 507 508 509 510 511 512 513 514 515 |
# File 'lib/debug/config.rb', line 504 def self.unix_domain_socket_dir case when path = CONFIG[:sock_dir] when path = ENV['XDG_RUNTIME_DIR'] when path = unix_domain_socket_tmpdir when path = unix_domain_socket_homedir else raise 'specify RUBY_DEBUG_SOCK_DIR environment variable.' end path end |
.unix_domain_socket_homedir ⇒ Object
492 493 494 495 496 497 498 499 500 501 502 |
# File 'lib/debug/config.rb', line 492 def self.unix_domain_socket_homedir if home = ENV['HOME'] path = File.join(home, '.rdbg-sock') unless File.exist?(path) Dir.mkdir(path, 0700) end (path) end end |
.unix_domain_socket_tmpdir ⇒ Object
477 478 479 480 481 482 483 484 485 486 487 488 489 490 |
# File 'lib/debug/config.rb', line 477 def self.unix_domain_socket_tmpdir require 'tmpdir' if tmpdir = Dir.tmpdir path = File.join(tmpdir, "rdbg-#{Process.uid}") unless File.exist?(path) d = Dir.mktmpdir File.rename(d, path) end (path) end end |
.warn(msg) ⇒ Object
2379 2380 2381 |
# File 'lib/debug/session.rb', line 2379 def self.warn msg log :WARN, msg end |