blob: 5c92ae68f6c52fa5e8347a9b6d10cd9b2d071d2b [file] [log] [blame]
Iliyan Malchev4929d6a2011-08-04 17:44:40 -07001#!/usr/bin/python
2#
Ben Chengb42dad02013-04-25 15:14:04 -07003# 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.
Iliyan Malchev4929d6a2011-08-04 17:44:40 -070016
17"""Module for looking up symbolic debugging information.
18
19The information can include symbol names, offsets, and source locations.
20"""
21
Andreas Gampe46b00d62017-05-17 15:12:27 -070022import atexit
Elliott Hughes08365932014-06-13 18:12:25 -070023import glob
Iliyan Malchev4929d6a2011-08-04 17:44:40 -070024import os
Yang Nie4b2a1a2014-11-06 17:42:33 -080025import platform
Iliyan Malchev4929d6a2011-08-04 17:44:40 -070026import re
Andreas Gampe46b00d62017-05-17 15:12:27 -070027import signal
Iliyan Malchev4929d6a2011-08-04 17:44:40 -070028import subprocess
Elliott Hughesc3c86192014-08-29 13:49:57 -070029import unittest
Iliyan Malchev4929d6a2011-08-04 17:44:40 -070030
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:
Dan Willemsend3fc8fa2017-10-17 14:04:56 -070039 cmd = "build/soong/soong_ui.bash --dumpvar-mode --abs TARGET_OUT_UNSTRIPPED"
Iliyan Malchev4929d6a2011-08-04 17:44:40 -070040 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
Christopher Ferrisbf8a9402016-03-11 15:50:46 -080047ARCH = None
Ben Chengb42dad02013-04-25 15:14:04 -070048
Elliott Hughesc3c86192014-08-29 13:49:57 -070049
50# These are private. Do not access them from other modules.
51_CACHED_TOOLCHAIN = None
52_CACHED_TOOLCHAIN_ARCH = None
53
Andreas Gampe3d97a462017-05-17 14:16:45 -070054# Caches for symbolized information.
55_SYMBOL_INFORMATION_ADDR2LINE_CACHE = {}
56_SYMBOL_INFORMATION_OBJDUMP_CACHE = {}
57_SYMBOL_DEMANGLING_CACHE = {}
58
Andreas Gampe46b00d62017-05-17 15:12:27 -070059# 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
Ben Chengb42dad02013-04-25 15:14:04 -0700131
Elliott Hughes08365932014-06-13 18:12:25 -0700132def 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]
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700137
Elliott Hughesc3c86192014-08-29 13:49:57 -0700138
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700139def FindToolchain():
Elliott Hughesc3c86192014-08-29 13:49:57 -0700140 """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
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700144
Elliott Hughesc3166be2014-07-07 15:06:28 -0700145 # We use slightly different names from GCC, and there's only one toolchain
Elliott Hughesc3c86192014-08-29 13:49:57 -0700146 # 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"
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700155
Yang Nie4b2a1a2014-11-06 17:42:33 -0800156 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))
Elliott Hughesc3c86192014-08-29 13:49:57 -0700159 if len(available_toolchains) == 0:
160 raise Exception("Could not find tool chain for %s" % (ARCH))
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700161
Elliott Hughesc3c86192014-08-29 13:49:57 -0700162 toolchain = sorted(available_toolchains)[-1]
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700163
Elliott Hughes08365932014-06-13 18:12:25 -0700164 if not os.path.exists(ToolPath("addr2line", toolchain)):
165 raise Exception("No addr2line for %s" % (toolchain))
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700166
Elliott Hughesc3c86192014-08-29 13:49:57 -0700167 _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
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700172
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700173def 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:
Ben Chengb42dad02013-04-25 15:14:04 -0700181 A list of the form [(source_symbol, source_location,
182 object_symbol_with_offset)].
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700183
Ben Chengb42dad02013-04-25 15:14:04 -0700184 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.
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700188
Ben Chengb42dad02013-04-25 15:14:04 -0700189 Usually you want to display the source_location and
190 object_symbol_with_offset from the last element in the list.
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700191 """
192 info = SymbolInformationForSet(lib, set([addr]))
Ben Chengb42dad02013-04-25 15:14:04 -0700193 return (info and info.get(addr)) or [(None, None, None)]
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700194
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:
Ben Chengb42dad02013-04-25 15:14:04 -0700204 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.
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700207
Ben Chengb42dad02013-04-25 15:14:04 -0700208 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.
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700212
Ben Chengb42dad02013-04-25 15:14:04 -0700213 Usually you want to display the source_location and
214 object_symbol_with_offset from the last element in the list.
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700215 """
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:
Ben Chengb42dad02013-04-25 15:14:04 -0700229 source_info = addr_to_line.get(addr)
230 if not source_info:
231 source_info = [(None, None)]
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700232 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
Ben Chengb42dad02013-04-25 15:14:04 -0700238 result[addr] = [(source_symbol, source_location, object_symbol_with_offset)
239 for (source_symbol, source_location) in source_info]
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700240
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:
Ben Chengb42dad02013-04-25 15:14:04 -0700252 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.
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700259 """
260 if not lib:
261 return None
262
Andreas Gampe3d97a462017-05-17 14:16:45 -0700263 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
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700285 symbols = SYMBOLS_DIR + lib
286 if not os.path.exists(symbols):
Christopher Ferrisece64c42015-08-20 20:09:09 -0700287 symbols = lib
288 if not os.path.exists(symbols):
289 return None
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700290
Christopher Ferris5f1b4f02016-09-19 13:24:37 -0700291 # Make sure the symbols path is not a directory.
292 if os.path.isdir(symbols):
293 return None
294
Ben Chengb42dad02013-04-25 15:14:04 -0700295 cmd = [ToolPath("addr2line"), "--functions", "--inlines",
296 "--demangle", "--exe=" + symbols]
Andreas Gampe46b00d62017-05-17 15:12:27 -0700297 child = _PIPE_ADDR2LINE_CACHE.GetProcess(cmd)
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700298
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700299 for addr in addrs:
Christopher Ferris6fc7aef2018-08-09 12:40:05 -0700300 try:
301 child.stdin.write("0x%s\n" % addr)
302 child.stdin.flush()
303 records = []
304 first = True
305 while True:
306 symbol = child.stdout.readline().strip()
307 if symbol == "??":
308 symbol = None
309 location = child.stdout.readline().strip()
310 if location == "??:0" or location == "??:?":
311 location = None
312 if symbol is None and location is None:
313 break
314 records.append((symbol, location))
315 if first:
316 # Write a blank line as a sentinel so we know when to stop
317 # reading inlines from the output.
318 # The blank line will cause addr2line to emit "??\n??:0\n".
319 child.stdin.write("\n")
320 first = False
321 except IOError as e:
322 # Remove the / in front of the library name to match other output.
323 records = [(None, lib[1:] + " ***Error: " + str(e))]
Ben Chengb42dad02013-04-25 15:14:04 -0700324 result[addr] = records
Andreas Gampe3d97a462017-05-17 14:16:45 -0700325 addr_cache[addr] = records
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700326 return result
327
328
Ben Chengb42dad02013-04-25 15:14:04 -0700329def StripPC(addr):
330 """Strips the Thumb bit a program counter address when appropriate.
331
332 Args:
333 addr: the program counter address
334
335 Returns:
336 The stripped program counter address.
337 """
338 global ARCH
Ben Chengb42dad02013-04-25 15:14:04 -0700339 if ARCH == "arm":
340 return addr & ~1
341 return addr
342
Elliott Hughesc3c86192014-08-29 13:49:57 -0700343
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700344def CallObjdumpForSet(lib, unique_addrs):
345 """Use objdump to find out the names of the containing functions.
346
347 Args:
348 lib: library (or executable) pathname containing symbols
349 unique_addrs: set of string hexidecimal addresses to find the functions for.
350
351 Returns:
352 A dictionary of the form {addr: (string symbol, offset)}.
353 """
354 if not lib:
355 return None
356
Andreas Gampe3d97a462017-05-17 14:16:45 -0700357 result = {}
358 addrs = sorted(unique_addrs)
359
360 addr_cache = None
361 if lib in _SYMBOL_INFORMATION_OBJDUMP_CACHE:
362 addr_cache = _SYMBOL_INFORMATION_OBJDUMP_CACHE[lib]
363
364 # Go through and handle all known addresses.
365 for x in range(len(addrs)):
366 next_addr = addrs.pop(0)
367 if next_addr in addr_cache:
368 result[next_addr] = addr_cache[next_addr]
369 else:
370 # Re-add, needs to be symbolized.
371 addrs.append(next_addr)
372
373 if not addrs:
374 # Everything was cached, we're done.
375 return result
376 else:
377 addr_cache = {}
378 _SYMBOL_INFORMATION_OBJDUMP_CACHE[lib] = addr_cache
379
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700380 symbols = SYMBOLS_DIR + lib
381 if not os.path.exists(symbols):
Christopher Ferrisece64c42015-08-20 20:09:09 -0700382 symbols = lib
383 if not os.path.exists(symbols):
384 return None
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700385
Ben Chengb42dad02013-04-25 15:14:04 -0700386 start_addr_dec = str(StripPC(int(addrs[0], 16)))
387 stop_addr_dec = str(StripPC(int(addrs[-1], 16)) + 8)
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700388 cmd = [ToolPath("objdump"),
389 "--section=.text",
390 "--demangle",
391 "--disassemble",
Ben Chengb42dad02013-04-25 15:14:04 -0700392 "--start-address=" + start_addr_dec,
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700393 "--stop-address=" + stop_addr_dec,
394 symbols]
395
396 # Function lines look like:
397 # 000177b0 <android::IBinder::~IBinder()+0x2c>:
398 # We pull out the address and function first. Then we check for an optional
399 # offset. This is tricky due to functions that look like "operator+(..)+0x2c"
400 func_regexp = re.compile("(^[a-f0-9]*) \<(.*)\>:$")
401 offset_regexp = re.compile("(.*)\+0x([a-f0-9]*)")
402
403 # A disassembly line looks like:
404 # 177b2: b510 push {r4, lr}
405 asm_regexp = re.compile("(^[ a-f0-9]*):[ a-f0-0]*.*$")
406
407 current_symbol = None # The current function symbol in the disassembly.
408 current_symbol_addr = 0 # The address of the current function.
409 addr_index = 0 # The address that we are currently looking for.
410
411 stream = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700412 for line in stream:
413 # Is it a function line like:
414 # 000177b0 <android::IBinder::~IBinder()>:
415 components = func_regexp.match(line)
416 if components:
417 # This is a new function, so record the current function and its address.
418 current_symbol_addr = int(components.group(1), 16)
419 current_symbol = components.group(2)
420
421 # Does it have an optional offset like: "foo(..)+0x2c"?
422 components = offset_regexp.match(current_symbol)
423 if components:
424 current_symbol = components.group(1)
425 offset = components.group(2)
426 if offset:
427 current_symbol_addr -= int(offset, 16)
428
429 # Is it an disassembly line like:
430 # 177b2: b510 push {r4, lr}
431 components = asm_regexp.match(line)
432 if components:
433 addr = components.group(1)
434 target_addr = addrs[addr_index]
435 i_addr = int(addr, 16)
Ben Chengb42dad02013-04-25 15:14:04 -0700436 i_target = StripPC(int(target_addr, 16))
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700437 if i_addr == i_target:
438 result[target_addr] = (current_symbol, i_target - current_symbol_addr)
Andreas Gampe3d97a462017-05-17 14:16:45 -0700439 addr_cache[target_addr] = result[target_addr]
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700440 addr_index += 1
441 if addr_index >= len(addrs):
442 break
443 stream.close()
444
445 return result
446
447
448def CallCppFilt(mangled_symbol):
Andreas Gampe3d97a462017-05-17 14:16:45 -0700449 if mangled_symbol in _SYMBOL_DEMANGLING_CACHE:
450 return _SYMBOL_DEMANGLING_CACHE[mangled_symbol]
451
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700452 cmd = [ToolPath("c++filt")]
Andreas Gampe46b00d62017-05-17 15:12:27 -0700453 process = _PIPE_CPPFILT_CACHE.GetProcess(cmd)
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700454 process.stdin.write(mangled_symbol)
455 process.stdin.write("\n")
Andreas Gampe46b00d62017-05-17 15:12:27 -0700456 process.stdin.flush()
457
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700458 demangled_symbol = process.stdout.readline().strip()
Andreas Gampe3d97a462017-05-17 14:16:45 -0700459
460 _SYMBOL_DEMANGLING_CACHE[mangled_symbol] = demangled_symbol
461
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700462 return demangled_symbol
463
Elliott Hughesc3c86192014-08-29 13:49:57 -0700464
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700465def FormatSymbolWithOffset(symbol, offset):
466 if offset == 0:
467 return symbol
468 return "%s+%d" % (symbol, offset)
Elliott Hughesc3c86192014-08-29 13:49:57 -0700469
470
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800471def GetAbiFromToolchain(toolchain_var, bits):
472 toolchain = os.environ.get(toolchain_var)
473 if not toolchain:
474 return None
475
476 toolchain_match = re.search("\/(aarch64|arm|mips|x86)\/", toolchain)
477 if toolchain_match:
478 abi = toolchain_match.group(1)
479 if abi == "aarch64":
480 return "arm64"
481 elif bits == 64:
482 if abi == "x86":
483 return "x86_64"
484 elif abi == "mips":
485 return "mips64"
486 return abi
487 return None
488
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700489def Get32BitArch():
490 # Check for ANDROID_TOOLCHAIN_2ND_ARCH first, if set, use that.
491 # If not try ANDROID_TOOLCHAIN to find the arch.
492 # If this is not set, then default to arm.
493 arch = GetAbiFromToolchain("ANDROID_TOOLCHAIN_2ND_ARCH", 32)
494 if not arch:
495 arch = GetAbiFromToolchain("ANDROID_TOOLCHAIN", 32)
496 if not arch:
497 return "arm"
498 return arch
499
500def Get64BitArch():
501 # Check for ANDROID_TOOLCHAIN, if it is set, we can figure out the
502 # arch this way. If this is not set, then default to arm64.
503 arch = GetAbiFromToolchain("ANDROID_TOOLCHAIN", 64)
504 if not arch:
505 return "arm64"
506 return arch
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800507
508def SetAbi(lines):
509 global ARCH
510
511 abi_line = re.compile("ABI: \'(.*)\'")
512 trace_line = re.compile("\#[0-9]+[ \t]+..[ \t]+([0-9a-f]{8}|[0-9a-f]{16})([ \t]+|$)")
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700513 asan_trace_line = re.compile("\#[0-9]+[ \t]+0x([0-9a-f]+)[ \t]+")
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800514
515 ARCH = None
516 for line in lines:
517 abi_match = abi_line.search(line)
518 if abi_match:
519 ARCH = abi_match.group(1)
520 break
521 trace_match = trace_line.search(line)
522 if trace_match:
523 # Try to guess the arch, we know the bitness.
524 if len(trace_match.group(1)) == 16:
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700525 ARCH = Get64BitArch()
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800526 else:
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700527 ARCH = Get32BitArch()
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800528 break
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700529 asan_trace_match = asan_trace_line.search(line)
530 if asan_trace_match:
531 # We might be able to guess the bitness by the length of the address.
532 if len(asan_trace_match.group(1)) > 8:
533 ARCH = Get64BitArch()
534 # We know for a fact this is 64 bit, so we are done.
535 break
536 else:
537 ARCH = Get32BitArch()
538 # This might be 32 bit, or just a small address. Keep going in this
539 # case, but if we couldn't figure anything else out, go with 32 bit.
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800540 if not ARCH:
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700541 raise Exception("Could not determine arch from input, use --arch=XXX to specify it")
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800542
Elliott Hughesc3c86192014-08-29 13:49:57 -0700543
544class FindToolchainTests(unittest.TestCase):
545 def assert_toolchain_found(self, abi):
546 global ARCH
547 ARCH = abi
548 FindToolchain() # Will throw on failure.
549
550 def test_toolchains_found(self):
551 self.assert_toolchain_found("arm")
552 self.assert_toolchain_found("arm64")
553 self.assert_toolchain_found("mips")
554 self.assert_toolchain_found("x86")
555 self.assert_toolchain_found("x86_64")
556
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800557class SetArchTests(unittest.TestCase):
558 def test_abi_check(self):
559 global ARCH
560
561 SetAbi(["ABI: 'arm'"])
562 self.assertEqual(ARCH, "arm")
563 SetAbi(["ABI: 'arm64'"])
564 self.assertEqual(ARCH, "arm64")
565
566 SetAbi(["ABI: 'mips'"])
567 self.assertEqual(ARCH, "mips")
568 SetAbi(["ABI: 'mips64'"])
569 self.assertEqual(ARCH, "mips64")
570
571 SetAbi(["ABI: 'x86'"])
572 self.assertEqual(ARCH, "x86")
573 SetAbi(["ABI: 'x86_64'"])
574 self.assertEqual(ARCH, "x86_64")
575
576 def test_32bit_trace_line_toolchain(self):
577 global ARCH
578
579 os.environ.clear()
580 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/arm/arm-linux-androideabi-4.9/bin"
581 SetAbi(["#00 pc 000374e0"])
582 self.assertEqual(ARCH, "arm")
583
584 os.environ.clear()
585 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/mips/arm-linux-androideabi-4.9/bin"
586 SetAbi(["#00 pc 000374e0"])
587 self.assertEqual(ARCH, "mips")
588
589 os.environ.clear()
590 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin"
591 SetAbi(["#00 pc 000374e0"])
592 self.assertEqual(ARCH, "x86")
593
594 def test_32bit_trace_line_toolchain_2nd(self):
595 global ARCH
596
597 os.environ.clear()
598 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/arm/arm-linux-androideabi-4.9/bin"
599 os.environ["ANDROID_TOOLCHAIN_ARCH"] = "linux-x86/aarch64/aarch64-linux-android-4.9/bin"
600 SetAbi(["#00 pc 000374e0"])
601 self.assertEqual(ARCH, "arm")
602
603 os.environ.clear()
604 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/mips/mips-linux-androideabi-4.9/bin"
605 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/unknown/unknown-linux-androideabi-4.9/bin"
606 SetAbi(["#00 pc 000374e0"])
607 self.assertEqual(ARCH, "mips")
608
609 os.environ.clear()
610 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/x86/x86-linux-androideabi-4.9/bin"
611 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/unknown/unknown-linux-androideabi-4.9/bin"
612 SetAbi(["#00 pc 000374e0"])
613 self.assertEqual(ARCH, "x86")
614
615 def test_64bit_trace_line_toolchain(self):
616 global ARCH
617
618 os.environ.clear()
619 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/aarch/aarch-linux-androideabi-4.9/bin"
620 SetAbi(["#00 pc 00000000000374e0"])
621 self.assertEqual(ARCH, "arm64")
622
623 os.environ.clear()
624 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/mips/arm-linux-androideabi-4.9/bin"
625 SetAbi(["#00 pc 00000000000374e0"])
626 self.assertEqual(ARCH, "mips64")
627
628 os.environ.clear()
629 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin"
630 SetAbi(["#00 pc 00000000000374e0"])
631 self.assertEqual(ARCH, "x86_64")
632
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700633 def test_trace_default_abis(self):
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800634 global ARCH
635
636 os.environ.clear()
637 SetAbi(["#00 pc 000374e0"])
638 self.assertEqual(ARCH, "arm")
639 SetAbi(["#00 pc 00000000000374e0"])
640 self.assertEqual(ARCH, "arm64")
641
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700642 def test_32bit_asan_trace_line_toolchain(self):
643 global ARCH
644
645 os.environ.clear()
646 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/arm/arm-linux-androideabi-4.9/bin"
647 SetAbi(["#10 0xb5eeba5d (/system/vendor/lib/egl/libGLESv1_CM_adreno.so+0xfa5d)"])
648 self.assertEqual(ARCH, "arm")
649
650 os.environ.clear()
651 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/mips/arm-linux-androideabi-4.9/bin"
652 SetAbi(["#10 0xb5eeba5d (/system/vendor/lib/egl/libGLESv1_CM_adreno.so+0xfa5d)"])
653 self.assertEqual(ARCH, "mips")
654
655 os.environ.clear()
656 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin"
657 SetAbi(["#10 0xb5eeba5d (/system/vendor/lib/egl/libGLESv1_CM_adreno.so+0xfa5d)"])
658 self.assertEqual(ARCH, "x86")
659
660 def test_32bit_asan_trace_line_toolchain_2nd(self):
661 global ARCH
662
663 os.environ.clear()
664 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/arm/arm-linux-androideabi-4.9/bin"
665 os.environ["ANDROID_TOOLCHAIN_ARCH"] = "linux-x86/aarch64/aarch64-linux-android-4.9/bin"
666 SetAbi(["#3 0xae1725b5 (/system/vendor/lib/libllvm-glnext.so+0x6435b5)"])
667 self.assertEqual(ARCH, "arm")
668
669 os.environ.clear()
670 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/mips/mips-linux-androideabi-4.9/bin"
671 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/unknown/unknown-linux-androideabi-4.9/bin"
672 SetAbi(["#3 0xae1725b5 (/system/vendor/lib/libllvm-glnext.so+0x6435b5)"])
673 self.assertEqual(ARCH, "mips")
674
675 os.environ.clear()
676 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/x86/x86-linux-androideabi-4.9/bin"
677 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/unknown/unknown-linux-androideabi-4.9/bin"
678 SetAbi(["#3 0xae1725b5 (/system/vendor/lib/libllvm-glnext.so+0x6435b5)"])
679 self.assertEqual(ARCH, "x86")
680
681 def test_64bit_asan_trace_line_toolchain(self):
682 global ARCH
683
684 os.environ.clear()
685 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/aarch/aarch-linux-androideabi-4.9/bin"
686 SetAbi(["#0 0x11b35d33bf (/system/lib/libclang_rt.asan-arm-android.so+0x823bf)"])
687 self.assertEqual(ARCH, "arm64")
688
689 os.environ.clear()
690 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/mips/arm-linux-androideabi-4.9/bin"
691 SetAbi(["#1 0x11b35d33bf (/system/lib/libclang_rt.asan-arm-android.so+0x823bf)"])
692 self.assertEqual(ARCH, "mips64")
693
694 os.environ.clear()
695 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin"
696 SetAbi(["#12 0x11b35d33bf (/system/lib/libclang_rt.asan-arm-android.so+0x823bf)"])
697 self.assertEqual(ARCH, "x86_64")
698
699 # Verify that if an address that might be 32 bit comes first, that
700 # encountering a 64 bit address returns a 64 bit abi.
701 ARCH = None
702 os.environ.clear()
703 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin"
704 SetAbi(["#12 0x5d33bf (/system/lib/libclang_rt.asan-arm-android.so+0x823bf)",
705 "#12 0x11b35d33bf (/system/lib/libclang_rt.asan-arm-android.so+0x823bf)"])
706 self.assertEqual(ARCH, "x86_64")
707
708 def test_asan_trace_default_abis(self):
709 global ARCH
710
711 os.environ.clear()
712 SetAbi(["#4 0x1234349ab (/system/vendor/lib/libllvm-glnext.so+0x64fc4f)"])
713 self.assertEqual(ARCH, "arm64")
714 SetAbi(["#1 0xae17ec4f (/system/vendor/lib/libllvm-glnext.so+0x64fc4f)"])
715 self.assertEqual(ARCH, "arm")
716
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800717 def test_no_abi(self):
718 global ARCH
719
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700720 self.assertRaisesRegexp(Exception, "Could not determine arch from input, use --arch=XXX to specify it", SetAbi, [])
Elliott Hughesc3c86192014-08-29 13:49:57 -0700721
722if __name__ == '__main__':
723 unittest.main()