xref: /aosp_15_r20/development/scripts/stack_core.py (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1*90c8c64dSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*90c8c64dSAndroid Build Coastguard Worker#
3*90c8c64dSAndroid Build Coastguard Worker# Copyright (C) 2013 The Android Open Source Project
4*90c8c64dSAndroid Build Coastguard Worker#
5*90c8c64dSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*90c8c64dSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*90c8c64dSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*90c8c64dSAndroid Build Coastguard Worker#
9*90c8c64dSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*90c8c64dSAndroid Build Coastguard Worker#
11*90c8c64dSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*90c8c64dSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*90c8c64dSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*90c8c64dSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*90c8c64dSAndroid Build Coastguard Worker# limitations under the License.
16*90c8c64dSAndroid Build Coastguard Worker
17*90c8c64dSAndroid Build Coastguard Worker"""stack symbolizes native crash dumps."""
18*90c8c64dSAndroid Build Coastguard Worker
19*90c8c64dSAndroid Build Coastguard Workerimport collections
20*90c8c64dSAndroid Build Coastguard Workerimport functools
21*90c8c64dSAndroid Build Coastguard Workerimport os
22*90c8c64dSAndroid Build Coastguard Workerimport pathlib
23*90c8c64dSAndroid Build Coastguard Workerimport re
24*90c8c64dSAndroid Build Coastguard Workerimport subprocess
25*90c8c64dSAndroid Build Coastguard Workerimport symbol
26*90c8c64dSAndroid Build Coastguard Workerimport tempfile
27*90c8c64dSAndroid Build Coastguard Workerimport unittest
28*90c8c64dSAndroid Build Coastguard Worker
29*90c8c64dSAndroid Build Coastguard Workerimport example_crashes
30*90c8c64dSAndroid Build Coastguard Worker
31*90c8c64dSAndroid Build Coastguard Workerdef ConvertTrace(lines):
32*90c8c64dSAndroid Build Coastguard Worker  tracer = TraceConverter()
33*90c8c64dSAndroid Build Coastguard Worker  print("Reading symbols from", symbol.SYMBOLS_DIR)
34*90c8c64dSAndroid Build Coastguard Worker  tracer.ConvertTrace(lines)
35*90c8c64dSAndroid Build Coastguard Worker
36*90c8c64dSAndroid Build Coastguard Workerclass TraceConverter:
37*90c8c64dSAndroid Build Coastguard Worker  process_info_line = re.compile(r"(pid: [0-9]+, tid: [0-9]+.*)")
38*90c8c64dSAndroid Build Coastguard Worker  revision_line = re.compile(r"(Revision: '(.*)')")
39*90c8c64dSAndroid Build Coastguard Worker  signal_line = re.compile(r"(signal [0-9]+ \(.*\).*)")
40*90c8c64dSAndroid Build Coastguard Worker  abort_message_line = re.compile(r"(Abort message: '.*')")
41*90c8c64dSAndroid Build Coastguard Worker  thread_line = re.compile(r"(.*)(--- ){15}---")
42*90c8c64dSAndroid Build Coastguard Worker  dalvik_jni_thread_line = re.compile("(\".*\" prio=[0-9]+ tid=[0-9]+ NATIVE.*)")
43*90c8c64dSAndroid Build Coastguard Worker  dalvik_native_thread_line = re.compile("(\".*\" sysTid=[0-9]+ nice=[0-9]+.*)")
44*90c8c64dSAndroid Build Coastguard Worker  register_line = re.compile("$a")
45*90c8c64dSAndroid Build Coastguard Worker  trace_line = re.compile("$a")
46*90c8c64dSAndroid Build Coastguard Worker  sanitizer_trace_line = re.compile("$a")
47*90c8c64dSAndroid Build Coastguard Worker  value_line = re.compile("$a")
48*90c8c64dSAndroid Build Coastguard Worker  code_line = re.compile("$a")
49*90c8c64dSAndroid Build Coastguard Worker  zipinfo_central_directory_line = re.compile(r"Central\s+directory\s+entry")
50*90c8c64dSAndroid Build Coastguard Worker  zipinfo_central_info_match = re.compile(
51*90c8c64dSAndroid Build Coastguard Worker      r"^\s*(\S+)$\s*offset of local header from start of archive:\s*(\d+)"
52*90c8c64dSAndroid Build Coastguard Worker      r".*^\s*compressed size:\s+(\d+)", re.M | re.S)
53*90c8c64dSAndroid Build Coastguard Worker  unreachable_line = re.compile(r"((\d+ bytes in \d+ unreachable allocations)|"
54*90c8c64dSAndroid Build Coastguard Worker                                r"(\d+ bytes unreachable at [0-9a-f]+)|"
55*90c8c64dSAndroid Build Coastguard Worker                                r"(referencing \d+ unreachable bytes in \d+ allocation(s)?)|"
56*90c8c64dSAndroid Build Coastguard Worker                                r"(and \d+ similar unreachable bytes in \d+ allocation(s)?))")
57*90c8c64dSAndroid Build Coastguard Worker  trace_lines = []
58*90c8c64dSAndroid Build Coastguard Worker  value_lines = []
59*90c8c64dSAndroid Build Coastguard Worker  last_frame = -1
60*90c8c64dSAndroid Build Coastguard Worker  width = "{8}"
61*90c8c64dSAndroid Build Coastguard Worker  spacing = ""
62*90c8c64dSAndroid Build Coastguard Worker  apk_info = dict()
63*90c8c64dSAndroid Build Coastguard Worker  lib_to_path = dict()
64*90c8c64dSAndroid Build Coastguard Worker  mte_fault_address = None
65*90c8c64dSAndroid Build Coastguard Worker  mte_stack_records = []
66*90c8c64dSAndroid Build Coastguard Worker
67*90c8c64dSAndroid Build Coastguard Worker  # We use the "file" command line tool to extract BuildId from ELF files.
68*90c8c64dSAndroid Build Coastguard Worker  ElfInfo = collections.namedtuple("ElfInfo", ["bitness", "build_id"])
69*90c8c64dSAndroid Build Coastguard Worker  readelf_output = re.compile(r"Class:\s*ELF(?P<bitness>32|64).*"
70*90c8c64dSAndroid Build Coastguard Worker                              r"Build ID:\s*(?P<build_id>[0-9a-f]+)",
71*90c8c64dSAndroid Build Coastguard Worker                              flags=re.DOTALL)
72*90c8c64dSAndroid Build Coastguard Worker
73*90c8c64dSAndroid Build Coastguard Worker  def UpdateBitnessRegexes(self):
74*90c8c64dSAndroid Build Coastguard Worker    if symbol.ARCH_IS_32BIT:
75*90c8c64dSAndroid Build Coastguard Worker      self.width = "{8}"
76*90c8c64dSAndroid Build Coastguard Worker      self.spacing = ""
77*90c8c64dSAndroid Build Coastguard Worker    else:
78*90c8c64dSAndroid Build Coastguard Worker      self.width = "{16}"
79*90c8c64dSAndroid Build Coastguard Worker      self.spacing = "        "
80*90c8c64dSAndroid Build Coastguard Worker    self.register_line = re.compile("    (([ ]*\\b(\S*)\\b +[0-9a-f]" + self.width + "){1,5}$)")
81*90c8c64dSAndroid Build Coastguard Worker
82*90c8c64dSAndroid Build Coastguard Worker    # Note that both trace and value line matching allow for variable amounts of
83*90c8c64dSAndroid Build Coastguard Worker    # whitespace (e.g. \t). This is because the we want to allow for the stack
84*90c8c64dSAndroid Build Coastguard Worker    # tool to operate on AndroidFeedback provided system logs. AndroidFeedback
85*90c8c64dSAndroid Build Coastguard Worker    # strips out double spaces that are found in tombsone files and logcat output.
86*90c8c64dSAndroid Build Coastguard Worker    #
87*90c8c64dSAndroid Build Coastguard Worker    # Examples of matched trace lines include lines from tombstone files like:
88*90c8c64dSAndroid Build Coastguard Worker    #   #00  pc 001cf42e  /data/data/com.my.project/lib/libmyproject.so
89*90c8c64dSAndroid Build Coastguard Worker    #
90*90c8c64dSAndroid Build Coastguard Worker    # Or lines from AndroidFeedback crash report system logs like:
91*90c8c64dSAndroid Build Coastguard Worker    #   03-25 00:51:05.520 I/DEBUG ( 65): #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so
92*90c8c64dSAndroid Build Coastguard Worker    # Please note the spacing differences.
93*90c8c64dSAndroid Build Coastguard Worker    self.trace_line = re.compile(
94*90c8c64dSAndroid Build Coastguard Worker        r".*"                                                 # Random start stuff.
95*90c8c64dSAndroid Build Coastguard Worker        r"\#(?P<frame>[0-9]+)"                                # Frame number.
96*90c8c64dSAndroid Build Coastguard Worker        r"[ \t]+..[ \t]+"                                     # (space)pc(space).
97*90c8c64dSAndroid Build Coastguard Worker        r"(?P<offset>[0-9a-f]" + self.width + ")[ \t]+"       # Offset (hex number given without
98*90c8c64dSAndroid Build Coastguard Worker                                                              #         0x prefix).
99*90c8c64dSAndroid Build Coastguard Worker        r"(?P<dso>\[[^\]]+\]|[^\r\n \t]*)"                    # Library name.
100*90c8c64dSAndroid Build Coastguard Worker        r"( \(offset (?P<so_offset>0x[0-9a-fA-F]+)\))?"       # Offset into the file to find the start of the shared so.
101*90c8c64dSAndroid Build Coastguard Worker        r"(?P<symbolpresent> \((?P<symbol>.*?)\))?"           # Is the symbol there? (non-greedy)
102*90c8c64dSAndroid Build Coastguard Worker        r"( \(BuildId: (?P<build_id>.*)\))?"                  # Optional build-id of the ELF file.
103*90c8c64dSAndroid Build Coastguard Worker        r"[ \t]*$")                                           # End of line (to expand non-greedy match).
104*90c8c64dSAndroid Build Coastguard Worker                                                              # pylint: disable-msg=C6310
105*90c8c64dSAndroid Build Coastguard Worker    # Sanitizer output. This is different from debuggerd output, and it is easier to handle this as
106*90c8c64dSAndroid Build Coastguard Worker    # its own regex. Example:
107*90c8c64dSAndroid Build Coastguard Worker    # 08-19 05:29:26.283   397   403 I         :     #0 0xb6a15237  (/system/lib/libclang_rt.asan-arm-android.so+0x4f237)
108*90c8c64dSAndroid Build Coastguard Worker    self.sanitizer_trace_line = re.compile(
109*90c8c64dSAndroid Build Coastguard Worker        r".*"                                                 # Random start stuff.
110*90c8c64dSAndroid Build Coastguard Worker        r"\#(?P<frame>[0-9]+)"                                # Frame number.
111*90c8c64dSAndroid Build Coastguard Worker        r"[ \t]+0x[0-9a-f]+[ \t]+"                            # PC, not interesting to us.
112*90c8c64dSAndroid Build Coastguard Worker        r"\("                                                 # Opening paren.
113*90c8c64dSAndroid Build Coastguard Worker        r"(?P<dso>[^+]+)"                                     # Library name.
114*90c8c64dSAndroid Build Coastguard Worker        r"\+"                                                 # '+'
115*90c8c64dSAndroid Build Coastguard Worker        r"0x(?P<offset>[0-9a-f]+)"                            # Offset (hex number given with
116*90c8c64dSAndroid Build Coastguard Worker                                                              #         0x prefix).
117*90c8c64dSAndroid Build Coastguard Worker        r"\)")                                                # Closing paren.
118*90c8c64dSAndroid Build Coastguard Worker                                                              # pylint: disable-msg=C6310
119*90c8c64dSAndroid Build Coastguard Worker    # Examples of matched value lines include:
120*90c8c64dSAndroid Build Coastguard Worker    #   bea4170c  8018e4e9  /data/data/com.my.project/lib/libmyproject.so
121*90c8c64dSAndroid Build Coastguard Worker    #   bea4170c  8018e4e9  /data/data/com.my.project/lib/libmyproject.so (symbol)
122*90c8c64dSAndroid Build Coastguard Worker    #   03-25 00:51:05.530 I/DEBUG ( 65): bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so
123*90c8c64dSAndroid Build Coastguard Worker    # Again, note the spacing differences.
124*90c8c64dSAndroid Build Coastguard Worker    self.value_line = re.compile(r"(.*)([0-9a-f]" + self.width + r")[ \t]+([0-9a-f]" + self.width + r")[ \t]+([^\r\n \t]*)( \((.*)\))?")
125*90c8c64dSAndroid Build Coastguard Worker    # Lines from 'code around' sections of the output will be matched before
126*90c8c64dSAndroid Build Coastguard Worker    # value lines because otheriwse the 'code around' sections will be confused as
127*90c8c64dSAndroid Build Coastguard Worker    # value lines.
128*90c8c64dSAndroid Build Coastguard Worker    #
129*90c8c64dSAndroid Build Coastguard Worker    # Examples include:
130*90c8c64dSAndroid Build Coastguard Worker    #   801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
131*90c8c64dSAndroid Build Coastguard Worker    #   03-25 00:51:05.530 I/DEBUG ( 65): 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
132*90c8c64dSAndroid Build Coastguard Worker    self.code_line = re.compile(r"(.*)[ \t]*[a-f0-9]" + self.width +
133*90c8c64dSAndroid Build Coastguard Worker                                r"[ \t]*[a-f0-9]" + self.width +
134*90c8c64dSAndroid Build Coastguard Worker                                r"[ \t]*[a-f0-9]" + self.width +
135*90c8c64dSAndroid Build Coastguard Worker                                r"[ \t]*[a-f0-9]" + self.width +
136*90c8c64dSAndroid Build Coastguard Worker                                r"[ \t]*[a-f0-9]" + self.width +
137*90c8c64dSAndroid Build Coastguard Worker                                r"[ \t]*[ \r\n]")  # pylint: disable-msg=C6310
138*90c8c64dSAndroid Build Coastguard Worker    self.mte_sync_line = re.compile(r".*signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\), fault addr 0x(?P<address>[0-9a-f]+)")
139*90c8c64dSAndroid Build Coastguard Worker    self.mte_stack_record_line = re.compile(r".*stack_record fp:0x(?P<fp>[0-9a-f]+) "
140*90c8c64dSAndroid Build Coastguard Worker                                            r"tag:0x(?P<tag>[0-9a-f]+) "
141*90c8c64dSAndroid Build Coastguard Worker                                            r"pc:(?P<object>[^+]+)\+0x(?P<offset>[0-9a-f]+)"
142*90c8c64dSAndroid Build Coastguard Worker                                            r"(?: \(BuildId: (?P<buildid>[A-Za-z0-9]+)\))?")
143*90c8c64dSAndroid Build Coastguard Worker
144*90c8c64dSAndroid Build Coastguard Worker  def CleanLine(self, ln):
145*90c8c64dSAndroid Build Coastguard Worker    # AndroidFeedback adds zero width spaces into its crash reports. These
146*90c8c64dSAndroid Build Coastguard Worker    # should be removed or the regular expresssions will fail to match.
147*90c8c64dSAndroid Build Coastguard Worker    return ln.encode().decode(encoding='utf8', errors='ignore')
148*90c8c64dSAndroid Build Coastguard Worker
149*90c8c64dSAndroid Build Coastguard Worker  def PrintTraceLines(self, trace_lines):
150*90c8c64dSAndroid Build Coastguard Worker    """Print back trace."""
151*90c8c64dSAndroid Build Coastguard Worker    maxlen = max(len(tl[1]) for tl in trace_lines)
152*90c8c64dSAndroid Build Coastguard Worker    print("\nStack Trace:")
153*90c8c64dSAndroid Build Coastguard Worker    print("  RELADDR   " + self.spacing + "FUNCTION".ljust(maxlen) + "  FILE:LINE")
154*90c8c64dSAndroid Build Coastguard Worker    for tl in self.trace_lines:
155*90c8c64dSAndroid Build Coastguard Worker      (addr, symbol_with_offset, location) = tl
156*90c8c64dSAndroid Build Coastguard Worker      print("  %8s  %s  %s" % (addr, symbol_with_offset.ljust(maxlen), location))
157*90c8c64dSAndroid Build Coastguard Worker
158*90c8c64dSAndroid Build Coastguard Worker  def PrintValueLines(self, value_lines):
159*90c8c64dSAndroid Build Coastguard Worker    """Print stack data values."""
160*90c8c64dSAndroid Build Coastguard Worker    maxlen = max(len(tl[2]) for tl in self.value_lines)
161*90c8c64dSAndroid Build Coastguard Worker    print("\nStack Data:")
162*90c8c64dSAndroid Build Coastguard Worker    print("  ADDR      " + self.spacing + "VALUE     " + "FUNCTION".ljust(maxlen) + "  FILE:LINE")
163*90c8c64dSAndroid Build Coastguard Worker    for vl in self.value_lines:
164*90c8c64dSAndroid Build Coastguard Worker      (addr, value, symbol_with_offset, location) = vl
165*90c8c64dSAndroid Build Coastguard Worker      print("  %8s  %8s  %s  %s" % (addr, value, symbol_with_offset.ljust(maxlen), location))
166*90c8c64dSAndroid Build Coastguard Worker
167*90c8c64dSAndroid Build Coastguard Worker  def MatchStackRecords(self):
168*90c8c64dSAndroid Build Coastguard Worker    if self.mte_fault_address is None:
169*90c8c64dSAndroid Build Coastguard Worker      return
170*90c8c64dSAndroid Build Coastguard Worker    fault_tag = (self.mte_fault_address >> 56) & 0xF
171*90c8c64dSAndroid Build Coastguard Worker    untagged_fault_address = self.mte_fault_address & ~(0xF << 56)
172*90c8c64dSAndroid Build Coastguard Worker    build_id_to_lib = {}
173*90c8c64dSAndroid Build Coastguard Worker    record_for_lib = collections.defaultdict(lambda: collections.defaultdict(set))
174*90c8c64dSAndroid Build Coastguard Worker    for lib, buildid, offset, fp, tag in self.mte_stack_records:
175*90c8c64dSAndroid Build Coastguard Worker      if buildid is not None:
176*90c8c64dSAndroid Build Coastguard Worker        if buildid not in build_id_to_lib:
177*90c8c64dSAndroid Build Coastguard Worker          basename = os.path.basename(lib).split("!")[-1]
178*90c8c64dSAndroid Build Coastguard Worker          newlib = self.GetLibraryByBuildId(symbol.SYMBOLS_DIR, basename, buildid)
179*90c8c64dSAndroid Build Coastguard Worker          if newlib is not None:
180*90c8c64dSAndroid Build Coastguard Worker            build_id_to_lib[buildid] = newlib
181*90c8c64dSAndroid Build Coastguard Worker            lib = newlib
182*90c8c64dSAndroid Build Coastguard Worker        else:
183*90c8c64dSAndroid Build Coastguard Worker          lib = build_id_to_lib[buildid]
184*90c8c64dSAndroid Build Coastguard Worker      record_for_lib[lib][offset].add((fp, tag))
185*90c8c64dSAndroid Build Coastguard Worker
186*90c8c64dSAndroid Build Coastguard Worker    for lib, values in record_for_lib.items():
187*90c8c64dSAndroid Build Coastguard Worker      records = symbol.GetStackRecordsForSet(lib, values.keys()) or []
188*90c8c64dSAndroid Build Coastguard Worker      for addr, function_name, local_name, file_line, frame_offset, size, tag_offset in records:
189*90c8c64dSAndroid Build Coastguard Worker        if frame_offset is None or size is None or tag_offset is None:
190*90c8c64dSAndroid Build Coastguard Worker          continue
191*90c8c64dSAndroid Build Coastguard Worker        for fp, tag in values[addr]:
192*90c8c64dSAndroid Build Coastguard Worker          obj_offset = untagged_fault_address - fp - frame_offset
193*90c8c64dSAndroid Build Coastguard Worker          if tag + tag_offset == fault_tag and obj_offset < size:
194*90c8c64dSAndroid Build Coastguard Worker            print('')
195*90c8c64dSAndroid Build Coastguard Worker            print('Potentially referenced stack object:')
196*90c8c64dSAndroid Build Coastguard Worker            print('  %d bytes inside a variable "%s" in stack frame of function "%s"'% (obj_offset, local_name, function_name))
197*90c8c64dSAndroid Build Coastguard Worker            print('  at %s' % file_line)
198*90c8c64dSAndroid Build Coastguard Worker
199*90c8c64dSAndroid Build Coastguard Worker  def PrintOutput(self, trace_lines, value_lines):
200*90c8c64dSAndroid Build Coastguard Worker    if self.trace_lines:
201*90c8c64dSAndroid Build Coastguard Worker      self.PrintTraceLines(self.trace_lines)
202*90c8c64dSAndroid Build Coastguard Worker    if self.value_lines:
203*90c8c64dSAndroid Build Coastguard Worker      self.PrintValueLines(self.value_lines)
204*90c8c64dSAndroid Build Coastguard Worker    if self.mte_stack_records:
205*90c8c64dSAndroid Build Coastguard Worker      self.MatchStackRecords()
206*90c8c64dSAndroid Build Coastguard Worker
207*90c8c64dSAndroid Build Coastguard Worker  def PrintDivider(self):
208*90c8c64dSAndroid Build Coastguard Worker    print("\n-----------------------------------------------------\n")
209*90c8c64dSAndroid Build Coastguard Worker
210*90c8c64dSAndroid Build Coastguard Worker  def DeleteApkTmpFiles(self):
211*90c8c64dSAndroid Build Coastguard Worker    for _, _, tmp_files in self.apk_info.values():
212*90c8c64dSAndroid Build Coastguard Worker      for tmp_file in tmp_files.values():
213*90c8c64dSAndroid Build Coastguard Worker        os.unlink(tmp_file)
214*90c8c64dSAndroid Build Coastguard Worker
215*90c8c64dSAndroid Build Coastguard Worker  def ConvertTrace(self, lines):
216*90c8c64dSAndroid Build Coastguard Worker    lines = [self.CleanLine(line) for line in lines]
217*90c8c64dSAndroid Build Coastguard Worker    try:
218*90c8c64dSAndroid Build Coastguard Worker      if symbol.ARCH_IS_32BIT is None:
219*90c8c64dSAndroid Build Coastguard Worker        symbol.SetBitness(lines)
220*90c8c64dSAndroid Build Coastguard Worker      self.UpdateBitnessRegexes()
221*90c8c64dSAndroid Build Coastguard Worker      for line in lines:
222*90c8c64dSAndroid Build Coastguard Worker        self.ProcessLine(line)
223*90c8c64dSAndroid Build Coastguard Worker      self.PrintOutput(self.trace_lines, self.value_lines)
224*90c8c64dSAndroid Build Coastguard Worker    finally:
225*90c8c64dSAndroid Build Coastguard Worker      # Delete any temporary files created while processing the lines.
226*90c8c64dSAndroid Build Coastguard Worker      self.DeleteApkTmpFiles()
227*90c8c64dSAndroid Build Coastguard Worker
228*90c8c64dSAndroid Build Coastguard Worker  def MatchTraceLine(self, line):
229*90c8c64dSAndroid Build Coastguard Worker    match = self.trace_line.match(line)
230*90c8c64dSAndroid Build Coastguard Worker    if match:
231*90c8c64dSAndroid Build Coastguard Worker      return {"frame": match.group("frame"),
232*90c8c64dSAndroid Build Coastguard Worker              "offset": match.group("offset"),
233*90c8c64dSAndroid Build Coastguard Worker              "so_offset": match.group("so_offset"),
234*90c8c64dSAndroid Build Coastguard Worker              "dso": match.group("dso"),
235*90c8c64dSAndroid Build Coastguard Worker              "symbol_present": bool(match.group("symbolpresent")),
236*90c8c64dSAndroid Build Coastguard Worker              "symbol_name": match.group("symbol"),
237*90c8c64dSAndroid Build Coastguard Worker              "build_id": match.group("build_id")}
238*90c8c64dSAndroid Build Coastguard Worker    match = self.sanitizer_trace_line.match(line)
239*90c8c64dSAndroid Build Coastguard Worker    if match:
240*90c8c64dSAndroid Build Coastguard Worker      return {"frame": match.group("frame"),
241*90c8c64dSAndroid Build Coastguard Worker              "offset": match.group("offset"),
242*90c8c64dSAndroid Build Coastguard Worker              "so_offset": None,
243*90c8c64dSAndroid Build Coastguard Worker              "dso": match.group("dso"),
244*90c8c64dSAndroid Build Coastguard Worker              "symbol_present": False,
245*90c8c64dSAndroid Build Coastguard Worker              "symbol_name": None,
246*90c8c64dSAndroid Build Coastguard Worker              "build_id": None}
247*90c8c64dSAndroid Build Coastguard Worker    return None
248*90c8c64dSAndroid Build Coastguard Worker
249*90c8c64dSAndroid Build Coastguard Worker  def ExtractLibFromApk(self, apk, shared_lib_name):
250*90c8c64dSAndroid Build Coastguard Worker    # Create a temporary file containing the shared library from the apk.
251*90c8c64dSAndroid Build Coastguard Worker    tmp_file = None
252*90c8c64dSAndroid Build Coastguard Worker    try:
253*90c8c64dSAndroid Build Coastguard Worker      tmp_fd, tmp_file = tempfile.mkstemp()
254*90c8c64dSAndroid Build Coastguard Worker      if subprocess.call(["unzip", "-p", apk, shared_lib_name], stdout=tmp_fd) == 0:
255*90c8c64dSAndroid Build Coastguard Worker        os.close(tmp_fd)
256*90c8c64dSAndroid Build Coastguard Worker        shared_file = tmp_file
257*90c8c64dSAndroid Build Coastguard Worker        tmp_file = None
258*90c8c64dSAndroid Build Coastguard Worker        return shared_file
259*90c8c64dSAndroid Build Coastguard Worker    finally:
260*90c8c64dSAndroid Build Coastguard Worker      if tmp_file:
261*90c8c64dSAndroid Build Coastguard Worker        os.close(tmp_fd)
262*90c8c64dSAndroid Build Coastguard Worker        os.unlink(tmp_file)
263*90c8c64dSAndroid Build Coastguard Worker    return None
264*90c8c64dSAndroid Build Coastguard Worker
265*90c8c64dSAndroid Build Coastguard Worker  def ProcessCentralInfo(self, offset_list, central_info):
266*90c8c64dSAndroid Build Coastguard Worker    match = self.zipinfo_central_info_match.search(central_info)
267*90c8c64dSAndroid Build Coastguard Worker    if not match:
268*90c8c64dSAndroid Build Coastguard Worker      raise Exception("Cannot find all info from zipinfo\n" + central_info)
269*90c8c64dSAndroid Build Coastguard Worker    name = match.group(1)
270*90c8c64dSAndroid Build Coastguard Worker    start = int(match.group(2))
271*90c8c64dSAndroid Build Coastguard Worker    end = start + int(match.group(3))
272*90c8c64dSAndroid Build Coastguard Worker
273*90c8c64dSAndroid Build Coastguard Worker    offset_list.append([name, start, end])
274*90c8c64dSAndroid Build Coastguard Worker    return name, start, end
275*90c8c64dSAndroid Build Coastguard Worker
276*90c8c64dSAndroid Build Coastguard Worker  def GetLibFromApk(self, apk, offset):
277*90c8c64dSAndroid Build Coastguard Worker    # Convert the string to hex.
278*90c8c64dSAndroid Build Coastguard Worker    offset = int(offset, 16)
279*90c8c64dSAndroid Build Coastguard Worker
280*90c8c64dSAndroid Build Coastguard Worker    # Check if we already have information about this offset.
281*90c8c64dSAndroid Build Coastguard Worker    if apk in self.apk_info:
282*90c8c64dSAndroid Build Coastguard Worker      apk_full_path, offset_list, tmp_files = self.apk_info[apk]
283*90c8c64dSAndroid Build Coastguard Worker      for file_name, start, end in offset_list:
284*90c8c64dSAndroid Build Coastguard Worker        if offset >= start and offset < end:
285*90c8c64dSAndroid Build Coastguard Worker          if file_name in tmp_files:
286*90c8c64dSAndroid Build Coastguard Worker            return file_name, tmp_files[file_name]
287*90c8c64dSAndroid Build Coastguard Worker          tmp_file = self.ExtractLibFromApk(apk_full_path, file_name)
288*90c8c64dSAndroid Build Coastguard Worker          if tmp_file:
289*90c8c64dSAndroid Build Coastguard Worker            tmp_files[file_name] = tmp_file
290*90c8c64dSAndroid Build Coastguard Worker            return file_name, tmp_file
291*90c8c64dSAndroid Build Coastguard Worker          break
292*90c8c64dSAndroid Build Coastguard Worker      return None, None
293*90c8c64dSAndroid Build Coastguard Worker
294*90c8c64dSAndroid Build Coastguard Worker    if not "ANDROID_PRODUCT_OUT" in os.environ:
295*90c8c64dSAndroid Build Coastguard Worker      print("ANDROID_PRODUCT_OUT environment variable not set.")
296*90c8c64dSAndroid Build Coastguard Worker      return None, None
297*90c8c64dSAndroid Build Coastguard Worker    out_dir = os.environ["ANDROID_PRODUCT_OUT"]
298*90c8c64dSAndroid Build Coastguard Worker    if not os.path.exists(out_dir):
299*90c8c64dSAndroid Build Coastguard Worker      print("ANDROID_PRODUCT_OUT", out_dir, "does not exist.")
300*90c8c64dSAndroid Build Coastguard Worker      return None, None
301*90c8c64dSAndroid Build Coastguard Worker    if apk.startswith("/"):
302*90c8c64dSAndroid Build Coastguard Worker      apk_full_path = out_dir + apk
303*90c8c64dSAndroid Build Coastguard Worker    else:
304*90c8c64dSAndroid Build Coastguard Worker      apk_full_path = os.path.join(out_dir, apk)
305*90c8c64dSAndroid Build Coastguard Worker    if not os.path.exists(apk_full_path):
306*90c8c64dSAndroid Build Coastguard Worker      print("Cannot find apk", apk)
307*90c8c64dSAndroid Build Coastguard Worker      return None, None
308*90c8c64dSAndroid Build Coastguard Worker
309*90c8c64dSAndroid Build Coastguard Worker    cmd = subprocess.Popen(["zipinfo", "-v", apk_full_path], stdout=subprocess.PIPE,
310*90c8c64dSAndroid Build Coastguard Worker                           encoding='utf8')
311*90c8c64dSAndroid Build Coastguard Worker    # Find the first central info marker.
312*90c8c64dSAndroid Build Coastguard Worker    for line in cmd.stdout:
313*90c8c64dSAndroid Build Coastguard Worker      if self.zipinfo_central_directory_line.search(line):
314*90c8c64dSAndroid Build Coastguard Worker        break
315*90c8c64dSAndroid Build Coastguard Worker
316*90c8c64dSAndroid Build Coastguard Worker    central_info = ""
317*90c8c64dSAndroid Build Coastguard Worker    file_name = None
318*90c8c64dSAndroid Build Coastguard Worker    offset_list = []
319*90c8c64dSAndroid Build Coastguard Worker    for line in cmd.stdout:
320*90c8c64dSAndroid Build Coastguard Worker      match = self.zipinfo_central_directory_line.search(line)
321*90c8c64dSAndroid Build Coastguard Worker      if match:
322*90c8c64dSAndroid Build Coastguard Worker        cur_name, start, end = self.ProcessCentralInfo(offset_list, central_info)
323*90c8c64dSAndroid Build Coastguard Worker        if not file_name and offset >= start and offset < end:
324*90c8c64dSAndroid Build Coastguard Worker          file_name = cur_name
325*90c8c64dSAndroid Build Coastguard Worker        central_info = ""
326*90c8c64dSAndroid Build Coastguard Worker      else:
327*90c8c64dSAndroid Build Coastguard Worker        central_info += line
328*90c8c64dSAndroid Build Coastguard Worker    if central_info:
329*90c8c64dSAndroid Build Coastguard Worker      cur_name, start, end = self.ProcessCentralInfo(offset_list, central_info)
330*90c8c64dSAndroid Build Coastguard Worker      if not file_name and offset >= start and offset < end:
331*90c8c64dSAndroid Build Coastguard Worker        file_name = cur_name
332*90c8c64dSAndroid Build Coastguard Worker
333*90c8c64dSAndroid Build Coastguard Worker    # Make sure the offset_list is sorted, the zip file does not guarantee
334*90c8c64dSAndroid Build Coastguard Worker    # that the entries are in order.
335*90c8c64dSAndroid Build Coastguard Worker    offset_list = sorted(offset_list, key=lambda entry: entry[1])
336*90c8c64dSAndroid Build Coastguard Worker
337*90c8c64dSAndroid Build Coastguard Worker    # Save the information from the zip.
338*90c8c64dSAndroid Build Coastguard Worker    tmp_files = dict()
339*90c8c64dSAndroid Build Coastguard Worker    self.apk_info[apk] = [apk_full_path, offset_list, tmp_files]
340*90c8c64dSAndroid Build Coastguard Worker    if not file_name:
341*90c8c64dSAndroid Build Coastguard Worker      return None, None
342*90c8c64dSAndroid Build Coastguard Worker    tmp_shared_lib = self.ExtractLibFromApk(apk_full_path, file_name)
343*90c8c64dSAndroid Build Coastguard Worker    if tmp_shared_lib:
344*90c8c64dSAndroid Build Coastguard Worker      tmp_files[file_name] = tmp_shared_lib
345*90c8c64dSAndroid Build Coastguard Worker      return file_name, tmp_shared_lib
346*90c8c64dSAndroid Build Coastguard Worker    return None, None
347*90c8c64dSAndroid Build Coastguard Worker
348*90c8c64dSAndroid Build Coastguard Worker  # Find all files in the symbols directory and group them by basename (without directory).
349*90c8c64dSAndroid Build Coastguard Worker  @functools.lru_cache(maxsize=None)
350*90c8c64dSAndroid Build Coastguard Worker  def GlobSymbolsDir(self, symbols_dir):
351*90c8c64dSAndroid Build Coastguard Worker    files_by_basename = {}
352*90c8c64dSAndroid Build Coastguard Worker    for path in sorted(pathlib.Path(symbols_dir).glob("**/*")):
353*90c8c64dSAndroid Build Coastguard Worker      if os.path.isfile(path):
354*90c8c64dSAndroid Build Coastguard Worker        files_by_basename.setdefault(path.name, []).append(path)
355*90c8c64dSAndroid Build Coastguard Worker    return files_by_basename
356*90c8c64dSAndroid Build Coastguard Worker
357*90c8c64dSAndroid Build Coastguard Worker  # Use the "file" command line tool to find the bitness and build_id of given ELF file.
358*90c8c64dSAndroid Build Coastguard Worker  @functools.lru_cache(maxsize=None)
359*90c8c64dSAndroid Build Coastguard Worker  def GetLibraryInfo(self, lib):
360*90c8c64dSAndroid Build Coastguard Worker    stdout = subprocess.check_output([symbol.ToolPath("llvm-readelf"), "-h", "-n", lib], text=True)
361*90c8c64dSAndroid Build Coastguard Worker    match = self.readelf_output.search(stdout)
362*90c8c64dSAndroid Build Coastguard Worker    if match:
363*90c8c64dSAndroid Build Coastguard Worker      return self.ElfInfo(bitness=match.group("bitness"), build_id=match.group("build_id"))
364*90c8c64dSAndroid Build Coastguard Worker    return None
365*90c8c64dSAndroid Build Coastguard Worker
366*90c8c64dSAndroid Build Coastguard Worker  # Search for a library with the given basename and build_id anywhere in the symbols directory.
367*90c8c64dSAndroid Build Coastguard Worker  @functools.lru_cache(maxsize=None)
368*90c8c64dSAndroid Build Coastguard Worker  def GetLibraryByBuildId(self, symbols_dir, basename, build_id):
369*90c8c64dSAndroid Build Coastguard Worker    for candidate in self.GlobSymbolsDir(symbols_dir).get(basename, []):
370*90c8c64dSAndroid Build Coastguard Worker      info = self.GetLibraryInfo(candidate)
371*90c8c64dSAndroid Build Coastguard Worker      if info and info.build_id == build_id:
372*90c8c64dSAndroid Build Coastguard Worker        return "/" + str(candidate.relative_to(symbols_dir))
373*90c8c64dSAndroid Build Coastguard Worker    return None
374*90c8c64dSAndroid Build Coastguard Worker
375*90c8c64dSAndroid Build Coastguard Worker  def GetLibPath(self, lib):
376*90c8c64dSAndroid Build Coastguard Worker    if lib in self.lib_to_path:
377*90c8c64dSAndroid Build Coastguard Worker      return self.lib_to_path[lib]
378*90c8c64dSAndroid Build Coastguard Worker
379*90c8c64dSAndroid Build Coastguard Worker    lib_path = self.FindLibPath(lib)
380*90c8c64dSAndroid Build Coastguard Worker    self.lib_to_path[lib] = lib_path
381*90c8c64dSAndroid Build Coastguard Worker    return lib_path
382*90c8c64dSAndroid Build Coastguard Worker
383*90c8c64dSAndroid Build Coastguard Worker  def FindLibPath(self, lib):
384*90c8c64dSAndroid Build Coastguard Worker    symbol_dir = symbol.SYMBOLS_DIR
385*90c8c64dSAndroid Build Coastguard Worker    if os.path.isfile(symbol_dir + lib):
386*90c8c64dSAndroid Build Coastguard Worker      return lib
387*90c8c64dSAndroid Build Coastguard Worker
388*90c8c64dSAndroid Build Coastguard Worker    # Try and rewrite any apex files if not found in symbols.
389*90c8c64dSAndroid Build Coastguard Worker    # For some reason, the directory in symbols does not match
390*90c8c64dSAndroid Build Coastguard Worker    # the path on system.
391*90c8c64dSAndroid Build Coastguard Worker    # The path is com.android.<directory> on device, but
392*90c8c64dSAndroid Build Coastguard Worker    # com.google.android.<directory> in symbols.
393*90c8c64dSAndroid Build Coastguard Worker    new_lib = lib.replace("/com.android.", "/com.google.android.")
394*90c8c64dSAndroid Build Coastguard Worker    if os.path.isfile(symbol_dir + new_lib):
395*90c8c64dSAndroid Build Coastguard Worker      return new_lib
396*90c8c64dSAndroid Build Coastguard Worker
397*90c8c64dSAndroid Build Coastguard Worker    # When using atest, test paths are different between the out/ directory
398*90c8c64dSAndroid Build Coastguard Worker    # and device. Apply fixups.
399*90c8c64dSAndroid Build Coastguard Worker    if not lib.startswith("/data/local/tests/") and not lib.startswith("/data/local/tmp/"):
400*90c8c64dSAndroid Build Coastguard Worker      print("WARNING: Cannot find %s in symbol directory" % lib)
401*90c8c64dSAndroid Build Coastguard Worker      return lib
402*90c8c64dSAndroid Build Coastguard Worker
403*90c8c64dSAndroid Build Coastguard Worker    test_name = lib.rsplit("/", 1)[-1]
404*90c8c64dSAndroid Build Coastguard Worker    test_dir = "/data/nativetest"
405*90c8c64dSAndroid Build Coastguard Worker    test_dir_bitness = ""
406*90c8c64dSAndroid Build Coastguard Worker    if symbol.ARCH_IS_32BIT:
407*90c8c64dSAndroid Build Coastguard Worker      bitness = "32"
408*90c8c64dSAndroid Build Coastguard Worker    else:
409*90c8c64dSAndroid Build Coastguard Worker      bitness = "64"
410*90c8c64dSAndroid Build Coastguard Worker      test_dir_bitness = "64"
411*90c8c64dSAndroid Build Coastguard Worker
412*90c8c64dSAndroid Build Coastguard Worker    # Unfortunately, the location of the real symbol file is not
413*90c8c64dSAndroid Build Coastguard Worker    # standardized, so we need to go hunting for it.
414*90c8c64dSAndroid Build Coastguard Worker
415*90c8c64dSAndroid Build Coastguard Worker    # This is in vendor, look for the value in:
416*90c8c64dSAndroid Build Coastguard Worker    #   /data/nativetest{64}/vendor/test_name/test_name
417*90c8c64dSAndroid Build Coastguard Worker    if lib.startswith("/data/local/tests/vendor/"):
418*90c8c64dSAndroid Build Coastguard Worker      lib_path = os.path.join(test_dir + test_dir_bitness, "vendor", test_name, test_name)
419*90c8c64dSAndroid Build Coastguard Worker      if os.path.isfile(symbol_dir + lib_path):
420*90c8c64dSAndroid Build Coastguard Worker        return lib_path
421*90c8c64dSAndroid Build Coastguard Worker
422*90c8c64dSAndroid Build Coastguard Worker    # Look for the path in:
423*90c8c64dSAndroid Build Coastguard Worker    #   /data/nativetest{64}/test_name/test_name
424*90c8c64dSAndroid Build Coastguard Worker    lib_path = os.path.join(test_dir + test_dir_bitness, test_name, test_name)
425*90c8c64dSAndroid Build Coastguard Worker    if os.path.isfile(symbol_dir + lib_path):
426*90c8c64dSAndroid Build Coastguard Worker      return lib_path
427*90c8c64dSAndroid Build Coastguard Worker
428*90c8c64dSAndroid Build Coastguard Worker    # CtsXXX tests are in really non-standard locations try:
429*90c8c64dSAndroid Build Coastguard Worker    #  /data/nativetest/{test_name}
430*90c8c64dSAndroid Build Coastguard Worker    lib_path = os.path.join(test_dir, test_name)
431*90c8c64dSAndroid Build Coastguard Worker    if os.path.isfile(symbol_dir + lib_path):
432*90c8c64dSAndroid Build Coastguard Worker      return lib_path
433*90c8c64dSAndroid Build Coastguard Worker    # Try:
434*90c8c64dSAndroid Build Coastguard Worker    #   /data/nativetest/{test_name}{32|64}
435*90c8c64dSAndroid Build Coastguard Worker    lib_path += bitness
436*90c8c64dSAndroid Build Coastguard Worker    if os.path.isfile(symbol_dir + lib_path):
437*90c8c64dSAndroid Build Coastguard Worker      return lib_path
438*90c8c64dSAndroid Build Coastguard Worker
439*90c8c64dSAndroid Build Coastguard Worker    # Cannot find location, give up and return the original path
440*90c8c64dSAndroid Build Coastguard Worker    print("WARNING: Cannot find %s in symbol directory" % lib)
441*90c8c64dSAndroid Build Coastguard Worker    return lib
442*90c8c64dSAndroid Build Coastguard Worker
443*90c8c64dSAndroid Build Coastguard Worker
444*90c8c64dSAndroid Build Coastguard Worker  def ProcessLine(self, line):
445*90c8c64dSAndroid Build Coastguard Worker    ret = False
446*90c8c64dSAndroid Build Coastguard Worker    process_header = self.process_info_line.search(line)
447*90c8c64dSAndroid Build Coastguard Worker    signal_header = self.signal_line.search(line)
448*90c8c64dSAndroid Build Coastguard Worker    abort_message_header = self.abort_message_line.search(line)
449*90c8c64dSAndroid Build Coastguard Worker    thread_header = self.thread_line.search(line)
450*90c8c64dSAndroid Build Coastguard Worker    register_header = self.register_line.search(line)
451*90c8c64dSAndroid Build Coastguard Worker    revision_header = self.revision_line.search(line)
452*90c8c64dSAndroid Build Coastguard Worker    dalvik_jni_thread_header = self.dalvik_jni_thread_line.search(line)
453*90c8c64dSAndroid Build Coastguard Worker    dalvik_native_thread_header = self.dalvik_native_thread_line.search(line)
454*90c8c64dSAndroid Build Coastguard Worker    unreachable_header = self.unreachable_line.search(line)
455*90c8c64dSAndroid Build Coastguard Worker    if process_header or signal_header or abort_message_header or thread_header or \
456*90c8c64dSAndroid Build Coastguard Worker        register_header or dalvik_jni_thread_header or dalvik_native_thread_header or \
457*90c8c64dSAndroid Build Coastguard Worker        revision_header or unreachable_header:
458*90c8c64dSAndroid Build Coastguard Worker      ret = True
459*90c8c64dSAndroid Build Coastguard Worker      if self.trace_lines or self.value_lines or self.mte_stack_records:
460*90c8c64dSAndroid Build Coastguard Worker        self.PrintOutput(self.trace_lines, self.value_lines)
461*90c8c64dSAndroid Build Coastguard Worker        self.PrintDivider()
462*90c8c64dSAndroid Build Coastguard Worker        self.trace_lines = []
463*90c8c64dSAndroid Build Coastguard Worker        self.value_lines = []
464*90c8c64dSAndroid Build Coastguard Worker        self.mte_fault_address = None
465*90c8c64dSAndroid Build Coastguard Worker        self.mte_stack_records = []
466*90c8c64dSAndroid Build Coastguard Worker        self.last_frame = -1
467*90c8c64dSAndroid Build Coastguard Worker      if self.mte_sync_line.match(line):
468*90c8c64dSAndroid Build Coastguard Worker        match = self.mte_sync_line.match(line)
469*90c8c64dSAndroid Build Coastguard Worker        self.mte_fault_address = int(match.group("address"), 16)
470*90c8c64dSAndroid Build Coastguard Worker      if process_header:
471*90c8c64dSAndroid Build Coastguard Worker        print(process_header.group(1))
472*90c8c64dSAndroid Build Coastguard Worker      if signal_header:
473*90c8c64dSAndroid Build Coastguard Worker        print(signal_header.group(1))
474*90c8c64dSAndroid Build Coastguard Worker      if abort_message_header:
475*90c8c64dSAndroid Build Coastguard Worker        print(abort_message_header.group(1))
476*90c8c64dSAndroid Build Coastguard Worker      if register_header:
477*90c8c64dSAndroid Build Coastguard Worker        print(register_header.group(1))
478*90c8c64dSAndroid Build Coastguard Worker      if thread_header:
479*90c8c64dSAndroid Build Coastguard Worker        print(thread_header.group(1))
480*90c8c64dSAndroid Build Coastguard Worker      if dalvik_jni_thread_header:
481*90c8c64dSAndroid Build Coastguard Worker        print(dalvik_jni_thread_header.group(1))
482*90c8c64dSAndroid Build Coastguard Worker      if dalvik_native_thread_header:
483*90c8c64dSAndroid Build Coastguard Worker        print(dalvik_native_thread_header.group(1))
484*90c8c64dSAndroid Build Coastguard Worker      if revision_header:
485*90c8c64dSAndroid Build Coastguard Worker        print(revision_header.group(1))
486*90c8c64dSAndroid Build Coastguard Worker      if unreachable_header:
487*90c8c64dSAndroid Build Coastguard Worker        print(unreachable_header.group(1))
488*90c8c64dSAndroid Build Coastguard Worker      return True
489*90c8c64dSAndroid Build Coastguard Worker    trace_line_dict = self.MatchTraceLine(line)
490*90c8c64dSAndroid Build Coastguard Worker    if trace_line_dict is not None:
491*90c8c64dSAndroid Build Coastguard Worker      ret = True
492*90c8c64dSAndroid Build Coastguard Worker      frame = int(trace_line_dict["frame"])
493*90c8c64dSAndroid Build Coastguard Worker      code_addr = trace_line_dict["offset"]
494*90c8c64dSAndroid Build Coastguard Worker      area = trace_line_dict["dso"]
495*90c8c64dSAndroid Build Coastguard Worker      so_offset = trace_line_dict["so_offset"]
496*90c8c64dSAndroid Build Coastguard Worker      symbol_present = trace_line_dict["symbol_present"]
497*90c8c64dSAndroid Build Coastguard Worker      symbol_name = trace_line_dict["symbol_name"]
498*90c8c64dSAndroid Build Coastguard Worker      build_id = trace_line_dict["build_id"]
499*90c8c64dSAndroid Build Coastguard Worker
500*90c8c64dSAndroid Build Coastguard Worker      if frame <= self.last_frame and (self.trace_lines or self.value_lines):
501*90c8c64dSAndroid Build Coastguard Worker        self.PrintOutput(self.trace_lines, self.value_lines)
502*90c8c64dSAndroid Build Coastguard Worker        self.PrintDivider()
503*90c8c64dSAndroid Build Coastguard Worker        self.trace_lines = []
504*90c8c64dSAndroid Build Coastguard Worker        self.value_lines = []
505*90c8c64dSAndroid Build Coastguard Worker      self.last_frame = frame
506*90c8c64dSAndroid Build Coastguard Worker
507*90c8c64dSAndroid Build Coastguard Worker      if area == "<unknown>" or area == "[heap]" or area == "[stack]":
508*90c8c64dSAndroid Build Coastguard Worker        self.trace_lines.append((code_addr, "", area))
509*90c8c64dSAndroid Build Coastguard Worker      else:
510*90c8c64dSAndroid Build Coastguard Worker        # If this is an apk, it usually means that there is actually
511*90c8c64dSAndroid Build Coastguard Worker        # a shared so that was loaded directly out of it. In that case,
512*90c8c64dSAndroid Build Coastguard Worker        # extract the shared library and the name of the shared library.
513*90c8c64dSAndroid Build Coastguard Worker        lib = None
514*90c8c64dSAndroid Build Coastguard Worker        # The format of the map name:
515*90c8c64dSAndroid Build Coastguard Worker        #   Some.apk!libshared.so
516*90c8c64dSAndroid Build Coastguard Worker        # or
517*90c8c64dSAndroid Build Coastguard Worker        #   Some.apk
518*90c8c64dSAndroid Build Coastguard Worker        if so_offset:
519*90c8c64dSAndroid Build Coastguard Worker          # If it ends in apk, we are done.
520*90c8c64dSAndroid Build Coastguard Worker          apk = None
521*90c8c64dSAndroid Build Coastguard Worker          if area.endswith(".apk"):
522*90c8c64dSAndroid Build Coastguard Worker            apk = area
523*90c8c64dSAndroid Build Coastguard Worker          else:
524*90c8c64dSAndroid Build Coastguard Worker            index = area.rfind(".so!")
525*90c8c64dSAndroid Build Coastguard Worker            if index != -1:
526*90c8c64dSAndroid Build Coastguard Worker              # Sometimes we'll see something like:
527*90c8c64dSAndroid Build Coastguard Worker              #   #01 pc abcd  libart.so!libart.so (offset 0x134000)
528*90c8c64dSAndroid Build Coastguard Worker              # Remove everything after the ! and zero the offset value.
529*90c8c64dSAndroid Build Coastguard Worker              area = area[0:index + 3]
530*90c8c64dSAndroid Build Coastguard Worker              so_offset = 0
531*90c8c64dSAndroid Build Coastguard Worker            else:
532*90c8c64dSAndroid Build Coastguard Worker              index = area.rfind(".apk!")
533*90c8c64dSAndroid Build Coastguard Worker              if index != -1:
534*90c8c64dSAndroid Build Coastguard Worker                apk = area[0:index + 4]
535*90c8c64dSAndroid Build Coastguard Worker          if apk:
536*90c8c64dSAndroid Build Coastguard Worker            lib_name, lib = self.GetLibFromApk(apk, so_offset)
537*90c8c64dSAndroid Build Coastguard Worker        else:
538*90c8c64dSAndroid Build Coastguard Worker          # Sometimes we'll see something like:
539*90c8c64dSAndroid Build Coastguard Worker          #   #01 pc abcd  libart.so!libart.so
540*90c8c64dSAndroid Build Coastguard Worker          # Remove everything after the !.
541*90c8c64dSAndroid Build Coastguard Worker          index = area.rfind(".so!")
542*90c8c64dSAndroid Build Coastguard Worker          if index != -1:
543*90c8c64dSAndroid Build Coastguard Worker            area = area[0:index + 3]
544*90c8c64dSAndroid Build Coastguard Worker        if not lib:
545*90c8c64dSAndroid Build Coastguard Worker          lib = area
546*90c8c64dSAndroid Build Coastguard Worker          lib_name = None
547*90c8c64dSAndroid Build Coastguard Worker
548*90c8c64dSAndroid Build Coastguard Worker        if build_id:
549*90c8c64dSAndroid Build Coastguard Worker          # If we have the build_id, do a brute-force search of the symbols directory.
550*90c8c64dSAndroid Build Coastguard Worker          basename = os.path.basename(lib).split("!")[-1]
551*90c8c64dSAndroid Build Coastguard Worker          lib = self.GetLibraryByBuildId(symbol.SYMBOLS_DIR, basename, build_id)
552*90c8c64dSAndroid Build Coastguard Worker          if not lib:
553*90c8c64dSAndroid Build Coastguard Worker            print("WARNING: Cannot find {} with build id {} in symbols directory."
554*90c8c64dSAndroid Build Coastguard Worker                  .format(basename, build_id))
555*90c8c64dSAndroid Build Coastguard Worker        else:
556*90c8c64dSAndroid Build Coastguard Worker          # When using atest, test paths are different between the out/ directory
557*90c8c64dSAndroid Build Coastguard Worker          # and device. Apply fixups.
558*90c8c64dSAndroid Build Coastguard Worker          lib = self.GetLibPath(lib)
559*90c8c64dSAndroid Build Coastguard Worker
560*90c8c64dSAndroid Build Coastguard Worker        # If a calls b which further calls c and c is inlined to b, we want to
561*90c8c64dSAndroid Build Coastguard Worker        # display "a -> b -> c" in the stack trace instead of just "a -> c"
562*90c8c64dSAndroid Build Coastguard Worker        info = symbol.SymbolInformation(lib, code_addr)
563*90c8c64dSAndroid Build Coastguard Worker        nest_count = len(info) - 1
564*90c8c64dSAndroid Build Coastguard Worker        for (source_symbol, source_location, symbol_with_offset) in info:
565*90c8c64dSAndroid Build Coastguard Worker          if not source_symbol:
566*90c8c64dSAndroid Build Coastguard Worker            if symbol_present:
567*90c8c64dSAndroid Build Coastguard Worker              source_symbol = symbol.CallCppFilt(symbol_name)
568*90c8c64dSAndroid Build Coastguard Worker            else:
569*90c8c64dSAndroid Build Coastguard Worker              source_symbol = "<unknown>"
570*90c8c64dSAndroid Build Coastguard Worker          if not symbol.VERBOSE:
571*90c8c64dSAndroid Build Coastguard Worker            source_symbol = symbol.FormatSymbolWithoutParameters(source_symbol)
572*90c8c64dSAndroid Build Coastguard Worker            symbol_with_offset = symbol.FormatSymbolWithoutParameters(symbol_with_offset)
573*90c8c64dSAndroid Build Coastguard Worker          if not source_location:
574*90c8c64dSAndroid Build Coastguard Worker            source_location = area
575*90c8c64dSAndroid Build Coastguard Worker            if lib_name:
576*90c8c64dSAndroid Build Coastguard Worker              source_location += "(" + lib_name + ")"
577*90c8c64dSAndroid Build Coastguard Worker          if nest_count > 0:
578*90c8c64dSAndroid Build Coastguard Worker            nest_count = nest_count - 1
579*90c8c64dSAndroid Build Coastguard Worker            arrow = "v------>"
580*90c8c64dSAndroid Build Coastguard Worker            if not symbol.ARCH_IS_32BIT:
581*90c8c64dSAndroid Build Coastguard Worker              arrow = "v-------------->"
582*90c8c64dSAndroid Build Coastguard Worker            self.trace_lines.append((arrow, source_symbol, source_location))
583*90c8c64dSAndroid Build Coastguard Worker          else:
584*90c8c64dSAndroid Build Coastguard Worker            if not symbol_with_offset:
585*90c8c64dSAndroid Build Coastguard Worker              symbol_with_offset = source_symbol
586*90c8c64dSAndroid Build Coastguard Worker            self.trace_lines.append((code_addr, symbol_with_offset, source_location))
587*90c8c64dSAndroid Build Coastguard Worker    if self.code_line.match(line):
588*90c8c64dSAndroid Build Coastguard Worker      # Code lines should be ignored. If this were exluded the 'code around'
589*90c8c64dSAndroid Build Coastguard Worker      # sections would trigger value_line matches.
590*90c8c64dSAndroid Build Coastguard Worker      return ret
591*90c8c64dSAndroid Build Coastguard Worker    if self.value_line.match(line):
592*90c8c64dSAndroid Build Coastguard Worker      ret = True
593*90c8c64dSAndroid Build Coastguard Worker      match = self.value_line.match(line)
594*90c8c64dSAndroid Build Coastguard Worker      (unused_, addr, value, area, symbol_present, symbol_name) = match.groups()
595*90c8c64dSAndroid Build Coastguard Worker      if area == "<unknown>" or area == "[heap]" or area == "[stack]" or not area:
596*90c8c64dSAndroid Build Coastguard Worker        self.value_lines.append((addr, value, "", area))
597*90c8c64dSAndroid Build Coastguard Worker      else:
598*90c8c64dSAndroid Build Coastguard Worker        info = symbol.SymbolInformation(area, value)
599*90c8c64dSAndroid Build Coastguard Worker        (source_symbol, source_location, object_symbol_with_offset) = info.pop()
600*90c8c64dSAndroid Build Coastguard Worker        # If there is no information, skip this.
601*90c8c64dSAndroid Build Coastguard Worker        if source_symbol or source_location or object_symbol_with_offset:
602*90c8c64dSAndroid Build Coastguard Worker          if not source_symbol:
603*90c8c64dSAndroid Build Coastguard Worker            if symbol_present:
604*90c8c64dSAndroid Build Coastguard Worker              source_symbol = symbol.CallCppFilt(symbol_name)
605*90c8c64dSAndroid Build Coastguard Worker            else:
606*90c8c64dSAndroid Build Coastguard Worker              source_symbol = "<unknown>"
607*90c8c64dSAndroid Build Coastguard Worker          if not source_location:
608*90c8c64dSAndroid Build Coastguard Worker            source_location = area
609*90c8c64dSAndroid Build Coastguard Worker          if not object_symbol_with_offset:
610*90c8c64dSAndroid Build Coastguard Worker            object_symbol_with_offset = source_symbol
611*90c8c64dSAndroid Build Coastguard Worker          self.value_lines.append((addr,
612*90c8c64dSAndroid Build Coastguard Worker                                   value,
613*90c8c64dSAndroid Build Coastguard Worker                                   object_symbol_with_offset,
614*90c8c64dSAndroid Build Coastguard Worker                                   source_location))
615*90c8c64dSAndroid Build Coastguard Worker    if self.mte_stack_record_line.match(line):
616*90c8c64dSAndroid Build Coastguard Worker      ret = True
617*90c8c64dSAndroid Build Coastguard Worker      match = self.mte_stack_record_line.match(line)
618*90c8c64dSAndroid Build Coastguard Worker      if self.mte_fault_address is not None:
619*90c8c64dSAndroid Build Coastguard Worker        self.mte_stack_records.append(
620*90c8c64dSAndroid Build Coastguard Worker          (match.group("object"),
621*90c8c64dSAndroid Build Coastguard Worker           match.group("buildid"),
622*90c8c64dSAndroid Build Coastguard Worker           int(match.group("offset"), 16),
623*90c8c64dSAndroid Build Coastguard Worker           int(match.group("fp"), 16),
624*90c8c64dSAndroid Build Coastguard Worker           int(match.group("tag"), 16)))
625*90c8c64dSAndroid Build Coastguard Worker
626*90c8c64dSAndroid Build Coastguard Worker    return ret
627*90c8c64dSAndroid Build Coastguard Worker
628*90c8c64dSAndroid Build Coastguard Worker
629*90c8c64dSAndroid Build Coastguard Workerclass RegisterPatternTests(unittest.TestCase):
630*90c8c64dSAndroid Build Coastguard Worker  def assert_register_matches(self, abi, example_crash, stupid_pattern):
631*90c8c64dSAndroid Build Coastguard Worker    tc = TraceConverter()
632*90c8c64dSAndroid Build Coastguard Worker    lines = example_crash.split('\n')
633*90c8c64dSAndroid Build Coastguard Worker    symbol.SetBitness(lines)
634*90c8c64dSAndroid Build Coastguard Worker    tc.UpdateBitnessRegexes()
635*90c8c64dSAndroid Build Coastguard Worker    for line in lines:
636*90c8c64dSAndroid Build Coastguard Worker      tc.ProcessLine(line)
637*90c8c64dSAndroid Build Coastguard Worker      is_register = (re.search(stupid_pattern, line) is not None)
638*90c8c64dSAndroid Build Coastguard Worker      matched = (tc.register_line.search(line) is not None)
639*90c8c64dSAndroid Build Coastguard Worker      self.assertEqual(matched, is_register, line)
640*90c8c64dSAndroid Build Coastguard Worker    tc.PrintOutput(tc.trace_lines, tc.value_lines)
641*90c8c64dSAndroid Build Coastguard Worker
642*90c8c64dSAndroid Build Coastguard Worker  def test_arm_registers(self):
643*90c8c64dSAndroid Build Coastguard Worker    self.assert_register_matches("arm", example_crashes.arm, '\\b(r0|r4|r8|ip|scr)\\b')
644*90c8c64dSAndroid Build Coastguard Worker
645*90c8c64dSAndroid Build Coastguard Worker  def test_arm64_registers(self):
646*90c8c64dSAndroid Build Coastguard Worker    self.assert_register_matches("arm64", example_crashes.arm64, '\\b(x0|x4|x8|x12|x16|x20|x24|x28|sp|v[1-3]?[0-9])\\b')
647*90c8c64dSAndroid Build Coastguard Worker
648*90c8c64dSAndroid Build Coastguard Worker  def test_x86_registers(self):
649*90c8c64dSAndroid Build Coastguard Worker    self.assert_register_matches("x86", example_crashes.x86, '\\b(eax|esi|xcs|eip)\\b')
650*90c8c64dSAndroid Build Coastguard Worker
651*90c8c64dSAndroid Build Coastguard Worker  def test_x86_64_registers(self):
652*90c8c64dSAndroid Build Coastguard Worker    self.assert_register_matches("x86_64", example_crashes.x86_64, '\\b(rax|rsi|r8|r12|cs|rip)\\b')
653*90c8c64dSAndroid Build Coastguard Worker
654*90c8c64dSAndroid Build Coastguard Worker  def test_riscv64_registers(self):
655*90c8c64dSAndroid Build Coastguard Worker    self.assert_register_matches("riscv64", example_crashes.riscv64, '\\b(gp|t2|t6|s3|s7|s11|a3|a7|sp)\\b')
656*90c8c64dSAndroid Build Coastguard Worker
657*90c8c64dSAndroid Build Coastguard Workerclass LibmemunreachablePatternTests(unittest.TestCase):
658*90c8c64dSAndroid Build Coastguard Worker  def test_libmemunreachable(self):
659*90c8c64dSAndroid Build Coastguard Worker    tc = TraceConverter()
660*90c8c64dSAndroid Build Coastguard Worker    lines = example_crashes.libmemunreachable.split('\n')
661*90c8c64dSAndroid Build Coastguard Worker
662*90c8c64dSAndroid Build Coastguard Worker    symbol.SetBitness(lines)
663*90c8c64dSAndroid Build Coastguard Worker    self.assertTrue(symbol.ARCH_IS_32BIT)
664*90c8c64dSAndroid Build Coastguard Worker    tc.UpdateBitnessRegexes()
665*90c8c64dSAndroid Build Coastguard Worker    header_lines = 0
666*90c8c64dSAndroid Build Coastguard Worker    trace_lines = 0
667*90c8c64dSAndroid Build Coastguard Worker    for line in lines:
668*90c8c64dSAndroid Build Coastguard Worker      tc.ProcessLine(line)
669*90c8c64dSAndroid Build Coastguard Worker      if re.search(tc.unreachable_line, line) is not None:
670*90c8c64dSAndroid Build Coastguard Worker        header_lines += 1
671*90c8c64dSAndroid Build Coastguard Worker      if tc.MatchTraceLine(line) is not None:
672*90c8c64dSAndroid Build Coastguard Worker        trace_lines += 1
673*90c8c64dSAndroid Build Coastguard Worker    self.assertEqual(header_lines, 3)
674*90c8c64dSAndroid Build Coastguard Worker    self.assertEqual(trace_lines, 2)
675*90c8c64dSAndroid Build Coastguard Worker    tc.PrintOutput(tc.trace_lines, tc.value_lines)
676*90c8c64dSAndroid Build Coastguard Worker
677*90c8c64dSAndroid Build Coastguard Workerclass LongASANStackTests(unittest.TestCase):
678*90c8c64dSAndroid Build Coastguard Worker  # Test that a long ASAN-style (non-padded frame numbers) stack trace is not split into two
679*90c8c64dSAndroid Build Coastguard Worker  # when the frame number becomes two digits. This happened before as the frame number was
680*90c8c64dSAndroid Build Coastguard Worker  # handled as a string and not converted to an integral.
681*90c8c64dSAndroid Build Coastguard Worker  def test_long_asan_crash(self):
682*90c8c64dSAndroid Build Coastguard Worker    tc = TraceConverter()
683*90c8c64dSAndroid Build Coastguard Worker    lines = example_crashes.long_asan_crash.splitlines()
684*90c8c64dSAndroid Build Coastguard Worker    symbol.SetBitness(lines)
685*90c8c64dSAndroid Build Coastguard Worker    tc.UpdateBitnessRegexes()
686*90c8c64dSAndroid Build Coastguard Worker    # Test by making sure trace_line_count is monotonically non-decreasing. If the stack trace
687*90c8c64dSAndroid Build Coastguard Worker    # is split, a separator is printed and trace_lines is flushed.
688*90c8c64dSAndroid Build Coastguard Worker    trace_line_count = 0
689*90c8c64dSAndroid Build Coastguard Worker    for line in lines:
690*90c8c64dSAndroid Build Coastguard Worker      tc.ProcessLine(line)
691*90c8c64dSAndroid Build Coastguard Worker      self.assertLessEqual(trace_line_count, len(tc.trace_lines))
692*90c8c64dSAndroid Build Coastguard Worker      trace_line_count = len(tc.trace_lines)
693*90c8c64dSAndroid Build Coastguard Worker    # The split happened at transition of frame #9 -> #10. Make sure we have parsed (and stored)
694*90c8c64dSAndroid Build Coastguard Worker    # more than ten frames.
695*90c8c64dSAndroid Build Coastguard Worker    self.assertGreater(trace_line_count, 10)
696*90c8c64dSAndroid Build Coastguard Worker    tc.PrintOutput(tc.trace_lines, tc.value_lines)
697*90c8c64dSAndroid Build Coastguard Worker
698*90c8c64dSAndroid Build Coastguard Workerclass ValueLinesTest(unittest.TestCase):
699*90c8c64dSAndroid Build Coastguard Worker  def test_value_line_skipped(self):
700*90c8c64dSAndroid Build Coastguard Worker    tc = TraceConverter()
701*90c8c64dSAndroid Build Coastguard Worker    symbol.ARCH_IS_32BIT = True
702*90c8c64dSAndroid Build Coastguard Worker    tc.UpdateBitnessRegexes()
703*90c8c64dSAndroid Build Coastguard Worker    tc.ProcessLine("    12345678  00001000  .")
704*90c8c64dSAndroid Build Coastguard Worker    self.assertEqual([], tc.value_lines)
705*90c8c64dSAndroid Build Coastguard Worker
706*90c8c64dSAndroid Build Coastguard Workerif __name__ == '__main__':
707*90c8c64dSAndroid Build Coastguard Worker    unittest.main(verbosity=2)
708