From 099c8c3c2bb7148d575c857d91e7d310ec1ba99b Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Fri, 1 Jul 2022 11:32:12 +0900 Subject: [PATCH 1/2] support `levels` and `startFrame` of`stackTrace` fix #637 --- lib/debug/server_dap.rb | 53 +++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/lib/debug/server_dap.rb b/lib/debug/server_dap.rb index a83bed5c7..ced2811d0 100644 --- a/lib/debug/server_dap.rb +++ b/lib/debug/server_dap.rb @@ -441,6 +441,7 @@ def process_protocol_request req when 'stackTrace' tid = req.dig('arguments', 'threadId') + if tc = find_waiting_tc(tid) request_tc [:dap, :backtrace, req] else @@ -551,9 +552,10 @@ def dap_event args case type when :backtrace - result[:stackFrames].each.with_index{|fi, i| + result[:stackFrames].each{|fi| + frame_depth = fi[:id] fi[:id] = id = @frame_map.size + 1 - @frame_map[id] = [req.dig('arguments', 'threadId'), i] + @frame_map[id] = [req.dig('arguments', 'threadId'), frame_depth] if fi[:source] if src = fi[:source][:sourceReference] src_id = @src_map.size + 1 @@ -620,27 +622,36 @@ def process_dap args case type when :backtrace - event! :dap_result, :backtrace, req, { - stackFrames: @target_frames.map{|frame| - path = frame.realpath || frame.path - source_name = path ? File.basename(path) : frame.location.to_s - - if !UI_DAP.local_fs || !(path && File.exist?(path)) - ref = frame.file_lines - end + start_frame = req.dig('arguments', 'startFrame') || 0 + levels = req.dig('arguments', 'levels') || 1_000 + frames = [] + @target_frames.each_with_index do |frame, i| + next if i < start_frame + break if (levels -= 1) < 0 + + path = frame.realpath || frame.path + source_name = path ? File.basename(path) : frame.location.to_s + + if !UI_DAP.local_fs || !(path && File.exist?(path)) + ref = frame.file_lines + end - { - # id: ??? # filled by SESSION - name: frame.name, - line: frame.location.lineno, - column: 1, - source: { - name: source_name, - path: path, - sourceReference: ref, - }, - } + frames << { + id: i, # id is refilled by SESSION + name: frame.name, + line: frame.location.lineno, + column: 1, + source: { + name: source_name, + path: path, + sourceReference: ref, + }, } + end + + event! :dap_result, :backtrace, req, { + stackFrames: frames, + totalFrames: @target_frames.size, } when :scopes fid = args.shift From d284a91a814d58bbbe5201b54faaf2d26ab55015 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Fri, 1 Jul 2022 14:49:12 +0900 Subject: [PATCH 2/2] `PP` trick for long `inspect` code * Stop `pp` when the output exceed the maximum number * Use `PP.singleline_pp` to disable breaking features fix #635 This does not solve the problem if the `pretty_print` consumes time for example using network connections. --- lib/debug/session.rb | 45 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/lib/debug/session.rb b/lib/debug/session.rb index 39512cfa9..89ca17cbc 100644 --- a/lib/debug/session.rb +++ b/lib/debug/session.rb @@ -32,6 +32,7 @@ require 'debug' # invalidate the $LOADED_FEATURE cache require 'json' if ENV['RUBY_DEBUG_TEST_UI'] == 'terminal' +require 'pp' class RubyVM::InstructionSequence def traceable_lines_norec lines @@ -2057,16 +2058,48 @@ def self.method_added tp end METHOD_ADDED_TRACKER = self.create_method_added_tracker - SHORT_INSPECT_LENGTH = 40 - def self.safe_inspect obj, max_length: SHORT_INSPECT_LENGTH, short: false - str = obj.inspect + class LimitedPP + def self.pp(obj, max=80) + out = self.new(max) + catch out do + PP.singleline_pp(obj, out) + end + out.buf + end + + attr_reader :buf + + def initialize max + @max = max + @cnt = 0 + @buf = String.new + end + + def <<(other) + @buf << other - if short && str.length > max_length - str[0...max_length] + '...' + if @buf.size >= @max + @buf = @buf[0..@max] + '...' + throw self + end + end + end + + 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 - str + rklass, roid = M_CLASS.bind_call(r), M_OBJECT_ID.bind_call(r) + "<\##{klass.name}:#{oid} contains <\##{rklass}:#{rid} and it does not have #inspect>" end rescue Exception => e str = "<#inspect raises #{e.inspect}>"