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