| """ | |
| Test lldb process launch flags. | |
| """ | |
| import os | |
| import lldb | |
| from lldbsuite.test.decorators import * | |
| from lldbsuite.test.lldbtest import * | |
| from lldbsuite.test import lldbutil | |
| from pathlib import Path | |
| class ProcessLaunchTestCase(TestBase): | |
| NO_DEBUG_INFO_TESTCASE = True | |
| SHARED_BUILD_TESTCASE = False | |
| def setUp(self): | |
| # Call super's setUp(). | |
| TestBase.setUp(self) | |
| self.runCmd("settings set auto-confirm true") | |
| def tearDown(self): | |
| self.runCmd("settings clear auto-confirm") | |
| TestBase.tearDown(self) | |
| @skipIfRemote | |
| def test_io(self): | |
| """Test that process launch I/O redirection flags work properly.""" | |
| self.build() | |
| exe = self.getBuildArtifact("a.out") | |
| self.expect("file " + exe, patterns=["Current executable set to .*a.out"]) | |
| in_file = os.path.join(self.getSourceDir(), "input-file.txt") | |
| out_file = lldbutil.append_to_process_working_directory(self, "output-test.out") | |
| err_file = lldbutil.append_to_process_working_directory(self, "output-test.err") | |
| # Make sure the output files do not exist before launching the process | |
| try: | |
| os.remove(out_file) | |
| except OSError: | |
| pass | |
| try: | |
| os.remove(err_file) | |
| except OSError: | |
| pass | |
| launch_command = "process launch -i '{0}' -o '{1}' -e '{2}' -w '{3}'".format( | |
| in_file, out_file, err_file, self.get_process_working_directory() | |
| ) | |
| if lldb.remote_platform: | |
| self.runCmd( | |
| 'platform put-file "{local}" "{remote}"'.format( | |
| local=in_file, remote=in_file | |
| ) | |
| ) | |
| self.expect(launch_command, patterns=["Process .* launched: .*a.out"]) | |
| success = True | |
| err_msg = "" | |
| out = lldbutil.read_file_on_target(self, out_file) | |
| if out != "This should go to stdout.\n": | |
| success = False | |
| err_msg = ( | |
| err_msg + " ERROR: stdout file does not contain correct output.\n" | |
| ) | |
| err = lldbutil.read_file_on_target(self, err_file) | |
| if err != "This should go to stderr.\n": | |
| success = False | |
| err_msg = ( | |
| err_msg + " ERROR: stderr file does not contain correct output.\n" | |
| ) | |
| if not success: | |
| self.fail(err_msg) | |
| # rdar://problem/9056462 | |
| # The process launch flag '-w' for setting the current working directory | |
| # not working? | |
| @skipIfRemote | |
| @expectedFailureAll(oslist=["freebsd", "linux"], bugnumber="llvm.org/pr20265") | |
| @expectedFailureNetBSD | |
| def test_set_working_dir_nonexisting(self): | |
| """Test that '-w dir' fails to set the working dir when running the inferior with a dir which doesn't exist.""" | |
| d = {"CXX_SOURCES": "print_cwd.cpp"} | |
| self.build(dictionary=d) | |
| self.setTearDownCleanup(d) | |
| exe = self.getBuildArtifact("a.out") | |
| self.runCmd("file " + exe) | |
| mywd = "my_working_dir" | |
| out_file_name = "my_working_dir_test.out" | |
| err_file_name = "my_working_dir_test.err" | |
| my_working_dir_path = self.getBuildArtifact(mywd) | |
| out_file_path = os.path.join(my_working_dir_path, out_file_name) | |
| err_file_path = os.path.join(my_working_dir_path, err_file_name) | |
| # Check that we get an error when we have a nonexisting path | |
| invalid_dir_path = mywd + "z" | |
| launch_command = "process launch -w %s -o %s -e %s" % ( | |
| invalid_dir_path, | |
| out_file_path, | |
| err_file_path, | |
| ) | |
| self.expect( | |
| launch_command, | |
| error=True, | |
| patterns=["error:.* No such file or directory: %s" % invalid_dir_path], | |
| ) | |
| @skipIfRemote | |
| @skipIfWasm # WASI gives the inferior no working directory and no file system | |
| def test_set_working_dir_existing(self): | |
| """Test that '-w dir' sets the working dir when running the inferior.""" | |
| d = {"CXX_SOURCES": "print_cwd.cpp"} | |
| self.build(dictionary=d) | |
| self.setTearDownCleanup(d) | |
| exe = self.getBuildArtifact("a.out") | |
| self.runCmd("file " + exe) | |
| mywd = "my_working_dir" | |
| out_file_name = "my_working_dir_test.out" | |
| err_file_name = "my_working_dir_test.err" | |
| my_working_dir_path = self.getBuildArtifact(mywd) | |
| lldbutil.mkdir_p(my_working_dir_path) | |
| out_file_path = os.path.join(my_working_dir_path, out_file_name) | |
| err_file_path = os.path.join(my_working_dir_path, err_file_name) | |
| # Make sure the output files do not exist before launching the process | |
| try: | |
| os.remove(out_file_path) | |
| os.remove(err_file_path) | |
| except OSError: | |
| pass | |
| launch_command = "process launch -w %s -o %s -e %s" % ( | |
| my_working_dir_path, | |
| out_file_path, | |
| err_file_path, | |
| ) | |
| self.expect(launch_command, patterns=["Process .* launched: .*a.out"]) | |
| success = True | |
| err_msg = "" | |
| # Check to see if the 'stdout' file was created | |
| try: | |
| out_f = open(out_file_path) | |
| except IOError: | |
| success = False | |
| err_msg = err_msg + "ERROR: stdout file was not created.\n" | |
| else: | |
| # Check to see if the 'stdout' file contains the right output | |
| line = out_f.readline() | |
| if self.TraceOn(): | |
| print("line:", line) | |
| if not re.search(mywd, line): | |
| success = False | |
| err_msg = ( | |
| err_msg + "The current working directory was not set correctly.\n" | |
| ) | |
| out_f.close() | |
| # Try to delete the 'stdout' and 'stderr' files | |
| try: | |
| os.remove(out_file_path) | |
| os.remove(err_file_path) | |
| except OSError: | |
| pass | |
| if not success: | |
| self.fail(err_msg) | |
| def test_environment_with_special_char(self): | |
| """Test that environment variables containing '*' and '}' are handled correctly by the inferior.""" | |
| source = "print_env.cpp" | |
| d = {"CXX_SOURCES": source} | |
| self.build(dictionary=d) | |
| self.setTearDownCleanup(d) | |
| evil_var = "INIT*MIDDLE}TAIL" | |
| target = self.createTestTarget() | |
| main_source_spec = lldb.SBFileSpec(source) | |
| breakpoint = target.BreakpointCreateBySourceRegex( | |
| "// Set breakpoint here.", main_source_spec | |
| ) | |
| process = target.LaunchSimple( | |
| None, ["EVIL=" + evil_var], self.get_process_working_directory() | |
| ) | |
| self.assertEqual(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) | |
| threads = lldbutil.get_threads_stopped_at_breakpoint(process, breakpoint) | |
| self.assertEqual(len(threads), 1) | |
| frame = threads[0].GetFrameAtIndex(0) | |
| sbvalue = frame.EvaluateExpression("evil") | |
| value = sbvalue.GetSummary().strip('"') | |
| self.assertEqual(value, evil_var) | |
| process.Continue() | |
| self.assertState(process.GetState(), lldb.eStateExited, PROCESS_EXITED) | |
| @skipIfRemote | |
| @skipIfWasm # WASI gives the inferior no working directory and no file system | |
| def test_target_launch_working_dir_prop(self): | |
| """Test that the setting `target.launch-working-dir` is correctly used when launching a process.""" | |
| d = {"CXX_SOURCES": "print_cwd.cpp"} | |
| self.build(dictionary=d) | |
| self.setTearDownCleanup(d) | |
| exe = self.getBuildArtifact("a.out") | |
| self.runCmd("file " + exe) | |
| mywd = "my_working_dir" | |
| out_file_name = "my_working_dir_test.out" | |
| my_working_dir_path = Path(self.getBuildArtifact(mywd)).resolve() | |
| lldbutil.mkdir_p(my_working_dir_path) | |
| out_file_path = os.path.join(my_working_dir_path, out_file_name) | |
| another_working_dir_path = Path( | |
| os.path.join(my_working_dir_path, "..") | |
| ).resolve() | |
| # If -w is not passed to process launch, then the setting will be used. | |
| self.runCmd( | |
| f"settings set target.launch-working-dir {another_working_dir_path}" | |
| ) | |
| launch_command = f"process launch -o {out_file_path}" | |
| self.expect( | |
| launch_command, | |
| patterns=["Process .* launched: .*a.out"], | |
| ) | |
| out = lldbutil.read_file_on_target(self, out_file_path) | |
| self.assertIn(f"stdout: {another_working_dir_path}", out) | |
| # If -w is passed to process launch, that value will be used instead of the setting. | |
| launch_command = f"process launch -w {my_working_dir_path} -o {out_file_path}" | |
| self.expect( | |
| launch_command, | |
| patterns=["Process .* launched: .*a.out"], | |
| ) | |
| out = lldbutil.read_file_on_target(self, out_file_path) | |
| self.assertIn(f"stdout: {my_working_dir_path}", out) | |
| # If set to empty, then LLDB's cwd will be used to launch the process. | |
| self.runCmd(f"settings set target.launch-working-dir ''") | |
| launch_command = f"process launch -o {out_file_path}" | |
| self.expect( | |
| launch_command, | |
| patterns=["Process .* launched: .*a.out"], | |
| ) | |
| out = lldbutil.read_file_on_target(self, out_file_path) | |
| self.assertNotIn(f"stdout: {another_working_dir_path}", out) |