Fix how the script gets data from apks.
The previous code used unzip to get the offsets of the files in
the zip file. This was an estimate, and it turns out to be wrong
in many cases. Replace that with zipinfo -v which gives the exact
offset of the files in the zip.
Also update the tmp files so that they are in a dictionary of their
own by apk, instead of embedded in the offset list.
Change-Id: I8f5efd95f2d2e39e20cc0b6b6571b799be21ef32
diff --git a/scripts/stack_core.py b/scripts/stack_core.py
index a62afd9..d29acbf 100755
--- a/scripts/stack_core.py
+++ b/scripts/stack_core.py
@@ -43,7 +43,10 @@
sanitizer_trace_line = re.compile("$a")
value_line = re.compile("$a")
code_line = re.compile("$a")
- unzip_line = re.compile("\s*(\d+)\s+\S+\s+\S+\s+(\S+)")
+ zipinfo_central_directory_line = re.compile("Central\s+directory\s+entry")
+ zipinfo_central_info_match = re.compile(
+ "^\s*(\S+)$\s*offset of local header from start of archive:\s*(\d+)"
+ ".*^\s*compressed size:\s+(\d+)", re.M | re.S)
trace_lines = []
value_lines = []
last_frame = -1
@@ -163,10 +166,9 @@
print "-----------------------------------------------------\n"
def DeleteApkTmpFiles(self):
- for _, offset_list in self.apk_info.values():
- for _, _, tmp_file in offset_list:
- if tmp_file:
- os.unlink(tmp_file)
+ for _, _, tmp_files in self.apk_info.values():
+ for tmp_file in tmp_files.values():
+ os.unlink(tmp_file)
def ConvertTrace(self, lines):
lines = map(self.CleanLine, lines)
@@ -216,20 +218,31 @@
os.unlink(tmp_file)
return None
+ def ProcessCentralInfo(self, offset_list, central_info):
+ match = self.zipinfo_central_info_match.search(central_info)
+ if not match:
+ raise Exception("Cannot find all info from zipinfo\n" + central_info)
+ name = match.group(1)
+ start = int(match.group(2))
+ end = start + int(match.group(3))
+
+ offset_list.append([name, start, end])
+ return name, start, end
+
def GetLibFromApk(self, apk, offset):
# Convert the string to hex.
offset = int(offset, 16)
# Check if we already have information about this offset.
if apk in self.apk_info:
- apk_full_path, offset_list = self.apk_info[apk]
- for current_offset, file_name, tmp_file in offset_list:
- if offset <= current_offset:
- if tmp_file:
- return file_name, tmp_file
- # This modifies the value in offset_list.
+ apk_full_path, offset_list, tmp_files = self.apk_info[apk]
+ for file_name, start, end in offset_list:
+ if offset >= start and offset < end:
+ if file_name in tmp_files:
+ return file_name, tmp_files[file_name]
tmp_file = self.ExtractLibFromApk(apk_full_path, file_name)
if tmp_file:
+ tmp_files[file_name] = tmp_file
return file_name, tmp_file
break
return None, None
@@ -249,28 +262,38 @@
print "Cannot find apk " + apk;
return None, None
- cmd = subprocess.Popen(["unzip", "-lqq", apk_full_path], stdout=subprocess.PIPE)
- current_offset = 0
- file_entry = None
+ cmd = subprocess.Popen(["zipinfo", "-v", apk_full_path], stdout=subprocess.PIPE)
+ # Find the first central info marker.
+ for line in cmd.stdout:
+ if self.zipinfo_central_directory_line.search(line):
+ break
+
+ central_info = ""
+ file_name = None
offset_list = []
for line in cmd.stdout:
- match = self.unzip_line.match(line)
+ match = self.zipinfo_central_directory_line.search(line)
if match:
- # Round the size up to a page boundary.
- current_offset += (int(match.group(1), 10) + 0x1000) & ~0xfff
- offset_entry = [current_offset - 1, match.group(2), None]
- offset_list.append(offset_entry)
- if offset < current_offset and not file_entry:
- file_entry = offset_entry
+ cur_name, start, end = self.ProcessCentralInfo(offset_list, central_info)
+ if not file_name and offset >= start and offset < end:
+ file_name = cur_name
+ central_info = ""
+ else:
+ central_info += line
+ if central_info:
+ cur_name, start, end = self.ProcessCentralInfo(offset_list, central_info)
+ if not file_name and offset >= start and offset < end:
+ file_name = cur_name
# Save the information from the zip.
- self.apk_info[apk] = [apk_full_path, offset_list]
- if not file_entry:
+ tmp_files = dict()
+ self.apk_info[apk] = [apk_full_path, offset_list, tmp_files]
+ if not file_name:
return None, None
- tmp_shared_lib = self.ExtractLibFromApk(apk_full_path, file_entry[1])
+ tmp_shared_lib = self.ExtractLibFromApk(apk_full_path, file_name)
if tmp_shared_lib:
- file_entry[2] = tmp_shared_lib
- return file_entry[1], file_entry[2]
+ tmp_files[file_name] = tmp_shared_lib
+ return file_name, tmp_shared_lib
return None, None
def ProcessLine(self, line):