TestLoadUnload.py revision 77ee3932a553e4b7deec3208aa5db106d3f6b18d
1"""
2Test that breakpoint by symbol name works correctly with dynamic libs.
3"""
4
5import os, time
6import re
7import unittest2
8import lldb
9from lldbtest import *
10
11class LoadUnloadTestCase(TestBase):
12
13    mydir = os.path.join("functionalities", "load_unload")
14
15    def setUp(self):
16        # Call super's setUp().
17        TestBase.setUp(self)
18        # Find the line number to break for main.cpp.
19        self.line = line_number('main.c',
20                                '// Set break point at this line for test_lldb_process_load_and_unload_commands().')
21        self.line_d_function = line_number('d.c',
22                                           '// Find this line number within d_dunction().')
23
24    @unittest2.expectedFailure
25    def test_modules_search_paths(self):
26        """Test target modules list after moving libd.dylib, and verifies that it works with 'target modules search-paths add'."""
27
28        # Invoke the default build rule.
29        self.buildDefault()
30
31        if sys.platform.startswith("darwin"):
32            dylibName = 'libd.dylib'
33
34        # Now let's move the dynamic library to a different directory than $CWD.
35
36        # The directory to relocate the dynamic library to.
37        new_dir = os.path.join(os.getcwd(), "dyld_path")
38
39        # This is the function to remove the dyld_path directory after the test.
40        def remove_dyld_dir():
41            import shutil
42            shutil.rmtree(new_dir)
43
44        old_dylib = os.path.join(os.getcwd(), dylibName)
45        new_dylib = os.path.join(new_dir, dylibName)
46
47        os.mkdir(new_dir)
48        os.rename(old_dylib, new_dylib)
49        self.addTearDownHook(remove_dyld_dir)
50
51        exe = os.path.join(os.getcwd(), "a.out")
52        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
53
54        self.expect("target modules list",
55            substrs = [old_dylib])
56        self.expect("target modules list -t 3",
57            patterns = ["%s-[^-]*-[^-]*" % self.getArchitecture()])
58        self.runCmd("target modules search-paths add %s %s" % (os.getcwd(), new_dir))
59
60        self.expect("target modules search-paths list",
61            substrs = [os.getcwd(), new_dir])
62
63        # Add teardown hook to clear image-search-paths after the test.
64        self.addTearDownHook(lambda: self.runCmd("target modules search-paths clear"))
65        self.expect("target modules list", "LLDB successfully locates the relocated dynamic library",
66            substrs = [new_dylib])
67
68
69    def test_dyld_library_path(self):
70        """Test DYLD_LIBRARY_PATH after moving libd.dylib, which defines d_function, somewhere else."""
71
72        # Invoke the default build rule.
73        self.buildDefault()
74
75        if sys.platform.startswith("darwin"):
76            dylibName = 'libd.dylib'
77            dsymName = 'libd.dylib.dSYM'
78            dylibPath = 'DYLD_LIBRARY_PATH'
79
80        # Now let's move the dynamic library to a different directory than $CWD.
81
82        # The directory to relocate the dynamic library and its debugging info.
83        new_dir = os.path.join(os.getcwd(), "dyld_path")
84
85        # This is the function to remove the dyld_path directory after the test.
86        def remove_dyld_dir():
87            import shutil
88            shutil.rmtree(new_dir)
89
90        old_dylib = os.path.join(os.getcwd(), dylibName)
91        new_dylib = os.path.join(new_dir, dylibName)
92        old_dSYM = os.path.join(os.getcwd(), dsymName)
93        new_dSYM = os.path.join(new_dir, dsymName)
94        #system(["ls", "-lR", "."])
95        os.mkdir(new_dir)
96        os.rename(old_dylib, new_dylib)
97        if dsymName:
98            os.rename(old_dSYM, new_dSYM)
99        self.addTearDownHook(remove_dyld_dir)
100        #system(["ls", "-lR", "."])
101
102        # With libd.dylib moved, a.out run should fail.
103        exe = os.path.join(os.getcwd(), "a.out")
104        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
105        # Set breakpoint by function name d_function.
106        self.expect("breakpoint set -n d_function", BREAKPOINT_CREATED,
107            substrs = ["Breakpoint created",
108                       "name = 'd_function'",
109                       "locations = 0 (pending)"])
110        self.runCmd("run")
111        self.expect("process status", "Not expected to hit the d_function breakpoint",
112                    matching=False,
113            substrs = ["stop reason = breakpoint"])
114        # Kill the inferior process.
115        self.runCmd("process kill")
116
117        # Try again with the DYLD_LIBRARY_PATH environment variable properly set.
118        env_cmd_string = "settings set target.process.env-vars " + dylibPath + "=" + new_dir
119        self.runCmd("env_cmd_string")
120        self.runCmd("run")
121        self.expect("thread backtrace", STOPPED_DUE_TO_BREAKPOINT,
122            patterns = ["frame #0.*d_function.*at d.c:%d" % self.line_d_function])
123
124    def test_lldb_process_load_and_unload_commands(self):
125        """Test that lldb process load/unload command work correctly."""
126
127        # Invoke the default build rule.
128        self.buildDefault()
129
130        exe = os.path.join(os.getcwd(), "a.out")
131        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
132
133        # Break at main.c before the call to dlopen().
134        # Use lldb's process load command to load the dylib, instead.
135
136        self.expect("breakpoint set -f main.c -l %d" % self.line,
137                    BREAKPOINT_CREATED,
138            startstr = "Breakpoint created: 1: file ='main.c', line = %d" %
139                        self.line)
140
141        self.runCmd("run", RUN_SUCCEEDED)
142
143        # Make sure that a_function does not exist at this point.
144        self.expect("image lookup -n a_function", "a_function should not exist yet",
145                    error=True, matching=False,
146            patterns = ["1 match found .* %s" % self.mydir])
147
148        # Use lldb 'process load' to load the dylib.
149        self.expect("process load liba.dylib", "liba.dylib loaded correctly",
150            patterns = ['Loading "liba.dylib".*ok',
151                        'Image [0-9]+ loaded'])
152
153        # Search for and match the "Image ([0-9]+) loaded" pattern.
154        output = self.res.GetOutput()
155        pattern = re.compile("Image ([0-9]+) loaded")
156        for l in output.split(os.linesep):
157            #print "l:", l
158            match = pattern.search(l)
159            if match:
160                break
161        index = match.group(1)
162
163        # Now we should have an entry for a_function.
164        self.expect("image lookup -n a_function", "a_function should now exist",
165            patterns = ["1 match found .*%s" % self.mydir])
166
167        # Use lldb 'process unload' to unload the dylib.
168        self.expect("process unload %s" % index, "liba.dylib unloaded correctly",
169            patterns = ["Unloading .* with index %s.*ok" % index])
170
171        self.runCmd("process continue")
172
173    def test_load_unload(self):
174        """Test breakpoint by name works correctly with dlopen'ing."""
175
176        # Invoke the default build rule.
177        self.buildDefault()
178
179        exe = os.path.join(os.getcwd(), "a.out")
180        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
181
182        # Break by function name a_function (not yet loaded).
183        self.expect("breakpoint set -n a_function", BREAKPOINT_CREATED,
184            startstr = "Breakpoint created: 1: name = 'a_function', locations = 0 (pending)")
185
186        self.runCmd("run", RUN_SUCCEEDED)
187
188        # The stop reason of the thread should be breakpoint and at a_function.
189        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
190            substrs = ['stopped',
191                       'a_function',
192                       'stop reason = breakpoint'])
193
194        # The breakpoint should have a hit count of 1.
195        self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE,
196            substrs = [' resolved, hit count = 1'])
197
198        # Issue the 'contnue' command.  We should stop agaian at a_function.
199        # The stop reason of the thread should be breakpoint and at a_function.
200        self.runCmd("continue")
201
202        # rdar://problem/8508987
203        # The a_function breakpoint should be encountered twice.
204        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
205            substrs = ['stopped',
206                       'a_function',
207                       'stop reason = breakpoint'])
208
209        # The breakpoint should have a hit count of 2.
210        self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE,
211            substrs = [' resolved, hit count = 2'])
212
213
214if __name__ == '__main__':
215    import atexit
216    lldb.SBDebugger.Initialize()
217    atexit.register(lambda: lldb.SBDebugger.Terminate())
218    unittest2.main()
219