]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - packages/Python/lldbsuite/test/functionalities/load_unload/TestLoadUnload.py
Vendor import of lldb trunk r338150:
[FreeBSD/FreeBSD.git] / packages / Python / lldbsuite / test / functionalities / load_unload / TestLoadUnload.py
1 """
2 Test that breakpoint by symbol name works correctly with dynamic libs.
3 """
4
5 from __future__ import print_function
6
7
8 import os
9 import time
10 import re
11 import lldb
12 from lldbsuite.test.decorators import *
13 from lldbsuite.test.lldbtest import *
14 from lldbsuite.test import lldbutil
15
16
17 @skipIfWindows  # Windows doesn't have dlopen and friends, dynamic libraries work differently
18 class LoadUnloadTestCase(TestBase):
19
20     mydir = TestBase.compute_mydir(__file__)
21
22     NO_DEBUG_INFO_TESTCASE = True
23    
24     def setUp(self):
25         # Call super's setUp().
26         TestBase.setUp(self)
27         self.setup_test()
28         # Invoke the default build rule.
29         self.build()
30         # Find the line number to break for main.cpp.
31         self.line = line_number(
32             'main.cpp',
33             '// Set break point at this line for test_lldb_process_load_and_unload_commands().')
34         self.line_d_function = line_number(
35             'd.cpp', '// Find this line number within d_dunction().')
36
37     def setup_test(self):
38         lldbutil.mkdir_p(self.getBuildArtifact("hidden"))
39         if not self.platformIsDarwin():
40             if not lldb.remote_platform and "LD_LIBRARY_PATH" in os.environ:
41                 self.runCmd(
42                     "settings set target.env-vars " +
43                     self.dylibPath +
44                     "=" +
45                     os.environ["LD_LIBRARY_PATH"] +
46                     ":" +
47                     self.getBuildDir())
48             else:
49                 if lldb.remote_platform:
50                     wd = lldb.remote_platform.GetWorkingDirectory()
51                 else:
52                     wd = self.getBuildDir()
53                 self.runCmd(
54                     "settings set target.env-vars " +
55                     self.dylibPath +
56                     "=" +
57                     wd)
58
59     def copy_shlibs_to_remote(self, hidden_dir=False):
60         """ Copies the shared libs required by this test suite to remote.
61         Does nothing in case of non-remote platforms.
62         """
63         if lldb.remote_platform:
64             ext = 'so'
65             if self.platformIsDarwin():
66                 ext = 'dylib'
67
68             shlibs = ['libloadunload_a.' + ext, 'libloadunload_b.' + ext,
69                       'libloadunload_c.' + ext, 'libloadunload_d.' + ext]
70             wd = lldb.remote_platform.GetWorkingDirectory()
71             cwd = os.getcwd()
72             for f in shlibs:
73                 err = lldb.remote_platform.Put(
74                     lldb.SBFileSpec(self.getBuildArtifact(f)),
75                     lldb.SBFileSpec(os.path.join(wd, f)))
76                 if err.Fail():
77                     raise RuntimeError(
78                         "Unable copy '%s' to '%s'.\n>>> %s" %
79                         (f, wd, err.GetCString()))
80             if hidden_dir:
81                 shlib = 'libloadunload_d.' + ext
82                 hidden_dir = os.path.join(wd, 'hidden')
83                 hidden_file = os.path.join(hidden_dir, shlib)
84                 err = lldb.remote_platform.MakeDirectory(hidden_dir)
85                 if err.Fail():
86                     raise RuntimeError(
87                         "Unable to create a directory '%s'." % hidden_dir)
88                 err = lldb.remote_platform.Put(
89                     lldb.SBFileSpec(os.path.join('hidden', shlib)),
90                     lldb.SBFileSpec(hidden_file))
91                 if err.Fail():
92                     raise RuntimeError(
93                         "Unable copy 'libloadunload_d.so' to '%s'.\n>>> %s" %
94                         (wd, err.GetCString()))
95
96     # libloadunload_d.so does not appear in the image list because executable
97     # dependencies are resolved relative to the debuggers PWD. Bug?
98     @expectedFailureAll(oslist=["linux"])
99     @skipIfFreeBSD  # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support
100     @not_remote_testsuite_ready
101     @skipIfWindows  # Windows doesn't have dlopen and friends, dynamic libraries work differently
102     def test_modules_search_paths(self):
103         """Test target modules list after loading a different copy of the library libd.dylib, and verifies that it works with 'target modules search-paths add'."""
104         if self.platformIsDarwin():
105             dylibName = 'libloadunload_d.dylib'
106         else:
107             dylibName = 'libloadunload_d.so'
108
109         # The directory with the dynamic library we did not link to.
110         new_dir = os.path.join(self.getBuildDir(), "hidden")
111
112         old_dylib = os.path.join(self.getBuildDir(), dylibName)
113         new_dylib = os.path.join(new_dir, dylibName)
114         exe = self.getBuildArtifact("a.out")
115         self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
116
117         self.expect("target modules list",
118                     substrs=[old_dylib])
119         # self.expect("target modules list -t 3",
120         #    patterns = ["%s-[^-]*-[^-]*" % self.getArchitecture()])
121         # Add an image search path substitution pair.
122         self.runCmd(
123             "target modules search-paths add %s %s" %
124             (self.getBuildDir(), new_dir))
125
126         self.expect("target modules search-paths list",
127                     substrs=[self.getBuildDir(), new_dir])
128
129         self.expect(
130             "target modules search-paths query %s" %
131             self.getBuildDir(),
132             "Image search path successfully transformed",
133             substrs=[new_dir])
134
135         # Obliterate traces of libd from the old location.
136         os.remove(old_dylib)
137         # Inform (DY)LD_LIBRARY_PATH of the new path, too.
138         env_cmd_string = "settings set target.env-vars " + self.dylibPath + "=" + new_dir
139         if self.TraceOn():
140             print("Set environment to: ", env_cmd_string)
141         self.runCmd(env_cmd_string)
142         self.runCmd("settings show target.env-vars")
143
144         remove_dyld_path_cmd = "settings remove target.env-vars " + self.dylibPath
145         self.addTearDownHook(
146             lambda: self.dbg.HandleCommand(remove_dyld_path_cmd))
147
148         self.runCmd("run")
149
150         self.expect(
151             "target modules list",
152             "LLDB successfully locates the relocated dynamic library",
153             substrs=[new_dylib])
154
155     # libloadunload_d.so does not appear in the image list because executable
156     # dependencies are resolved relative to the debuggers PWD. Bug?
157     @expectedFailureAll(oslist=["linux"])
158     @skipIfFreeBSD  # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support
159     @expectedFailureAndroid  # wrong source file shows up for hidden library
160     @skipIfWindows  # Windows doesn't have dlopen and friends, dynamic libraries work differently
161     def test_dyld_library_path(self):
162         """Test (DY)LD_LIBRARY_PATH after moving libd.dylib, which defines d_function, somewhere else."""
163         self.copy_shlibs_to_remote(hidden_dir=True)
164
165         exe = self.getBuildArtifact("a.out")
166         self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
167
168         # Shut off ANSI color usage so we don't get ANSI escape sequences
169         # mixed in with stop locations.
170         self.dbg.SetUseColor(False)
171
172         if self.platformIsDarwin():
173             dylibName = 'libloadunload_d.dylib'
174             dsymName = 'libloadunload_d.dylib.dSYM'
175         else:
176             dylibName = 'libloadunload_d.so'
177
178         # The directory to relocate the dynamic library and its debugging info.
179         special_dir = "hidden"
180         if lldb.remote_platform:
181             wd = lldb.remote_platform.GetWorkingDirectory()
182         else:
183             wd = self.getBuildDir()
184
185         old_dir = wd
186         new_dir = os.path.join(wd, special_dir)
187         old_dylib = os.path.join(old_dir, dylibName)
188
189         remove_dyld_path_cmd = "settings remove target.env-vars " \
190                                + self.dylibPath
191         self.addTearDownHook(
192             lambda: self.dbg.HandleCommand(remove_dyld_path_cmd))
193
194         # For now we don't track (DY)LD_LIBRARY_PATH, so the old
195         # library will be in the modules list.
196         self.expect("target modules list",
197                     substrs=[os.path.basename(old_dylib)],
198                     matching=True)
199
200         lldbutil.run_break_set_by_file_and_line(
201             self, "d.cpp", self.line_d_function, num_expected_locations=1)
202         # After run, make sure the non-hidden library is picked up.
203         self.expect("run", substrs=["return", "700"])
204
205         self.runCmd("continue")
206
207         # Add the hidden directory first in the search path.
208         env_cmd_string = ("settings set target.env-vars %s=%s" %
209                           (self.dylibPath, new_dir))
210         if not self.platformIsDarwin():
211             env_cmd_string += ":" + wd
212         self.runCmd(env_cmd_string)
213         
214         # This time, the hidden library should be picked up.
215         self.expect("run", substrs=["return", "12345"])
216
217     @expectedFailureAll(
218         bugnumber="llvm.org/pr25805",
219         hostoslist=["windows"],
220         triple='.*-android')
221     @skipIfFreeBSD  # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support
222     @skipIfWindows  # Windows doesn't have dlopen and friends, dynamic libraries work differently
223     def test_lldb_process_load_and_unload_commands(self):
224         """Test that lldb process load/unload command work correctly."""
225         self.copy_shlibs_to_remote()
226
227         exe = self.getBuildArtifact("a.out")
228         self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
229
230         # Break at main.cpp before the call to dlopen().
231         # Use lldb's process load command to load the dylib, instead.
232
233         lldbutil.run_break_set_by_file_and_line(
234             self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True)
235
236         self.runCmd("run", RUN_SUCCEEDED)
237
238         ctx = self.platformContext
239         dylibName = ctx.shlib_prefix + 'loadunload_a.' + ctx.shlib_extension
240         localDylibPath = self.getBuildArtifact(dylibName)
241         if lldb.remote_platform:
242             wd = lldb.remote_platform.GetWorkingDirectory()
243             remoteDylibPath = lldbutil.join_remote_paths(wd, dylibName)
244         else:
245             remoteDylibPath = localDylibPath
246
247         # Make sure that a_function does not exist at this point.
248         self.expect(
249             "image lookup -n a_function",
250             "a_function should not exist yet",
251             error=True,
252             matching=False,
253             patterns=["1 match found"])
254
255         # Use lldb 'process load' to load the dylib.
256         self.expect(
257             "process load %s --install=%s" % (localDylibPath, remoteDylibPath),
258             "%s loaded correctly" % dylibName,
259             patterns=[
260                 'Loading "%s".*ok' % localDylibPath,
261                 'Image [0-9]+ loaded'])
262
263         # Search for and match the "Image ([0-9]+) loaded" pattern.
264         output = self.res.GetOutput()
265         pattern = re.compile("Image ([0-9]+) loaded")
266         for l in output.split(os.linesep):
267             #print("l:", l)
268             match = pattern.search(l)
269             if match:
270                 break
271         index = match.group(1)
272
273         # Now we should have an entry for a_function.
274         self.expect(
275             "image lookup -n a_function",
276             "a_function should now exist",
277             patterns=[
278                 "1 match found .*%s" %
279                 dylibName])
280
281         # Use lldb 'process unload' to unload the dylib.
282         self.expect(
283             "process unload %s" %
284             index,
285             "%s unloaded correctly" %
286             dylibName,
287             patterns=[
288                 "Unloading .* with index %s.*ok" %
289                 index])
290
291         self.runCmd("process continue")
292
293     @skipIfFreeBSD  # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support
294     def test_load_unload(self):
295         """Test breakpoint by name works correctly with dlopen'ing."""
296         self.copy_shlibs_to_remote()
297
298         exe = self.getBuildArtifact("a.out")
299         self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
300
301         # Break by function name a_function (not yet loaded).
302         lldbutil.run_break_set_by_symbol(
303             self, "a_function", num_expected_locations=0)
304
305         self.runCmd("run", RUN_SUCCEEDED)
306
307         # The stop reason of the thread should be breakpoint and at a_function.
308         self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
309                     substrs=['stopped',
310                              'a_function',
311                              'stop reason = breakpoint'])
312
313         # The breakpoint should have a hit count of 1.
314         self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE,
315                     substrs=[' resolved, hit count = 1'])
316
317         # Issue the 'contnue' command.  We should stop agaian at a_function.
318         # The stop reason of the thread should be breakpoint and at a_function.
319         self.runCmd("continue")
320
321         # rdar://problem/8508987
322         # The a_function breakpoint should be encountered twice.
323         self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
324                     substrs=['stopped',
325                              'a_function',
326                              'stop reason = breakpoint'])
327
328         # The breakpoint should have a hit count of 2.
329         self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE,
330                     substrs=[' resolved, hit count = 2'])
331
332     @skipIfFreeBSD  # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support
333     @skipIfWindows  # Windows doesn't have dlopen and friends, dynamic libraries work differently
334     def test_step_over_load(self):
335         """Test stepping over code that loads a shared library works correctly."""
336         self.copy_shlibs_to_remote()
337
338         exe = self.getBuildArtifact("a.out")
339         self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
340
341         # Break by function name a_function (not yet loaded).
342         lldbutil.run_break_set_by_file_and_line(
343             self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True)
344
345         self.runCmd("run", RUN_SUCCEEDED)
346
347         # The stop reason of the thread should be breakpoint and at a_function.
348         self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
349                     substrs=['stopped',
350                              'stop reason = breakpoint'])
351
352         self.runCmd(
353             "thread step-over",
354             "Stepping over function that loads library")
355
356         # The stop reason should be step end.
357         self.expect("thread list", "step over succeeded.",
358                     substrs=['stopped',
359                              'stop reason = step over'])
360
361     # We can't find a breakpoint location for d_init before launching because
362     # executable dependencies are resolved relative to the debuggers PWD. Bug?
363     @expectedFailureAll(oslist=["linux"])
364     @skipIfFreeBSD  # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support
365     @skipIfWindows  # Windows doesn't have dlopen and friends, dynamic libraries work differently
366     def test_static_init_during_load(self):
367         """Test that we can set breakpoints correctly in static initializers"""
368         self.copy_shlibs_to_remote()
369
370         exe = self.getBuildArtifact("a.out")
371         self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
372
373         a_init_bp_num = lldbutil.run_break_set_by_symbol(
374             self, "a_init", num_expected_locations=0)
375         b_init_bp_num = lldbutil.run_break_set_by_symbol(
376             self, "b_init", num_expected_locations=0)
377         d_init_bp_num = lldbutil.run_break_set_by_symbol(
378             self, "d_init", num_expected_locations=1)
379
380         self.runCmd("run", RUN_SUCCEEDED)
381
382         self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
383                     substrs=['stopped',
384                              'd_init',
385                              'stop reason = breakpoint %d' % d_init_bp_num])
386
387         self.runCmd("continue")
388         self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
389                     substrs=['stopped',
390                              'b_init',
391                              'stop reason = breakpoint %d' % b_init_bp_num])
392         self.expect("thread backtrace",
393                     substrs=['b_init',
394                              'dlopen',
395                              'main'])
396
397         self.runCmd("continue")
398         self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
399                     substrs=['stopped',
400                              'a_init',
401                              'stop reason = breakpoint %d' % a_init_bp_num])
402         self.expect("thread backtrace",
403                     substrs=['a_init',
404                              'dlopen',
405                              'main'])