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
diff --git a/scripts/stack_core.py b/scripts/stack_core.py
index 8ab5520..7ed0e40 100755
--- a/scripts/stack_core.py
+++ b/scripts/stack_core.py
@@ -307,6 +307,12 @@
     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 @@
         #   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 @@
                 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 @@
           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 505677c..99e60c6 100755
--- a/scripts/symbol.py
+++ b/scripts/symbol.py
@@ -105,12 +105,14 @@
     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 @@
     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 @@
       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))]