From a0822a72e1e30d726dd96f69d466255e04496d9a Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Wed, 22 Jan 2025 13:11:23 -0800 Subject: [PATCH] Fix the stack decoder. Trying to decode a stack with a dex pc frame which comes from an apk, the script throws a number of errors. Fix those errors. Bug: 356896129 Test: Ran a backtrace with an apk and verified it properly shows the right Test: file and doesn't emit any crashes. Change-Id: I883e72e71d97b44c57d82664e6198d40164478a0 --- scripts/stack_core.py | 31 ++++++++++++++++++++----------- scripts/symbol.py | 23 +++++++++++++---------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/scripts/stack_core.py b/scripts/stack_core.py index 8ab55201087..7ed0e400413 100755 --- a/scripts/stack_core.py +++ b/scripts/stack_core.py @@ -307,6 +307,12 @@ def ProcessCentralInfo(self, offset_list, central_info): name = match.group(1) start = int(match.group(2)) end = start + int(match.group(3)) + # When the actual apk data is mapped in to the process, it will be + # mapped in on a page boundary. This means the header data can start + # after the actual offset and the code will get the wrong file. + # Rounding down to a page boundary (assumes 4096 page size) fixes + # this problem. + start = start & ~0xfff offset_list.append([name, start, end]) return name, start, end @@ -553,6 +559,7 @@ def ProcessLine(self, line): # Some.apk!libshared.so # or # Some.apk + lib_extracted = False if so_offset: # If it ends in apk, we are done. apk = None @@ -572,6 +579,7 @@ def ProcessLine(self, line): apk = area[0:index + 4] if apk: lib_name, lib = self.GetLibFromApk(apk, so_offset) + lib_extracted = lib != None else: # Sometimes we'll see something like: # #01 pc abcd libart.so!libart.so @@ -583,17 +591,18 @@ def ProcessLine(self, line): lib = area lib_name = None - if build_id: - # If we have the build_id, do a brute-force search of the symbols directory. - basename = os.path.basename(lib).split("!")[-1] - lib = self.GetLibraryByBuildId(symbol.SYMBOLS_DIR, basename, build_id) - if not lib: - print("WARNING: Cannot find {} with build id {} in symbols directory." - .format(basename, build_id)) - else: - # When using atest, test paths are different between the out/ directory - # and device. Apply fixups. - lib = self.GetLibPath(lib) + if not lib_extracted: + if build_id: + # If we have the build_id, do a brute-force search of the symbols directory. + basename = os.path.basename(lib).split("!")[-1] + lib = self.GetLibraryByBuildId(symbol.SYMBOLS_DIR, basename, build_id) + if not lib: + print("WARNING: Cannot find {} with build id {} in symbols directory." + .format(basename, build_id)) + else: + # When using atest, test paths are different between the out/ directory + # and device. Apply fixups. + lib = self.GetLibPath(lib) # If a calls b which further calls c and c is inlined to b, we want to # display "a -> b -> c" in the stack trace instead of just "a -> c" diff --git a/scripts/symbol.py b/scripts/symbol.py index 505677c1554..99e60c66399 100755 --- a/scripts/symbol.py +++ b/scripts/symbol.py @@ -105,12 +105,14 @@ def GetProcess(self, cmd): return pipe def SpawnProcess(self, cmd): - return subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True) + return subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True) def TerminateProcess(self, pipe): - pipe.stdin.close() - pipe.stdout.close() - pipe.terminate() + if pipe.poll() is None: + # Process is still running. + pipe.stdin.close() + pipe.stdout.close() + pipe.terminate() pipe.wait() def KillAllProcesses(self): @@ -304,7 +306,7 @@ def GetStackRecordsForSet(lib, unique_addrs): frame_offset, size, tag_offset may be None. """ child = _GetJSONSymbolizerForLib(lib) - if child is None: + if child is None or child.poll() is not None: return None records = [] for addr in unique_addrs: @@ -374,11 +376,12 @@ def CallLlvmSymbolizerForSet(lib, unique_addrs): child.stdin.flush() records = [] json_result = json.loads(child.stdout.readline().strip()) - for symbol in json_result["Symbol"]: - function_name = symbol["FunctionName"] - # GNU style location: file_name:line_num - location = ("%s:%s" % (symbol["FileName"], symbol["Line"])) - records.append((function_name, location)) + if "Symbol" in json_result: + for symbol in json_result["Symbol"]: + function_name = symbol["FunctionName"] + # GNU style location: file_name:line_num + location = ("%s:%s" % (symbol["FileName"], symbol["Line"])) + records.append((function_name, location)) except IOError as e: # Remove the / in front of the library name to match other output. records = [(None, lib[1:] + " ***Error: " + str(e))]