Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DAP: introduce Rdbg Trace Inspector #959

Merged
merged 1 commit into from
Apr 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
334 changes: 334 additions & 0 deletions lib/debug/dap_custom/traceInspector.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
module DEBUGGER__
module DAP_TraceInspector
class MultiTracer < Tracer
def initialize ui, evts, trace_params, max_log_size: nil, **kw
@evts = evts
@log = []
@trace_params = trace_params
if max_log_size
@max_log_size = max_log_size
else
@max_log_size = 50000
end
@dropped_trace_cnt = 0
super(ui, **kw)
@type = 'multi'
@name = 'TraceInspector'
end

attr_accessor :dropped_trace_cnt
attr_reader :log

def setup
@tracer = TracePoint.new(*@evts){|tp|
next if skip?(tp)

case tp.event
when :call, :c_call, :b_call
if @trace_params
params = parameters_info tp
end
append(call_trace_log(tp, params: params))
when :return, :c_return, :b_return
return_str = DEBUGGER__.safe_inspect(tp.return_value, short: true, max_length: 4096)
append(call_trace_log(tp, return_str: return_str))
when :line
append(line_trace_log(tp))
end
}
end

def parameters_info tp
b = tp.binding
tp.parameters.map{|_type, name|
begin
{ name: name, value: DEBUGGER__.safe_inspect(b.local_variable_get(name), short: true, max_length: 4096) }
rescue NameError, TypeError
nil
end
}.compact
end

def call_identifier_str tp
if tp.defined_class
minfo(tp)
else
"block"
end
end

def append log
if @log.size >= @max_log_size
@dropped_trace_cnt += 1
@log.shift
end
@log << log
end

def call_trace_log tp, return_str: nil, params: nil
log = {
depth: DEBUGGER__.frame_depth,
name: call_identifier_str(tp),
threadId: Thread.current.instance_variable_get(:@__thread_client_id),
location: {
path: tp.path,
line: tp.lineno
}
}
log[:returnValue] = return_str if return_str
log[:parameters] = params if params && params.size > 0
log
end

def line_trace_log tp
{
depth: DEBUGGER__.frame_depth,
threadId: Thread.current.instance_variable_get(:@__thread_client_id),
location: {
path: tp.path,
line: tp.lineno
}
}
end

def skip? tp
super || !@evts.include?(tp.event)
end

def skip_with_pattern?(tp)
super && !tp.method_id&.match?(@pattern)
end
end

class Custom_Recorder < ThreadClient::Recorder
def initialize max_log_size: nil
if max_log_size
@max_log_size = max_log_size
else
@max_log_size = 50000
end
@dropped_trace_cnt = 0
super()
end

attr_accessor :dropped_trace_cnt

def append frames
if @log.size >= @max_log_size
@dropped_trace_cnt += 1
@log.shift
end
@log << frames
end
end

module Custom_UI_DAP
def custom_dap_request_rdbgTraceInspector(req)
@q_msg << req
end
end

module Custom_Session
def process_trace_cmd req
cmd = req.dig('arguments', 'subCommand')
case cmd
when 'enable'
events = req.dig('arguments', 'events')
evts = []
trace_params = false
filter = req.dig('arguments', 'filterRegExp')
max_log_size = req.dig('arguments', 'maxLogSize')
events.each{|evt|
case evt
when 'traceLine'
evts << :line
when 'traceCall'
evts << :call
evts << :b_call
when 'traceReturn'
evts << :return
evts << :b_return
when 'traceParams'
trace_params = true
when 'traceClanguageCall'
evts << :c_call
when 'traceClanguageReturn'
evts << :c_return
else
raise "unknown trace type #{evt}"
end
}
add_tracer MultiTracer.new @ui, evts, trace_params, max_log_size: max_log_size, pattern: filter
@ui.respond req, {}
when 'disable'
if t = find_multi_trace
t.disable
end
@ui.respond req, {}
when 'collect'
logs = []
if t = find_multi_trace
logs = t.log
if t.dropped_trace_cnt > 0
@ui.puts "Return #{logs.size} traces and #{t.dropped_trace_cnt} traces are dropped"
else
@ui.puts "Return #{logs.size} traces"
end
t.dropped_trace_cnt = 0
end
@ui.respond req, logs: logs
else
raise "Unknown trace sub command #{cmd}"
end
return :retry
end

def find_multi_trace
@tracers.values.each{|t|
if t.type == 'multi'
return t
end
}
return nil
end

def process_record_cmd req
cmd = req.dig('arguments', 'subCommand')
case cmd
when 'enable'
@tc << [:dap, :rdbgTraceInspector, req]
when 'disable'
@tc << [:dap, :rdbgTraceInspector, req]
when 'step'
tid = req.dig('arguments', 'threadId')
count = req.dig('arguments', 'count')
if tc = find_waiting_tc(tid)
@ui.respond req, {}
tc << [:step, :in, count]
else
fail_response req
end
when 'stepBack'
tid = req.dig('arguments', 'threadId')
count = req.dig('arguments', 'count')
if tc = find_waiting_tc(tid)
@ui.respond req, {}
tc << [:step, :back, count]
else
fail_response req
end
when 'collect'
tid = req.dig('arguments', 'threadId')
if tc = find_waiting_tc(tid)
tc << [:dap, :rdbgTraceInspector, req]
else
fail_response req
end
else
raise "Unknown record sub command #{cmd}"
end
end

def custom_dap_request_rdbgTraceInspector(req)
cmd = req.dig('arguments', 'command')
case cmd
when 'trace'
process_trace_cmd req
when 'record'
process_record_cmd req
else
raise "Unknown command #{cmd}"
end
end

def custom_dap_request_event_rdbgTraceInspector(req, result)
cmd = req.dig('arguments', 'command')
case cmd
when 'record'
process_event_record_cmd(req, result)
else
raise "Unknown command #{cmd}"
end
end

def process_event_record_cmd(req, result)
cmd = req.dig('arguments', 'subCommand')
case cmd
when 'enable'
@ui.respond req, {}
when 'disable'
@ui.respond req, {}
when 'collect'
cnt = result.delete :dropped_trace_cnt
if cnt > 0
@ui.puts "Return #{result[:logs].size} traces and #{cnt} traces are dropped"
else
@ui.puts "Return #{result[:logs].size} traces"
end
@ui.respond req, result
else
raise "Unknown command #{cmd}"
end
end
end

module Custom_ThreadClient
def custom_dap_request_rdbgTraceInspector(req)
cmd = req.dig('arguments', 'command')
case cmd
when 'record'
process_record_cmd(req)
else
raise "Unknown command #{cmd}"
end
end

def process_record_cmd(req)
cmd = req.dig('arguments', 'subCommand')
case cmd
when 'enable'
size = req.dig('arguments', 'maxLogSize')
@recorder = Custom_Recorder.new max_log_size: size
@recorder.enable
event! :protocol_result, :rdbgTraceInspector, req
when 'disable'
if @recorder&.enabled?
@recorder.disable
end
@recorder = nil
event! :protocol_result, :rdbgTraceInspector, req
when 'collect'
logs = []
log_index = nil
unless @recorder.nil?
log_index = @recorder.log_index
@recorder.log.each{|frames|
crt_frame = frames[0]
log = {
name: crt_frame.name,
location: {
path: crt_frame.location.path,
line: crt_frame.location.lineno,
},
depth: crt_frame.frame_depth
}
if params = crt_frame.iseq_parameters_info
log[:parameters] = params
end
if return_str = crt_frame.return_str
log[:returnValue] = return_str
end
logs << log
}
end
event! :protocol_result, :rdbgTraceInspector, req, logs: logs, stoppedIndex: log_index, dropped_trace_cnt: @recorder.dropped_trace_cnt
@recorder.dropped_trace_cnt = 0
else
raise "Unknown command #{cmd}"
end
end
end

::DEBUGGER__::SESSION.extend_feature session: Custom_Session, thread_client: Custom_ThreadClient, ui: Custom_UI_DAP
end
end
9 changes: 9 additions & 0 deletions lib/debug/frame_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ def local_variables
end
end

def iseq_parameters_info
case frame_type
when :block, :method
parameters_info
else
nil
end
end

def parameters_info
vars = iseq.parameters_symbols
vars.map{|var|
Expand Down
12 changes: 12 additions & 0 deletions lib/debug/server_dap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,14 @@ def recv_request
retry
end

def load_rdbgExtension req
if exts = req.dig('arguments', 'rdbgExtension')
exts.each{|ext|
require_relative "dap_custom/#{File.basename(ext)}"
}
end
end

def process
while req = recv_request
raise "not a request: #{req.inspect}" unless req['type'] == 'request'
Expand All @@ -288,6 +296,8 @@ def process
UI_DAP.local_fs_map_set req.dig('arguments', 'localfs') || req.dig('arguments', 'localfsMap') || true
@nonstop = true

load_rdbgExtension req

when 'attach'
send_response req
UI_DAP.local_fs_map_set req.dig('arguments', 'localfs') || req.dig('arguments', 'localfsMap')
Expand All @@ -298,6 +308,8 @@ def process
@nonstop = false
end

load_rdbgExtension req

when 'configurationDone'
send_response req

Expand Down
6 changes: 5 additions & 1 deletion lib/debug/thread_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1300,10 +1300,14 @@ def initialize
frame._callee = b.eval('__callee__')
end
}
@log << frames
append(frames)
}
end

def append frames
@log << frames
end

def enable
unless @tp_recorder.enabled?
@log.clear
Expand Down
Loading