blob: 1211e748a974766b31effa743a1cc85768df385e [file] [log] [blame]
Alex Light4fa48352020-12-29 13:45:04 -08001#!/usr/bin/python3
2#
3# Copyright (C) 2021 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
17#
18# This script runs dex2oat on the host to compile a provided JAR or APK.
19#
20
21import argparse
22import shlex
23import subprocess
24import os
25import os.path
26
27
28def run_print(lst):
29 return " ".join(map(shlex.quote, lst))
30
31
32def parse_args():
33 parser = argparse.ArgumentParser(
34 description="compile dex or jar files",
35 epilog="Unrecognized options are passed on to dex2oat unmodified.")
36 parser.add_argument(
37 "--dex2oat",
38 action="store",
39 default=os.path.expandvars("$ANDROID_HOST_OUT/bin/dex2oatd64"),
40 help="selects the dex2oat to use.")
41 parser.add_argument(
42 "--debug",
43 action="store_true",
44 default=False,
45 help="launches dex2oatd with lldb-server g :5039. Connect using vscode or remote lldb"
46 )
47 parser.add_argument(
48 "--profman",
49 action="store",
50 default=os.path.expandvars("$ANDROID_HOST_OUT/bin/profmand"),
51 help="selects the profman to use.")
52 parser.add_argument(
53 "--debug-profman",
54 action="store_true",
55 default=False,
56 help="launches profman with lldb-server g :5039. Connect using vscode or remote lldb"
57 )
58 profs = parser.add_mutually_exclusive_group()
59 profs.add_argument(
60 "--profile-file",
61 action="store",
62 help="Use this profile file. Probably want to pass --compiler-filter=speed-profile with this."
63 )
64 profs.add_argument(
65 "--profile-line",
66 action="append",
67 default=[],
68 help="functions to add to a profile. Probably want to pass --compiler-filter=speed-profile with this. All functions are marked as 'hot'. Use --profile-file for more control."
69 )
70 parser.add_argument(
71 "--arch",
72 action="store",
73 choices=["arm", "arm64", "x86", "x86_64", "host64", "host32"],
74 default="host64",
75 help="architecture to compile for. Defaults to host64")
76 parser.add_argument(
77 "--odex-file",
78 action="store",
79 help="odex file to write. File discarded if not set",
80 default=None)
81 parser.add_argument(
82 "--save-profile",
83 action="store",
84 type=argparse.FileType("w"),
85 default=None,
86 help="File path to store the profile to")
87 parser.add_argument(
88 "dex_files", help="dex/jar files", nargs="+", metavar="DEX")
89 return parser.parse_known_args()
90
91
92def get_bcp_runtime_args(image, arch):
93 if arch != "host32" and arch != "host64":
94 args = [
95 "art/tools/host_bcp.sh",
96 os.path.expandvars(
97 "${{OUT}}/system/framework/oat/{}/services.odex".format(arch)),
98 "--use-first-dir"
99 ]
100 print("Running: {}".format(run_print(args)))
101 print("=START=======================================")
102 res = subprocess.run(args, capture_output=True, text=True)
103 print("=END=========================================")
104 res.check_returncode()
105 return res.stdout.split()
106 else:
107 # Host we just use the bcp locations for both.
108 res = open(
109 os.path.expandvars(
110 "$ANDROID_HOST_OUT/apex/art_boot_images/javalib/{}/boot.oat".format(
111 "x86" if arch == "host32" else "x86_64")), "rb").read()
112 bcp_tag = b"bootclasspath\0"
113 bcp_start = res.find(bcp_tag) + len(bcp_tag)
114 bcp = res[bcp_start:bcp_start + res[bcp_start:].find(b"\0")]
115 str_bcp = bcp.decode()
116 return [
117 "--runtime-arg", "-Xbootclasspath:{}".format(str_bcp), "--runtime-arg",
118 "-Xbootclasspath-locations:{}".format(str_bcp)
119 ]
120
121
122def fdfile(fd):
123 return "/proc/{}/fd/{}".format(os.getpid(), fd)
124
125
126def get_profile_args(args, location_base):
127 """Handle all the profile file options."""
128 if args.profile_file is None and len(args.profile_line) == 0:
129 return []
130 if args.debug_profman:
131 profman_args = ["lldb-server", "g", ":5039", "--", args.profman]
132 else:
133 profman_args = [args.profman]
134 if args.save_profile:
135 prof_out_fd = args.save_profile.fileno()
136 os.set_inheritable(prof_out_fd, True)
137 else:
138 prof_out_fd = os.memfd_create("reference_prof", flags=0)
139 if args.debug_profman:
140 profman_args.append("--reference-profile-file={}".format(
141 fdfile(prof_out_fd)))
142 else:
143 profman_args.append("--reference-profile-file-fd={}".format(prof_out_fd))
144 if args.profile_file:
145 profman_args.append("--create-profile-from={}".format(args.profile_file))
146 else:
147 prof_in_fd = os.memfd_create("input_prof", flags=0)
148 # Why on earth does fdopen take control of the fd and not mention it in the docs.
149 with os.fdopen(os.dup(prof_in_fd), "w") as prof_in:
150 for l in args.profile_line:
151 print(l, file=prof_in)
152 profman_args.append("--create-profile-from={}".format(fdfile(prof_in_fd)))
153 for f in args.dex_files:
154 profman_args.append("--apk={}".format(f))
155 profman_args.append("--dex-location={}".format(
156 os.path.join(location_base, os.path.basename(f))))
157 print("Running: {}".format(run_print(profman_args)))
158 print("=START=======================================")
159 subprocess.run(profman_args, close_fds=False).check_returncode()
160 print("=END=========================================")
161 if args.debug:
162 return ["--profile-file={}".format(fdfile(prof_out_fd))]
163 else:
164 return ["--profile-file={}".format(fdfile(prof_out_fd))]
165
166
167def main():
168 args, extra = parse_args()
169 if args.arch == "host32" or args.arch == "host64":
170 location_base = os.path.expandvars("${ANDROID_HOST_OUT}/framework/")
171 real_arch = "x86" if args.arch == "host32" else "x86_64"
172 boot_image = os.path.expandvars(
173 "$ANDROID_HOST_OUT/apex/art_boot_images/javalib/boot.art")
174 android_root = os.path.expandvars("$ANDROID_HOST_OUT")
175 for f in args.dex_files:
176 extra.append("--dex-location={}".format(
177 os.path.join(location_base, os.path.basename(f))))
178 extra.append("--dex-file={}".format(f))
179 else:
180 location_base = "/system/framework"
181 real_arch = args.arch
182 boot_image = os.path.expandvars(":".join([
183 "${OUT}/apex/com.android.art.debug/javalib/boot.art",
184 "${OUT}/system/framework/boot-framework.art"
185 ]))
186 android_root = os.path.expandvars("$OUT/system")
187 for f in args.dex_files:
188 extra.append("--dex-location={}".format(
189 os.path.join(location_base, os.path.basename(f))))
190 extra.append("--dex-file={}".format(f))
191 extra += get_bcp_runtime_args(boot_image, args.arch)
192 extra += get_profile_args(args, location_base)
193 extra.append("--instruction-set={}".format(real_arch))
194 extra.append("--boot-image={}".format(boot_image))
195 extra.append("--android-root={}".format(android_root))
196 extra += ["--runtime-arg", "-Xms64m", "--runtime-arg", "-Xmx512m"]
197 if args.odex_file is not None:
198 extra.append("--oat-file={}".format(args.odex_file))
199 else:
200 if args.debug:
201 raise Exception("Debug requires a real output file. :(")
202 extra.append("--oat-fd={}".format(os.memfd_create("odex_fd", flags=0)))
203 extra.append("--oat-location={}".format("/tmp/odex_fd.odex"))
204 extra.append("--output-vdex-fd={}".format(
205 os.memfd_create("vdex_fd", flags=0)))
206 pre_args = []
207 if args.debug:
208 pre_args = ["lldb-server", "g", ":5039", "--"]
209 pre_args.append(args.dex2oat)
210 print("Running: {}".format(run_print(pre_args + extra)))
211 print("=START=======================================")
212 subprocess.run(pre_args + extra, close_fds=False).check_returncode()
213 print("=END=========================================")
214
215
216if __name__ == "__main__":
217 main()