1""" This module contains functions used by the test cases to hide the 2architecture and/or the platform dependent nature of the tests. """ 3 4# System modules 5import itertools 6import json 7import re 8import subprocess 9import sys 10import os 11from packaging import version 12 13# LLDB modules 14import lldb 15from . import configuration 16from . import lldbtest_config 17import lldbsuite.test.lldbplatform as lldbplatform 18from lldbsuite.test.builders import get_builder 19from lldbsuite.test.lldbutil import is_exe 20 21 22def check_first_register_readable(test_case): 23 arch = test_case.getArchitecture() 24 25 if arch in ["x86_64", "i386"]: 26 test_case.expect("register read eax", substrs=["eax = 0x"]) 27 elif arch in ["arm", "armv7", "armv7k", "armv8l", "armv7l"]: 28 test_case.expect("register read r0", substrs=["r0 = 0x"]) 29 elif arch in ["aarch64", "arm64", "arm64e", "arm64_32"]: 30 test_case.expect("register read x0", substrs=["x0 = 0x"]) 31 elif re.match("mips", arch): 32 test_case.expect("register read zero", substrs=["zero = 0x"]) 33 elif arch in ["s390x"]: 34 test_case.expect("register read r0", substrs=["r0 = 0x"]) 35 elif arch in ["powerpc64le"]: 36 test_case.expect("register read r0", substrs=["r0 = 0x"]) 37 elif re.match("^rv(32|64)", arch): 38 test_case.expect("register read zero", substrs=["zero = 0x"]) 39 else: 40 # TODO: Add check for other architectures 41 test_case.fail( 42 "Unsupported architecture for test case (arch: %s)" 43 % test_case.getArchitecture() 44 ) 45 46 47def _run_adb_command(cmd, device_id): 48 device_id_args = [] 49 if device_id: 50 device_id_args = ["-s", device_id] 51 full_cmd = ["adb"] + device_id_args + cmd 52 p = subprocess.Popen(full_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 53 stdout, stderr = p.communicate() 54 return p.returncode, stdout, stderr 55 56 57def target_is_android(): 58 return configuration.lldb_platform_name == "remote-android" 59 60 61def android_device_api(): 62 if not hasattr(android_device_api, "result"): 63 assert configuration.lldb_platform_url is not None 64 device_id = None 65 parsed_url = urlparse(configuration.lldb_platform_url) 66 host_name = parsed_url.netloc.split(":")[0] 67 if host_name != "localhost": 68 device_id = host_name 69 if device_id.startswith("[") and device_id.endswith("]"): 70 device_id = device_id[1:-1] 71 retcode, stdout, stderr = _run_adb_command( 72 ["shell", "getprop", "ro.build.version.sdk"], device_id 73 ) 74 if retcode == 0: 75 android_device_api.result = int(stdout) 76 else: 77 raise LookupError( 78 ">>> Unable to determine the API level of the Android device.\n" 79 ">>> stdout:\n%s\n" 80 ">>> stderr:\n%s\n" % (stdout, stderr) 81 ) 82 return android_device_api.result 83 84 85def match_android_device(device_arch, valid_archs=None, valid_api_levels=None): 86 if not target_is_android(): 87 return False 88 if valid_archs is not None and device_arch not in valid_archs: 89 return False 90 if valid_api_levels is not None and android_device_api() not in valid_api_levels: 91 return False 92 93 return True 94 95 96def finalize_build_dictionary(dictionary): 97 # Provide uname-like platform name 98 platform_name_to_uname = { 99 "linux": "Linux", 100 "netbsd": "NetBSD", 101 "freebsd": "FreeBSD", 102 "windows": "Windows_NT", 103 "macosx": "Darwin", 104 "darwin": "Darwin", 105 } 106 107 if dictionary is None: 108 dictionary = {} 109 if target_is_android(): 110 dictionary["OS"] = "Android" 111 dictionary["PIE"] = 1 112 elif platformIsDarwin(): 113 dictionary["OS"] = "Darwin" 114 else: 115 dictionary["OS"] = platform_name_to_uname[getPlatform()] 116 117 dictionary["HOST_OS"] = platform_name_to_uname[getHostPlatform()] 118 119 return dictionary 120 121 122def _get_platform_os(p): 123 # Use the triple to determine the platform if set. 124 triple = p.GetTriple() 125 if triple: 126 platform = triple.split("-")[2] 127 if platform.startswith("freebsd"): 128 platform = "freebsd" 129 elif platform.startswith("netbsd"): 130 platform = "netbsd" 131 elif platform.startswith("openbsd"): 132 platform = "openbsd" 133 return platform 134 135 return "" 136 137 138def getHostPlatform(): 139 """Returns the host platform running the test suite.""" 140 return _get_platform_os(lldb.SBPlatform("host")) 141 142 143def getDarwinOSTriples(): 144 return lldbplatform.translate(lldbplatform.darwin_all) 145 146 147def getPlatform(): 148 """Returns the target platform which the tests are running on.""" 149 # Use the Apple SDK to determine the platform if set. 150 if configuration.apple_sdk: 151 platform = configuration.apple_sdk 152 dot = platform.find(".") 153 if dot != -1: 154 platform = platform[:dot] 155 if platform == "iphoneos": 156 platform = "ios" 157 return platform 158 159 return _get_platform_os(lldb.selected_platform) 160 161 162def platformIsDarwin(): 163 """Returns true if the OS triple for the selected platform is any valid apple OS""" 164 return getPlatform() in getDarwinOSTriples() 165 166 167def findMainThreadCheckerDylib(): 168 if not platformIsDarwin(): 169 return "" 170 171 if getPlatform() in lldbplatform.translate(lldbplatform.darwin_embedded): 172 return "/Developer/usr/lib/libMainThreadChecker.dylib" 173 174 with os.popen("xcode-select -p") as output: 175 xcode_developer_path = output.read().strip() 176 mtc_dylib_path = "%s/usr/lib/libMainThreadChecker.dylib" % xcode_developer_path 177 if os.path.isfile(mtc_dylib_path): 178 return mtc_dylib_path 179 180 return "" 181 182 183class _PlatformContext(object): 184 """Value object class which contains platform-specific options.""" 185 186 def __init__( 187 self, shlib_environment_var, shlib_path_separator, shlib_prefix, shlib_extension 188 ): 189 self.shlib_environment_var = shlib_environment_var 190 self.shlib_path_separator = shlib_path_separator 191 self.shlib_prefix = shlib_prefix 192 self.shlib_extension = shlib_extension 193 194 195def createPlatformContext(): 196 if platformIsDarwin(): 197 return _PlatformContext("DYLD_LIBRARY_PATH", ":", "lib", "dylib") 198 elif getPlatform() in ("linux", "freebsd", "netbsd", "openbsd"): 199 return _PlatformContext("LD_LIBRARY_PATH", ":", "lib", "so") 200 else: 201 return _PlatformContext("PATH", ";", "", "dll") 202 203 204def hasChattyStderr(test_case): 205 """Some targets produce garbage on the standard error output. This utility function 206 determines whether the tests can be strict about the expected stderr contents.""" 207 if match_android_device( 208 test_case.getArchitecture(), ["aarch64"], range(22, 25 + 1) 209 ): 210 return True # The dynamic linker on the device will complain about unknown DT entries 211 return False 212 213 214def builder_module(): 215 return get_builder(sys.platform) 216 217 218def getArchitecture(): 219 """Returns the architecture in effect the test suite is running with.""" 220 module = builder_module() 221 arch = module.getArchitecture() 222 if arch == "amd64": 223 arch = "x86_64" 224 if arch in ["armv7l", "armv8l"]: 225 arch = "arm" 226 return arch 227 228 229lldbArchitecture = None 230 231 232def getLLDBArchitecture(): 233 """Returns the architecture of the lldb binary.""" 234 global lldbArchitecture 235 if not lldbArchitecture: 236 # These two target settings prevent lldb from doing setup that does 237 # nothing but slow down the end goal of printing the architecture. 238 command = [ 239 lldbtest_config.lldbExec, 240 "-x", 241 "-b", 242 "-o", 243 "settings set target.preload-symbols false", 244 "-o", 245 "settings set target.load-script-from-symbol-file false", 246 "-o", 247 "file " + lldbtest_config.lldbExec, 248 ] 249 250 output = subprocess.check_output(command) 251 str = output.decode() 252 253 for line in str.splitlines(): 254 m = re.search(r"Current executable set to '.*' \((.*)\)\.", line) 255 if m: 256 lldbArchitecture = m.group(1) 257 break 258 259 return lldbArchitecture 260 261 262def getCompiler(): 263 """Returns the compiler in effect the test suite is running with.""" 264 module = builder_module() 265 return module.getCompiler() 266 267 268def getCompilerBinary(): 269 """Returns the compiler binary the test suite is running with.""" 270 return getCompiler().split()[0] 271 272 273def getCompilerVersion(): 274 """Returns a string that represents the compiler version. 275 Supports: llvm, clang. 276 """ 277 compiler = getCompilerBinary() 278 version_output = subprocess.check_output([compiler, "--version"], errors="replace") 279 m = re.search("version ([0-9.]+)", version_output) 280 if m: 281 return m.group(1) 282 return "unknown" 283 284 285def getDwarfVersion(): 286 """Returns the dwarf version generated by clang or '0'.""" 287 if configuration.dwarf_version: 288 return str(configuration.dwarf_version) 289 if "clang" in getCompiler(): 290 try: 291 triple = builder_module().getTriple(getArchitecture()) 292 target = ["-target", triple] if triple else [] 293 driver_output = subprocess.check_output( 294 [getCompiler()] + target + "-g -c -x c - -o - -###".split(), 295 stderr=subprocess.STDOUT, 296 ) 297 driver_output = driver_output.decode("utf-8") 298 for line in driver_output.split(os.linesep): 299 m = re.search("dwarf-version=([0-9])", line) 300 if m: 301 return m.group(1) 302 except subprocess.CalledProcessError: 303 pass 304 return "0" 305 306 307def expectedCompilerVersion(compiler_version): 308 """Returns True iff compiler_version[1] matches the current compiler version. 309 Use compiler_version[0] to specify the operator used to determine if a match has occurred. 310 Any operator other than the following defaults to an equality test: 311 '>', '>=', "=>", '<', '<=', '=<', '!=', "!" or 'not' 312 313 If the current compiler version cannot be determined, we assume it is close to the top 314 of trunk, so any less-than or equal-to comparisons will return False, and any 315 greater-than or not-equal-to comparisons will return True. 316 """ 317 if compiler_version is None: 318 return True 319 operator = str(compiler_version[0]) 320 version_str = str(compiler_version[1]) 321 322 if not version_str: 323 return True 324 325 test_compiler_version_str = getCompilerVersion() 326 if test_compiler_version_str == "unknown": 327 # Assume the compiler version is at or near the top of trunk. 328 return operator in [">", ">=", "!", "!=", "not"] 329 330 actual_version = version.parse(version_str) 331 test_compiler_version = version.parse(test_compiler_version_str) 332 333 if operator == ">": 334 return test_compiler_version > actual_version 335 if operator == ">=" or operator == "=>": 336 return test_compiler_version >= actual_version 337 if operator == "<": 338 return test_compiler_version < actual_version 339 if operator == "<=" or operator == "=<": 340 return test_compiler_version <= actual_version 341 if operator == "!=" or operator == "!" or operator == "not": 342 return version_str not in test_compiler_version_str 343 return version_str in test_compiler_version_str 344 345 346def expectedCompiler(compilers): 347 """Returns True iff any element of compilers is a sub-string of the current compiler.""" 348 if compilers is None: 349 return True 350 351 for compiler in compilers: 352 if compiler in getCompiler(): 353 return True 354 355 return False 356 357 358# This is a helper function to determine if a specific version of Xcode's linker 359# contains a TLS bug. We want to skip TLS tests if they contain this bug, but 360# adding a linker/linker_version conditions to a decorator is challenging due to 361# the number of ways linkers can enter the build process. 362def xcode15LinkerBug(): 363 """Returns true iff a test is running on a darwin platform and the host linker is between versions 1000 and 1109.""" 364 darwin_platforms = lldbplatform.translate(lldbplatform.darwin_all) 365 if getPlatform() not in darwin_platforms: 366 return False 367 368 try: 369 raw_version_details = subprocess.check_output( 370 ("xcrun", "ld", "-version_details") 371 ) 372 version_details = json.loads(raw_version_details) 373 version = version_details.get("version", "0") 374 version_tuple = tuple(int(x) for x in version.split(".")) 375 if (1000,) <= version_tuple <= (1109,): 376 return True 377 except: 378 pass 379 380 return False 381