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