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