
tools/python/llvm_symbolizer.py generates error logs, which when called from the stack symbolization scripts can produce a lot of unhelpful logs, notably the error logged when the |lib| parameter of |GetSymbolInformation| is not valid: "Can't run llvm-symbolizer! Given binary is not a valid target. path=%s" See for instance these logs: https://chromium-swarm.appspot.com/task?id=61a979f2cac67b10&w=true This intents to silence most of those errors, especially for the test bots, by updating the stack tool: third_party/android_platform/development/scripts/stack et al. This prevent calls to |GetSymbolInformation| if: - the library is not a .so file - the library is not a valid file Logs are produced only if the script is run in verbose mode. Bug: 1340999 Change-Id: I36e21e6f0567809297e83002722743dda30288f3 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4444157 Reviewed-by: Andrew Grieve <agrieve@chromium.org> Commit-Queue: Pâris Meuleman <pmeuleman@chromium.org> Cr-Commit-Position: refs/heads/main@{#1139389}
135 lines
4.0 KiB
Python
135 lines
4.0 KiB
Python
# Copyright 2017 The Chromium Authors
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
import functools
|
|
import logging
|
|
import os
|
|
import subprocess
|
|
import threading
|
|
|
|
_CHROME_SRC = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
|
|
_LLVM_SYMBOLIZER_PATH = os.path.join(
|
|
_CHROME_SRC, 'third_party', 'llvm-build', 'Release+Asserts', 'bin',
|
|
'llvm-symbolizer')
|
|
|
|
_UNKNOWN = '<UNKNOWN>'
|
|
|
|
_ELF_MAGIC_HEADER_BYTES = b'\x7f\x45\x4c\x46'
|
|
|
|
@functools.lru_cache
|
|
def IsValidLLVMSymbolizerTarget(file_path):
|
|
""" Verify the passed file is a valid target for llvm-symbolization
|
|
|
|
Args:
|
|
file_path: Path to a file to be checked
|
|
|
|
Return:
|
|
True if the file exists and has the correct ELF header, False otherwise
|
|
"""
|
|
try:
|
|
with open(file_path, 'rb') as f:
|
|
header_bytes = f.read(4)
|
|
return header_bytes == _ELF_MAGIC_HEADER_BYTES
|
|
except IOError:
|
|
return False
|
|
|
|
|
|
class LLVMSymbolizer(object):
|
|
def __init__(self):
|
|
"""Create a LLVMSymbolizer instance that interacts with the llvm symbolizer.
|
|
|
|
The purpose of the LLVMSymbolizer is to get function names and line
|
|
numbers of an address from the symbols library.
|
|
"""
|
|
self._llvm_symbolizer_subprocess = None
|
|
self._llvm_symbolizer_parameters = [
|
|
'--functions',
|
|
'--demangle',
|
|
'--inlines',
|
|
]
|
|
|
|
# Allow only one thread to call GetSymbolInformation at a time.
|
|
self._lock = threading.Lock()
|
|
|
|
def Start(self):
|
|
"""Start the llvm symbolizer subprocess.
|
|
|
|
Create a subprocess of the llvm symbolizer executable, which will be used
|
|
to retrieve function names etc.
|
|
"""
|
|
if os.path.isfile(_LLVM_SYMBOLIZER_PATH):
|
|
self._llvm_symbolizer_subprocess = subprocess.Popen(
|
|
[_LLVM_SYMBOLIZER_PATH] + self._llvm_symbolizer_parameters,
|
|
stdout=subprocess.PIPE,
|
|
stdin=subprocess.PIPE,
|
|
universal_newlines=True)
|
|
else:
|
|
logging.error('Cannot find llvm_symbolizer here: %s.' %
|
|
_LLVM_SYMBOLIZER_PATH)
|
|
self._llvm_symbolizer_subprocess = None
|
|
|
|
def Close(self):
|
|
"""Close the llvm symbolizer subprocess.
|
|
|
|
Close the subprocess by closing stdin, stdout and killing the subprocess.
|
|
"""
|
|
with self._lock:
|
|
if self._llvm_symbolizer_subprocess:
|
|
self._llvm_symbolizer_subprocess.kill()
|
|
self._llvm_symbolizer_subprocess = None
|
|
|
|
def __enter__(self):
|
|
"""Start the llvm symbolizer subprocess."""
|
|
self.Start()
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
"""Close the llvm symbolizer subprocess."""
|
|
self.Close()
|
|
|
|
def GetSymbolInformation(self, lib, addr):
|
|
"""Return the corresponding function names and line numbers.
|
|
|
|
Args:
|
|
lib: library to search for info.
|
|
addr: address to look for info.
|
|
|
|
Returns:
|
|
A triplet of address, module-name and list of symbols
|
|
"""
|
|
if (self._llvm_symbolizer_subprocess is None):
|
|
logging.error('Can\'t run llvm-symbolizer! ' +
|
|
'Subprocess for llvm-symbolizer has not been started!')
|
|
return [(_UNKNOWN, lib)]
|
|
|
|
if not lib:
|
|
logging.error('Can\'t run llvm-symbolizer! No target is given!')
|
|
return [(_UNKNOWN, lib)]
|
|
|
|
if not IsValidLLVMSymbolizerTarget(lib):
|
|
logging.error(
|
|
'Can\'t run llvm-symbolizer! ' +
|
|
'Given binary is not a valid target. path=%s', lib)
|
|
return [(_UNKNOWN, lib)]
|
|
|
|
proc = self._llvm_symbolizer_subprocess
|
|
with self._lock:
|
|
proc.stdin.write('%s %s\n' % (lib, hex(addr)))
|
|
proc.stdin.flush()
|
|
result = []
|
|
# Read until an empty line is observed, which indicates the end of the
|
|
# output. Each line with a function name is always followed by one line
|
|
# with the corresponding line number.
|
|
while True:
|
|
line = proc.stdout.readline()
|
|
if line != '\n':
|
|
line_numbers = proc.stdout.readline()
|
|
result.append((line[:-1], line_numbers[:-1]))
|
|
else:
|
|
return result
|
|
|
|
@staticmethod
|
|
def IsValidTarget(path):
|
|
return IsValidLLVMSymbolizerTarget(path)
|