module DEBUGGER__::UI_DAP
Constants
- SHOW_PROTOCOL
Public Class Methods
setup(sock_path)
click to toggle source
# File debug-1.4.0/lib/debug/server_dap.rb, line 13 def self.setup sock_path dir = Dir.mktmpdir("ruby-debug-vscode-") at_exit{ CONFIG[:skip_path] = [//] # skip all FileUtils.rm_rf dir } Dir.chdir(dir) do Dir.mkdir('.vscode') open('README.rb', 'w'){|f| f.puts <<~MSG # Wait for starting the attaching to the Ruby process # This file will be removed at the end of the debuggee process. # # Note that vscode-rdbg extension is needed. Please install if you don't have. MSG } open('.vscode/launch.json', 'w'){|f| f.puts JSON.pretty_generate({ version: '0.2.0', configurations: [ { type: "rdbg", name: "Attach with rdbg", request: "attach", rdbgPath: File.expand_path('../../exe/rdbg', __dir__), debugPort: sock_path, autoAttach: true, } ] }) } end cmds = ['code', "#{dir}/", "#{dir}/README.rb"] cmdline = cmds.join(' ') ssh_cmdline = "code --remote ssh-remote+[SSH hostname] #{dir}/ #{dir}/README.rb" STDERR.puts "Launching: #{cmdline}" env = ENV.delete_if{|k, h| /RUBY/ =~ k}.to_h unless system(env, *cmds) DEBUGGER__.warn <<~MESSAGE Can not invoke the command. Use the command-line on your terminal (with modification if you need). #{cmdline} If your application is running on a SSH remote host, please try: #{ssh_cmdline} MESSAGE end end
Public Instance Methods
dap_setup(bytes)
click to toggle source
# File debug-1.4.0/lib/debug/server_dap.rb, line 74 def dap_setup bytes CONFIG.set_config no_color: true @seq = 0 show_protocol :>, bytes req = JSON.load(bytes) # capability send_response(req, ## Supported supportsConfigurationDoneRequest: true, supportsFunctionBreakpoints: true, supportsConditionalBreakpoints: true, supportTerminateDebuggee: true, supportsTerminateRequest: true, exceptionBreakpointFilters: [ { filter: 'any', label: 'rescue any exception', #supportsCondition: true, #conditionDescription: '', }, { filter: 'RuntimeError', label: 'rescue RuntimeError', default: true, #supportsCondition: true, #conditionDescription: '', }, ], supportsExceptionFilterOptions: true, supportsStepBack: true, supportsEvaluateForHovers: true, supportsCompletionsRequest: true, ## Will be supported # supportsExceptionOptions: true, # supportsHitConditionalBreakpoints: # supportsSetVariable: true, # supportSuspendDebuggee: # supportsLogPoints: # supportsLoadedSourcesRequest: # supportsDataBreakpoints: # supportsBreakpointLocationsRequest: ## Possible? # supportsRestartFrame: # completionTriggerCharacters: # supportsModulesRequest: # additionalModuleColumns: # supportedChecksumAlgorithms: # supportsRestartRequest: # supportsValueFormattingOptions: # supportsExceptionInfoRequest: # supportsDelayedStackTraceLoading: # supportsTerminateThreadsRequest: # supportsSetExpression: # supportsClipboardContext: ## Never # supportsGotoTargetsRequest: # supportsStepInTargetsRequest: # supportsReadMemoryRequest: # supportsDisassembleRequest: # supportsCancelRequest: # supportsSteppingGranularity: # supportsInstructionBreakpoints: ) send_event 'initialized' end
event(type, *args)
click to toggle source
# File debug-1.4.0/lib/debug/server_dap.rb, line 365 def event type, *args case type when :suspend_bp _i, bp, tid = *args if bp.kind_of?(CatchBreakpoint) reason = 'exception' text = bp.description else reason = 'breakpoint' text = bp ? bp.description : 'temporary bp' end send_event 'stopped', reason: reason, description: text, text: text, threadId: tid, allThreadsStopped: true when :suspend_trap _sig, tid = *args send_event 'stopped', reason: 'pause', threadId: tid, allThreadsStopped: true when :suspended tid, = *args send_event 'stopped', reason: 'step', threadId: tid, allThreadsStopped: true end end
process()
click to toggle source
# File debug-1.4.0/lib/debug/server_dap.rb, line 204 def process while req = recv_request raise "not a request: #{req.inpsect}" unless req['type'] == 'request' args = req.dig('arguments') case req['command'] ## boot/configuration when 'launch' send_response req @is_attach = false when 'attach' send_response req Process.kill(:SIGURG, Process.pid) @is_attach = true when 'setBreakpoints' path = args.dig('source', 'path') bp_args = args['breakpoints'] bps = [] bp_args.each{|bp| line = bp['line'] if cond = bp['condition'] bps << SESSION.add_line_breakpoint(path, line, cond: cond) else bps << SESSION.add_line_breakpoint(path, line) end } send_response req, breakpoints: (bps.map do |bp| {verified: true,} end) when 'setFunctionBreakpoints' send_response req when 'setExceptionBreakpoints' process_filter = ->(filter_id) { case filter_id when 'any' bp = SESSION.add_catch_breakpoint 'Exception' when 'RuntimeError' bp = SESSION.add_catch_breakpoint 'RuntimeError' else bp = nil end { verified: bp ? true : false, message: bp.inspect, } } filters = args.fetch('filters').map {|filter_id| process_filter.call(filter_id) } filters += args.fetch('filterOptions', {}).map{|bp_info| process_filter.call(bp_info.dig('filterId')) } send_response req, breakpoints: filters when 'configurationDone' send_response req if defined?(@is_attach) && @is_attach @q_msg << 'p' send_event 'stopped', reason: 'pause', threadId: 1, allThreadsStopped: true else @q_msg << 'continue' end when 'disconnect' if args.fetch("terminateDebuggee", false) @q_msg << 'kill!' else @q_msg << 'continue' end send_response req ## control when 'continue' @q_msg << 'c' send_response req, allThreadsContinued: true when 'next' begin @session.check_postmortem @q_msg << 'n' send_response req rescue PostmortemError send_response req, success: false, message: 'postmortem mode', result: "'Next' is not supported while postmortem mode" end when 'stepIn' begin @session.check_postmortem @q_msg << 's' send_response req rescue PostmortemError send_response req, success: false, message: 'postmortem mode', result: "'stepIn' is not supported while postmortem mode" end when 'stepOut' begin @session.check_postmortem @q_msg << 'fin' send_response req rescue PostmortemError send_response req, success: false, message: 'postmortem mode', result: "'stepOut' is not supported while postmortem mode" end when 'terminate' send_response req exit when 'pause' send_response req Process.kill(:SIGURG, Process.pid) when 'reverseContinue' send_response req, success: false, message: 'cancelled', result: "Reverse Continue is not supported. Only \"Step back\" is supported." when 'stepBack' @q_msg << req ## query when 'threads' send_response req, threads: SESSION.managed_thread_clients.map{|tc| { id: tc.id, name: tc.name, } } when 'stackTrace', 'scopes', 'variables', 'evaluate', 'source', 'completions' @q_msg << req else raise "Unknown request: #{req.inspect}" end end end
puts(result)
click to toggle source
# File debug-1.4.0/lib/debug/server_dap.rb, line 360 def puts result # STDERR.puts "puts: #{result}" # send_event 'output', category: 'stderr', output: "PUTS!!: " + result.to_s end
readline(prompt)
click to toggle source
called by the SESSION thread
# File debug-1.4.0/lib/debug/server_dap.rb, line 348 def readline prompt @q_msg.pop || 'kill!' end
recv_request()
click to toggle source
# File debug-1.4.0/lib/debug/server_dap.rb, line 180 def recv_request r = IO.select([@sock]) @session.process_group.sync do raise RetryBecauseCantRead unless IO.select([@sock], nil, nil, 0) case header = @sock.gets when /Content-Length: (\d+)/ b = @sock.read(2) raise b.inspect unless b == "\r\n" l = @sock.read(s = $1.to_i) show_protocol :>, l JSON.load(l) when nil nil else raise "unrecognized line: #{l} (#{l.size} bytes)" end end rescue RetryBecauseCantRead retry end
respond(req, res)
click to toggle source
# File debug-1.4.0/lib/debug/server_dap.rb, line 356 def respond req, res send_response(req, **res) end
send(**kw)
click to toggle source
# File debug-1.4.0/lib/debug/server_dap.rb, line 145 def send **kw kw[:seq] = @seq += 1 str = JSON.dump(kw) show_protocol '<', str @sock.write "Content-Length: #{str.bytesize}\r\n\r\n#{str}" end
send_event(name, **kw)
click to toggle source
# File debug-1.4.0/lib/debug/server_dap.rb, line 169 def send_event name, **kw if kw.empty? send type: 'event', event: name else send type: 'event', event: name, body: kw end end
send_response(req, success: true, message: nil, **kw)
click to toggle source
# File debug-1.4.0/lib/debug/server_dap.rb, line 152 def send_response req, success: true, message: nil, **kw if kw.empty? send type: 'response', command: req['command'], request_seq: req['seq'], success: success, message: message || (success ? 'Success' : 'Failed') else send type: 'response', command: req['command'], request_seq: req['seq'], success: success, message: message || (success ? 'Success' : 'Failed'), body: kw end end
show_protocol(dir, msg)
click to toggle source
# File debug-1.4.0/lib/debug/server_dap.rb, line 68 def show_protocol dir, msg if SHOW_PROTOCOL $stderr.puts "\##{Process.pid}:[#{dir}] #{msg}" end end
sock(skip: false) { |$stderr| ... }
click to toggle source
# File debug-1.4.0/lib/debug/server_dap.rb, line 352 def sock skip: false yield $stderr end