1#!/usr/bin/python 2# 3# Copyright (C) 2013 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""Module for looking up symbolic debugging information. 18 19The information can include symbol names, offsets, and source locations. 20""" 21 22import atexit 23import glob 24import os 25import platform 26import re 27import signal 28import subprocess 29import unittest 30 31try: 32 ANDROID_BUILD_TOP = str(os.environ["ANDROID_BUILD_TOP"]) 33 if not ANDROID_BUILD_TOP: 34 ANDROID_BUILD_TOP = "." 35except: 36 ANDROID_BUILD_TOP = "." 37 38def FindSymbolsDir(): 39 saveddir = os.getcwd() 40 os.chdir(ANDROID_BUILD_TOP) 41 stream = None 42 try: 43 cmd = "build/soong/soong_ui.bash --dumpvar-mode --abs TARGET_OUT_UNSTRIPPED" 44 stream = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True).stdout 45 return os.path.join(ANDROID_BUILD_TOP, str(stream.read().strip())) 46 finally: 47 if stream is not None: 48 stream.close() 49 os.chdir(saveddir) 50 51SYMBOLS_DIR = FindSymbolsDir() 52 53ARCH = None 54 55 56# These are private. Do not access them from other modules. 57_CACHED_TOOLCHAIN = None 58_CACHED_TOOLCHAIN_ARCH = None 59 60# Caches for symbolized information. 61_SYMBOL_INFORMATION_ADDR2LINE_CACHE = {} 62_SYMBOL_INFORMATION_OBJDUMP_CACHE = {} 63_SYMBOL_DEMANGLING_CACHE = {} 64 65# Caches for pipes to subprocesses. 66 67class ProcessCache: 68 _cmd2pipe = {} 69 _lru = [] 70 71 # Max number of open pipes. 72 _PIPE_MAX_OPEN = 10 73 74 def GetProcess(self, cmd): 75 cmd_tuple = tuple(cmd) # Need to use a tuple as lists can't be dict keys. 76 # Pipe already available? 77 if cmd_tuple in self._cmd2pipe: 78 pipe = self._cmd2pipe[cmd_tuple] 79 # Update LRU. 80 self._lru = [(cmd_tuple, pipe)] + [i for i in self._lru if i[0] != cmd_tuple] 81 return pipe 82 83 # Not cached, yet. Open a new one. 84 85 # Check if too many are open, close the old ones. 86 while len(self._lru) >= self._PIPE_MAX_OPEN: 87 open_cmd, open_pipe = self._lru.pop() 88 del self._cmd2pipe[open_cmd] 89 self.TerminateProcess(open_pipe) 90 91 # Create and put into cache. 92 pipe = self.SpawnProcess(cmd) 93 self._cmd2pipe[cmd_tuple] = pipe 94 self._lru = [(cmd_tuple, pipe)] + self._lru 95 return pipe 96 97 def SpawnProcess(self, cmd): 98 return subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) 99 100 def TerminateProcess(self, pipe): 101 pipe.stdin.close() 102 pipe.stdout.close() 103 pipe.terminate() 104 pipe.wait() 105 106 def KillAllProcesses(self): 107 for _, open_pipe in self._lru: 108 self.TerminateProcess(open_pipe) 109 _cmd2pipe = {} 110 _lru = [] 111 112 113_PIPE_ADDR2LINE_CACHE = ProcessCache() 114_PIPE_CPPFILT_CACHE = ProcessCache() 115 116 117# Process cache cleanup on shutdown. 118 119def CloseAllPipes(): 120 _PIPE_ADDR2LINE_CACHE.KillAllProcesses() 121 _PIPE_CPPFILT_CACHE.KillAllProcesses() 122 123 124atexit.register(CloseAllPipes) 125 126 127def PipeTermHandler(signum, frame): 128 CloseAllPipes() 129 os._exit(0) 130 131 132for sig in (signal.SIGABRT, signal.SIGINT, signal.SIGTERM): 133 signal.signal(sig, PipeTermHandler) 134 135 136 137 138def ToolPath(tool, toolchain=None): 139 """Return a fully-qualified path to the specified tool""" 140 if not toolchain: 141 toolchain = FindToolchain() 142 return glob.glob(os.path.join(toolchain, "*-" + tool))[0] 143 144 145def FindToolchain(): 146 """Returns the toolchain matching ARCH.""" 147 global _CACHED_TOOLCHAIN, _CACHED_TOOLCHAIN_ARCH 148 if _CACHED_TOOLCHAIN is not None and _CACHED_TOOLCHAIN_ARCH == ARCH: 149 return _CACHED_TOOLCHAIN 150 151 # We use slightly different names from GCC, and there's only one toolchain 152 # for x86/x86_64. Note that these are the names of the top-level directory 153 # rather than the _different_ names used lower down the directory hierarchy! 154 gcc_dir = ARCH 155 if gcc_dir == "arm64": 156 gcc_dir = "aarch64" 157 elif gcc_dir == "mips64": 158 gcc_dir = "mips" 159 elif gcc_dir == "x86_64": 160 gcc_dir = "x86" 161 162 os_name = platform.system().lower(); 163 164 available_toolchains = glob.glob("%s/prebuilts/gcc/%s-x86/%s/*-linux-*/bin/" % (ANDROID_BUILD_TOP, os_name, gcc_dir)) 165 if len(available_toolchains) == 0: 166 raise Exception("Could not find tool chain for %s" % (ARCH)) 167 168 toolchain = sorted(available_toolchains)[-1] 169 170 if not os.path.exists(ToolPath("addr2line", toolchain)): 171 raise Exception("No addr2line for %s" % (toolchain)) 172 173 _CACHED_TOOLCHAIN = toolchain 174 _CACHED_TOOLCHAIN_ARCH = ARCH 175 print("Using %s toolchain from: %s" % (_CACHED_TOOLCHAIN_ARCH, _CACHED_TOOLCHAIN)) 176 return _CACHED_TOOLCHAIN 177 178 179def SymbolInformation(lib, addr): 180 """Look up symbol information about an address. 181 182 Args: 183 lib: library (or executable) pathname containing symbols 184 addr: string hexidecimal address 185 186 Returns: 187 A list of the form [(source_symbol, source_location, 188 object_symbol_with_offset)]. 189 190 If the function has been inlined then the list may contain 191 more than one element with the symbols for the most deeply 192 nested inlined location appearing first. The list is 193 always non-empty, even if no information is available. 194 195 Usually you want to display the source_location and 196 object_symbol_with_offset from the last element in the list. 197 """ 198 info = SymbolInformationForSet(lib, set([addr])) 199 return (info and info.get(addr)) or [(None, None, None)] 200 201 202def SymbolInformationForSet(lib, unique_addrs): 203 """Look up symbol information for a set of addresses from the given library. 204 205 Args: 206 lib: library (or executable) pathname containing symbols 207 unique_addrs: set of hexidecimal addresses 208 209 Returns: 210 A dictionary of the form {addr: [(source_symbol, source_location, 211 object_symbol_with_offset)]} where each address has a list of 212 associated symbols and locations. The list is always non-empty. 213 214 If the function has been inlined then the list may contain 215 more than one element with the symbols for the most deeply 216 nested inlined location appearing first. The list is 217 always non-empty, even if no information is available. 218 219 Usually you want to display the source_location and 220 object_symbol_with_offset from the last element in the list. 221 """ 222 if not lib: 223 return None 224 225 addr_to_line = CallAddr2LineForSet(lib, unique_addrs) 226 if not addr_to_line: 227 return None 228 229 addr_to_objdump = CallObjdumpForSet(lib, unique_addrs) 230 if not addr_to_objdump: 231 return None 232 233 result = {} 234 for addr in unique_addrs: 235 source_info = addr_to_line.get(addr) 236 if not source_info: 237 source_info = [(None, None)] 238 if addr in addr_to_objdump: 239 (object_symbol, object_offset) = addr_to_objdump.get(addr) 240 object_symbol_with_offset = FormatSymbolWithOffset(object_symbol, 241 object_offset) 242 else: 243 object_symbol_with_offset = None 244 result[addr] = [(source_symbol, source_location, object_symbol_with_offset) 245 for (source_symbol, source_location) in source_info] 246 247 return result 248 249 250def CallAddr2LineForSet(lib, unique_addrs): 251 """Look up line and symbol information for a set of addresses. 252 253 Args: 254 lib: library (or executable) pathname containing symbols 255 unique_addrs: set of string hexidecimal addresses look up. 256 257 Returns: 258 A dictionary of the form {addr: [(symbol, file:line)]} where 259 each address has a list of associated symbols and locations 260 or an empty list if no symbol information was found. 261 262 If the function has been inlined then the list may contain 263 more than one element with the symbols for the most deeply 264 nested inlined location appearing first. 265 """ 266 if not lib: 267 return None 268 269 result = {} 270 addrs = sorted(unique_addrs) 271 272 if lib in _SYMBOL_INFORMATION_ADDR2LINE_CACHE: 273 addr_cache = _SYMBOL_INFORMATION_ADDR2LINE_CACHE[lib] 274 275 # Go through and handle all known addresses. 276 for x in range(len(addrs)): 277 next_addr = addrs.pop(0) 278 if next_addr in addr_cache: 279 result[next_addr] = addr_cache[next_addr] 280 else: 281 # Re-add, needs to be symbolized. 282 addrs.append(next_addr) 283 284 if not addrs: 285 # Everything was cached, we're done. 286 return result 287 else: 288 addr_cache = {} 289 _SYMBOL_INFORMATION_ADDR2LINE_CACHE[lib] = addr_cache 290 291 symbols = SYMBOLS_DIR + lib 292 if not os.path.exists(symbols): 293 symbols = lib 294 if not os.path.exists(symbols): 295 return None 296 297 # Make sure the symbols path is not a directory. 298 if os.path.isdir(symbols): 299 return None 300 301 cmd = [ToolPath("addr2line"), "--functions", "--inlines", 302 "--demangle", "--exe=" + symbols] 303 child = _PIPE_ADDR2LINE_CACHE.GetProcess(cmd) 304 305 for addr in addrs: 306 try: 307 child.stdin.write("0x%s\n" % addr) 308 child.stdin.flush() 309 records = [] 310 first = True 311 while True: 312 symbol = child.stdout.readline().strip() 313 if symbol == "??": 314 symbol = None 315 location = child.stdout.readline().strip() 316 if location == "??:0" or location == "??:?": 317 location = None 318 if symbol is None and location is None: 319 break 320 records.append((symbol, location)) 321 if first: 322 # Write a blank line as a sentinel so we know when to stop 323 # reading inlines from the output. 324 # The blank line will cause addr2line to emit "??\n??:0\n". 325 child.stdin.write("\n") 326 first = False 327 except IOError as e: 328 # Remove the / in front of the library name to match other output. 329 records = [(None, lib[1:] + " ***Error: " + str(e))] 330 result[addr] = records 331 addr_cache[addr] = records 332 return result 333 334 335def StripPC(addr): 336 """Strips the Thumb bit a program counter address when appropriate. 337 338 Args: 339 addr: the program counter address 340 341 Returns: 342 The stripped program counter address. 343 """ 344 global ARCH 345 if ARCH == "arm": 346 return addr & ~1 347 return addr 348 349 350def CallObjdumpForSet(lib, unique_addrs): 351 """Use objdump to find out the names of the containing functions. 352 353 Args: 354 lib: library (or executable) pathname containing symbols 355 unique_addrs: set of string hexidecimal addresses to find the functions for. 356 357 Returns: 358 A dictionary of the form {addr: (string symbol, offset)}. 359 """ 360 if not lib: 361 return None 362 363 result = {} 364 addrs = sorted(unique_addrs) 365 366 addr_cache = None 367 if lib in _SYMBOL_INFORMATION_OBJDUMP_CACHE: 368 addr_cache = _SYMBOL_INFORMATION_OBJDUMP_CACHE[lib] 369 370 # Go through and handle all known addresses. 371 for x in range(len(addrs)): 372 next_addr = addrs.pop(0) 373 if next_addr in addr_cache: 374 result[next_addr] = addr_cache[next_addr] 375 else: 376 # Re-add, needs to be symbolized. 377 addrs.append(next_addr) 378 379 if not addrs: 380 # Everything was cached, we're done. 381 return result 382 else: 383 addr_cache = {} 384 _SYMBOL_INFORMATION_OBJDUMP_CACHE[lib] = addr_cache 385 386 symbols = SYMBOLS_DIR + lib 387 if not os.path.exists(symbols): 388 symbols = lib 389 if not os.path.exists(symbols): 390 return None 391 392 start_addr_dec = str(StripPC(int(addrs[0], 16))) 393 stop_addr_dec = str(StripPC(int(addrs[-1], 16)) + 8) 394 cmd = [ToolPath("objdump"), 395 "--section=.text", 396 "--demangle", 397 "--disassemble", 398 "--start-address=" + start_addr_dec, 399 "--stop-address=" + stop_addr_dec, 400 symbols] 401 402 # Function lines look like: 403 # 000177b0 <android::IBinder::~IBinder()+0x2c>: 404 # We pull out the address and function first. Then we check for an optional 405 # offset. This is tricky due to functions that look like "operator+(..)+0x2c" 406 func_regexp = re.compile("(^[a-f0-9]*) \<(.*)\>:$") 407 offset_regexp = re.compile("(.*)\+0x([a-f0-9]*)") 408 409 # A disassembly line looks like: 410 # 177b2: b510 push {r4, lr} 411 asm_regexp = re.compile("(^[ a-f0-9]*):[ a-f0-0]*.*$") 412 413 current_symbol = None # The current function symbol in the disassembly. 414 current_symbol_addr = 0 # The address of the current function. 415 addr_index = 0 # The address that we are currently looking for. 416 417 stream = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout 418 for line in stream: 419 # Is it a function line like: 420 # 000177b0 <android::IBinder::~IBinder()>: 421 components = func_regexp.match(line) 422 if components: 423 # This is a new function, so record the current function and its address. 424 current_symbol_addr = int(components.group(1), 16) 425 current_symbol = components.group(2) 426 427 # Does it have an optional offset like: "foo(..)+0x2c"? 428 components = offset_regexp.match(current_symbol) 429 if components: 430 current_symbol = components.group(1) 431 offset = components.group(2) 432 if offset: 433 current_symbol_addr -= int(offset, 16) 434 435 # Is it an disassembly line like: 436 # 177b2: b510 push {r4, lr} 437 components = asm_regexp.match(line) 438 if components: 439 addr = components.group(1) 440 target_addr = addrs[addr_index] 441 i_addr = int(addr, 16) 442 i_target = StripPC(int(target_addr, 16)) 443 if i_addr == i_target: 444 result[target_addr] = (current_symbol, i_target - current_symbol_addr) 445 addr_cache[target_addr] = result[target_addr] 446 addr_index += 1 447 if addr_index >= len(addrs): 448 break 449 stream.close() 450 451 return result 452 453 454def CallCppFilt(mangled_symbol): 455 if mangled_symbol in _SYMBOL_DEMANGLING_CACHE: 456 return _SYMBOL_DEMANGLING_CACHE[mangled_symbol] 457 458 cmd = [ToolPath("c++filt")] 459 process = _PIPE_CPPFILT_CACHE.GetProcess(cmd) 460 process.stdin.write(mangled_symbol) 461 process.stdin.write("\n") 462 process.stdin.flush() 463 464 demangled_symbol = process.stdout.readline().strip() 465 466 _SYMBOL_DEMANGLING_CACHE[mangled_symbol] = demangled_symbol 467 468 return demangled_symbol 469 470 471def FormatSymbolWithOffset(symbol, offset): 472 if offset == 0: 473 return symbol 474 return "%s+%d" % (symbol, offset) 475 476 477def GetAbiFromToolchain(toolchain_var, bits): 478 toolchain = os.environ.get(toolchain_var) 479 if not toolchain: 480 return None 481 482 toolchain_match = re.search("\/(aarch64|arm|mips|x86)\/", toolchain) 483 if toolchain_match: 484 abi = toolchain_match.group(1) 485 if abi == "aarch64": 486 return "arm64" 487 elif bits == 64: 488 if abi == "x86": 489 return "x86_64" 490 elif abi == "mips": 491 return "mips64" 492 return abi 493 return None 494 495def Get32BitArch(): 496 # Check for ANDROID_TOOLCHAIN_2ND_ARCH first, if set, use that. 497 # If not try ANDROID_TOOLCHAIN to find the arch. 498 # If this is not set, then default to arm. 499 arch = GetAbiFromToolchain("ANDROID_TOOLCHAIN_2ND_ARCH", 32) 500 if not arch: 501 arch = GetAbiFromToolchain("ANDROID_TOOLCHAIN", 32) 502 if not arch: 503 return "arm" 504 return arch 505 506def Get64BitArch(): 507 # Check for ANDROID_TOOLCHAIN, if it is set, we can figure out the 508 # arch this way. If this is not set, then default to arm64. 509 arch = GetAbiFromToolchain("ANDROID_TOOLCHAIN", 64) 510 if not arch: 511 return "arm64" 512 return arch 513 514def SetAbi(lines): 515 global ARCH 516 517 abi_line = re.compile("ABI: \'(.*)\'") 518 trace_line = re.compile("\#[0-9]+[ \t]+..[ \t]+([0-9a-f]{8}|[0-9a-f]{16})([ \t]+|$)") 519 asan_trace_line = re.compile("\#[0-9]+[ \t]+0x([0-9a-f]+)[ \t]+") 520 521 ARCH = None 522 for line in lines: 523 abi_match = abi_line.search(line) 524 if abi_match: 525 ARCH = abi_match.group(1) 526 break 527 trace_match = trace_line.search(line) 528 if trace_match: 529 # Try to guess the arch, we know the bitness. 530 if len(trace_match.group(1)) == 16: 531 ARCH = Get64BitArch() 532 else: 533 ARCH = Get32BitArch() 534 break 535 asan_trace_match = asan_trace_line.search(line) 536 if asan_trace_match: 537 # We might be able to guess the bitness by the length of the address. 538 if len(asan_trace_match.group(1)) > 8: 539 ARCH = Get64BitArch() 540 # We know for a fact this is 64 bit, so we are done. 541 break 542 else: 543 ARCH = Get32BitArch() 544 # This might be 32 bit, or just a small address. Keep going in this 545 # case, but if we couldn't figure anything else out, go with 32 bit. 546 if not ARCH: 547 raise Exception("Could not determine arch from input, use --arch=XXX to specify it") 548 549 550class FindToolchainTests(unittest.TestCase): 551 def assert_toolchain_found(self, abi): 552 global ARCH 553 ARCH = abi 554 FindToolchain() # Will throw on failure. 555 556 @unittest.skipIf(ANDROID_BUILD_TOP == '.', 'Test only supported in an Android tree.') 557 def test_toolchains_found(self): 558 self.assert_toolchain_found("arm") 559 self.assert_toolchain_found("arm64") 560 self.assert_toolchain_found("mips") 561 self.assert_toolchain_found("x86") 562 self.assert_toolchain_found("x86_64") 563 564class SetArchTests(unittest.TestCase): 565 def test_abi_check(self): 566 global ARCH 567 568 SetAbi(["ABI: 'arm'"]) 569 self.assertEqual(ARCH, "arm") 570 SetAbi(["ABI: 'arm64'"]) 571 self.assertEqual(ARCH, "arm64") 572 573 SetAbi(["ABI: 'mips'"]) 574 self.assertEqual(ARCH, "mips") 575 SetAbi(["ABI: 'mips64'"]) 576 self.assertEqual(ARCH, "mips64") 577 578 SetAbi(["ABI: 'x86'"]) 579 self.assertEqual(ARCH, "x86") 580 SetAbi(["ABI: 'x86_64'"]) 581 self.assertEqual(ARCH, "x86_64") 582 583 def test_32bit_trace_line_toolchain(self): 584 global ARCH 585 586 os.environ.clear() 587 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/arm/arm-linux-androideabi-4.9/bin" 588 SetAbi(["#00 pc 000374e0"]) 589 self.assertEqual(ARCH, "arm") 590 591 os.environ.clear() 592 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/mips/arm-linux-androideabi-4.9/bin" 593 SetAbi(["#00 pc 000374e0"]) 594 self.assertEqual(ARCH, "mips") 595 596 os.environ.clear() 597 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin" 598 SetAbi(["#00 pc 000374e0"]) 599 self.assertEqual(ARCH, "x86") 600 601 def test_32bit_trace_line_toolchain_2nd(self): 602 global ARCH 603 604 os.environ.clear() 605 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/arm/arm-linux-androideabi-4.9/bin" 606 os.environ["ANDROID_TOOLCHAIN_ARCH"] = "linux-x86/aarch64/aarch64-linux-android-4.9/bin" 607 SetAbi(["#00 pc 000374e0"]) 608 self.assertEqual(ARCH, "arm") 609 610 os.environ.clear() 611 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/mips/mips-linux-androideabi-4.9/bin" 612 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/unknown/unknown-linux-androideabi-4.9/bin" 613 SetAbi(["#00 pc 000374e0"]) 614 self.assertEqual(ARCH, "mips") 615 616 os.environ.clear() 617 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/x86/x86-linux-androideabi-4.9/bin" 618 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/unknown/unknown-linux-androideabi-4.9/bin" 619 SetAbi(["#00 pc 000374e0"]) 620 self.assertEqual(ARCH, "x86") 621 622 def test_64bit_trace_line_toolchain(self): 623 global ARCH 624 625 os.environ.clear() 626 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/aarch/aarch-linux-androideabi-4.9/bin" 627 SetAbi(["#00 pc 00000000000374e0"]) 628 self.assertEqual(ARCH, "arm64") 629 630 os.environ.clear() 631 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/mips/arm-linux-androideabi-4.9/bin" 632 SetAbi(["#00 pc 00000000000374e0"]) 633 self.assertEqual(ARCH, "mips64") 634 635 os.environ.clear() 636 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin" 637 SetAbi(["#00 pc 00000000000374e0"]) 638 self.assertEqual(ARCH, "x86_64") 639 640 def test_trace_default_abis(self): 641 global ARCH 642 643 os.environ.clear() 644 SetAbi(["#00 pc 000374e0"]) 645 self.assertEqual(ARCH, "arm") 646 SetAbi(["#00 pc 00000000000374e0"]) 647 self.assertEqual(ARCH, "arm64") 648 649 def test_32bit_asan_trace_line_toolchain(self): 650 global ARCH 651 652 os.environ.clear() 653 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/arm/arm-linux-androideabi-4.9/bin" 654 SetAbi(["#10 0xb5eeba5d (/system/vendor/lib/egl/libGLESv1_CM_adreno.so+0xfa5d)"]) 655 self.assertEqual(ARCH, "arm") 656 657 os.environ.clear() 658 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/mips/arm-linux-androideabi-4.9/bin" 659 SetAbi(["#10 0xb5eeba5d (/system/vendor/lib/egl/libGLESv1_CM_adreno.so+0xfa5d)"]) 660 self.assertEqual(ARCH, "mips") 661 662 os.environ.clear() 663 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin" 664 SetAbi(["#10 0xb5eeba5d (/system/vendor/lib/egl/libGLESv1_CM_adreno.so+0xfa5d)"]) 665 self.assertEqual(ARCH, "x86") 666 667 def test_32bit_asan_trace_line_toolchain_2nd(self): 668 global ARCH 669 670 os.environ.clear() 671 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/arm/arm-linux-androideabi-4.9/bin" 672 os.environ["ANDROID_TOOLCHAIN_ARCH"] = "linux-x86/aarch64/aarch64-linux-android-4.9/bin" 673 SetAbi(["#3 0xae1725b5 (/system/vendor/lib/libllvm-glnext.so+0x6435b5)"]) 674 self.assertEqual(ARCH, "arm") 675 676 os.environ.clear() 677 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/mips/mips-linux-androideabi-4.9/bin" 678 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/unknown/unknown-linux-androideabi-4.9/bin" 679 SetAbi(["#3 0xae1725b5 (/system/vendor/lib/libllvm-glnext.so+0x6435b5)"]) 680 self.assertEqual(ARCH, "mips") 681 682 os.environ.clear() 683 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/x86/x86-linux-androideabi-4.9/bin" 684 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/unknown/unknown-linux-androideabi-4.9/bin" 685 SetAbi(["#3 0xae1725b5 (/system/vendor/lib/libllvm-glnext.so+0x6435b5)"]) 686 self.assertEqual(ARCH, "x86") 687 688 def test_64bit_asan_trace_line_toolchain(self): 689 global ARCH 690 691 os.environ.clear() 692 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/aarch/aarch-linux-androideabi-4.9/bin" 693 SetAbi(["#0 0x11b35d33bf (/system/lib/libclang_rt.asan-arm-android.so+0x823bf)"]) 694 self.assertEqual(ARCH, "arm64") 695 696 os.environ.clear() 697 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/mips/arm-linux-androideabi-4.9/bin" 698 SetAbi(["#1 0x11b35d33bf (/system/lib/libclang_rt.asan-arm-android.so+0x823bf)"]) 699 self.assertEqual(ARCH, "mips64") 700 701 os.environ.clear() 702 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin" 703 SetAbi(["#12 0x11b35d33bf (/system/lib/libclang_rt.asan-arm-android.so+0x823bf)"]) 704 self.assertEqual(ARCH, "x86_64") 705 706 # Verify that if an address that might be 32 bit comes first, that 707 # encountering a 64 bit address returns a 64 bit abi. 708 ARCH = None 709 os.environ.clear() 710 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin" 711 SetAbi(["#12 0x5d33bf (/system/lib/libclang_rt.asan-arm-android.so+0x823bf)", 712 "#12 0x11b35d33bf (/system/lib/libclang_rt.asan-arm-android.so+0x823bf)"]) 713 self.assertEqual(ARCH, "x86_64") 714 715 def test_asan_trace_default_abis(self): 716 global ARCH 717 718 os.environ.clear() 719 SetAbi(["#4 0x1234349ab (/system/vendor/lib/libllvm-glnext.so+0x64fc4f)"]) 720 self.assertEqual(ARCH, "arm64") 721 SetAbi(["#1 0xae17ec4f (/system/vendor/lib/libllvm-glnext.so+0x64fc4f)"]) 722 self.assertEqual(ARCH, "arm") 723 724 def test_no_abi(self): 725 global ARCH 726 727 # Python2 vs Python3 compatibility: Python3 warns on Regexp deprecation, but Regex 728 # does not provide that name. 729 if not hasattr(unittest.TestCase, 'assertRaisesRegex'): 730 unittest.TestCase.assertRaisesRegex = getattr(unittest.TestCase, 'assertRaisesRegexp') 731 self.assertRaisesRegex(Exception, 732 "Could not determine arch from input, use --arch=XXX to specify it", 733 SetAbi, []) 734 735if __name__ == '__main__': 736 unittest.main(verbosity=2) 737