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/source_repository.rb,
lib/debug/session.rb
Overview
$VERBOSE = true
Defined Under Namespace
Modules: Color, ForkInterceptor, GlobalVariablesHelper, 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"], # 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"], 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.7.1"- 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
- 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
2143 2144 2145 |
# File 'lib/debug/session.rb', line 2143 def self.add_catch_breakpoint pat ::DEBUGGER__::SESSION.add_catch_breakpoint pat end |
.add_line_breakpoint(file, line, **kw) ⇒ Object
manual configuration methods
2139 2140 2141 |
# File 'lib/debug/session.rb', line 2139 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
422 423 424 425 426 427 428 429 430 431 432 433 |
# File 'lib/debug/config.rb', line 422 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 unless (dir_mode = fs.mode) == 040700 # 4: dir, 7:rwx raise "#{path}'s mode is #{dir_mode.to_s(8)} (should be 040700)" end path end |
.check_loglevel(level) ⇒ Object
2342 2343 2344 2345 2346 |
# File 'lib/debug/session.rb', line 2342 def self.check_loglevel level lv = LOG_LEVELS[level] config_lv = LOG_LEVELS[CONFIG[:log_level]] lv <= config_lv end |
.commands ⇒ Object
526 527 528 |
# File 'lib/debug/config.rb', line 526 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.
2388 2389 2390 |
# File 'lib/debug/session.rb', line 2388 def self.compare_path(a, b) a&.downcase == b&.downcase end |
.create_unix_domain_socket_name(base_dir = unix_domain_socket_dir) ⇒ Object
480 481 482 |
# File 'lib/debug/config.rb', line 480 def self.create_unix_domain_socket_name(base_dir = unix_domain_socket_dir) create_unix_domain_socket_name_prefix(base_dir) + "-#{Process.pid}" end |
.create_unix_domain_socket_name_prefix(base_dir = unix_domain_socket_dir) ⇒ Object
475 476 477 478 |
# File 'lib/debug/config.rb', line 475 def self.create_unix_domain_socket_name_prefix(base_dir = unix_domain_socket_dir) user = ENV['USER'] || 'UnknownUser' File.join(base_dir, "ruby-debug-#{user}") end |
.debug(&b) ⇒ Object
2348 2349 2350 2351 2352 |
# File 'lib/debug/session.rb', line 2348 def self.debug(&b) if check_loglevel :DEBUG log :DEBUG, b.call end end |
.help ⇒ Object
530 531 532 533 534 535 536 537 538 539 540 541 |
# File 'lib/debug/config.rb', line 530 def self.help r = [] self.helps.each{|cat, cmds| r << "### #{cat}" r << '' cmds.each{|_, desc| r << desc } r << '' } r.join("\n") end |
.helps ⇒ Object
522 523 524 |
# File 'lib/debug/config.rb', line 522 def self.helps (defined?(@helps) && @helps) || parse_help end |
.info(msg) ⇒ Object
2338 2339 2340 |
# File 'lib/debug/session.rb', line 2338 def self.info msg log :INFO, msg end |
.load_rc ⇒ Object
2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 |
# File 'lib/debug/session.rb', line 2258 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
2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 |
# File 'lib/debug/session.rb', line 2354 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
2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 |
# File 'lib/debug/session.rb', line 2180 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
2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 |
# File 'lib/debug/session.rb', line 2191 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
2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 |
# File 'lib/debug/session.rb', line 2204 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
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 |
# File 'lib/debug/config.rb', line 486 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
2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 |
# File 'lib/debug/session.rb', line 2149 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} else return loc if loc.absolute_path end end nil end |
.safe_inspect(obj, max_length: SHORT_INSPECT_LENGTH, short: false) ⇒ Object
2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 |
# File 'lib/debug/session.rb', line 2316 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
2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 |
# File 'lib/debug/session.rb', line 2219 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
2253 2254 2255 |
# File 'lib/debug/session.rb', line 2253 def skip? @skip_all end |
.skip_all ⇒ Object
2249 2250 2251 |
# File 'lib/debug/session.rb', line 2249 def skip_all @skip_all = true end |
.start(nonstop: false, **kw) ⇒ Object
start methods
2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 |
# File 'lib/debug/session.rb', line 2166 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
2375 2376 2377 2378 2379 2380 2381 |
# File 'lib/debug/session.rb', line 2375 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
462 463 464 465 466 467 468 469 470 471 472 473 |
# File 'lib/debug/config.rb', line 462 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
450 451 452 453 454 455 456 457 458 459 460 |
# File 'lib/debug/config.rb', line 450 def self.unix_domain_socket_homedir if home = ENV['HOME'] path = File.join(home, '.ruby-debug-sock') unless File.exist?(path) Dir.mkdir(path, 0700) end (path) end end |
.unix_domain_socket_tmpdir ⇒ Object
435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'lib/debug/config.rb', line 435 def self.unix_domain_socket_tmpdir require 'tmpdir' if tmpdir = Dir.tmpdir path = File.join(tmpdir, "ruby-debug-sock-#{Process.uid}") unless File.exist?(path) d = Dir.mktmpdir File.rename(d, path) end (path) end end |
.warn(msg) ⇒ Object
2334 2335 2336 |
# File 'lib/debug/session.rb', line 2334 def self.warn msg log :WARN, msg end |