Merge "Try to locate symbols using build-id." am: b0f82fbc61

Original change: https://android-review.googlesource.com/c/platform/development/+/1873614

Change-Id: I8f1139b2558feadce2d0a25dab9e27f234a06acb
This commit is contained in:
Treehugger Robot
2021-10-28 23:30:50 +00:00
committed by Automerger Merge Worker

View File

@@ -16,7 +16,10 @@
"""stack symbolizes native crash dumps.""" """stack symbolizes native crash dumps."""
import collections
import functools
import os import os
import pathlib
import re import re
import subprocess import subprocess
import symbol import symbol
@@ -67,6 +70,11 @@ class TraceConverter:
"x86_64": "rax|rbx|rcx|rdx|rsi|rdi|r8|r9|r10|r11|r12|r13|r14|r15|cs|ss|rip|rbp|rsp|eflags", "x86_64": "rax|rbx|rcx|rdx|rsi|rdi|r8|r9|r10|r11|r12|r13|r14|r15|cs|ss|rip|rbp|rsp|eflags",
} }
# We use the "file" command line tool to extract BuildId from ELF files.
ElfInfo = collections.namedtuple("ElfInfo", ["bitness", "build_id"])
file_tool_output = re.compile(r"ELF (?P<bitness>32|64)-bit .*"
r"BuildID(\[.*\])?=(?P<build_id>[0-9a-f]+)")
def UpdateAbiRegexes(self): def UpdateAbiRegexes(self):
if symbol.ARCH == "arm64" or symbol.ARCH == "mips64" or symbol.ARCH == "x86_64": if symbol.ARCH == "arm64" or symbol.ARCH == "mips64" or symbol.ARCH == "x86_64":
self.width = "{16}" self.width = "{16}"
@@ -96,7 +104,9 @@ class TraceConverter:
# 0x prefix). # 0x prefix).
r"(?P<dso>\[[^\]]+\]|[^\r\n \t]*)" # Library name. r"(?P<dso>\[[^\]]+\]|[^\r\n \t]*)" # Library name.
r"( \(offset (?P<so_offset>0x[0-9a-fA-F]+)\))?" # Offset into the file to find the start of the shared so. r"( \(offset (?P<so_offset>0x[0-9a-fA-F]+)\))?" # Offset into the file to find the start of the shared so.
r"(?P<symbolpresent> \((?P<symbol>.*)\))?") # Is the symbol there? r"(?P<symbolpresent> \((?P<symbol>.*?)\))?" # Is the symbol there? (non-greedy)
r"( \(BuildId: (?P<build_id>.*)\))?" # Optional build-id of the ELF file.
r"[ \t]*$") # End of line (to expand non-greedy match).
# pylint: disable-msg=C6310 # pylint: disable-msg=C6310
# Sanitizer output. This is different from debuggerd output, and it is easier to handle this as # Sanitizer output. This is different from debuggerd output, and it is easier to handle this as
# its own regex. Example: # its own regex. Example:
@@ -183,22 +193,24 @@ class TraceConverter:
self.DeleteApkTmpFiles() self.DeleteApkTmpFiles()
def MatchTraceLine(self, line): def MatchTraceLine(self, line):
if self.trace_line.match(line):
match = self.trace_line.match(line) match = self.trace_line.match(line)
if match:
return {"frame": match.group("frame"), return {"frame": match.group("frame"),
"offset": match.group("offset"), "offset": match.group("offset"),
"so_offset": match.group("so_offset"), "so_offset": match.group("so_offset"),
"dso": match.group("dso"), "dso": match.group("dso"),
"symbol_present": bool(match.group("symbolpresent")), "symbol_present": bool(match.group("symbolpresent")),
"symbol_name": match.group("symbol")} "symbol_name": match.group("symbol"),
if self.sanitizer_trace_line.match(line): "build_id": match.group("build_id")}
match = self.sanitizer_trace_line.match(line) match = self.sanitizer_trace_line.match(line)
if match:
return {"frame": match.group("frame"), return {"frame": match.group("frame"),
"offset": match.group("offset"), "offset": match.group("offset"),
"so_offset": None, "so_offset": None,
"dso": match.group("dso"), "dso": match.group("dso"),
"symbol_present": False, "symbol_present": False,
"symbol_name": None} "symbol_name": None,
"build_id": None}
return None return None
def ExtractLibFromApk(self, apk, shared_lib_name): def ExtractLibFromApk(self, apk, shared_lib_name):
@@ -300,6 +312,32 @@ class TraceConverter:
return file_name, tmp_shared_lib return file_name, tmp_shared_lib
return None, None return None, None
# Find all files in the symbols directory and group them by basename (without directory).
@functools.cache
def GlobSymbolsDir(self, symbols_dir):
files_by_basename = {}
for path in sorted(pathlib.Path(symbols_dir).glob("**/*")):
files_by_basename.setdefault(path.name, []).append(path)
return files_by_basename
# Use the "file" command line tool to find the bitness and build_id of given ELF file.
@functools.cache
def GetLibraryInfo(self, lib):
stdout = subprocess.check_output(["file", lib], text=True)
match = self.file_tool_output.search(stdout)
if match:
return self.ElfInfo(bitness=match.group("bitness"), build_id=match.group("build_id"))
return None
# Search for a library with the given basename and build_id anywhere in the symbols directory.
@functools.cache
def GetLibraryByBuildId(self, symbols_dir, basename, build_id):
for candidate in self.GlobSymbolsDir(symbols_dir).get(basename):
info = self.GetLibraryInfo(candidate)
if info and info.build_id == build_id:
return "/" + str(candidate.relative_to(symbols_dir))
return None
def GetLibPath(self, lib): def GetLibPath(self, lib):
symbol_dir = symbol.SYMBOLS_DIR symbol_dir = symbol.SYMBOLS_DIR
if os.path.isfile(symbol_dir + lib): if os.path.isfile(symbol_dir + lib):
@@ -401,6 +439,7 @@ class TraceConverter:
so_offset = trace_line_dict["so_offset"] so_offset = trace_line_dict["so_offset"]
symbol_present = trace_line_dict["symbol_present"] symbol_present = trace_line_dict["symbol_present"]
symbol_name = trace_line_dict["symbol_name"] symbol_name = trace_line_dict["symbol_name"]
build_id = trace_line_dict["build_id"]
if frame <= self.last_frame and (self.trace_lines or self.value_lines): if frame <= self.last_frame and (self.trace_lines or self.value_lines):
self.PrintOutput(self.trace_lines, self.value_lines) self.PrintOutput(self.trace_lines, self.value_lines)
@@ -443,6 +482,13 @@ class TraceConverter:
lib = area lib = area
lib_name = None lib_name = None
if build_id:
# If we have the build_id, do a brute-force search of the symbols directory.
lib = self.GetLibraryByBuildId(symbol.SYMBOLS_DIR, os.path.basename(lib), build_id)
if not lib:
print("WARNING: Cannot find {} with build id {} in symbols directory."
.format(os.path.basename(lib), build_id))
else:
# When using atest, test paths are different between the out/ directory # When using atest, test paths are different between the out/ directory
# and device. Apply fixups. # and device. Apply fixups.
lib = self.GetLibPath(lib) lib = self.GetLibPath(lib)