1"""
2Test lldb process launch flags.
3"""
4
5from __future__ import print_function
6
7import os
8
9import lldb
10from lldbsuite.test.decorators import *
11from lldbsuite.test.lldbtest import *
12from lldbsuite.test import lldbutil
13
14import six
15
16
17class ProcessLaunchTestCase(TestBase):
18
19    mydir = TestBase.compute_mydir(__file__)
20    NO_DEBUG_INFO_TESTCASE = True
21
22    def setUp(self):
23        # Call super's setUp().
24        TestBase.setUp(self)
25        self.runCmd("settings set auto-confirm true")
26
27    def tearDown(self):
28        self.runCmd("settings clear auto-confirm")
29        TestBase.tearDown(self)
30
31    @skipIfRemote
32    @skipIfReproducer
33    def test_io(self):
34        """Test that process launch I/O redirection flags work properly."""
35        self.build()
36        exe = self.getBuildArtifact("a.out")
37        self.expect("file " + exe,
38                    patterns=["Current executable set to .*a.out"])
39
40        in_file = os.path.join(self.getSourceDir(), "input-file.txt")
41        out_file = lldbutil.append_to_process_working_directory(self, "output-test.out")
42        err_file = lldbutil.append_to_process_working_directory(self, "output-test.err")
43
44        # Make sure the output files do not exist before launching the process
45        try:
46            os.remove(out_file)
47        except OSError:
48            pass
49
50        try:
51            os.remove(err_file)
52        except OSError:
53            pass
54
55        launch_command = "process launch -i '{0}' -o '{1}' -e '{2}' -w '{3}'".format(
56                in_file, out_file, err_file, self.get_process_working_directory())
57
58        if lldb.remote_platform:
59            self.runCmd('platform put-file "{local}" "{remote}"'.format(
60                local=in_file, remote=in_file))
61
62        self.expect(launch_command,
63                    patterns=["Process .* launched: .*a.out"])
64
65        success = True
66        err_msg = ""
67
68        out = lldbutil.read_file_on_target(self, out_file)
69        if out != "This should go to stdout.\n":
70            success = False
71            err_msg = err_msg + "    ERROR: stdout file does not contain correct output.\n"
72
73
74        err = lldbutil.read_file_on_target(self, err_file)
75        if err != "This should go to stderr.\n":
76            success = False
77            err_msg = err_msg + "    ERROR: stderr file does not contain correct output.\n"
78
79        if not success:
80            self.fail(err_msg)
81
82    # rdar://problem/9056462
83    # The process launch flag '-w' for setting the current working directory
84    # not working?
85    @skipIfRemote
86    @expectedFailureAll(oslist=["freebsd", "linux"], bugnumber="llvm.org/pr20265")
87    @expectedFailureNetBSD
88    @skipIfReproducer
89    def test_set_working_dir_nonexisting(self):
90        """Test that '-w dir' fails to set the working dir when running the inferior with a dir which doesn't exist."""
91        d = {'CXX_SOURCES': 'print_cwd.cpp'}
92        self.build(dictionary=d)
93        self.setTearDownCleanup(d)
94        exe = self.getBuildArtifact("a.out")
95        self.runCmd("file " + exe)
96
97        mywd = 'my_working_dir'
98        out_file_name = "my_working_dir_test.out"
99        err_file_name = "my_working_dir_test.err"
100
101        my_working_dir_path = self.getBuildArtifact(mywd)
102        out_file_path = os.path.join(my_working_dir_path, out_file_name)
103        err_file_path = os.path.join(my_working_dir_path, err_file_name)
104
105        # Check that we get an error when we have a nonexisting path
106        invalid_dir_path = mywd + 'z'
107        launch_command = "process launch -w %s -o %s -e %s" % (
108            invalid_dir_path, out_file_path, err_file_path)
109
110        self.expect(
111            launch_command, error=True, patterns=[
112                "error:.* No such file or directory: %s" %
113                invalid_dir_path])
114
115    @skipIfRemote
116    @skipIfReproducer
117    def test_set_working_dir_existing(self):
118        """Test that '-w dir' sets the working dir when running the inferior."""
119        d = {'CXX_SOURCES': 'print_cwd.cpp'}
120        self.build(dictionary=d)
121        self.setTearDownCleanup(d)
122        exe = self.getBuildArtifact("a.out")
123        self.runCmd("file " + exe)
124
125        mywd = 'my_working_dir'
126        out_file_name = "my_working_dir_test.out"
127        err_file_name = "my_working_dir_test.err"
128
129        my_working_dir_path = self.getBuildArtifact(mywd)
130        lldbutil.mkdir_p(my_working_dir_path)
131        out_file_path = os.path.join(my_working_dir_path, out_file_name)
132        err_file_path = os.path.join(my_working_dir_path, err_file_name)
133
134        # Make sure the output files do not exist before launching the process
135        try:
136            os.remove(out_file_path)
137            os.remove(err_file_path)
138        except OSError:
139            pass
140
141        launch_command = "process launch -w %s -o %s -e %s" % (
142            my_working_dir_path, out_file_path, err_file_path)
143
144        self.expect(launch_command,
145                    patterns=["Process .* launched: .*a.out"])
146
147        success = True
148        err_msg = ""
149
150        # Check to see if the 'stdout' file was created
151        try:
152            out_f = open(out_file_path)
153        except IOError:
154            success = False
155            err_msg = err_msg + "ERROR: stdout file was not created.\n"
156        else:
157            # Check to see if the 'stdout' file contains the right output
158            line = out_f.readline()
159            if self.TraceOn():
160                print("line:", line)
161            if not re.search(mywd, line):
162                success = False
163                err_msg = err_msg + "The current working directory was not set correctly.\n"
164                out_f.close()
165
166        # Try to delete the 'stdout' and 'stderr' files
167        try:
168            os.remove(out_file_path)
169            os.remove(err_file_path)
170        except OSError:
171            pass
172
173        if not success:
174            self.fail(err_msg)
175
176    @skipIfReproducer
177    def test_environment_with_special_char(self):
178        """Test that environment variables containing '*' and '}' are handled correctly by the inferior."""
179        source = 'print_env.cpp'
180        d = {'CXX_SOURCES': source}
181        self.build(dictionary=d)
182        self.setTearDownCleanup(d)
183        exe = self.getBuildArtifact("a.out")
184
185        evil_var = 'INIT*MIDDLE}TAIL'
186
187        target = self.dbg.CreateTarget(exe)
188        main_source_spec = lldb.SBFileSpec(source)
189        breakpoint = target.BreakpointCreateBySourceRegex(
190            '// Set breakpoint here.', main_source_spec)
191
192        process = target.LaunchSimple(None,
193                                      ['EVIL=' + evil_var],
194                                      self.get_process_working_directory())
195        self.assertEqual(
196            process.GetState(),
197            lldb.eStateStopped,
198            PROCESS_STOPPED)
199
200        threads = lldbutil.get_threads_stopped_at_breakpoint(
201            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.assertEqual(process.GetState(), lldb.eStateExited, PROCESS_EXITED)
210