Skip to content

Commit 6620cd2

Browse files
[LLDB] Add a target.launch-working-dir setting (#113521)
Internally we use bazel in a way in which it can drop you in a LLDB session with the target launched in a particular cwd, which is needed for things to work. We've been making this automation work via `process launch -w`. However, if later the user wants to restart the process with `r`, then they end up using a different cwd for relaunching the process. As a way to fix this, I'm adding a target-level setting that allows configuring a default cwd used for launching the process without needing the user to specify it manually.
1 parent 9f0f6df commit 6620cd2

File tree

7 files changed

+85
-1
lines changed

7 files changed

+85
-1
lines changed

lldb/include/lldb/Target/Target.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "lldb/Utility/RealpathPrefixes.h"
3838
#include "lldb/Utility/Timeout.h"
3939
#include "lldb/lldb-public.h"
40+
#include "llvm/ADT/StringRef.h"
4041

4142
namespace lldb_private {
4243

@@ -114,6 +115,8 @@ class TargetProperties : public Properties {
114115

115116
void SetDisableSTDIO(bool b);
116117

118+
llvm::StringRef GetLaunchWorkingDirectory() const;
119+
117120
const char *GetDisassemblyFlavor() const;
118121

119122
InlineStrategy GetInlineStrategy() const;

lldb/source/Commands/CommandObjectProcess.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,13 @@ class CommandObjectProcessLaunch : public CommandObjectProcessLaunchOrAttach {
201201
if (target->GetDisableSTDIO())
202202
m_options.launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO);
203203

204+
if (!m_options.launch_info.GetWorkingDirectory()) {
205+
if (llvm::StringRef wd = target->GetLaunchWorkingDirectory();
206+
!wd.empty()) {
207+
m_options.launch_info.SetWorkingDirectory(FileSpec(wd));
208+
}
209+
}
210+
204211
// Merge the launch info environment with the target environment.
205212
Environment target_env = target->GetEnvironment();
206213
m_options.launch_info.GetEnvironment().insert(target_env.begin(),

lldb/source/Commands/Options.td

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,10 @@ let Command = "process launch" in {
691691
def process_launch_plugin : Option<"plugin", "P">, Arg<"Plugin">,
692692
Desc<"Name of the process plugin you want to use.">;
693693
def process_launch_working_dir : Option<"working-dir", "w">, Arg<"DirectoryName">,
694-
Desc<"Set the current working directory to <path> when running the inferior.">;
694+
Desc<"Set the current working directory to <path> when running the inferior. This option "
695+
"applies only to the current `process launch` invocation. If "
696+
"`target.launch-working-dir` is set and this option is given, the value of this "
697+
"option will be used instead of the setting.">;
695698
def process_launch_arch : Option<"arch", "a">, Arg<"Architecture">,
696699
Desc<"Set the architecture for the process to launch when ambiguous.">;
697700
def process_launch_environment : Option<"environment", "E">,

lldb/source/Target/Target.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4471,6 +4471,11 @@ void TargetProperties::SetDisableSTDIO(bool b) {
44714471
const uint32_t idx = ePropertyDisableSTDIO;
44724472
SetPropertyAtIndex(idx, b);
44734473
}
4474+
llvm::StringRef TargetProperties::GetLaunchWorkingDirectory() const {
4475+
const uint32_t idx = ePropertyLaunchWorkingDir;
4476+
return GetPropertyAtIndexAs<llvm::StringRef>(
4477+
idx, g_target_properties[idx].default_cstr_value);
4478+
}
44744479

44754480
const char *TargetProperties::GetDisassemblyFlavor() const {
44764481
const uint32_t idx = ePropertyDisassemblyFlavor;

lldb/source/Target/TargetProperties.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,13 @@ let Definition = "target" in {
201201
def DebugUtilityExpression: Property<"debug-utility-expression", "Boolean">,
202202
DefaultFalse,
203203
Desc<"Enable debugging of LLDB-internal utility expressions.">;
204+
def LaunchWorkingDir: Property<"launch-working-dir", "String">,
205+
DefaultStringValue<"">,
206+
Desc<"A default value for the working directory to use when launching processes. "
207+
"It is ignored when empty. This setting is only used when the target is "
208+
"launched. If you change this setting, the new value will only apply to "
209+
"subsequent launches. Commands that take an explicit working directory "
210+
"will override this setting.">;
204211
}
205212

206213
let Definition = "process_experimental" in {

lldb/test/API/commands/process/launch/TestProcessLaunch.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from lldbsuite.test.decorators import *
99
from lldbsuite.test.lldbtest import *
1010
from lldbsuite.test import lldbutil
11+
from pathlib import Path
1112

1213

1314
class ProcessLaunchTestCase(TestBase):
@@ -206,3 +207,59 @@ def test_environment_with_special_char(self):
206207
self.assertEqual(value, evil_var)
207208
process.Continue()
208209
self.assertState(process.GetState(), lldb.eStateExited, PROCESS_EXITED)
210+
211+
def test_target_launch_working_dir_prop(self):
212+
"""Test that the setting `target.launch-working-dir` is correctly used when launching a process."""
213+
d = {"CXX_SOURCES": "print_cwd.cpp"}
214+
self.build(dictionary=d)
215+
self.setTearDownCleanup(d)
216+
exe = self.getBuildArtifact("a.out")
217+
self.runCmd("file " + exe)
218+
219+
mywd = "my_working_dir"
220+
out_file_name = "my_working_dir_test.out"
221+
222+
my_working_dir_path = self.getBuildArtifact(mywd)
223+
lldbutil.mkdir_p(my_working_dir_path)
224+
out_file_path = os.path.join(my_working_dir_path, out_file_name)
225+
another_working_dir_path = Path(
226+
os.path.join(my_working_dir_path, "..")
227+
).resolve()
228+
229+
# If -w is not passed to process launch, then the setting will be used.
230+
self.runCmd(
231+
f"settings set target.launch-working-dir {another_working_dir_path}"
232+
)
233+
launch_command = f"process launch -o {out_file_path}"
234+
235+
self.expect(
236+
launch_command,
237+
patterns=["Process .* launched: .*a.out"],
238+
)
239+
240+
out = lldbutil.read_file_on_target(self, out_file_path)
241+
242+
self.assertIn(f"stdout: {another_working_dir_path}", out)
243+
244+
# If -w is passed to process launch, that value will be used instead of the setting.
245+
launch_command = f"process launch -w {my_working_dir_path} -o {out_file_path}"
246+
247+
self.expect(
248+
launch_command,
249+
patterns=["Process .* launched: .*a.out"],
250+
)
251+
252+
out = lldbutil.read_file_on_target(self, out_file_path)
253+
self.assertIn(f"stdout: {my_working_dir_path}", out)
254+
255+
# If set to empty, then LLDB's cwd will be used to launch the process.
256+
self.runCmd(f"settings set target.launch-working-dir ''")
257+
launch_command = f"process launch -o {out_file_path}"
258+
259+
self.expect(
260+
launch_command,
261+
patterns=["Process .* launched: .*a.out"],
262+
)
263+
264+
out = lldbutil.read_file_on_target(self, out_file_path)
265+
self.assertNotIn(f"stdout: {another_working_dir_path}", out)

llvm/docs/ReleaseNotes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,8 @@ Changes to LLDB
309309
* Program stdout/stderr redirection will now open the file with O_TRUNC flag, make sure to truncate the file if path already exists.
310310
* eg. `settings set target.output-path/target.error-path <path/to/file>`
311311

312+
* A new setting `target.launch-working-dir` can be used to set a persistent cwd that is used by default by `process launch` and `run`.
313+
312314
Changes to BOLT
313315
---------------------------------
314316

0 commit comments

Comments
 (0)