Module: MilkTea::DAP::Server::ServerUtilities

Included in:
MilkTea::DAP::Server
Defined in:
lib/milk_tea/dap/server/utilities.rb

Constant Summary collapse

PERF_LOG_THRESHOLD_MS =
20

Instance Method Summary collapse

Instance Method Details

#cleanup_tmp_dirsObject



149
150
151
152
153
154
# File 'lib/milk_tea/dap/server/utilities.rb', line 149

def cleanup_tmp_dirs
  @tmp_dirs.each do |dir|
    FileUtils.remove_entry(dir) if File.exist?(dir)
  end
  @tmp_dirs.clear
end

#dap_perf_logging?Boolean

Returns:

  • (Boolean)


74
75
76
# File 'lib/milk_tea/dap/server/utilities.rb', line 74

def dap_perf_logging?
  @dap_perf_logging ||= !ENV.fetch('MILK_TEA_DAP_PERF', nil).to_s.empty?
end

#dap_perf_verbose?Boolean

Returns:

  • (Boolean)


78
79
80
# File 'lib/milk_tea/dap/server/utilities.rb', line 78

def dap_perf_verbose?
  @dap_perf_verbose ||= ENV.fetch('MILK_TEA_DAP_PERF', nil).to_s == 'verbose'
end

#dap_set(payload, key, value) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
# File 'lib/milk_tea/dap/server/utilities.rb', line 106

def dap_set(payload, key, value)
  return unless payload.is_a?(Hash)

  if payload.key?(key)
    payload[key] = value
  elsif payload.key?(key.to_sym)
    payload[key.to_sym] = value
  else
    payload[key] = value
  end
end

#dap_value(payload, key) ⇒ Object



100
101
102
103
104
# File 'lib/milk_tea/dap/server/utilities.rb', line 100

def dap_value(payload, key)
  return nil unless payload.is_a?(Hash)

  payload[key] || payload[key.to_sym]
end

#join_background_threads(timeout: 0.5) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/milk_tea/dap/server/utilities.rb', line 135

def join_background_threads(timeout: 0.5)
  threads = @background_threads_mutex.synchronize do
    threads = @background_threads
    @background_threads = []
    threads
  end

  threads.each do |thread|
    thread.join(timeout)
  rescue StandardError
    nil
  end
end

#normalize_reference_key(value) ⇒ Object



118
119
120
121
122
123
124
125
126
# File 'lib/milk_tea/dap/server/utilities.rb', line 118

def normalize_reference_key(value)
  return nil if value.nil?
  return value if value.is_a?(Integer)

  text = value.to_s.strip
  return text.to_i if text.match?(/\A\d+\z/)

  text
end

#process_message(message) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/milk_tea/dap/server/utilities.rb', line 38

def process_message(message)
  if message["type"] == "response"
    handle_client_response(message)
    return
  end

  return unless message["type"] == "request"

  command = message["command"]
  handler = @handlers[command]

  unless command == "initialize" || @session.initialized?
    write_error_response(message, "initialize request must be sent first")
    return
  end

  t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)

  if handler
    handler.call(message)
  elsif using_lldb_backend?
    backend_response = backend_request(command, message["arguments"] || {})
    write_backend_response(message, backend_response)
  else
    write_error_response(message, "Command '#{command}' is not supported by the process backend. Use the lldb-dap backend for full debugging features.")
  end

  elapsed_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) * 1000).round(1)
  if dap_perf_logging? && (dap_perf_verbose? || elapsed_ms > PERF_LOG_THRESHOLD_MS)
    detail = dap_perf_verbose? ? " #{summarize_dap_arguments(message['arguments'])}" : ""
    warn "[DAP perf] #{command} #{elapsed_ms}ms#{detail}"
  end
rescue StandardError => e
  write_error_response(message, "Internal error: #{e.message}")
end

#register_handlersObject



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/milk_tea/dap/server/utilities.rb', line 9

def register_handlers
  @handlers["initialize"] = method(:handle_initialize)
  @handlers["setBreakpoints"] = method(:handle_set_breakpoints)
  @handlers["setFunctionBreakpoints"] = method(:handle_set_function_breakpoints)
  @handlers["configurationDone"] = method(:handle_configuration_done)
  @handlers["launch"] = method(:handle_launch)
  @handlers["attach"] = method(:handle_attach)
  @handlers["threads"] = method(:handle_threads)
  @handlers["stackTrace"] = method(:handle_stack_trace)
  @handlers["scopes"] = method(:handle_scopes)
  @handlers["variables"] = method(:handle_variables)
  @handlers["continue"] = method(:handle_continue)
  @handlers["next"] = method(:handle_next)
  @handlers["stepIn"] = method(:handle_step_in)
  @handlers["stepOut"] = method(:handle_step_out)
  @handlers["pause"] = method(:handle_pause)
  @handlers["terminate"] = method(:handle_terminate)
  @handlers["disconnect"] = method(:handle_disconnect)
  @handlers["setExceptionBreakpoints"] = method(:handle_set_exception_breakpoints)
  @handlers["evaluate"] = method(:handle_evaluate)
  @handlers["setExpression"] = method(:handle_set_expression)
  @handlers["setVariable"] = method(:handle_set_variable)
  @handlers["dataBreakpointInfo"] = method(:handle_data_breakpoint_info)
  @handlers["source"] = method(:handle_source)
  @handlers["loadedSources"] = method(:handle_loaded_sources)
  @handlers["restart"] = method(:handle_restart)
  @handlers["cancel"] = method(:handle_cancel)
end

#summarize_dap_arguments(arguments) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/milk_tea/dap/server/utilities.rb', line 82

def summarize_dap_arguments(arguments)
  return "" unless arguments.is_a?(Hash)

  keys = arguments.keys.map(&:to_s).sort
  source_path = arguments.dig('source', 'path')
  frame_id = arguments['frameId']
  expression = arguments['expression']

  bits = []
  bits << "keys=#{keys.join(',')}" unless keys.empty?
  bits << "source=#{source_path}" if source_path
  bits << "frameId=#{frame_id}" if frame_id
  bits << "expr=#{expression.inspect}" if expression
  bits.join(' ')
rescue StandardError
  ""
end

#track_background_thread(thread) ⇒ Object



128
129
130
131
132
133
# File 'lib/milk_tea/dap/server/utilities.rb', line 128

def track_background_thread(thread)
  @background_threads_mutex.synchronize do
    @background_threads << thread
  end
  thread
end