Merge changes I37eb4151,If40a376d,Ic43b5cc6,Iaa9f304c

am: 82f245e9d6

Change-Id: Id3a05f509395ee986bd6a8708814910651788765
This commit is contained in:
Andreas Gampe
2018-10-30 14:41:57 -07:00
committed by android-build-merger
7 changed files with 137 additions and 77 deletions

View File

@@ -27,3 +27,23 @@ python_library_host {
}, },
} }
python_test_host {
name: "python-native_heapdump_viewer_test",
main: "native_heapdump_viewer_tests.py",
srcs: [
"native_heapdump_viewer.py",
"native_heapdump_viewer_tests.py",
],
test_config: "native_heapdump_viewer-tests.xml",
test_suites: ["general-tests"],
}
python_test_host {
name: "python-symbol_test",
main: "symbol.py",
// Would be nice to use the library above, but as it's single-source
// this doesn't work.
srcs: ["symbol.py"],
test_config: "symbol-tests.xml",
test_suites: ["general-tests"],
}

10
scripts/TEST_MAPPING Normal file
View File

@@ -0,0 +1,10 @@
{
"presubmit": [
{
"name": "python-native_heapdump_viewer_test"
},
{
"name": "python-symbol_test"
}
]
}

View File

@@ -0,0 +1,7 @@
<configuration description="Config for python tradefed symbol test">
<option name="test-suite-tag" value="python-native_heapdump_viewer_test" />
<test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
<option name="par-file-name" value="python-native_heapdump_viewer_test" />
<option name="test-timeout" value="2m" />
</test>
</configuration>

View File

@@ -82,13 +82,13 @@ Usage:
elif sys.argv[i] == "--reverse": elif sys.argv[i] == "--reverse":
self.reverse_frames = True self.reverse_frames = True
elif sys.argv[i][0] == '-': elif sys.argv[i][0] == '-':
print "Invalid option " + sys.argv[i] print("Invalid option %s" % (sys.argv[i]))
else: else:
extra_args.append(sys.argv[i]) extra_args.append(sys.argv[i])
i += 1 i += 1
if len(extra_args) != 1: if len(extra_args) != 1:
print self._usage print(self._usage)
sys.exit(1) sys.exit(1)
self.native_heap = extra_args[0] self.native_heap = extra_args[0]
@@ -118,10 +118,11 @@ def GetVersion(native_heap):
re_line = re.compile("Android\s+Native\s+Heap\s+Dump\s+(?P<version>v\d+\.\d+)\s*$") re_line = re.compile("Android\s+Native\s+Heap\s+Dump\s+(?P<version>v\d+\.\d+)\s*$")
matched = 0 matched = 0
for line in open(native_heap, "r"): with open(native_heap, "r") as f:
m = re_line.match(line) for line in f:
if m: m = re_line.match(line)
return m.group('version') if m:
return m.group('version')
return None return None
def GetNumFieldValidByParsingLines(native_heap): def GetNumFieldValidByParsingLines(native_heap):
@@ -143,24 +144,25 @@ def GetNumFieldValidByParsingLines(native_heap):
re_line = re.compile("z\s+(?P<zygote>\d+)\s+sz\s+(?P<size>\d+)\s+num\s+(?P<num_allocations>\d+)") re_line = re.compile("z\s+(?P<zygote>\d+)\s+sz\s+(?P<size>\d+)\s+num\s+(?P<num_allocations>\d+)")
matched = 0 matched = 0
backtrace_size = 0 backtrace_size = 0
for line in open(native_heap, "r"): with open(native_heap, "r") as f:
if backtrace_size == 0: for line in f:
m = re_backtrace.match(line) if backtrace_size == 0:
if m: m = re_backtrace.match(line)
backtrace_size = int(m.group('backtrace_size')) if m:
parts = line.split() backtrace_size = int(m.group('backtrace_size'))
if len(parts) > 7 and parts[0] == "z" and parts[2] == "sz": parts = line.split()
m = re_line.match(line) if len(parts) > 7 and parts[0] == "z" and parts[2] == "sz":
if m: m = re_line.match(line)
num_allocations = int(m.group('num_allocations')) if m:
if num_allocations == backtrace_size: num_allocations = int(m.group('num_allocations'))
# At least three lines must match this pattern before if num_allocations == backtrace_size:
# considering this the old buggy version of malloc debug. # At least three lines must match this pattern before
matched += 1 # considering this the old buggy version of malloc debug.
if matched == 3: matched += 1
return False if matched == 3:
else: return False
return True else:
return True
return matched == 0 return matched == 0
def GetNumFieldValid(native_heap): def GetNumFieldValid(native_heap):
@@ -220,34 +222,35 @@ def ParseNativeHeap(native_heap, reverse_frames, num_field_valid, app_symboldir)
re_map = re.compile("(?P<start>[0-9a-f]+)-(?P<end>[0-9a-f]+) .... (?P<offset>[0-9a-f]+) [0-9a-f]+:[0-9a-f]+ [0-9]+ +(?P<name>.*)") re_map = re.compile("(?P<start>[0-9a-f]+)-(?P<end>[0-9a-f]+) .... (?P<offset>[0-9a-f]+) [0-9a-f]+:[0-9a-f]+ [0-9]+ +(?P<name>.*)")
for line in open(native_heap, "r"): with open(native_heap, "r") as f:
# Format of line: for line in f:
# z 0 sz 50 num 1 bt 000000000000a100 000000000000b200 # Format of line:
parts = line.split() # z 0 sz 50 num 1 bt 000000000000a100 000000000000b200
if len(parts) > 7 and parts[0] == "z" and parts[2] == "sz": parts = line.split()
is_zygote = parts[1] != "1" if len(parts) > 7 and parts[0] == "z" and parts[2] == "sz":
size = int(parts[3]) is_zygote = parts[1] != "1"
if num_field_valid: size = int(parts[3])
num_allocs = int(parts[5]) if num_field_valid:
num_allocs = int(parts[5])
else:
num_allocs = 1
frames = list(map(lambda x: int(x, 16), parts[7:]))
if reverse_frames:
frames = list(reversed(frames))
backtraces.append(Backtrace(is_zygote, size, num_allocs, frames))
else: else:
num_allocs = 1 # Parse map line:
frames = map(lambda x: int(x, 16), parts[7:]) # 720de01000-720ded7000 r-xp 00000000 fd:00 495 /system/lib64/libc.so
if reverse_frames: m = re_map.match(line)
frames = list(reversed(frames)) if m:
backtraces.append(Backtrace(is_zygote, size, num_allocs, frames)) # Offset of mapping start
else: start = int(m.group('start'), 16)
# Parse map line: # Offset of mapping end
# 720de01000-720ded7000 r-xp 00000000 fd:00 495 /system/lib64/libc.so end = int(m.group('end'), 16)
m = re_map.match(line) # Offset within file that is mapped
if m: offset = int(m.group('offset'), 16)
# Offset of mapping start name = m.group('name')
start = int(m.group('start'), 16) mappings.append(GetMappingFromOffset(Mapping(start, end, offset, name), app_symboldir))
# Offset of mapping end
end = int(m.group('end'), 16)
# Offset within file that is mapped
offset = int(m.group('offset'), 16)
name = m.group('name')
mappings.append(GetMappingFromOffset(Mapping(start, end, offset, name), app_symboldir))
return backtraces, mappings return backtraces, mappings
def FindMapping(mappings, addr): def FindMapping(mappings, addr):
@@ -300,7 +303,7 @@ def ResolveAddrs(html_output, symboldir, app_symboldir, backtraces, mappings):
# Resolve functions and line numbers. # Resolve functions and line numbers.
if html_output == False: if html_output == False:
print "Resolving symbols using directory %s..." % symboldir print("Resolving symbols using directory %s..." % symboldir)
for lib in addrs_by_lib: for lib in addrs_by_lib:
sofile = app_symboldir + lib sofile = app_symboldir + lib
@@ -335,7 +338,7 @@ def ResolveAddrs(html_output, symboldir, app_symboldir, backtraces, mappings):
resolved_addrs[addrs_by_lib[lib][x]] = FrameDescription("---", "---", lib) resolved_addrs[addrs_by_lib[lib][x]] = FrameDescription("---", "---", lib)
else: else:
if html_output == False: if html_output == False:
print "%s not found for symbol resolution" % lib print("%s not found for symbol resolution" % lib)
fd = FrameDescription("???", "???", lib) fd = FrameDescription("???", "???", lib)
for addr in addrs_by_lib[lib]: for addr in addrs_by_lib[lib]:
@@ -374,7 +377,7 @@ def Display(resolved_addrs, indent, total, parent_total, node):
parent_percent = 0 parent_percent = 0
if parent_total != 0: if parent_total != 0:
parent_percent = 100 * node.size / float(parent_total) parent_percent = 100 * node.size / float(parent_total)
print "%9d %6.2f%% %6.2f%% %8d %s%s %s %s %s" % (node.size, total_percent, parent_percent, node.number, indent, node.addr, fd.library, fd.function, fd.location) print("%9d %6.2f%% %6.2f%% %8d %s%s %s %s %s" % (node.size, total_percent, parent_percent, node.number, indent, node.addr, fd.library, fd.function, fd.location))
children = sorted(node.children.values(), key=lambda x: x.size, reverse=True) children = sorted(node.children.values(), key=lambda x: x.size, reverse=True)
for child in children: for child in children:
Display(resolved_addrs, indent + " ", total, node.size, child) Display(resolved_addrs, indent + " ", total, node.size, child)
@@ -395,23 +398,23 @@ def DisplayHtml(verbose, resolved_addrs, total, node, extra, label_count):
label = label.replace("<", "&lt;") label = label.replace("<", "&lt;")
label = label.replace(">", "&gt;") label = label.replace(">", "&gt;")
children = sorted(node.children.values(), key=lambda x: x.size, reverse=True) children = sorted(node.children.values(), key=lambda x: x.size, reverse=True)
print '<li>' print('<li>')
if len(children) > 0: if len(children) > 0:
print '<label for="' + str(label_count) + '">' + label + '</label>' print('<label for="' + str(label_count) + '">' + label + '</label>')
print '<input type="checkbox" id="' + str(label_count) + '"/>' print('<input type="checkbox" id="' + str(label_count) + '"/>')
print '<ol>' print('<ol>')
label_count += 1 label_count += 1
for child in children: for child in children:
label_count = DisplayHtml(verbose, resolved_addrs, total, child, "", label_count) label_count = DisplayHtml(verbose, resolved_addrs, total, child, "", label_count)
print '</ol>' print('</ol>')
else: else:
print label print(label)
print '</li>' print('</li>')
return label_count return label_count
def CreateHtml(verbose, app, zygote, resolved_addrs): def CreateHtml(verbose, app, zygote, resolved_addrs):
print """ print("""
<!DOCTYPE html> <!DOCTYPE html>
<html><head><style> <html><head><style>
li input { li input {
@@ -433,13 +436,13 @@ label {
</style></head><body>Native allocation HTML viewer<br><br> </style></head><body>Native allocation HTML viewer<br><br>
Click on an individual line to expand/collapse to see the details of the Click on an individual line to expand/collapse to see the details of the
allocation data<ol> allocation data<ol>
""" """)
label_count = 0 label_count = 0
label_count = DisplayHtml(verbose, resolved_addrs, app.size, app, "app ", label_count) label_count = DisplayHtml(verbose, resolved_addrs, app.size, app, "app ", label_count)
if zygote.size > 0: if zygote.size > 0:
DisplayHtml(verbose, resolved_addrs, zygote.size, zygote, "zygote ", label_count) DisplayHtml(verbose, resolved_addrs, zygote.size, zygote, "zygote ", label_count)
print "</ol></body></html>" print("</ol></body></html>")
def main(): def main():
args = Args() args = Args()
@@ -468,12 +471,12 @@ def main():
if args.html_output: if args.html_output:
CreateHtml(args.verbose, app, zygote, resolved_addrs) CreateHtml(args.verbose, app, zygote, resolved_addrs)
else: else:
print "" print("")
print "%9s %6s %6s %8s %s %s %s %s" % ("BYTES", "%TOTAL", "%PARENT", "COUNT", "ADDR", "LIBRARY", "FUNCTION", "LOCATION") print("%9s %6s %6s %8s %s %s %s %s" % ("BYTES", "%TOTAL", "%PARENT", "COUNT", "ADDR", "LIBRARY", "FUNCTION", "LOCATION"))
Display(resolved_addrs, "", app.size, app.size + zygote.size, app) Display(resolved_addrs, "", app.size, app.size + zygote.size, app)
print "" print("")
Display(resolved_addrs, "", zygote.size, app.size + zygote.size, zygote) Display(resolved_addrs, "", zygote.size, app.size + zygote.size, zygote)
print "" print("")
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@@ -27,7 +27,7 @@ class NativeHeapdumpViewerTest(unittest.TestCase):
def CreateTmpFile(self, contents): def CreateTmpFile(self, contents):
fd, self._tmp_file_name = tempfile.mkstemp() fd, self._tmp_file_name = tempfile.mkstemp()
os.write(fd, contents) os.write(fd, contents.encode())
os.close(fd) os.close(fd)
return self._tmp_file_name return self._tmp_file_name
@@ -36,7 +36,7 @@ class NativeHeapdumpViewerTest(unittest.TestCase):
try: try:
os.unlink(self._tmp_file_name) os.unlink(self._tmp_file_name)
except Exception: except Exception:
print "Failed to delete " + heap print("Failed to delete %s" % (heap))
class GetNumFieldValidTest(NativeHeapdumpViewerTest): class GetNumFieldValidTest(NativeHeapdumpViewerTest):
_map_data = """ _map_data = """
@@ -180,4 +180,4 @@ END
self.assertEqual("/system/lib64/libutils.so", mappings[1].name) self.assertEqual("/system/lib64/libutils.so", mappings[1].name)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main(verbosity=2)

7
scripts/symbol-tests.xml Normal file
View File

@@ -0,0 +1,7 @@
<configuration description="Config for python tradefed symbol test">
<option name="test-suite-tag" value="python-symbol_test" />
<test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
<option name="par-file-name" value="python-symbol_test" />
<option name="test-timeout" value="2m" />
</test>
</configuration>

View File

@@ -28,18 +28,24 @@ import signal
import subprocess import subprocess
import unittest import unittest
ANDROID_BUILD_TOP = os.environ["ANDROID_BUILD_TOP"] try:
if not ANDROID_BUILD_TOP: ANDROID_BUILD_TOP = str(os.environ["ANDROID_BUILD_TOP"])
if not ANDROID_BUILD_TOP:
ANDROID_BUILD_TOP = "."
except:
ANDROID_BUILD_TOP = "." ANDROID_BUILD_TOP = "."
def FindSymbolsDir(): def FindSymbolsDir():
saveddir = os.getcwd() saveddir = os.getcwd()
os.chdir(ANDROID_BUILD_TOP) os.chdir(ANDROID_BUILD_TOP)
stream = None
try: try:
cmd = "build/soong/soong_ui.bash --dumpvar-mode --abs TARGET_OUT_UNSTRIPPED" cmd = "build/soong/soong_ui.bash --dumpvar-mode --abs TARGET_OUT_UNSTRIPPED"
stream = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True).stdout stream = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True).stdout
return os.path.join(ANDROID_BUILD_TOP, stream.read().strip()) return os.path.join(ANDROID_BUILD_TOP, str(stream.read().strip()))
finally: finally:
if stream is not None:
stream.close()
os.chdir(saveddir) os.chdir(saveddir)
SYMBOLS_DIR = FindSymbolsDir() SYMBOLS_DIR = FindSymbolsDir()
@@ -166,7 +172,7 @@ def FindToolchain():
_CACHED_TOOLCHAIN = toolchain _CACHED_TOOLCHAIN = toolchain
_CACHED_TOOLCHAIN_ARCH = ARCH _CACHED_TOOLCHAIN_ARCH = ARCH
print "Using %s toolchain from: %s" % (_CACHED_TOOLCHAIN_ARCH, _CACHED_TOOLCHAIN) print("Using %s toolchain from: %s" % (_CACHED_TOOLCHAIN_ARCH, _CACHED_TOOLCHAIN))
return _CACHED_TOOLCHAIN return _CACHED_TOOLCHAIN
@@ -547,6 +553,7 @@ class FindToolchainTests(unittest.TestCase):
ARCH = abi ARCH = abi
FindToolchain() # Will throw on failure. FindToolchain() # Will throw on failure.
@unittest.skipIf(ANDROID_BUILD_TOP == '.', 'Test only supported in an Android tree.')
def test_toolchains_found(self): def test_toolchains_found(self):
self.assert_toolchain_found("arm") self.assert_toolchain_found("arm")
self.assert_toolchain_found("arm64") self.assert_toolchain_found("arm64")
@@ -717,7 +724,13 @@ class SetArchTests(unittest.TestCase):
def test_no_abi(self): def test_no_abi(self):
global ARCH global ARCH
self.assertRaisesRegexp(Exception, "Could not determine arch from input, use --arch=XXX to specify it", SetAbi, []) # Python2 vs Python3 compatibility: Python3 warns on Regexp deprecation, but Regex
# does not provide that name.
if not hasattr(unittest.TestCase, 'assertRaisesRegex'):
unittest.TestCase.assertRaisesRegex = getattr(unittest.TestCase, 'assertRaisesRegexp')
self.assertRaisesRegex(Exception,
"Could not determine arch from input, use --arch=XXX to specify it",
SetAbi, [])
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main(verbosity=2)