-
Notifications
You must be signed in to change notification settings - Fork 127
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
629 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,314 @@ | ||
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', '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', '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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.