blob: 4860aa81851aafeccf6bba5b7f5f6e8f78ec61e9 [file] [log] [blame]
Florian Mayera8ff9032020-03-04 11:31:48 -08001#!/usr/bin/env python
2
3# Copyright (C) 2020 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17from __future__ import absolute_import
18from __future__ import division
19from __future__ import print_function
20
21import argparse
22import os
23import subprocess
24import sys
25import tempfile
26import time
27
28NULL = open(os.devnull)
29
30PACKAGES_LIST_CFG = '''data_sources {
31 config {
32 name: "android.packages_list"
33 }
34}
35'''
36
37CFG_IDENT = ' '
38CFG = '''buffers {{
39 size_kb: 100024
40 fill_policy: RING_BUFFER
41}}
42
43data_sources {{
44 config {{
45 name: "android.java_hprof"
46 java_hprof_config {{
47{target_cfg}
48{continuous_dump_config}
49 }}
50 }}
51}}
52
53duration_ms: 20000
54'''
55
56CONTINUOUS_DUMP = """
57 continuous_dump_config {{
58 dump_phase_ms: 0
59 dump_interval_ms: {dump_interval}
60 }}
61"""
62
63PERFETTO_CMD = ('CFG=\'{cfg}\'; echo ${{CFG}} | '
64 'perfetto --txt -c - -o '
65 '/data/misc/perfetto-traces/java-profile-{user} -d')
66
67def main(argv):
68 parser = argparse.ArgumentParser()
69 parser.add_argument(
70 "-o",
71 "--output",
72 help="Filename to save profile to.",
73 metavar="FILE",
74 default=None)
75 parser.add_argument(
76 "-p",
77 "--pid",
78 help="Comma-separated list of PIDs to "
79 "profile.",
80 metavar="PIDS")
81 parser.add_argument(
82 "-n",
83 "--name",
84 help="Comma-separated list of process "
85 "names to profile.",
86 metavar="NAMES")
87 parser.add_argument(
88 "-c",
89 "--continuous-dump",
90 help="Dump interval in ms. 0 to disable continuous dump.",
91 type=int,
92 default=0)
93 parser.add_argument(
94 "--no-versions",
95 action="store_true",
96 help="Do not get version information about APKs.")
97 parser.add_argument(
98 "--print-config",
99 action="store_true",
100 help="Print config instead of running. For debugging.")
101
102 args = parser.parse_args()
103
104 fail = False
105 if args.pid is None and args.name is None:
106 print("FATAL: Neither PID nor NAME given.", file=sys.stderr)
107 fail = True
108
109 target_cfg = ""
110 if args.pid:
111 for pid in args.pid.split(','):
112 try:
113 pid = int(pid)
114 except ValueError:
115 print("FATAL: invalid PID %s" % pid, file=sys.stderr)
116 fail = True
117 target_cfg += '{}pid: {}\n'.format(CFG_IDENT, pid)
118 if args.name:
119 for name in args.name.split(','):
120 target_cfg += '{}process_cmdline: "{}"\n'.format(CFG_IDENT, name)
121
122 if fail:
123 parser.print_help()
124 return 1
125
126 output_file = args.output
127 if output_file is None:
128 fd, name = tempfile.mkstemp('profile')
129 os.close(fd)
130 output_file = name
131
132 continuous_dump_cfg = ""
133 if args.continuous_dump:
134 continuous_dump_cfg = CONTINUOUS_DUMP.format(
135 dump_interval=args.continuous_dump)
136 cfg = CFG.format(
137 target_cfg=target_cfg,
138 continuous_dump_config=continuous_dump_cfg)
139 if not args.no_versions:
140 cfg += PACKAGES_LIST_CFG
141
142 if args.print_config:
143 print(cfg)
144 return 0
145
146 user = subprocess.check_output(['adb', 'shell', 'whoami']).strip()
147 perfetto_pid = subprocess.check_output(
148 ['adb', 'exec-out',
149 PERFETTO_CMD.format(cfg=cfg, user=user)]).strip()
150 try:
151 int(perfetto_pid.strip())
152 except ValueError:
153 print("Failed to invoke perfetto: {}".format(perfetto_pid), file=sys.stderr)
154 return 1
155
156 print("Dumping Java Heap.")
157 exists = True
158
159 # Wait for perfetto cmd to return.
160 while exists:
161 exists = subprocess.call(
162 ['adb', 'shell', '[ -d /proc/{} ]'.format(perfetto_pid)]) == 0
163 time.sleep(1)
164
165 subprocess.check_call(
166 ['adb', 'pull', '/data/misc/perfetto-traces/java-profile-{}'.format(user),
167 output_file], stdout=NULL)
168
169 print("Wrote profile to {}".format(output_file))
170 print("This can be viewed using https://ui.perfetto.dev.")
171
172
173if __name__ == '__main__':
174 sys.exit(main(sys.argv))