Skip to content

Commit db3054a

Browse files
piyushjaiswal98Piyush Jaiswal
andauthored
[lldb-dap] Add --no-lldbinit as a CLI flag (#156131)
TLDR ---------- This PR adds `--no-lldbinit` as a new CLI flag to the `lldb-dap` Motivation ----------- Rcently Users reported being unable to control `.lldbinit` file sourcing when debugging through VS Code. #155802. VS Code extensions cannot easily inject custom parameters into the DAP initialize request. Adding `--no-lldbinit` as a CLI flag solves this problem by allowing the decision to skip `.lldbinit` files to be made at debugger startup, before any initialization requests are processed. VS Code extensions can control this behavior by specifying the flag through `debugAdapterArgs` or similar mechanisms in launch configurations. ``` { "type": <extension-type>, "request": "launch", "name": "Debug with --no-lldbinit", "program": "${workspaceFolder}/your-program", "debugAdapterArgs": ["--no-lldbinit"] } ``` Summary ---------- This PR introduces a new command-line flag `--no-lldbinit` (with alias `-x`) to `lldb-dap`. The flag prevents automatic parsing of `.lldbinit` files during debugger initialization, giving users control over whether their LLDB initialization scripts are loaded. ### Key Changes: 1. **CLI Option Definition** (`Options.td`): Added the `--no-lldbinit` flag with `-x` alias 2. **Core Implementation** (`DAP.cpp`): Added support for storing and using the no-lldbinit flag 3. **Initialization Handler** (`InitializeRequestHandler.cpp`): Modified to respect the flag during debugger initialization 4. **Main Tool** (`lldb-dap.cpp`): Added argument parsing for the new flag 5. **Test Infrastructure** (`dap_server.py & lldbdap_testcase.py`): Enhanced test framework to support additional arguments Test Plan --------- ### New Test Coverage (`TestDAP_launch.py`) **Test Method:** `test_no_lldbinit_flag()` **Test Strategy:** 1. **Setup**: Creates a temporary `.lldbinit` file with specific settings that would normally be loaded 2. **Execution**: Launches lldb-dap with the `--no-lldbinit` flag 3. **Verification**: Confirms that the settings from `.lldbinit` are NOT applied, proving the flag works correctly **Test Environment:** * Uses a temporary home directory with a custom `.lldbinit` file * Sets specific LLDB settings (`stop-disassembly-display never`, `target.x86-disassembly-flavor intel`) * Launches debug adapter with `--no-lldbinit` flag via `additional_args` parameter **Validation Approach:** * Executes `settings show stop-disassembly-display` command during initialization * Verifies the output does NOT contain "never" (which would indicate `.lldbinit` was sourced) * Confirms that initialization commands are still executed properly ### Testing Infrastructure Enhancements **File Modifications:** * `dap_server.py`: Enhanced to accept `additional_args` parameter for passing extra CLI flags * `lldbdap_testcase.py`: Updated `build_and_create_debug_adapter()` method to support additional arguments and environment variables ### Unit Test Integration **Unit Test Updates** (`DAPTest.cpp`): * Added initialization of the new flag in test setup to ensure consistent test behavior **Test Run** <img width="1759" height="1373" alt="Screenshot 2025-08-29 at 5 56 18 PM" src="https://github.com/user-attachments/assets/769b319a-5009-4ade-aff8-c5f548b38123" /> --------- Co-authored-by: Piyush Jaiswal <piyushjais@meta.com>
1 parent aa02206 commit db3054a

File tree

10 files changed

+87
-14
lines changed

10 files changed

+87
-14
lines changed

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1490,12 +1490,17 @@ def __init__(
14901490
init_commands: list[str] = [],
14911491
log_file: Optional[TextIO] = None,
14921492
env: Optional[dict[str, str]] = None,
1493+
additional_args: list[str] = [],
14931494
):
14941495
self.process = None
14951496
self.connection = None
14961497
if executable is not None:
14971498
process, connection = DebugAdapterServer.launch(
1498-
executable=executable, connection=connection, env=env, log_file=log_file
1499+
executable=executable,
1500+
connection=connection,
1501+
env=env,
1502+
log_file=log_file,
1503+
additional_args=additional_args,
14991504
)
15001505
self.process = process
15011506
self.connection = connection
@@ -1528,6 +1533,7 @@ def launch(
15281533
env: Optional[dict[str, str]] = None,
15291534
log_file: Optional[TextIO] = None,
15301535
connection: Optional[str] = None,
1536+
additional_args: list[str] = [],
15311537
) -> tuple[subprocess.Popen, Optional[str]]:
15321538
adapter_env = os.environ.copy()
15331539
if env is not None:
@@ -1537,6 +1543,9 @@ def launch(
15371543
adapter_env["LLDBDAP_LOG"] = log_file
15381544
args = [executable]
15391545

1546+
# Add additional arguments first (like --no-lldbinit)
1547+
args.extend(additional_args)
1548+
15401549
if connection is not None:
15411550
args.append("--connection")
15421551
args.append(connection)

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ def create_debug_adapter(
2121
self,
2222
lldbDAPEnv: Optional[dict[str, str]] = None,
2323
connection: Optional[str] = None,
24+
additional_args: Optional[list[str]] = None,
2425
):
2526
"""Create the Visual Studio Code debug adapter"""
2627
self.assertTrue(
@@ -33,15 +34,17 @@ def create_debug_adapter(
3334
init_commands=self.setUpCommands(),
3435
log_file=log_file_path,
3536
env=lldbDAPEnv,
37+
additional_args=additional_args or [],
3638
)
3739

3840
def build_and_create_debug_adapter(
3941
self,
4042
lldbDAPEnv: Optional[dict[str, str]] = None,
4143
dictionary: Optional[dict] = None,
44+
additional_args: Optional[list[str]] = None,
4245
):
4346
self.build(dictionary=dictionary)
44-
self.create_debug_adapter(lldbDAPEnv)
47+
self.create_debug_adapter(lldbDAPEnv, additional_args=additional_args)
4548

4649
def build_and_create_debug_adapter_for_attach(self):
4750
"""Variant of build_and_create_debug_adapter that builds a uniquely

lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import lldbdap_testcase
88
import os
99
import re
10+
import tempfile
1011

1112
# Many tests are skipped on Windows because get_stdout() returns None there.
1213
# Despite the test program printing correctly. See
@@ -582,3 +583,42 @@ def test_version(self):
582583
version_string.splitlines(),
583584
"version string does not match",
584585
)
586+
587+
def test_no_lldbinit_flag(self):
588+
"""
589+
Test that the --no-lldbinit flag prevents sourcing .lldbinit files.
590+
"""
591+
# Create a temporary .lldbinit file in the home directory
592+
with tempfile.TemporaryDirectory() as temp_home:
593+
lldbinit_path = os.path.join(temp_home, ".lldbinit")
594+
595+
# Write a command to the .lldbinit file that would set a unique setting
596+
with open(lldbinit_path, "w") as f:
597+
f.write("settings set stop-disassembly-display never\n")
598+
f.write("settings set target.x86-disassembly-flavor intel\n")
599+
600+
# Test with --no-lldbinit flag (should NOT source .lldbinit)
601+
self.build_and_create_debug_adapter(
602+
lldbDAPEnv={"HOME": temp_home}, additional_args=["--no-lldbinit"]
603+
)
604+
program = self.getBuildArtifact("a.out")
605+
606+
# Use initCommands to check if .lldbinit was sourced
607+
initCommands = ["settings show stop-disassembly-display"]
608+
609+
# Launch with initCommands to check the setting
610+
self.launch(program, initCommands=initCommands, stopOnEntry=True)
611+
612+
# Get console output to verify the setting was NOT set from .lldbinit
613+
output = self.get_console()
614+
self.assertTrue(output and len(output) > 0, "expect console output")
615+
616+
# Verify the setting has default value, not "never" from .lldbinit
617+
self.assertNotIn(
618+
"never",
619+
output,
620+
"Setting should have default value when --no-lldbinit is used",
621+
)
622+
623+
# Verify the initCommands were executed
624+
self.verify_commands("initCommands", output, initCommands)

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,13 @@ static std::string capitalize(llvm::StringRef str) {
121121
llvm::StringRef DAP::debug_adapter_path = "";
122122

123123
DAP::DAP(Log *log, const ReplMode default_repl_mode,
124-
std::vector<std::string> pre_init_commands,
124+
std::vector<std::string> pre_init_commands, bool no_lldbinit,
125125
llvm::StringRef client_name, DAPTransport &transport, MainLoop &loop)
126126
: log(log), transport(transport), broadcaster("lldb-dap"),
127127
progress_event_reporter(
128128
[&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
129-
repl_mode(default_repl_mode), m_client_name(client_name), m_loop(loop) {
129+
repl_mode(default_repl_mode), no_lldbinit(no_lldbinit),
130+
m_client_name(client_name), m_loop(loop) {
130131
configuration.preInitCommands = std::move(pre_init_commands);
131132
RegisterRequests();
132133
}

lldb/tools/lldb-dap/DAP.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ struct DAP final : private DAPTransport::MessageHandler {
156156
/// The set of features supported by the connected client.
157157
llvm::DenseSet<ClientFeature> clientFeatures;
158158

159+
/// Whether to disable sourcing .lldbinit files.
160+
bool no_lldbinit;
161+
159162
/// The initial thread list upon attaching.
160163
std::vector<protocol::Thread> initial_thread_list;
161164

@@ -178,13 +181,16 @@ struct DAP final : private DAPTransport::MessageHandler {
178181
/// \param[in] pre_init_commands
179182
/// LLDB commands to execute as soon as the debugger instance is
180183
/// allocated.
184+
/// \param[in] no_lldbinit
185+
/// Whether to disable sourcing .lldbinit files.
181186
/// \param[in] transport
182187
/// Transport for this debug session.
183188
/// \param[in] loop
184189
/// Main loop associated with this instance.
185190
DAP(Log *log, const ReplMode default_repl_mode,
186-
std::vector<std::string> pre_init_commands, llvm::StringRef client_name,
187-
DAPTransport &transport, lldb_private::MainLoop &loop);
191+
std::vector<std::string> pre_init_commands, bool no_lldbinit,
192+
llvm::StringRef client_name, DAPTransport &transport,
193+
lldb_private::MainLoop &loop);
188194

189195
~DAP();
190196

lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,11 @@ llvm::Expected<InitializeResponse> InitializeRequestHandler::Run(
4242

4343
// The sourceInitFile option is not part of the DAP specification. It is an
4444
// extension used by the test suite to prevent sourcing `.lldbinit` and
45-
// changing its behavior.
46-
if (arguments.lldbExtSourceInitFile.value_or(true)) {
45+
// changing its behavior. The CLI flag --no-lldbinit takes precedence over
46+
// the DAP parameter.
47+
bool should_source_init_files =
48+
!dap.no_lldbinit && arguments.lldbExtSourceInitFile.value_or(true);
49+
if (should_source_init_files) {
4750
dap.debugger.SkipLLDBInitFiles(false);
4851
dap.debugger.SkipAppInitFiles(false);
4952
lldb::SBCommandReturnObject init;

lldb/tools/lldb-dap/Options.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,9 @@ def pre_init_command: S<"pre-init-command">,
6161
def: Separate<["-"], "c">,
6262
Alias<pre_init_command>,
6363
HelpText<"Alias for --pre-init-command">;
64+
65+
def no_lldbinit: F<"no-lldbinit">,
66+
HelpText<"Do not automatically parse any '.lldbinit' files.">;
67+
def: Flag<["-"], "x">,
68+
Alias<no_lldbinit>,
69+
HelpText<"Alias for --no-lldbinit">;

lldb/tools/lldb-dap/tool/lldb-dap.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,8 @@ validateConnection(llvm::StringRef conn) {
258258
static llvm::Error
259259
serveConnection(const Socket::SocketProtocol &protocol, const std::string &name,
260260
Log *log, const ReplMode default_repl_mode,
261-
const std::vector<std::string> &pre_init_commands) {
261+
const std::vector<std::string> &pre_init_commands,
262+
bool no_lldbinit) {
262263
Status status;
263264
static std::unique_ptr<Socket> listener = Socket::Create(protocol, status);
264265
if (status.Fail()) {
@@ -303,8 +304,8 @@ serveConnection(const Socket::SocketProtocol &protocol, const std::string &name,
303304
llvm::set_thread_name(client_name + ".runloop");
304305
MainLoop loop;
305306
Transport transport(client_name, log, io, io);
306-
DAP dap(log, default_repl_mode, pre_init_commands, client_name, transport,
307-
loop);
307+
DAP dap(log, default_repl_mode, pre_init_commands, no_lldbinit,
308+
client_name, transport, loop);
308309

309310
if (auto Err = dap.ConfigureIO()) {
310311
llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
@@ -508,6 +509,8 @@ int main(int argc, char *argv[]) {
508509
pre_init_commands.push_back(arg);
509510
}
510511

512+
bool no_lldbinit = input_args.hasArg(OPT_no_lldbinit);
513+
511514
if (!connection.empty()) {
512515
auto maybeProtoclAndName = validateConnection(connection);
513516
if (auto Err = maybeProtoclAndName.takeError()) {
@@ -520,7 +523,7 @@ int main(int argc, char *argv[]) {
520523
std::string name;
521524
std::tie(protocol, name) = *maybeProtoclAndName;
522525
if (auto Err = serveConnection(protocol, name, log.get(), default_repl_mode,
523-
pre_init_commands)) {
526+
pre_init_commands, no_lldbinit)) {
524527
llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
525528
"Connection failed: ");
526529
return EXIT_FAILURE;
@@ -556,8 +559,8 @@ int main(int argc, char *argv[]) {
556559
constexpr llvm::StringLiteral client_name = "stdio";
557560
MainLoop loop;
558561
Transport transport(client_name, log.get(), input, output);
559-
DAP dap(log.get(), default_repl_mode, pre_init_commands, client_name,
560-
transport, loop);
562+
DAP dap(log.get(), default_repl_mode, pre_init_commands, no_lldbinit,
563+
client_name, transport, loop);
561564

562565
// stdout/stderr redirection to the IDE's console
563566
if (auto Err = dap.ConfigureIO(stdout, stderr)) {

lldb/unittests/DAP/DAPTest.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ TEST_F(DAPTest, SendProtocolMessages) {
2828
/*log=*/nullptr,
2929
/*default_repl_mode=*/ReplMode::Auto,
3030
/*pre_init_commands=*/{},
31+
/*no_lldbinit=*/false,
3132
/*client_name=*/"test_client",
3233
/*transport=*/*transport,
3334
/*loop=*/loop,

lldb/unittests/DAP/TestBase.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ void DAPTestBase::SetUp() {
5555
/*log=*/log.get(),
5656
/*default_repl_mode=*/ReplMode::Auto,
5757
/*pre_init_commands=*/std::vector<std::string>(),
58+
/*no_lldbinit=*/false,
5859
/*client_name=*/"test_client",
5960
/*transport=*/*transport, /*loop=*/loop);
6061
}

0 commit comments

Comments
 (0)