blob: 6f0d4934b3e3e8d10abc4394eb9f3193e5ad94e0 [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
Andreas Gampee547eb32018-10-29 18:31:37 -070031try:
32 ANDROID_BUILD_TOP = str(os.environ["ANDROID_BUILD_TOP"])
33 if not ANDROID_BUILD_TOP:
34 ANDROID_BUILD_TOP = "."
35except:
Iliyan Malchev4929d6a2011-08-04 17:44:40 -070036 ANDROID_BUILD_TOP = "."
37
38def FindSymbolsDir():
39 saveddir = os.getcwd()
40 os.chdir(ANDROID_BUILD_TOP)
Andreas Gampe9240b452018-10-26 14:17:30 -070041 stream = None
Iliyan Malchev4929d6a2011-08-04 17:44:40 -070042 try:
Dan Willemsend3fc8fa2017-10-17 14:04:56 -070043 cmd = "build/soong/soong_ui.bash --dumpvar-mode --abs TARGET_OUT_UNSTRIPPED"
Iliyan Malchev4929d6a2011-08-04 17:44:40 -070044 stream = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True).stdout
Andreas Gampe9240b452018-10-26 14:17:30 -070045 return os.path.join(ANDROID_BUILD_TOP, str(stream.read().strip()))
Iliyan Malchev4929d6a2011-08-04 17:44:40 -070046 finally:
Andreas Gampe9240b452018-10-26 14:17:30 -070047 if stream is not None:
48 stream.close()
Iliyan Malchev4929d6a2011-08-04 17:44:40 -070049 os.chdir(saveddir)
50
51SYMBOLS_DIR = FindSymbolsDir()
52
Christopher Ferrisbf8a9402016-03-11 15:50:46 -080053ARCH = None
Ben Chengb42dad02013-04-25 15:14:04 -070054
Elliott Hughesc3c86192014-08-29 13:49:57 -070055
56# These are private. Do not access them from other modules.
57_CACHED_TOOLCHAIN = None
58_CACHED_TOOLCHAIN_ARCH = None
Christopher Ferris49eda0e2020-12-09 14:34:01 -080059_CACHED_CXX_FILT = None
Elliott Hughesc3c86192014-08-29 13:49:57 -070060
Andreas Gampe3d97a462017-05-17 14:16:45 -070061# Caches for symbolized information.
62_SYMBOL_INFORMATION_ADDR2LINE_CACHE = {}
63_SYMBOL_INFORMATION_OBJDUMP_CACHE = {}
64_SYMBOL_DEMANGLING_CACHE = {}
65
Andreas Gampe46b00d62017-05-17 15:12:27 -070066# Caches for pipes to subprocesses.
67
68class ProcessCache:
69 _cmd2pipe = {}
70 _lru = []
71
72 # Max number of open pipes.
73 _PIPE_MAX_OPEN = 10
74
75 def GetProcess(self, cmd):
76 cmd_tuple = tuple(cmd) # Need to use a tuple as lists can't be dict keys.
77 # Pipe already available?
78 if cmd_tuple in self._cmd2pipe:
79 pipe = self._cmd2pipe[cmd_tuple]
80 # Update LRU.
81 self._lru = [(cmd_tuple, pipe)] + [i for i in self._lru if i[0] != cmd_tuple]
82 return pipe
83
84 # Not cached, yet. Open a new one.
85
86 # Check if too many are open, close the old ones.
87 while len(self._lru) >= self._PIPE_MAX_OPEN:
88 open_cmd, open_pipe = self._lru.pop()
89 del self._cmd2pipe[open_cmd]
90 self.TerminateProcess(open_pipe)
91
92 # Create and put into cache.
93 pipe = self.SpawnProcess(cmd)
94 self._cmd2pipe[cmd_tuple] = pipe
95 self._lru = [(cmd_tuple, pipe)] + self._lru
96 return pipe
97
98 def SpawnProcess(self, cmd):
99 return subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
100
101 def TerminateProcess(self, pipe):
102 pipe.stdin.close()
103 pipe.stdout.close()
104 pipe.terminate()
105 pipe.wait()
106
107 def KillAllProcesses(self):
108 for _, open_pipe in self._lru:
109 self.TerminateProcess(open_pipe)
110 _cmd2pipe = {}
111 _lru = []
112
113
114_PIPE_ADDR2LINE_CACHE = ProcessCache()
115_PIPE_CPPFILT_CACHE = ProcessCache()
116
117
118# Process cache cleanup on shutdown.
119
120def CloseAllPipes():
121 _PIPE_ADDR2LINE_CACHE.KillAllProcesses()
122 _PIPE_CPPFILT_CACHE.KillAllProcesses()
123
124
125atexit.register(CloseAllPipes)
126
127
128def PipeTermHandler(signum, frame):
129 CloseAllPipes()
130 os._exit(0)
131
132
133for sig in (signal.SIGABRT, signal.SIGINT, signal.SIGTERM):
134 signal.signal(sig, PipeTermHandler)
135
136
137
Ben Chengb42dad02013-04-25 15:14:04 -0700138
Elliott Hughes08365932014-06-13 18:12:25 -0700139def ToolPath(tool, toolchain=None):
140 """Return a fully-qualified path to the specified tool"""
141 if not toolchain:
142 toolchain = FindToolchain()
Christopher Ferris49eda0e2020-12-09 14:34:01 -0800143 return os.path.join(toolchain, tool)
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700144
Elliott Hughesc3c86192014-08-29 13:49:57 -0700145
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700146def FindToolchain():
Elliott Hughesc3c86192014-08-29 13:49:57 -0700147 """Returns the toolchain matching ARCH."""
Christopher Ferris49eda0e2020-12-09 14:34:01 -0800148
Elliott Hughesc3c86192014-08-29 13:49:57 -0700149 global _CACHED_TOOLCHAIN, _CACHED_TOOLCHAIN_ARCH
150 if _CACHED_TOOLCHAIN is not None and _CACHED_TOOLCHAIN_ARCH == ARCH:
151 return _CACHED_TOOLCHAIN
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700152
Christopher Ferris49eda0e2020-12-09 14:34:01 -0800153 llvm_binutils_dir = ANDROID_BUILD_TOP + "/prebuilts/clang/host/linux-x86/llvm-binutils-stable/";
154 if not os.path.exists(llvm_binutils_dir):
155 raise Exception("Could not find llvm tool chain directory %s" % (llvm_binutils_dir))
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700156
Christopher Ferris49eda0e2020-12-09 14:34:01 -0800157 _CACHED_TOOLCHAIN = llvm_binutils_dir
Elliott Hughesc3c86192014-08-29 13:49:57 -0700158 _CACHED_TOOLCHAIN_ARCH = ARCH
Andreas Gampe9240b452018-10-26 14:17:30 -0700159 print("Using %s toolchain from: %s" % (_CACHED_TOOLCHAIN_ARCH, _CACHED_TOOLCHAIN))
Elliott Hughesc3c86192014-08-29 13:49:57 -0700160 return _CACHED_TOOLCHAIN
161
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700162
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700163def SymbolInformation(lib, addr):
164 """Look up symbol information about an address.
165
166 Args:
167 lib: library (or executable) pathname containing symbols
168 addr: string hexidecimal address
169
170 Returns:
Ben Chengb42dad02013-04-25 15:14:04 -0700171 A list of the form [(source_symbol, source_location,
172 object_symbol_with_offset)].
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700173
Ben Chengb42dad02013-04-25 15:14:04 -0700174 If the function has been inlined then the list may contain
175 more than one element with the symbols for the most deeply
176 nested inlined location appearing first. The list is
177 always non-empty, even if no information is available.
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700178
Ben Chengb42dad02013-04-25 15:14:04 -0700179 Usually you want to display the source_location and
180 object_symbol_with_offset from the last element in the list.
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700181 """
182 info = SymbolInformationForSet(lib, set([addr]))
Ben Chengb42dad02013-04-25 15:14:04 -0700183 return (info and info.get(addr)) or [(None, None, None)]
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700184
185
186def SymbolInformationForSet(lib, unique_addrs):
187 """Look up symbol information for a set of addresses from the given library.
188
189 Args:
190 lib: library (or executable) pathname containing symbols
191 unique_addrs: set of hexidecimal addresses
192
193 Returns:
Ben Chengb42dad02013-04-25 15:14:04 -0700194 A dictionary of the form {addr: [(source_symbol, source_location,
195 object_symbol_with_offset)]} where each address has a list of
196 associated symbols and locations. The list is always non-empty.
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700197
Ben Chengb42dad02013-04-25 15:14:04 -0700198 If the function has been inlined then the list may contain
199 more than one element with the symbols for the most deeply
200 nested inlined location appearing first. The list is
201 always non-empty, even if no information is available.
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700202
Ben Chengb42dad02013-04-25 15:14:04 -0700203 Usually you want to display the source_location and
204 object_symbol_with_offset from the last element in the list.
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700205 """
206 if not lib:
207 return None
208
Christopher Ferris49eda0e2020-12-09 14:34:01 -0800209 addr_to_line = CallLlvmSymbolizerForSet(lib, unique_addrs)
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700210 if not addr_to_line:
211 return None
212
213 addr_to_objdump = CallObjdumpForSet(lib, unique_addrs)
214 if not addr_to_objdump:
215 return None
216
217 result = {}
218 for addr in unique_addrs:
Ben Chengb42dad02013-04-25 15:14:04 -0700219 source_info = addr_to_line.get(addr)
220 if not source_info:
221 source_info = [(None, None)]
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700222 if addr in addr_to_objdump:
223 (object_symbol, object_offset) = addr_to_objdump.get(addr)
224 object_symbol_with_offset = FormatSymbolWithOffset(object_symbol,
225 object_offset)
226 else:
227 object_symbol_with_offset = None
Ben Chengb42dad02013-04-25 15:14:04 -0700228 result[addr] = [(source_symbol, source_location, object_symbol_with_offset)
229 for (source_symbol, source_location) in source_info]
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700230
231 return result
232
233
Christopher Ferris49eda0e2020-12-09 14:34:01 -0800234def CallLlvmSymbolizerForSet(lib, unique_addrs):
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700235 """Look up line and symbol information for a set of addresses.
236
237 Args:
238 lib: library (or executable) pathname containing symbols
239 unique_addrs: set of string hexidecimal addresses look up.
240
241 Returns:
Ben Chengb42dad02013-04-25 15:14:04 -0700242 A dictionary of the form {addr: [(symbol, file:line)]} where
243 each address has a list of associated symbols and locations
244 or an empty list if no symbol information was found.
245
246 If the function has been inlined then the list may contain
247 more than one element with the symbols for the most deeply
248 nested inlined location appearing first.
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700249 """
250 if not lib:
251 return None
252
Andreas Gampe3d97a462017-05-17 14:16:45 -0700253 result = {}
254 addrs = sorted(unique_addrs)
255
256 if lib in _SYMBOL_INFORMATION_ADDR2LINE_CACHE:
257 addr_cache = _SYMBOL_INFORMATION_ADDR2LINE_CACHE[lib]
258
259 # Go through and handle all known addresses.
260 for x in range(len(addrs)):
261 next_addr = addrs.pop(0)
262 if next_addr in addr_cache:
263 result[next_addr] = addr_cache[next_addr]
264 else:
265 # Re-add, needs to be symbolized.
266 addrs.append(next_addr)
267
268 if not addrs:
269 # Everything was cached, we're done.
270 return result
271 else:
272 addr_cache = {}
273 _SYMBOL_INFORMATION_ADDR2LINE_CACHE[lib] = addr_cache
274
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700275 symbols = SYMBOLS_DIR + lib
276 if not os.path.exists(symbols):
Christopher Ferrisece64c42015-08-20 20:09:09 -0700277 symbols = lib
278 if not os.path.exists(symbols):
279 return None
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700280
Christopher Ferris5f1b4f02016-09-19 13:24:37 -0700281 # Make sure the symbols path is not a directory.
282 if os.path.isdir(symbols):
283 return None
284
Christopher Ferris49eda0e2020-12-09 14:34:01 -0800285 cmd = [ToolPath("llvm-symbolizer"), "--functions", "--inlines",
286 "--demangle", "--obj=" + symbols, "--output-style=GNU"]
Andreas Gampe46b00d62017-05-17 15:12:27 -0700287 child = _PIPE_ADDR2LINE_CACHE.GetProcess(cmd)
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700288
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700289 for addr in addrs:
Christopher Ferris6fc7aef2018-08-09 12:40:05 -0700290 try:
291 child.stdin.write("0x%s\n" % addr)
292 child.stdin.flush()
293 records = []
294 first = True
295 while True:
296 symbol = child.stdout.readline().strip()
Christopher Ferris49eda0e2020-12-09 14:34:01 -0800297 if not symbol:
Christopher Ferris6fc7aef2018-08-09 12:40:05 -0700298 break
Christopher Ferris49eda0e2020-12-09 14:34:01 -0800299 location = child.stdout.readline().strip()
Christopher Ferris6fc7aef2018-08-09 12:40:05 -0700300 records.append((symbol, location))
301 if first:
302 # Write a blank line as a sentinel so we know when to stop
303 # reading inlines from the output.
Christopher Ferris49eda0e2020-12-09 14:34:01 -0800304 # The blank line will cause llvm-symbolizer to emit a blank line.
Christopher Ferris6fc7aef2018-08-09 12:40:05 -0700305 child.stdin.write("\n")
306 first = False
307 except IOError as e:
308 # Remove the / in front of the library name to match other output.
309 records = [(None, lib[1:] + " ***Error: " + str(e))]
Ben Chengb42dad02013-04-25 15:14:04 -0700310 result[addr] = records
Andreas Gampe3d97a462017-05-17 14:16:45 -0700311 addr_cache[addr] = records
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700312 return result
313
314
Ben Chengb42dad02013-04-25 15:14:04 -0700315def StripPC(addr):
316 """Strips the Thumb bit a program counter address when appropriate.
317
318 Args:
319 addr: the program counter address
320
321 Returns:
322 The stripped program counter address.
323 """
324 global ARCH
Ben Chengb42dad02013-04-25 15:14:04 -0700325 if ARCH == "arm":
326 return addr & ~1
327 return addr
328
Elliott Hughesc3c86192014-08-29 13:49:57 -0700329
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700330def CallObjdumpForSet(lib, unique_addrs):
331 """Use objdump to find out the names of the containing functions.
332
333 Args:
334 lib: library (or executable) pathname containing symbols
335 unique_addrs: set of string hexidecimal addresses to find the functions for.
336
337 Returns:
338 A dictionary of the form {addr: (string symbol, offset)}.
339 """
340 if not lib:
341 return None
342
Andreas Gampe3d97a462017-05-17 14:16:45 -0700343 result = {}
344 addrs = sorted(unique_addrs)
345
346 addr_cache = None
347 if lib in _SYMBOL_INFORMATION_OBJDUMP_CACHE:
348 addr_cache = _SYMBOL_INFORMATION_OBJDUMP_CACHE[lib]
349
350 # Go through and handle all known addresses.
351 for x in range(len(addrs)):
352 next_addr = addrs.pop(0)
353 if next_addr in addr_cache:
354 result[next_addr] = addr_cache[next_addr]
355 else:
356 # Re-add, needs to be symbolized.
357 addrs.append(next_addr)
358
359 if not addrs:
360 # Everything was cached, we're done.
361 return result
362 else:
363 addr_cache = {}
364 _SYMBOL_INFORMATION_OBJDUMP_CACHE[lib] = addr_cache
365
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700366 symbols = SYMBOLS_DIR + lib
367 if not os.path.exists(symbols):
Christopher Ferrisece64c42015-08-20 20:09:09 -0700368 symbols = lib
369 if not os.path.exists(symbols):
370 return None
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700371
Ben Chengb42dad02013-04-25 15:14:04 -0700372 start_addr_dec = str(StripPC(int(addrs[0], 16)))
373 stop_addr_dec = str(StripPC(int(addrs[-1], 16)) + 8)
Christopher Ferris49eda0e2020-12-09 14:34:01 -0800374 cmd = [ToolPath("llvm-objdump"),
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700375 "--section=.text",
376 "--demangle",
377 "--disassemble",
Ben Chengb42dad02013-04-25 15:14:04 -0700378 "--start-address=" + start_addr_dec,
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700379 "--stop-address=" + stop_addr_dec,
380 symbols]
381
382 # Function lines look like:
383 # 000177b0 <android::IBinder::~IBinder()+0x2c>:
384 # We pull out the address and function first. Then we check for an optional
385 # offset. This is tricky due to functions that look like "operator+(..)+0x2c"
386 func_regexp = re.compile("(^[a-f0-9]*) \<(.*)\>:$")
387 offset_regexp = re.compile("(.*)\+0x([a-f0-9]*)")
388
389 # A disassembly line looks like:
390 # 177b2: b510 push {r4, lr}
391 asm_regexp = re.compile("(^[ a-f0-9]*):[ a-f0-0]*.*$")
392
393 current_symbol = None # The current function symbol in the disassembly.
394 current_symbol_addr = 0 # The address of the current function.
395 addr_index = 0 # The address that we are currently looking for.
396
397 stream = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700398 for line in stream:
399 # Is it a function line like:
400 # 000177b0 <android::IBinder::~IBinder()>:
401 components = func_regexp.match(line)
402 if components:
403 # This is a new function, so record the current function and its address.
404 current_symbol_addr = int(components.group(1), 16)
405 current_symbol = components.group(2)
406
407 # Does it have an optional offset like: "foo(..)+0x2c"?
408 components = offset_regexp.match(current_symbol)
409 if components:
410 current_symbol = components.group(1)
411 offset = components.group(2)
412 if offset:
413 current_symbol_addr -= int(offset, 16)
414
415 # Is it an disassembly line like:
416 # 177b2: b510 push {r4, lr}
417 components = asm_regexp.match(line)
418 if components:
419 addr = components.group(1)
420 target_addr = addrs[addr_index]
421 i_addr = int(addr, 16)
Ben Chengb42dad02013-04-25 15:14:04 -0700422 i_target = StripPC(int(target_addr, 16))
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700423 if i_addr == i_target:
424 result[target_addr] = (current_symbol, i_target - current_symbol_addr)
Andreas Gampe3d97a462017-05-17 14:16:45 -0700425 addr_cache[target_addr] = result[target_addr]
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700426 addr_index += 1
427 if addr_index >= len(addrs):
428 break
429 stream.close()
430
431 return result
432
433
434def CallCppFilt(mangled_symbol):
Andreas Gampe3d97a462017-05-17 14:16:45 -0700435 if mangled_symbol in _SYMBOL_DEMANGLING_CACHE:
436 return _SYMBOL_DEMANGLING_CACHE[mangled_symbol]
437
Christopher Ferris49eda0e2020-12-09 14:34:01 -0800438 # TODO: Replace with llvm-cxxfilt when available.
439 global _CACHED_CXX_FILT
440 if not _CACHED_CXX_FILT:
441 os_name = platform.system().lower()
442 toolchains = glob.glob("%s/prebuilts/gcc/%s-*/host/*-linux-*/bin/*c++filt" %
443 (ANDROID_BUILD_TOP, os_name))
444 if not toolchains:
445 raise Exception("Could not find gcc c++filt tool")
446 _CACHED_CXX_FILT = sorted(toolchains)[-1]
447
448 cmd = [_CACHED_CXX_FILT]
Andreas Gampe46b00d62017-05-17 15:12:27 -0700449 process = _PIPE_CPPFILT_CACHE.GetProcess(cmd)
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700450 process.stdin.write(mangled_symbol)
451 process.stdin.write("\n")
Andreas Gampe46b00d62017-05-17 15:12:27 -0700452 process.stdin.flush()
453
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700454 demangled_symbol = process.stdout.readline().strip()
Andreas Gampe3d97a462017-05-17 14:16:45 -0700455
456 _SYMBOL_DEMANGLING_CACHE[mangled_symbol] = demangled_symbol
457
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700458 return demangled_symbol
459
Elliott Hughesc3c86192014-08-29 13:49:57 -0700460
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700461def FormatSymbolWithOffset(symbol, offset):
462 if offset == 0:
463 return symbol
464 return "%s+%d" % (symbol, offset)
Elliott Hughesc3c86192014-08-29 13:49:57 -0700465
466
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800467def 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
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700485def 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
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800503
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]+|$)")
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700509 asan_trace_line = re.compile("\#[0-9]+[ \t]+0x([0-9a-f]+)[ \t]+")
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800510
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:
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700521 ARCH = Get64BitArch()
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800522 else:
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700523 ARCH = Get32BitArch()
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800524 break
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700525 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.
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800536 if not ARCH:
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700537 raise Exception("Could not determine arch from input, use --arch=XXX to specify it")
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800538
Elliott Hughesc3c86192014-08-29 13:49:57 -0700539
540class FindToolchainTests(unittest.TestCase):
541 def assert_toolchain_found(self, abi):
542 global ARCH
543 ARCH = abi
544 FindToolchain() # Will throw on failure.
545
Andreas Gampee547eb32018-10-29 18:31:37 -0700546 @unittest.skipIf(ANDROID_BUILD_TOP == '.', 'Test only supported in an Android tree.')
Elliott Hughesc3c86192014-08-29 13:49:57 -0700547 def test_toolchains_found(self):
548 self.assert_toolchain_found("arm")
549 self.assert_toolchain_found("arm64")
550 self.assert_toolchain_found("mips")
551 self.assert_toolchain_found("x86")
552 self.assert_toolchain_found("x86_64")
553
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800554class SetArchTests(unittest.TestCase):
555 def test_abi_check(self):
556 global ARCH
557
558 SetAbi(["ABI: 'arm'"])
559 self.assertEqual(ARCH, "arm")
560 SetAbi(["ABI: 'arm64'"])
561 self.assertEqual(ARCH, "arm64")
562
563 SetAbi(["ABI: 'mips'"])
564 self.assertEqual(ARCH, "mips")
565 SetAbi(["ABI: 'mips64'"])
566 self.assertEqual(ARCH, "mips64")
567
568 SetAbi(["ABI: 'x86'"])
569 self.assertEqual(ARCH, "x86")
570 SetAbi(["ABI: 'x86_64'"])
571 self.assertEqual(ARCH, "x86_64")
572
573 def test_32bit_trace_line_toolchain(self):
574 global ARCH
575
576 os.environ.clear()
577 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/arm/arm-linux-androideabi-4.9/bin"
578 SetAbi(["#00 pc 000374e0"])
579 self.assertEqual(ARCH, "arm")
580
581 os.environ.clear()
582 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/mips/arm-linux-androideabi-4.9/bin"
583 SetAbi(["#00 pc 000374e0"])
584 self.assertEqual(ARCH, "mips")
585
586 os.environ.clear()
587 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin"
588 SetAbi(["#00 pc 000374e0"])
589 self.assertEqual(ARCH, "x86")
590
591 def test_32bit_trace_line_toolchain_2nd(self):
592 global ARCH
593
594 os.environ.clear()
595 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/arm/arm-linux-androideabi-4.9/bin"
596 os.environ["ANDROID_TOOLCHAIN_ARCH"] = "linux-x86/aarch64/aarch64-linux-android-4.9/bin"
597 SetAbi(["#00 pc 000374e0"])
598 self.assertEqual(ARCH, "arm")
599
600 os.environ.clear()
601 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/mips/mips-linux-androideabi-4.9/bin"
602 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/unknown/unknown-linux-androideabi-4.9/bin"
603 SetAbi(["#00 pc 000374e0"])
604 self.assertEqual(ARCH, "mips")
605
606 os.environ.clear()
607 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/x86/x86-linux-androideabi-4.9/bin"
608 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/unknown/unknown-linux-androideabi-4.9/bin"
609 SetAbi(["#00 pc 000374e0"])
610 self.assertEqual(ARCH, "x86")
611
612 def test_64bit_trace_line_toolchain(self):
613 global ARCH
614
615 os.environ.clear()
616 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/aarch/aarch-linux-androideabi-4.9/bin"
617 SetAbi(["#00 pc 00000000000374e0"])
618 self.assertEqual(ARCH, "arm64")
619
620 os.environ.clear()
621 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/mips/arm-linux-androideabi-4.9/bin"
622 SetAbi(["#00 pc 00000000000374e0"])
623 self.assertEqual(ARCH, "mips64")
624
625 os.environ.clear()
626 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin"
627 SetAbi(["#00 pc 00000000000374e0"])
628 self.assertEqual(ARCH, "x86_64")
629
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700630 def test_trace_default_abis(self):
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800631 global ARCH
632
633 os.environ.clear()
634 SetAbi(["#00 pc 000374e0"])
635 self.assertEqual(ARCH, "arm")
636 SetAbi(["#00 pc 00000000000374e0"])
637 self.assertEqual(ARCH, "arm64")
638
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700639 def test_32bit_asan_trace_line_toolchain(self):
640 global ARCH
641
642 os.environ.clear()
643 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/arm/arm-linux-androideabi-4.9/bin"
644 SetAbi(["#10 0xb5eeba5d (/system/vendor/lib/egl/libGLESv1_CM_adreno.so+0xfa5d)"])
645 self.assertEqual(ARCH, "arm")
646
647 os.environ.clear()
648 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/mips/arm-linux-androideabi-4.9/bin"
649 SetAbi(["#10 0xb5eeba5d (/system/vendor/lib/egl/libGLESv1_CM_adreno.so+0xfa5d)"])
650 self.assertEqual(ARCH, "mips")
651
652 os.environ.clear()
653 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin"
654 SetAbi(["#10 0xb5eeba5d (/system/vendor/lib/egl/libGLESv1_CM_adreno.so+0xfa5d)"])
655 self.assertEqual(ARCH, "x86")
656
657 def test_32bit_asan_trace_line_toolchain_2nd(self):
658 global ARCH
659
660 os.environ.clear()
661 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/arm/arm-linux-androideabi-4.9/bin"
662 os.environ["ANDROID_TOOLCHAIN_ARCH"] = "linux-x86/aarch64/aarch64-linux-android-4.9/bin"
663 SetAbi(["#3 0xae1725b5 (/system/vendor/lib/libllvm-glnext.so+0x6435b5)"])
664 self.assertEqual(ARCH, "arm")
665
666 os.environ.clear()
667 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/mips/mips-linux-androideabi-4.9/bin"
668 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/unknown/unknown-linux-androideabi-4.9/bin"
669 SetAbi(["#3 0xae1725b5 (/system/vendor/lib/libllvm-glnext.so+0x6435b5)"])
670 self.assertEqual(ARCH, "mips")
671
672 os.environ.clear()
673 os.environ["ANDROID_TOOLCHAIN_2ND_ARCH"] = "linux-x86/x86/x86-linux-androideabi-4.9/bin"
674 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/unknown/unknown-linux-androideabi-4.9/bin"
675 SetAbi(["#3 0xae1725b5 (/system/vendor/lib/libllvm-glnext.so+0x6435b5)"])
676 self.assertEqual(ARCH, "x86")
677
678 def test_64bit_asan_trace_line_toolchain(self):
679 global ARCH
680
681 os.environ.clear()
682 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/aarch/aarch-linux-androideabi-4.9/bin"
683 SetAbi(["#0 0x11b35d33bf (/system/lib/libclang_rt.asan-arm-android.so+0x823bf)"])
684 self.assertEqual(ARCH, "arm64")
685
686 os.environ.clear()
687 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/mips/arm-linux-androideabi-4.9/bin"
688 SetAbi(["#1 0x11b35d33bf (/system/lib/libclang_rt.asan-arm-android.so+0x823bf)"])
689 self.assertEqual(ARCH, "mips64")
690
691 os.environ.clear()
692 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin"
693 SetAbi(["#12 0x11b35d33bf (/system/lib/libclang_rt.asan-arm-android.so+0x823bf)"])
694 self.assertEqual(ARCH, "x86_64")
695
696 # Verify that if an address that might be 32 bit comes first, that
697 # encountering a 64 bit address returns a 64 bit abi.
698 ARCH = None
699 os.environ.clear()
700 os.environ["ANDROID_TOOLCHAIN"] = "linux-x86/x86/arm-linux-androideabi-4.9/bin"
701 SetAbi(["#12 0x5d33bf (/system/lib/libclang_rt.asan-arm-android.so+0x823bf)",
702 "#12 0x11b35d33bf (/system/lib/libclang_rt.asan-arm-android.so+0x823bf)"])
703 self.assertEqual(ARCH, "x86_64")
704
705 def test_asan_trace_default_abis(self):
706 global ARCH
707
708 os.environ.clear()
709 SetAbi(["#4 0x1234349ab (/system/vendor/lib/libllvm-glnext.so+0x64fc4f)"])
710 self.assertEqual(ARCH, "arm64")
711 SetAbi(["#1 0xae17ec4f (/system/vendor/lib/libllvm-glnext.so+0x64fc4f)"])
712 self.assertEqual(ARCH, "arm")
713
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800714 def test_no_abi(self):
715 global ARCH
716
Andreas Gampe9240b452018-10-26 14:17:30 -0700717 # Python2 vs Python3 compatibility: Python3 warns on Regexp deprecation, but Regex
718 # does not provide that name.
719 if not hasattr(unittest.TestCase, 'assertRaisesRegex'):
720 unittest.TestCase.assertRaisesRegex = getattr(unittest.TestCase, 'assertRaisesRegexp')
721 self.assertRaisesRegex(Exception,
722 "Could not determine arch from input, use --arch=XXX to specify it",
723 SetAbi, [])
Elliott Hughesc3c86192014-08-29 13:49:57 -0700724
725if __name__ == '__main__':
Andreas Gampe9240b452018-10-26 14:17:30 -0700726 unittest.main(verbosity=2)