xref: /llvm-project/lldb/test/API/commands/process/launch/TestProcessLaunch.py (revision e952728f88c8b0e0208dc991dd9a04fe8c211cfb)
1"""
2Test lldb process launch flags.
3"""
4
5import os
6
7import lldb
8from lldbsuite.test.decorators import *
9from lldbsuite.test.lldbtest import *
10from lldbsuite.test import lldbutil
11from pathlib import Path
12
13
14class ProcessLaunchTestCase(TestBase):
15    NO_DEBUG_INFO_TESTCASE = True
16
17    def setUp(self):
18        # Call super's setUp().
19        TestBase.setUp(self)
20        self.runCmd("settings set auto-confirm true")
21
22    def tearDown(self):
23        self.runCmd("settings clear auto-confirm")
24        TestBase.tearDown(self)
25
26    @skipIfRemote
27    def test_io(self):
28        """Test that process launch I/O redirection flags work properly."""
29        self.build()
30        exe = self.getBuildArtifact("a.out")
31        self.expect("file " + exe, patterns=["Current executable set to .*a.out"])
32
33        in_file = os.path.join(self.getSourceDir(), "input-file.txt")
34        out_file = lldbutil.append_to_process_working_directory(self, "output-test.out")
35        err_file = lldbutil.append_to_process_working_directory(self, "output-test.err")
36
37        # Make sure the output files do not exist before launching the process
38        try:
39            os.remove(out_file)
40        except OSError:
41            pass
42
43        try:
44            os.remove(err_file)
45        except OSError:
46            pass
47
48        launch_command = "process launch -i '{0}' -o '{1}' -e '{2}' -w '{3}'".format(
49            in_file, out_file, err_file, self.get_process_working_directory()
50        )
51
52        if lldb.remote_platform:
53            self.runCmd(
54                'platform put-file "{local}" "{remote}"'.format(
55                    local=in_file, remote=in_file
56                )
57            )
58
59        self.expect(launch_command, patterns=["Process .* launched: .*a.out"])
60
61        success = True
62        err_msg = ""
63
64        out = lldbutil.read_file_on_target(self, out_file)
65        if out != "This should go to stdout.\n":
66            success = False
67            err_msg = (
68                err_msg + "    ERROR: stdout file does not contain correct output.\n"
69            )
70
71        err = lldbutil.read_file_on_target(self, err_file)
72        if err != "This should go to stderr.\n":
73            success = False
74            err_msg = (
75                err_msg + "    ERROR: stderr file does not contain correct output.\n"
76            )
77
78        if not success:
79            self.fail(err_msg)
80
81    # rdar://problem/9056462
82    # The process launch flag '-w' for setting the current working directory
83    # not working?
84    @skipIfRemote
85    @expectedFailureAll(oslist=["freebsd", "linux"], bugnumber="llvm.org/pr20265")
86    @expectedFailureNetBSD
87    def test_set_working_dir_nonexisting(self):
88        """Test that '-w dir' fails to set the working dir when running the inferior with a dir which doesn't exist."""
89        d = {"CXX_SOURCES": "print_cwd.cpp"}
90        self.build(dictionary=d)
91        self.setTearDownCleanup(d)
92        exe = self.getBuildArtifact("a.out")
93        self.runCmd("file " + exe)
94
95        mywd = "my_working_dir"
96        out_file_name = "my_working_dir_test.out"
97        err_file_name = "my_working_dir_test.err"
98
99        my_working_dir_path = self.getBuildArtifact(mywd)
100        out_file_path = os.path.join(my_working_dir_path, out_file_name)
101        err_file_path = os.path.join(my_working_dir_path, err_file_name)
102
103        # Check that we get an error when we have a nonexisting path
104        invalid_dir_path = mywd + "z"
105        launch_command = "process launch -w %s -o %s -e %s" % (
106            invalid_dir_path,
107            out_file_path,
108            err_file_path,
109        )
110
111        self.expect(
112            launch_command,
113            error=True,
114            patterns=["error:.* No such file or directory: %s" % invalid_dir_path],
115        )
116
117    @skipIfRemote
118    def test_set_working_dir_existing(self):
119        """Test that '-w dir' sets the working dir when running the inferior."""
120        d = {"CXX_SOURCES": "print_cwd.cpp"}
121        self.build(dictionary=d)
122        self.setTearDownCleanup(d)
123        exe = self.getBuildArtifact("a.out")
124        self.runCmd("file " + exe)
125
126        mywd = "my_working_dir"
127        out_file_name = "my_working_dir_test.out"
128        err_file_name = "my_working_dir_test.err"
129
130        my_working_dir_path = self.getBuildArtifact(mywd)
131        lldbutil.mkdir_p(my_working_dir_path)
132        out_file_path = os.path.join(my_working_dir_path, out_file_name)
133        err_file_path = os.path.join(my_working_dir_path, err_file_name)
134
135        # Make sure the output files do not exist before launching the process
136        try:
137            os.remove(out_file_path)
138            os.remove(err_file_path)
139        except OSError:
140            pass
141
142        launch_command = "process launch -w %s -o %s -e %s" % (
143            my_working_dir_path,
144            out_file_path,
145            err_file_path,
146        )
147
148        self.expect(launch_command, patterns=["Process .* launched: .*a.out"])
149
150        success = True
151        err_msg = ""
152
153        # Check to see if the 'stdout' file was created
154        try:
155            out_f = open(out_file_path)
156        except IOError:
157            success = False
158            err_msg = err_msg + "ERROR: stdout file was not created.\n"
159        else:
160            # Check to see if the 'stdout' file contains the right output
161            line = out_f.readline()
162            if self.TraceOn():
163                print("line:", line)
164            if not re.search(mywd, line):
165                success = False
166                err_msg = (
167                    err_msg + "The current working directory was not set correctly.\n"
168                )
169                out_f.close()
170
171        # Try to delete the 'stdout' and 'stderr' files
172        try:
173            os.remove(out_file_path)
174            os.remove(err_file_path)
175        except OSError:
176            pass
177
178        if not success:
179            self.fail(err_msg)
180
181    def test_environment_with_special_char(self):
182        """Test that environment variables containing '*' and '}' are handled correctly by the inferior."""
183        source = "print_env.cpp"
184        d = {"CXX_SOURCES": source}
185        self.build(dictionary=d)
186        self.setTearDownCleanup(d)
187
188        evil_var = "INIT*MIDDLE}TAIL"
189
190        target = self.createTestTarget()
191        main_source_spec = lldb.SBFileSpec(source)
192        breakpoint = target.BreakpointCreateBySourceRegex(
193            "// Set breakpoint here.", main_source_spec
194        )
195
196        process = target.LaunchSimple(
197            None, ["EVIL=" + evil_var], self.get_process_working_directory()
198        )
199        self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED)
200
201        threads = lldbutil.get_threads_stopped_at_breakpoint(process, breakpoint)
202        self.assertEqual(len(threads), 1)
203        frame = threads[0].GetFrameAtIndex(0)
204        sbvalue = frame.EvaluateExpression("evil")
205        value = sbvalue.GetSummary().strip('"')
206
207        self.assertEqual(value, evil_var)
208        process.Continue()
209        self.assertState(process.GetState(), lldb.eStateExited, PROCESS_EXITED)
210
211    @skipIfRemote
212    def test_target_launch_working_dir_prop(self):
213        """Test that the setting `target.launch-working-dir` is correctly used when launching a process."""
214        d = {"CXX_SOURCES": "print_cwd.cpp"}
215        self.build(dictionary=d)
216        self.setTearDownCleanup(d)
217        exe = self.getBuildArtifact("a.out")
218        self.runCmd("file " + exe)
219
220        mywd = "my_working_dir"
221        out_file_name = "my_working_dir_test.out"
222
223        my_working_dir_path = self.getBuildArtifact(mywd)
224        lldbutil.mkdir_p(my_working_dir_path)
225        out_file_path = os.path.join(my_working_dir_path, out_file_name)
226        another_working_dir_path = Path(
227            os.path.join(my_working_dir_path, "..")
228        ).resolve()
229
230        # If -w is not passed to process launch, then the setting will be used.
231        self.runCmd(
232            f"settings set target.launch-working-dir {another_working_dir_path}"
233        )
234        launch_command = f"process launch -o {out_file_path}"
235
236        self.expect(
237            launch_command,
238            patterns=["Process .* launched: .*a.out"],
239        )
240
241        out = lldbutil.read_file_on_target(self, out_file_path)
242
243        self.assertIn(f"stdout: {another_working_dir_path}", out)
244
245        # If -w is passed to process launch, that value will be used instead of the setting.
246        launch_command = f"process launch -w {my_working_dir_path} -o {out_file_path}"
247
248        self.expect(
249            launch_command,
250            patterns=["Process .* launched: .*a.out"],
251        )
252
253        out = lldbutil.read_file_on_target(self, out_file_path)
254        self.assertIn(f"stdout: {my_working_dir_path}", out)
255
256        # If set to empty, then LLDB's cwd will be used to launch the process.
257        self.runCmd(f"settings set target.launch-working-dir ''")
258        launch_command = f"process launch -o {out_file_path}"
259
260        self.expect(
261            launch_command,
262            patterns=["Process .* launched: .*a.out"],
263        )
264
265        out = lldbutil.read_file_on_target(self, out_file_path)
266        self.assertNotIn(f"stdout: {another_working_dir_path}", out)
267