blob: 419aa3582a3e756c4b8311ef522a5b2e60d325e8 [file] [log] [blame]
#!/usr/bin/python
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Prints the total PSS attributed to Chrome's code pages in an application.
This scripts assumes a device with Monochrome, and requires root access.
For instance, to get chrome's code page memory footprint:
$ tools/android/native_lib_memory/code_pages_pss.py
--app-package com.android.chrome
--chrome-package com.android.chrome --verbose
To get Webview's footprint in AGSA:
$ tools/android/native_lib_memory/code_pages_pss.py
--app-package com.google.android.googlequicksearchbox
--chrome-package com.android.chrome --verbose
"""
import argparse
import logging
import os
import re
import sys
_SRC_PATH = os.path.join(
os.path.dirname(__file__), os.pardir, os.pardir, os.pardir)
sys.path.append(os.path.join(_SRC_PATH, 'third_party', 'catapult', 'devil'))
from devil.android import device_utils
def _GetPssFromProcSmapsLinesInKb(lines, chrome_package, verbose):
"""Same as |_GetPssInKb()|, starting from the content of /proc/[pid]/smaps.
Args:
lines: ([str]) Content of /proc/[pid]/smaps.
chrome_package: (str) Chrome's package name, e.g. 'com.android.chrome'.
verbose: (bool) Verbose output.
"""
SMAPS_ENTRY_START_RE = '^[0-9a-f]{1,16}-[0-9a-f]{1,16} '
assert re.search(SMAPS_ENTRY_START_RE,
'7fffc6fbe000-7fffc6fc0000 r-xp '
'00000000 00:00 0 [vdso]')
# Various kernels have various amount of detail for each /proc/[pid]/smaps
# entry. Detect the start of a new entry to find the length on this kernel.
assert re.search(SMAPS_ENTRY_START_RE, lines[0])
length = 0
for (index, line) in enumerate(lines[1:]):
if re.search(SMAPS_ENTRY_START_RE, line):
length = index + 1
break
assert length
pss_offset = 0
for (index, line) in enumerate(lines[:length]):
if line.startswith('Pss:'):
pss_offset = index
break
assert pss_offset
pss = 0
for (index, line) in enumerate(lines):
# Look for read-only, executable private mappings coming from Chrome's
# APK. The APK is in the file path.
# Example line:
# 9d241000-9fb82000 r-xp 009a0000 fd:00 5201 [...]
# /data/app/com.android.chrome-G1amO4b7kPsNBi51zH8QWQ==/base.apk
if not re.search(SMAPS_ENTRY_START_RE, line):
continue
if not ('r-xp' in line and chrome_package in line):
continue
if verbose:
print '\n'.join(lines[index:index + length])
pss_line = lines[index + pss_offset]
assert pss_line.startswith('Pss:')
assert pss_line.endswith(' kB')
pss += int(pss_line[len('Pss:'):-2])
return pss
def _GetPssInKb(device, pid, chrome_package, verbose):
"""Returns the PSS taken by chrome's code pages for a pid, in kB.
Args:
device: (device_utils.DeviceUtils) Device to get the data from.
pid: (int) PID of the process to inspect.
chrome_package: (str) Chrome's package name, e.g. 'com.android.chrome'.
verbose: (bool) Verbose output. Prints relevant /proc/[pid]/smaps entries
if set.
Returns:
(int) PSS in kB from Chrome's code pages in this process.
"""
command = ['cat', '/proc/%d/smaps' % pid]
lines = device.RunShellCommand(command, check_return=True)
return _GetPssFromProcSmapsLinesInKb(lines, chrome_package, verbose)
def _CreateArgumentParser():
parser = argparse.ArgumentParser()
parser.add_argument('--app-package', help='Application to inspect.',
required=True)
parser.add_argument('--chrome-package', help='Chrome package to look for.',
required=True)
parser.add_argument('--verbose', help='Verbose output.',
action='store_true')
return parser
def main():
logging.basicConfig(level=logging.INFO)
parser = _CreateArgumentParser()
args = parser.parse_args()
devices = device_utils.DeviceUtils.HealthyDevices()
if not devices:
logging.error('No connected devices')
return
device = devices[0]
device.EnableRoot()
processes = device.ListProcesses(args.app_package)
logging.info('Processes:\n\t' + '\n\t'.join(p.name for p in processes))
total_pss_kb = 0
for process in processes:
total_pss_kb += _GetPssInKb(device, process.pid, args.chrome_package,
args.verbose)
print 'Total PSS from code pages = %dkB' % total_pss_kb
if __name__ == '__main__':
main()