Skip to content

Commit acfdb2d

Browse files
committed
[lldb-da] Refactoring lldb-dap port listening mode to allow multiple connections.
This refactors the port listening mode to allocate a new DAP object for each connection, allowing multiple connections to run concurrently.
1 parent 0fb8fac commit acfdb2d

File tree

12 files changed

+506
-151
lines changed

12 files changed

+506
-151
lines changed

lldb/packages/Python/lldbsuite/test/lldbtest.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import signal
4040
from subprocess import *
4141
import sys
42+
import socket
4243
import time
4344
import traceback
4445

@@ -250,6 +251,13 @@ def which(program):
250251
return None
251252

252253

254+
def pickrandomport():
255+
"""Returns a random open port."""
256+
with socket.socket() as sock:
257+
sock.bind(("", 0))
258+
return sock.getsockname()[1]
259+
260+
253261
class ValueCheck:
254262
def __init__(
255263
self,

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -903,7 +903,7 @@ def request_setBreakpoints(self, file_path, line_array, data=None):
903903
"sourceModified": False,
904904
}
905905
if line_array is not None:
906-
args_dict["lines"] = "%s" % line_array
906+
args_dict["lines"] = line_array
907907
breakpoints = []
908908
for i, line in enumerate(line_array):
909909
breakpoint_data = None
@@ -1154,34 +1154,38 @@ class DebugAdaptorServer(DebugCommunication):
11541154
def __init__(
11551155
self,
11561156
executable=None,
1157+
launch=True,
11571158
port=None,
1159+
unix_socket=None,
11581160
init_commands=[],
11591161
log_file=None,
11601162
env=None,
11611163
):
11621164
self.process = None
1163-
if executable is not None:
1164-
adaptor_env = os.environ.copy()
1165-
if env is not None:
1166-
adaptor_env.update(env)
1167-
1168-
if log_file:
1169-
adaptor_env["LLDBDAP_LOG"] = log_file
1170-
self.process = subprocess.Popen(
1171-
[executable],
1172-
stdin=subprocess.PIPE,
1173-
stdout=subprocess.PIPE,
1174-
stderr=subprocess.PIPE,
1175-
env=adaptor_env,
1165+
if launch:
1166+
self.process = DebugAdaptorServer.launch(
1167+
executable,
1168+
port=port,
1169+
unix_socket=unix_socket,
1170+
log_file=log_file,
1171+
env=env,
11761172
)
1177-
DebugCommunication.__init__(
1178-
self, self.process.stdout, self.process.stdin, init_commands, log_file
1179-
)
1180-
elif port is not None:
1173+
1174+
if port:
11811175
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
11821176
s.connect(("127.0.0.1", port))
11831177
DebugCommunication.__init__(
1184-
self, s.makefile("r"), s.makefile("w"), init_commands
1178+
self, s.makefile("rb"), s.makefile("wb"), init_commands, log_file
1179+
)
1180+
elif unix_socket:
1181+
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
1182+
s.connect(unix_socket)
1183+
DebugCommunication.__init__(
1184+
self, s.makefile("rb"), s.makefile("wb"), init_commands, log_file
1185+
)
1186+
else:
1187+
DebugCommunication.__init__(
1188+
self, self.process.stdout, self.process.stdin, init_commands, log_file
11851189
)
11861190

11871191
def get_pid(self):
@@ -1196,6 +1200,39 @@ def terminate(self):
11961200
self.process.wait()
11971201
self.process = None
11981202

1203+
@classmethod
1204+
def launch(
1205+
cls, executable: str, /, port=None, unix_socket=None, log_file=None, env=None
1206+
) -> subprocess.Popen:
1207+
adaptor_env = os.environ.copy()
1208+
if env:
1209+
adaptor_env.update(env)
1210+
1211+
if log_file:
1212+
adaptor_env["LLDBDAP_LOG"] = log_file
1213+
1214+
args = [executable]
1215+
if port:
1216+
args.append("--port")
1217+
args.append(str(port))
1218+
elif unix_socket:
1219+
args.append("--unix-socket")
1220+
args.append(unix_socket)
1221+
1222+
proc = subprocess.Popen(
1223+
args,
1224+
stdin=subprocess.PIPE,
1225+
stdout=subprocess.PIPE,
1226+
stderr=sys.stdout,
1227+
env=adaptor_env,
1228+
)
1229+
1230+
if port or unix_socket:
1231+
# Wait for the server to startup.
1232+
time.sleep(0.1)
1233+
1234+
return proc
1235+
11991236

12001237
def attach_options_specified(options):
12011238
if options.pid is not None:

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,36 @@ class DAPTestCaseBase(TestBase):
1313
timeoutval = 10 * (10 if ('ASAN_OPTIONS' in os.environ) else 1)
1414
NO_DEBUG_INFO_TESTCASE = True
1515

16-
def create_debug_adaptor(self, lldbDAPEnv=None):
16+
def create_debug_adaptor(self, env=None, launch=True, port=None, unix_socket=None):
1717
"""Create the Visual Studio Code debug adaptor"""
1818
self.assertTrue(
1919
is_exe(self.lldbDAPExec), "lldb-dap must exist and be executable"
2020
)
2121
log_file_path = self.getBuildArtifact("dap.txt")
2222
self.dap_server = dap_server.DebugAdaptorServer(
2323
executable=self.lldbDAPExec,
24+
launch=launch,
25+
port=port,
26+
unix_socket=unix_socket,
2427
init_commands=self.setUpCommands(),
2528
log_file=log_file_path,
26-
env=lldbDAPEnv,
29+
env=env,
2730
)
2831

29-
def build_and_create_debug_adaptor(self, lldbDAPEnv=None):
32+
def build_and_create_debug_adaptor(
33+
self,
34+
lldbDAPEnv=None,
35+
lldbDAPLaunch=True,
36+
lldbDAPPort=None,
37+
lldbDAPUnixSocket=None,
38+
):
3039
self.build()
31-
self.create_debug_adaptor(lldbDAPEnv)
40+
self.create_debug_adaptor(
41+
env=lldbDAPEnv,
42+
launch=lldbDAPLaunch,
43+
port=lldbDAPPort,
44+
unix_socket=lldbDAPUnixSocket,
45+
)
3246

3347
def set_source_breakpoints(self, source_path, lines, data=None):
3448
"""Sets source breakpoints and returns an array of strings containing
@@ -475,11 +489,19 @@ def build_and_launch(
475489
customThreadFormat=None,
476490
launchCommands=None,
477491
expectFailure=False,
492+
lldbDAPPort=None,
493+
lldbDAPUnixSocket=None,
494+
lldbDAPLaunch=True,
478495
):
479496
"""Build the default Makefile target, create the DAP debug adaptor,
480497
and launch the process.
481498
"""
482-
self.build_and_create_debug_adaptor(lldbDAPEnv)
499+
self.build_and_create_debug_adaptor(
500+
lldbDAPEnv=lldbDAPEnv,
501+
lldbDAPLaunch=lldbDAPLaunch,
502+
lldbDAPPort=lldbDAPPort,
503+
lldbDAPUnixSocket=lldbDAPUnixSocket,
504+
)
483505
self.assertTrue(os.path.exists(program), "executable must exist")
484506

485507
return self.launch(
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""
2+
Test lldb-dap server integration.
3+
"""
4+
5+
import os
6+
import tempfile
7+
8+
import dap_server
9+
from lldbsuite.test.decorators import *
10+
from lldbsuite.test.lldbtest import *
11+
import lldbdap_testcase
12+
13+
14+
class TestDAP_server(lldbdap_testcase.DAPTestCaseBase):
15+
def do_test_server(self, port=None, unix_socket=None):
16+
log_file_path = self.getBuildArtifact("dap.txt")
17+
server = dap_server.DebugAdaptorServer.launch(
18+
self.lldbDAPExec, port=port, unix_socket=unix_socket, log_file=log_file_path
19+
)
20+
21+
def cleanup():
22+
server.terminate()
23+
server.wait()
24+
25+
self.addTearDownHook(cleanup)
26+
27+
self.build()
28+
program = self.getBuildArtifact("a.out")
29+
source = "main.c"
30+
breakpoint_line = line_number(source, "// breakpoint")
31+
32+
# Initial connection over the port.
33+
self.create_debug_adaptor(launch=False, port=port, unix_socket=unix_socket)
34+
self.launch(
35+
program,
36+
disconnectAutomatically=False,
37+
)
38+
self.set_source_breakpoints(source, [breakpoint_line])
39+
self.continue_to_next_stop()
40+
self.continue_to_exit()
41+
output = self.get_stdout()
42+
self.assertEquals(output, "hello world!\r\n")
43+
self.dap_server.request_disconnect()
44+
45+
# Second connection over the port.
46+
self.create_debug_adaptor(launch=False, port=port, unix_socket=unix_socket)
47+
self.launch(program)
48+
self.set_source_breakpoints(source, [breakpoint_line])
49+
self.continue_to_next_stop()
50+
self.continue_to_exit()
51+
output = self.get_stdout()
52+
self.assertEquals(output, "hello world!\r\n")
53+
54+
def test_server_port(self):
55+
"""
56+
Test launching a binary with a lldb-dap in server mode on a specific port.
57+
"""
58+
port = pickrandomport()
59+
self.do_test_server(port=port)
60+
61+
def test_server_unix_socket(self):
62+
"""
63+
Test launching a binary with a lldb-dap in server mode on a unix socket.
64+
"""
65+
dir = tempfile.gettempdir()
66+
self.do_test_server(unix_socket=dir + "/dap-connection-" + str(os.getpid()))
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include <stdio.h>
2+
3+
int main(int argc, char const *argv[]) {
4+
printf("hello world!\n"); // breakpoint 1
5+
return 0;
6+
}

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,19 @@ using namespace lldb_dap;
3232

3333
namespace lldb_dap {
3434

35-
DAP::DAP(llvm::StringRef path, ReplMode repl_mode)
35+
DAP::DAP(llvm::StringRef path, std::shared_ptr<std::ofstream> log,
36+
ReplMode repl_mode, std::vector<std::string> pre_init_commands)
3637
: debug_adaptor_path(path), broadcaster("lldb-dap"),
37-
exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID),
38-
stop_at_entry(false), is_attach(false),
38+
log(log), exception_breakpoints(), pre_init_commands(pre_init_commands),
39+
focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false),
3940
enable_auto_variable_summaries(false),
4041
enable_synthetic_child_debugging(false),
4142
display_extended_backtrace(false),
4243
restarting_process_id(LLDB_INVALID_PROCESS_ID),
4344
configuration_done_sent(false), waiting_for_run_in_terminal(false),
4445
progress_event_reporter(
4546
[&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
46-
reverse_request_seq(0), repl_mode(repl_mode) {
47-
const char *log_file_path = getenv("LLDBDAP_LOG");
48-
#if defined(_WIN32)
49-
// Windows opens stdout and stdin in text mode which converts \n to 13,10
50-
// while the value is just 10 on Darwin/Linux. Setting the file mode to binary
51-
// fixes this.
52-
int result = _setmode(fileno(stdout), _O_BINARY);
53-
assert(result);
54-
result = _setmode(fileno(stdin), _O_BINARY);
55-
UNUSED_IF_ASSERT_DISABLED(result);
56-
assert(result);
57-
#endif
58-
if (log_file_path)
59-
log.reset(new std::ofstream(log_file_path));
60-
}
47+
reverse_request_seq(0), repl_mode(repl_mode) {}
6148

6249
DAP::~DAP() = default;
6350

lldb/tools/lldb-dap/DAP.h

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,33 @@
99
#ifndef LLDB_TOOLS_LLDB_DAP_DAP_H
1010
#define LLDB_TOOLS_LLDB_DAP_DAP_H
1111

12-
#include <cstdio>
13-
#include <iosfwd>
14-
#include <map>
15-
#include <optional>
16-
#include <thread>
17-
18-
#include "llvm/ADT/DenseMap.h"
19-
#include "llvm/ADT/DenseSet.h"
20-
#include "llvm/ADT/StringMap.h"
21-
#include "llvm/ADT/StringRef.h"
22-
#include "llvm/Support/JSON.h"
23-
#include "llvm/Support/Threading.h"
24-
#include "llvm/Support/raw_ostream.h"
25-
12+
#include "ExceptionBreakpoint.h"
13+
#include "FunctionBreakpoint.h"
14+
#include "IOStream.h"
15+
#include "InstructionBreakpoint.h"
16+
#include "ProgressEvent.h"
17+
#include "SourceBreakpoint.h"
2618
#include "lldb/API/SBAttachInfo.h"
2719
#include "lldb/API/SBCommandInterpreter.h"
2820
#include "lldb/API/SBCommandReturnObject.h"
2921
#include "lldb/API/SBDebugger.h"
3022
#include "lldb/API/SBEvent.h"
23+
#include "lldb/API/SBFile.h"
3124
#include "lldb/API/SBFormat.h"
3225
#include "lldb/API/SBLaunchInfo.h"
3326
#include "lldb/API/SBTarget.h"
3427
#include "lldb/API/SBThread.h"
35-
36-
#include "ExceptionBreakpoint.h"
37-
#include "FunctionBreakpoint.h"
38-
#include "IOStream.h"
39-
#include "InstructionBreakpoint.h"
40-
#include "ProgressEvent.h"
41-
#include "SourceBreakpoint.h"
28+
#include "llvm/ADT/DenseMap.h"
29+
#include "llvm/ADT/DenseSet.h"
30+
#include "llvm/ADT/StringMap.h"
31+
#include "llvm/ADT/StringRef.h"
32+
#include "llvm/Support/JSON.h"
33+
#include "llvm/Support/Threading.h"
34+
#include "llvm/Support/raw_ostream.h"
35+
#include <iosfwd>
36+
#include <map>
37+
#include <optional>
38+
#include <thread>
4239

4340
#define VARREF_LOCALS (int64_t)1
4441
#define VARREF_GLOBALS (int64_t)2
@@ -140,13 +137,16 @@ struct DAP {
140137
llvm::StringRef debug_adaptor_path;
141138
InputStream input;
142139
OutputStream output;
140+
lldb::SBFile in;
141+
lldb::SBFile out;
142+
lldb::SBFile err;
143143
lldb::SBDebugger debugger;
144144
lldb::SBTarget target;
145145
Variables variables;
146146
lldb::SBBroadcaster broadcaster;
147147
std::thread event_thread;
148148
std::thread progress_event_thread;
149-
std::unique_ptr<std::ofstream> log;
149+
std::shared_ptr<std::ofstream> log;
150150
llvm::StringMap<SourceBreakpointMap> source_breakpoints;
151151
FunctionBreakpointMap function_breakpoints;
152152
InstructionBreakpointMap instruction_breakpoints;
@@ -198,10 +198,14 @@ struct DAP {
198198
// will contain that expression.
199199
std::string last_nonempty_var_expression;
200200

201-
DAP(llvm::StringRef path, ReplMode repl_mode);
201+
DAP(llvm::StringRef path, std::shared_ptr<std::ofstream> log,
202+
ReplMode repl_mode, std::vector<std::string> pre_init_commands);
202203
~DAP();
204+
205+
DAP() = delete;
203206
DAP(const DAP &rhs) = delete;
204207
void operator=(const DAP &rhs) = delete;
208+
205209
ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter);
206210
ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id);
207211

0 commit comments

Comments
 (0)