blob: a51e5ee926f42fd597e591f4bf532e0f2856782c [file] [log] [blame]
#! /usr/bin/env python3
#
# Copyright 2023, The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
from collections import namedtuple
import subprocess
try:
from tqdm import tqdm
except:
def tqdm(x):
return x
ProcEntry = namedtuple('ProcEntry', 'pid, ppid, cmd, name, etc_args')
def get_mem_stats(
zygote_pid,
target_pid,
target_name,
imgdiag_path,
boot_image,
device_out_dir,
host_out_dir,
):
imgdiag_output_path = (
f'{device_out_dir}/imgdiag_{target_name}_{target_pid}.txt'
)
cmd_collect = (
'adb shell '
f'"{imgdiag_path} --zygote-diff-pid={zygote_pid} --image-diff-pid={target_pid} '
f'--output={imgdiag_output_path} --boot-image={boot_image} --dump-dirty-objects"'
)
try:
subprocess.run(cmd_collect, shell=True, check=True)
except:
print('imgdiag call failed on:', target_pid, target_name)
return
cmd_pull = f'adb pull {imgdiag_output_path} {host_out_dir}'
subprocess.run(cmd_pull, shell=True, check=True, capture_output=True)
def main():
parser = argparse.ArgumentParser(
description=(
'Run imgdiag on selected processes and pull results from the device.'
),
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
'process_names',
nargs='*',
help='Process names to use. If none - dump all zygote children.',
)
parser.add_argument(
'--boot-image',
dest='boot_image',
default='/data/misc/apexdata/com.android.art/dalvik-cache/boot.art',
help='Path to boot.art',
)
parser.add_argument(
'--zygote',
default='zygote64',
help='Zygote process name',
)
parser.add_argument(
'--imgdiag',
default='/apex/com.android.art/bin/imgdiag64',
help='Path to imgdiag binary.',
)
parser.add_argument(
'--device-out-dir',
default='/data/local/tmp/imgdiag_out',
help='Directory for imgdiag output files on the device.',
)
parser.add_argument(
'--host-out-dir',
default='./',
help='Directory for imgdiag output files on the host.',
)
args = parser.parse_args()
res = subprocess.run(
args='adb shell ps -o pid:1,ppid:1,cmd:1,args:1',
capture_output=True,
shell=True,
check=True,
text=True,
)
proc_entries = []
for line in res.stdout.splitlines()[1:]: # skip header
pid, ppid, cmd, name, *etc_args = line.split(' ')
entry = ProcEntry(int(pid), int(ppid), cmd, name, etc_args)
proc_entries.append(entry)
zygote_entry = next(e for e in proc_entries if e.name == args.zygote)
zygote_children = [e for e in proc_entries if e.ppid == zygote_entry.pid]
if args.process_names:
zygote_children = [e for e in proc_entries if e.name in args.process_names]
print('\n'.join(str(e.pid) + ' ' + e.name for e in zygote_children))
subprocess.run(
args=f'adb shell "mkdir -p {args.device_out_dir}"', check=True, shell=True
)
subprocess.run(args=f'mkdir -p {args.host_out_dir}', check=True, shell=True)
for entry in tqdm(zygote_children):
get_mem_stats(
zygote_pid=entry.ppid,
target_pid=entry.pid,
target_name=entry.name,
imgdiag_path=args.imgdiag,
boot_image=args.boot_image,
device_out_dir=args.device_out_dir,
host_out_dir=args.host_out_dir,
)
if __name__ == '__main__':
main()