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