1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <sys/stat.h>
18
19 #include <string>
20 #include <string_view>
21 #include <unordered_map>
22
23 #include "android-base/parsebool.h"
24 #include "android-base/properties.h"
25 #include "android-base/stringprintf.h"
26 #include "android-base/strings.h"
27 #include "arch/instruction_set.h"
28 #include "base/file_utils.h"
29 #include "base/globals.h"
30 #include "base/mem_map.h"
31 #include "base/stl_util.h"
32 #include "odr_common.h"
33 #include "odr_compilation_log.h"
34 #include "odr_config.h"
35 #include "odr_metrics.h"
36 #include "odrefresh.h"
37 #include "odrefresh/odrefresh.h"
38
39 namespace {
40
41 using ::android::base::GetProperty;
42 using ::android::base::ParseBool;
43 using ::android::base::ParseBoolResult;
44 using ::art::odrefresh::CompilationOptions;
45 using ::art::odrefresh::ExitCode;
46 using ::art::odrefresh::kCheckedSystemPropertyPrefixes;
47 using ::art::odrefresh::kSystemProperties;
48 using ::art::odrefresh::kSystemPropertySystemServerCompilerFilterOverride;
49 using ::art::odrefresh::OdrCompilationLog;
50 using ::art::odrefresh::OdrConfig;
51 using ::art::odrefresh::OdrMetrics;
52 using ::art::odrefresh::OnDeviceRefresh;
53 using ::art::odrefresh::QuotePath;
54 using ::art::odrefresh::ShouldDisablePartialCompilation;
55 using ::art::odrefresh::ShouldDisableRefresh;
56 using ::art::odrefresh::SystemPropertyConfig;
57 using ::art::odrefresh::SystemPropertyForeach;
58 using ::art::odrefresh::ZygoteKind;
59
UsageMsgV(const char * fmt,va_list ap)60 void UsageMsgV(const char* fmt, va_list ap) {
61 std::string error;
62 android::base::StringAppendV(&error, fmt, ap);
63 if (isatty(fileno(stderr))) {
64 std::cerr << error << std::endl;
65 } else {
66 LOG(ERROR) << error;
67 }
68 }
69
UsageMsg(const char * fmt,...)70 void UsageMsg(const char* fmt, ...) {
71 va_list ap;
72 va_start(ap, fmt);
73 UsageMsgV(fmt, ap);
74 va_end(ap);
75 }
76
ArgumentError(const char * fmt,...)77 NO_RETURN void ArgumentError(const char* fmt, ...) {
78 va_list ap;
79 va_start(ap, fmt);
80 UsageMsgV(fmt, ap);
81 va_end(ap);
82 UsageMsg("Try '--help' for more information.");
83 exit(EX_USAGE);
84 }
85
ParseZygoteKind(const char * input,ZygoteKind * zygote_kind)86 bool ParseZygoteKind(const char* input, ZygoteKind* zygote_kind) {
87 std::string_view z(input);
88 if (z == "zygote32") {
89 *zygote_kind = ZygoteKind::kZygote32;
90 return true;
91 } else if (z == "zygote32_64") {
92 *zygote_kind = ZygoteKind::kZygote32_64;
93 return true;
94 } else if (z == "zygote64_32") {
95 *zygote_kind = ZygoteKind::kZygote64_32;
96 return true;
97 } else if (z == "zygote64") {
98 *zygote_kind = ZygoteKind::kZygote64;
99 return true;
100 }
101 return false;
102 }
103
GetEnvironmentVariableOrDie(const char * name)104 std::string GetEnvironmentVariableOrDie(const char* name) {
105 const char* value = getenv(name);
106 LOG_ALWAYS_FATAL_IF(value == nullptr, "%s is not defined.", name);
107 return value;
108 }
109
GetEnvironmentVariableOrDefault(const char * name,std::string default_value)110 std::string GetEnvironmentVariableOrDefault(const char* name, std::string default_value) {
111 const char* value = getenv(name);
112 if (value == nullptr) {
113 return default_value;
114 }
115 return value;
116 }
117
ArgumentMatches(std::string_view argument,std::string_view prefix,std::string * value)118 bool ArgumentMatches(std::string_view argument, std::string_view prefix, std::string* value) {
119 if (argument.starts_with(prefix)) {
120 *value = std::string(argument.substr(prefix.size()));
121 return true;
122 }
123 return false;
124 }
125
ArgumentEquals(std::string_view argument,std::string_view expected)126 bool ArgumentEquals(std::string_view argument, std::string_view expected) {
127 return argument == expected;
128 }
129
InitializeConfig(int argc,char ** argv,OdrConfig * config)130 int InitializeConfig(int argc, char** argv, OdrConfig* config) {
131 config->SetApexInfoListFile("/apex/apex-info-list.xml");
132 config->SetArtBinDir(art::GetArtBinDir());
133 config->SetBootClasspath(GetEnvironmentVariableOrDie("BOOTCLASSPATH"));
134 config->SetDex2oatBootclasspath(GetEnvironmentVariableOrDie("DEX2OATBOOTCLASSPATH"));
135 config->SetSystemServerClasspath(GetEnvironmentVariableOrDie("SYSTEMSERVERCLASSPATH"));
136 config->SetStandaloneSystemServerJars(
137 GetEnvironmentVariableOrDefault("STANDALONE_SYSTEMSERVER_JARS", /*default_value=*/""));
138 config->SetIsa(art::kRuntimeISA);
139
140 std::string zygote;
141 int n = 1;
142 for (; n < argc - 1; ++n) {
143 const char* arg = argv[n];
144 std::string value;
145 if (ArgumentEquals(arg, "--compilation-os-mode")) {
146 config->SetCompilationOsMode(true);
147 } else if (ArgumentMatches(arg, "--dalvik-cache=", &value)) {
148 art::OverrideDalvikCacheSubDirectory(value);
149 config->SetArtifactDirectory(GetApexDataDalvikCacheDirectory(art::InstructionSet::kNone));
150 } else if (ArgumentMatches(arg, "--zygote-arch=", &value)) {
151 zygote = value;
152 } else if (ArgumentMatches(arg, "--boot-image-compiler-filter=", &value)) {
153 config->SetBootImageCompilerFilter(value);
154 } else if (ArgumentMatches(arg, "--system-server-compiler-filter=", &value)) {
155 config->SetSystemServerCompilerFilter(value);
156 } else if (ArgumentMatches(arg, "--staging-dir=", &value)) {
157 // Keep this for compatibility with CompOS in old platforms.
158 LOG(WARNING) << "--staging-dir is deprecated and its value is ignored";
159 } else if (ArgumentEquals(arg, "--dry-run")) {
160 config->SetDryRun();
161 } else if (ArgumentMatches(arg, "--partial-compilation=", &value)) {
162 config->SetPartialCompilation(ParseBool(value) == ParseBoolResult::kTrue);
163 } else if (ArgumentEquals(arg, "--no-refresh")) {
164 config->SetRefresh(false);
165 } else if (ArgumentEquals(arg, "--minimal")) {
166 config->SetMinimal(true);
167 } else if (ArgumentEquals(arg, "--only-boot-images")) {
168 config->SetOnlyBootImages(true);
169 } else {
170 ArgumentError("Unrecognized argument: '%s'", arg);
171 }
172 }
173
174 if (zygote.empty()) {
175 // Use ro.zygote by default, if not overridden by --zygote-arch flag.
176 zygote = GetProperty("ro.zygote", {});
177 }
178 ZygoteKind zygote_kind;
179 if (!ParseZygoteKind(zygote.c_str(), &zygote_kind)) {
180 LOG(FATAL) << "Unknown zygote: " << QuotePath(zygote);
181 }
182 config->SetZygoteKind(zygote_kind);
183
184 if (config->GetSystemServerCompilerFilter().empty()) {
185 std::string filter = GetProperty("dalvik.vm.systemservercompilerfilter", "");
186 filter = GetProperty(kSystemPropertySystemServerCompilerFilterOverride, filter);
187 config->SetSystemServerCompilerFilter(filter);
188 }
189
190 if (!config->HasPartialCompilation() &&
191 ShouldDisablePartialCompilation(
192 GetProperty("ro.build.version.security_patch", /*default_value=*/""))) {
193 config->SetPartialCompilation(false);
194 }
195
196 if (ShouldDisableRefresh(GetProperty("ro.build.version.sdk", /*default_value=*/""))) {
197 config->SetRefresh(false);
198 }
199
200 return n;
201 }
202
GetSystemProperties(std::unordered_map<std::string,std::string> * system_properties)203 void GetSystemProperties(std::unordered_map<std::string, std::string>* system_properties) {
204 SystemPropertyForeach([&](std::string_view name, const char* value) {
205 if (strlen(value) == 0) {
206 return;
207 }
208 for (const char* prefix : kCheckedSystemPropertyPrefixes) {
209 if (name.starts_with(prefix)) {
210 (*system_properties)[std::string(name)] = value;
211 }
212 }
213 });
214 for (const SystemPropertyConfig& system_property_config : *kSystemProperties.get()) {
215 (*system_properties)[system_property_config.name] =
216 GetProperty(system_property_config.name, system_property_config.default_value);
217 }
218 }
219
UsageHelp(const char * argv0)220 NO_RETURN void UsageHelp(const char* argv0) {
221 std::string name(android::base::Basename(argv0));
222 UsageMsg("Usage: %s [OPTION...] ACTION", name.c_str());
223 UsageMsg("On-device refresh tool for boot classpath and system server");
224 UsageMsg("following an update of the ART APEX.");
225 UsageMsg("");
226 UsageMsg("Valid ACTION choices are:");
227 UsageMsg("");
228 UsageMsg("--check Check compilation artifacts are up-to-date based on metadata.");
229 UsageMsg("--compile Compile boot classpath and system_server jars when necessary.");
230 UsageMsg("--force-compile Unconditionally compile the bootclass path and system_server jars.");
231 UsageMsg("--help Display this help information.");
232 UsageMsg("");
233 UsageMsg("Available OPTIONs are:");
234 UsageMsg("");
235 UsageMsg("--dry-run");
236 UsageMsg("--partial-compilation Only generate artifacts that are out-of-date or");
237 UsageMsg(" missing.");
238 UsageMsg("--no-refresh Do not refresh existing artifacts.");
239 UsageMsg("--compilation-os-mode Indicate that odrefresh is running in Compilation");
240 UsageMsg(" OS.");
241 UsageMsg("--dalvik-cache=<DIR> Write artifacts to .../<DIR> rather than");
242 UsageMsg(" .../dalvik-cache");
243 UsageMsg("--zygote-arch=<STRING> Zygote kind that overrides ro.zygote");
244 UsageMsg("--boot-image-compiler-filter=<STRING>");
245 UsageMsg(" Compiler filter for the boot image. Default: ");
246 UsageMsg(" speed-profile");
247 UsageMsg("--system-server-compiler-filter=<STRING>");
248 UsageMsg(" Compiler filter that overrides");
249 UsageMsg(" dalvik.vm.systemservercompilerfilter");
250 UsageMsg("--minimal Generate a minimal boot image only.");
251 UsageMsg("--only-boot-images Generate boot images only.");
252
253 exit(EX_USAGE);
254 }
255
256 } // namespace
257
main(int argc,char ** argv)258 int main(int argc, char** argv) {
259 // odrefresh is launched by `init` which sets the umask of forked processed to
260 // 077 (S_IRWXG | S_IRWXO). This blocks the ability to make files and directories readable
261 // by others and prevents system_server from loading generated artifacts.
262 umask(S_IWGRP | S_IWOTH);
263
264 // Explicitly initialize logging (b/201042799).
265 android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
266
267 OdrConfig config(argv[0]);
268 int n = InitializeConfig(argc, argv, &config);
269
270 art::MemMap::Init(); // Needed by DexFileLoader.
271
272 argv += n;
273 argc -= n;
274 if (argc != 1) {
275 ArgumentError("Expected 1 argument, but have %d.", argc);
276 }
277
278 GetSystemProperties(config.MutableSystemProperties());
279
280 OdrMetrics metrics(config.GetArtifactDirectory());
281 OnDeviceRefresh odr(config);
282
283 std::string_view action(argv[0]);
284 CompilationOptions compilation_options;
285 if (action == "--check") {
286 // Fast determination of whether artifacts are up to date.
287 ExitCode exit_code = odr.CheckArtifactsAreUpToDate(metrics, &compilation_options);
288 // Normally, `--check` should not write metrics. If compilation is not required, there's no need
289 // to write metrics; if compilation is required, `--compile` will write metrics. Therefore,
290 // `--check` should only write metrics when things went wrong.
291 metrics.SetEnabled(exit_code != ExitCode::kOkay && exit_code != ExitCode::kCompilationRequired);
292 return exit_code;
293 } else if (action == "--compile") {
294 ExitCode exit_code = odr.CheckArtifactsAreUpToDate(metrics, &compilation_options);
295 if (exit_code != ExitCode::kCompilationRequired) {
296 // No compilation required, so only write metrics when things went wrong.
297 metrics.SetEnabled(exit_code != ExitCode::kOkay);
298 return exit_code;
299 }
300 if (config.GetSystemProperties().GetBool("dalvik.vm.disable-odrefresh",
301 /*default_value=*/false)) {
302 LOG(INFO) << "Compilation skipped because it's disabled by system property";
303 return ExitCode::kOkay;
304 }
305 OdrCompilationLog compilation_log;
306 if (!compilation_log.ShouldAttemptCompile(metrics.GetTrigger())) {
307 LOG(INFO) << "Compilation skipped because it was attempted recently";
308 return ExitCode::kOkay;
309 }
310 // Compilation required, so always write metrics.
311 metrics.SetEnabled(true);
312 ExitCode compile_result = odr.Compile(metrics, compilation_options);
313 compilation_log.Log(metrics.GetArtApexVersion(),
314 metrics.GetArtApexLastUpdateMillis(),
315 metrics.GetTrigger(),
316 compile_result);
317 return compile_result;
318 } else if (action == "--force-compile") {
319 // Clean-up existing files.
320 if (!odr.RemoveArtifactsDirectory()) {
321 metrics.SetStatus(OdrMetrics::Status::kIoError);
322 return ExitCode::kCleanupFailed;
323 }
324 return odr.Compile(metrics, CompilationOptions::CompileAll(odr));
325 } else if (action == "--help") {
326 UsageHelp(argv[0]);
327 } else {
328 ArgumentError("Unknown argument: %s", action.data());
329 }
330 }
331