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