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