blob: 77ee1b33d9b401dc0509cce7acb553520c15663b [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:
300 child.stdin.write("0x%s\n" % addr)
301 child.stdin.flush()
Ben Chengb42dad02013-04-25 15:14:04 -0700302 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()
Christopher Ferrisece64c42015-08-20 20:09:09 -0700309 if location == "??:0" or location == "??:?":
Ben Chengb42dad02013-04-25 15:14:04 -0700310 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
Andreas Gampe3d97a462017-05-17 14:16:45 -0700321 addr_cache[addr] = records
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700322 return result
323
324
Ben Chengb42dad02013-04-25 15:14:04 -0700325def 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
Ben Chengb42dad02013-04-25 15:14:04 -0700335 if ARCH == "arm":
336 return addr & ~1
337 return addr
338
Elliott Hughesc3c86192014-08-29 13:49:57 -0700339
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700340def 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
Andreas Gampe3d97a462017-05-17 14:16:45 -0700353 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
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700376 symbols = SYMBOLS_DIR + lib
377 if not os.path.exists(symbols):
Christopher Ferrisece64c42015-08-20 20:09:09 -0700378 symbols = lib
379 if not os.path.exists(symbols):
380 return None
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700381
Ben Chengb42dad02013-04-25 15:14:04 -0700382 start_addr_dec = str(StripPC(int(addrs[0], 16)))
383 stop_addr_dec = str(StripPC(int(addrs[-1], 16)) + 8)
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700384 cmd = [ToolPath("objdump"),
385 "--section=.text",
386 "--demangle",
387 "--disassemble",
Ben Chengb42dad02013-04-25 15:14:04 -0700388 "--start-address=" + start_addr_dec,
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700389 "--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
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700408 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)
Ben Chengb42dad02013-04-25 15:14:04 -0700432 i_target = StripPC(int(target_addr, 16))
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700433 if i_addr == i_target:
434 result[target_addr] = (current_symbol, i_target - current_symbol_addr)
Andreas Gampe3d97a462017-05-17 14:16:45 -0700435 addr_cache[target_addr] = result[target_addr]
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700436 addr_index += 1
437 if addr_index >= len(addrs):
438 break
439 stream.close()
440
441 return result
442
443
444def CallCppFilt(mangled_symbol):
Andreas Gampe3d97a462017-05-17 14:16:45 -0700445 if mangled_symbol in _SYMBOL_DEMANGLING_CACHE:
446 return _SYMBOL_DEMANGLING_CACHE[mangled_symbol]
447
Iliyan Malchev4929d6a2011-08-04 17:44:40 -0700448 cmd = [ToolPath("c++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
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
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800553class 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
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700629 def test_trace_default_abis(self):
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800630 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
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700638 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
Christopher Ferrisbf8a9402016-03-11 15:50:46 -0800713 def test_no_abi(self):
714 global ARCH
715
Christopher Ferris5b820ba2016-09-06 14:07:29 -0700716 self.assertRaisesRegexp(Exception, "Could not determine arch from input, use --arch=XXX to specify it", SetAbi, [])
Elliott Hughesc3c86192014-08-29 13:49:57 -0700717
718if __name__ == '__main__':
719 unittest.main()