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_src_lines_frame:['RUBY_DEBUG_SHOW_SRC_LINES_FRAME', "UI: Show n lines source code on frame operations", :int, "1"], 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"], no_repeat: ['RUBY_DEBUG_NO_REPEAT', "UI: Do not repeat last line when empty line",: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 (default: ${XDG_STATE_HOME-~/.local/state}/rdbg/history)", :string, nil], 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"], port_range: ['RUBY_DEBUG_PORT_RANGE', "REMOTE: TCP/IP remote debugging: length of port range"], 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.11.0"
- 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
2186 2187 2188 |
# File 'lib/debug/session.rb', line 2186 def self.add_catch_breakpoint pat ::DEBUGGER__::SESSION.add_catch_breakpoint pat end |
.add_line_breakpoint(file, line, **kw) ⇒ Object
manual configuration methods
2182 2183 2184 |
# File 'lib/debug/session.rb', line 2182 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
469 470 471 472 473 474 475 476 477 478 479 480 481 |
# File 'lib/debug/config.rb', line 469 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
2395 2396 2397 2398 2399 |
# File 'lib/debug/session.rb', line 2395 def self.check_loglevel level lv = LOG_LEVELS[level] config_lv = LOG_LEVELS[CONFIG[:log_level]] lv <= config_lv end |
.commands ⇒ Object
576 577 578 |
# File 'lib/debug/config.rb', line 576 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.
2441 2442 2443 |
# File 'lib/debug/session.rb', line 2441 def self.compare_path(a, b) a&.downcase == b&.downcase end |
.create_unix_domain_socket_name(base_dir = unix_domain_socket_dir) ⇒ Object
527 528 529 530 531 532 |
# File 'lib/debug/config.rb', line 527 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
523 524 525 |
# File 'lib/debug/config.rb', line 523 def self.create_unix_domain_socket_name_prefix(base_dir = unix_domain_socket_dir) File.join(base_dir, "rdbg") end |
.debug(&b) ⇒ Object
2401 2402 2403 2404 2405 |
# File 'lib/debug/session.rb', line 2401 def self.debug(&b) if check_loglevel :DEBUG log :DEBUG, b.call end end |
.help ⇒ Object
580 581 582 583 584 585 586 587 588 589 590 591 |
# File 'lib/debug/config.rb', line 580 def self.help r = [] self.helps.each{|cat, cmds| r << "### #{cat}" r << '' cmds.each{|_, desc| r << desc } r << '' } r.join("\n") end |
.helps ⇒ Object
572 573 574 |
# File 'lib/debug/config.rb', line 572 def self.helps (defined?(@helps) && @helps) || parse_help end |
.info(msg) ⇒ Object
2391 2392 2393 |
# File 'lib/debug/session.rb', line 2391 def self.info msg log :INFO, msg end |
.load_rc ⇒ Object
2302 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 2329 2330 2331 2332 2333 2334 2335 2336 |
# File 'lib/debug/session.rb', line 2302 def self.load_rc rc_file_paths = [ [File.('~/.rdbgrc'), true], [File.('~/.rdbgrc.rb'), true], # ['./.rdbgrc', true], # disable because of security concern ] if (xdg_home = ENV["XDG_CONFIG_HOME"]) rc_file_paths << [File.(File.join(xdg_home, "rdbg", "config")), true] rc_file_paths << [File.(File.join(xdg_home, "rdbg", "config.rb")), true] end rc_file_paths << [CONFIG[:init_script], false] rc_file_paths.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
2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 |
# File 'lib/debug/session.rb', line 2407 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
2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 |
# File 'lib/debug/session.rb', line 2224 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
2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 |
# File 'lib/debug/session.rb', line 2235 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
2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 |
# File 'lib/debug/session.rb', line 2248 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
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 565 566 567 568 569 570 |
# File 'lib/debug/config.rb', line 536 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
2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 |
# File 'lib/debug/session.rb', line 2192 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
2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 |
# File 'lib/debug/session.rb', line 2369 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
2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 |
# File 'lib/debug/session.rb', line 2263 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
2297 2298 2299 |
# File 'lib/debug/session.rb', line 2297 def skip? @skip_all end |
.skip_all ⇒ Object
2293 2294 2295 |
# File 'lib/debug/session.rb', line 2293 def skip_all @skip_all = true end |
.start(nonstop: false, **kw) ⇒ Object
start methods
2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 |
# File 'lib/debug/session.rb', line 2210 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
2428 2429 2430 2431 2432 2433 2434 |
# File 'lib/debug/session.rb', line 2428 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
510 511 512 513 514 515 516 517 518 519 520 521 |
# File 'lib/debug/config.rb', line 510 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
498 499 500 501 502 503 504 505 506 507 508 |
# File 'lib/debug/config.rb', line 498 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
483 484 485 486 487 488 489 490 491 492 493 494 495 496 |
# File 'lib/debug/config.rb', line 483 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
2387 2388 2389 |
# File 'lib/debug/session.rb', line 2387 def self.warn msg log :WARN, msg end |