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 "odrefresh/odrefresh.h"
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <inttypes.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <sysexits.h>
27 #include <time.h>
28 #include <unistd.h>
29
30 #include <algorithm>
31 #include <cstdarg>
32 #include <cstdint>
33 #include <cstdlib>
34 #include <fstream>
35 #include <initializer_list>
36 #include <iosfwd>
37 #include <iostream>
38 #include <memory>
39 #include <optional>
40 #include <ostream>
41 #include <sstream>
42 #include <string>
43 #include <string_view>
44 #include <type_traits>
45 #include <utility>
46 #include <vector>
47
48 #include "android-base/file.h"
49 #include "android-base/logging.h"
50 #include "android-base/macros.h"
51 #include "android-base/properties.h"
52 #include "android-base/stringprintf.h"
53 #include "android-base/strings.h"
54 #include "android/log.h"
55 #include "arch/instruction_set.h"
56 #include "base/file_utils.h"
57 #include "base/globals.h"
58 #include "base/macros.h"
59 #include "base/os.h"
60 #include "base/string_view_cpp20.h"
61 #include "base/unix_file/fd_file.h"
62 #include "com_android_apex.h"
63 #include "com_android_art.h"
64 #include "dex/art_dex_file_loader.h"
65 #include "dexoptanalyzer.h"
66 #include "exec_utils.h"
67 #include "log/log.h"
68 #include "palette/palette.h"
69 #include "palette/palette_types.h"
70
71 #include "odr_artifacts.h"
72 #include "odr_compilation_log.h"
73 #include "odr_config.h"
74 #include "odr_fs_utils.h"
75 #include "odr_metrics.h"
76
77 namespace art {
78 namespace odrefresh {
79
80 namespace apex = com::android::apex;
81 namespace art_apex = com::android::art;
82
83 namespace {
84
85 // Name of cache info file in the ART Apex artifact cache.
86 static constexpr const char* kCacheInfoFile = "cache-info.xml";
87
UsageErrorV(const char * fmt,va_list ap)88 static void UsageErrorV(const char* fmt, va_list ap) {
89 std::string error;
90 android::base::StringAppendV(&error, fmt, ap);
91 if (isatty(fileno(stderr))) {
92 std::cerr << error << std::endl;
93 } else {
94 LOG(ERROR) << error;
95 }
96 }
97
UsageError(const char * fmt,...)98 static void UsageError(const char* fmt, ...) {
99 va_list ap;
100 va_start(ap, fmt);
101 UsageErrorV(fmt, ap);
102 va_end(ap);
103 }
104
ArgumentError(const char * fmt,...)105 NO_RETURN static void ArgumentError(const char* fmt, ...) {
106 va_list ap;
107 va_start(ap, fmt);
108 UsageErrorV(fmt, ap);
109 va_end(ap);
110 UsageError("Try '--help' for more information.");
111 exit(EX_USAGE);
112 }
113
UsageHelp(const char * argv0)114 NO_RETURN static void UsageHelp(const char* argv0) {
115 std::string name(android::base::Basename(argv0));
116 UsageError("Usage: %s ACTION", name.c_str());
117 UsageError("On-device refresh tool for boot class path extensions and system server");
118 UsageError("following an update of the ART APEX.");
119 UsageError("");
120 UsageError("Valid ACTION choices are:");
121 UsageError("");
122 UsageError(
123 "--check Check compilation artifacts are up-to-date based on metadata (fast).");
124 UsageError("--compile Compile boot class path extensions and system_server jars");
125 UsageError(" when necessary.");
126 UsageError("--force-compile Unconditionally compile the boot class path extensions and");
127 UsageError(" system_server jars.");
128 UsageError("--verify Verify artifacts are up-to-date with dexoptanalyzer (slow).");
129 UsageError("--help Display this help information.");
130 exit(EX_USAGE);
131 }
132
Concatenate(std::initializer_list<std::string_view> args)133 static std::string Concatenate(std::initializer_list<std::string_view> args) {
134 std::stringstream ss;
135 for (auto arg : args) {
136 ss << arg;
137 }
138 return ss.str();
139 }
140
GetEnvironmentVariableOrDie(const char * name)141 static std::string GetEnvironmentVariableOrDie(const char* name) {
142 const char* value = getenv(name);
143 LOG_ALWAYS_FATAL_IF(value == nullptr, "%s is not defined.", name);
144 return value;
145 }
146
QuotePath(std::string_view path)147 static std::string QuotePath(std::string_view path) {
148 return Concatenate({"'", path, "'"});
149 }
150
EraseFiles(const std::vector<std::unique_ptr<File>> & files)151 static void EraseFiles(const std::vector<std::unique_ptr<File>>& files) {
152 for (auto& file : files) {
153 file->Erase(/*unlink=*/true);
154 }
155 }
156
157 // Moves `files` to the directory `output_directory_path`.
158 //
159 // If any of the files cannot be moved, then all copies of the files are removed from both
160 // the original location and the output location.
161 //
162 // Returns true if all files are moved, false otherwise.
MoveOrEraseFiles(const std::vector<std::unique_ptr<File>> & files,std::string_view output_directory_path)163 static bool MoveOrEraseFiles(const std::vector<std::unique_ptr<File>>& files,
164 std::string_view output_directory_path) {
165 std::vector<std::unique_ptr<File>> output_files;
166 for (auto& file : files) {
167 const std::string file_basename(android::base::Basename(file->GetPath()));
168 const std::string output_file_path = Concatenate({output_directory_path, "/", file_basename});
169 const std::string input_file_path = file->GetPath();
170
171 output_files.emplace_back(OS::CreateEmptyFileWriteOnly(output_file_path.c_str()));
172 if (output_files.back() == nullptr) {
173 PLOG(ERROR) << "Failed to open " << QuotePath(output_file_path);
174 output_files.pop_back();
175 EraseFiles(output_files);
176 EraseFiles(files);
177 return false;
178 }
179
180 static constexpr mode_t kFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
181 if (fchmod(output_files.back()->Fd(), kFileMode) != 0) {
182 PLOG(ERROR) << "Could not set file mode on " << QuotePath(output_file_path);
183 EraseFiles(output_files);
184 EraseFiles(files);
185 return false;
186 }
187
188 const size_t file_bytes = file->GetLength();
189 if (!output_files.back()->Copy(file.get(), /*offset=*/0, file_bytes)) {
190 PLOG(ERROR) << "Failed to copy " << QuotePath(file->GetPath())
191 << " to " << QuotePath(output_file_path);
192 EraseFiles(output_files);
193 EraseFiles(files);
194 return false;
195 }
196
197 if (!file->Erase(/*unlink=*/true)) {
198 PLOG(ERROR) << "Failed to erase " << QuotePath(file->GetPath());
199 EraseFiles(output_files);
200 EraseFiles(files);
201 return false;
202 }
203
204 if (output_files.back()->FlushCloseOrErase() != 0) {
205 PLOG(ERROR) << "Failed to flush and close file " << QuotePath(output_file_path);
206 EraseFiles(output_files);
207 EraseFiles(files);
208 return false;
209 }
210 }
211 return true;
212 }
213
214 } // namespace
215
ParseZygoteKind(const char * input,ZygoteKind * zygote_kind)216 bool ParseZygoteKind(const char* input, ZygoteKind* zygote_kind) {
217 std::string_view z(input);
218 if (z == "zygote32") {
219 *zygote_kind = ZygoteKind::kZygote32;
220 return true;
221 } else if (z == "zygote32_64") {
222 *zygote_kind = ZygoteKind::kZygote32_64;
223 return true;
224 } else if (z == "zygote64_32") {
225 *zygote_kind = ZygoteKind::kZygote64_32;
226 return true;
227 } else if (z == "zygote64") {
228 *zygote_kind = ZygoteKind::kZygote64;
229 return true;
230 }
231 return false;
232 }
233
234 class OnDeviceRefresh final {
235 private:
236 // Maximum execution time for odrefresh from start to end.
237 static constexpr time_t kMaximumExecutionSeconds = 300;
238
239 // Maximum execution time for any child process spawned.
240 static constexpr time_t kMaxChildProcessSeconds = 90;
241
242 // Configuration to use.
243 const OdrConfig& config_;
244
245 // Path to cache information file that is used to speed up artifact checking.
246 const std::string cache_info_filename_;
247
248 // List of boot extension components that should be compiled.
249 std::vector<std::string> boot_extension_compilable_jars_;
250
251 // List of system_server components that should be compiled.
252 std::vector<std::string> systemserver_compilable_jars_;
253
254 const time_t start_time_;
255
256 public:
OnDeviceRefresh(const OdrConfig & config)257 explicit OnDeviceRefresh(const OdrConfig& config)
258 : config_{config},
259 cache_info_filename_{Concatenate({kOdrefreshArtifactDirectory, "/", kCacheInfoFile})},
260 start_time_{time(nullptr)} {
261 for (const std::string& jar : android::base::Split(config_.GetDex2oatBootClasspath(), ":")) {
262 // Boot class path extensions are those not in the ART APEX. Updatable APEXes should not
263 // have DEX files in the DEX2OATBOOTCLASSPATH. At the time of writing i18n is a non-updatable
264 // APEX and so does appear in the DEX2OATBOOTCLASSPATH.
265 if (!LocationIsOnArtModule(jar)) {
266 boot_extension_compilable_jars_.emplace_back(jar);
267 }
268 }
269
270 for (const std::string& jar : android::base::Split(config_.GetSystemServerClasspath(), ":")) {
271 // Only consider DEX files on the SYSTEMSERVERCLASSPATH for compilation that do not reside
272 // in APEX modules. Otherwise, we'll recompile on boot any time one of these APEXes updates.
273 if (!LocationIsOnApex(jar)) {
274 systemserver_compilable_jars_.emplace_back(jar);
275 }
276 }
277 }
278
GetExecutionTimeUsed() const279 time_t GetExecutionTimeUsed() const { return time(nullptr) - start_time_; }
280
GetExecutionTimeRemaining() const281 time_t GetExecutionTimeRemaining() const {
282 return kMaximumExecutionSeconds - GetExecutionTimeUsed();
283 }
284
GetSubprocessTimeout() const285 time_t GetSubprocessTimeout() const {
286 return std::max(GetExecutionTimeRemaining(), kMaxChildProcessSeconds);
287 }
288
289 // Gets the `ApexInfo` associated with the currently active ART APEX.
GetArtApexInfo() const290 std::optional<apex::ApexInfo> GetArtApexInfo() const {
291 auto info_list = apex::readApexInfoList(config_.GetApexInfoListFile().c_str());
292 if (!info_list.has_value()) {
293 return {};
294 }
295
296 for (const apex::ApexInfo& info : info_list->getApexInfo()) {
297 if (info.getIsActive() && info.getModuleName() == "com.android.art") {
298 return info;
299 }
300 }
301 return {};
302 }
303
304 // Reads the ART APEX cache information (if any) found in `kOdrefreshArtifactDirectory`.
ReadCacheInfo()305 std::optional<art_apex::CacheInfo> ReadCacheInfo() {
306 return art_apex::read(cache_info_filename_.c_str());
307 }
308
309 // Write ART APEX cache information to `kOdrefreshArtifactDirectory`.
WriteCacheInfo() const310 void WriteCacheInfo() const {
311 if (OS::FileExists(cache_info_filename_.c_str())) {
312 if (unlink(cache_info_filename_.c_str()) != 0) {
313 PLOG(ERROR) << "Failed to unlink() file " << QuotePath(cache_info_filename_);
314 }
315 }
316
317 const std::string dir_name = android::base::Dirname(cache_info_filename_);
318 if (!EnsureDirectoryExists(dir_name)) {
319 LOG(ERROR) << "Could not create directory: " << QuotePath(dir_name);
320 return;
321 }
322
323 std::optional<art_apex::ArtModuleInfo> art_module_info = GenerateArtModuleInfo();
324 if (!art_module_info.has_value()) {
325 LOG(ERROR) << "Unable to generate cache provenance";
326 return;
327 }
328
329 // There can be only one CacheProvence in the XML file, but `xsdc` does not have
330 // minOccurs/maxOccurs in the xsd schema.
331 const std::vector<art_apex::ArtModuleInfo> art_module_infos { art_module_info.value() };
332
333 std::optional<std::vector<art_apex::Component>> bcp_components =
334 GenerateBootExtensionComponents();
335 if (!bcp_components.has_value()) {
336 LOG(ERROR) << "No boot classpath extension components.";
337 return;
338 }
339
340 std::optional<std::vector<art_apex::Component>> system_server_components =
341 GenerateSystemServerComponents();
342 if (!system_server_components.has_value()) {
343 LOG(ERROR) << "No system_server extension components.";
344 return;
345 }
346
347 std::ofstream out(cache_info_filename_.c_str());
348 art_apex::CacheInfo info{art_module_infos,
349 {{art_apex::Dex2oatBootClasspath{bcp_components.value()}}},
350 {{art_apex::SystemServerClasspath{system_server_components.value()}}}};
351
352 art_apex::write(out, info);
353 }
354
355 // Returns cache provenance information based on the current ART APEX version and filesystem
356 // information.
GenerateArtModuleInfo() const357 std::optional<art_apex::ArtModuleInfo> GenerateArtModuleInfo() const {
358 auto info = GetArtApexInfo();
359 if (!info.has_value()) {
360 LOG(ERROR) << "Could not update " << QuotePath(cache_info_filename_) << " : no ART Apex info";
361 return {};
362 }
363 // The lastUpdateMillis is an addition to ApexInfoList.xsd to support samegrade installs.
364 int64_t last_update_millis = info->hasLastUpdateMillis() ? info->getLastUpdateMillis() : 0;
365 return art_apex::ArtModuleInfo{
366 info->getVersionCode(), info->getVersionName(), last_update_millis};
367 }
368
CheckComponents(const std::vector<art_apex::Component> & expected_components,const std::vector<art_apex::Component> & actual_components,std::string * error_msg) const369 bool CheckComponents(const std::vector<art_apex::Component>& expected_components,
370 const std::vector<art_apex::Component>& actual_components,
371 std::string* error_msg) const {
372 if (expected_components.size() != actual_components.size()) {
373 return false;
374 }
375
376 for (size_t i = 0; i < expected_components.size(); ++i) {
377 const art_apex::Component& expected = expected_components[i];
378 const art_apex::Component& actual = actual_components[i];
379
380 if (expected.getFile() != actual.getFile()) {
381 *error_msg = android::base::StringPrintf("Component %zu file differs ('%s' != '%s')",
382 i,
383 expected.getFile().c_str(),
384 actual.getFile().c_str());
385 return false;
386 }
387 if (expected.getSize() != actual.getSize()) {
388 *error_msg = android::base::StringPrintf("Component %zu size differs (%" PRIu64
389 " != %" PRIu64 ")",
390 i,
391 expected.getSize(),
392 actual.getSize());
393 return false;
394 }
395 if (expected.getChecksums() != actual.getChecksums()) {
396 *error_msg = android::base::StringPrintf("Component %zu checksums differ ('%s' != '%s')",
397 i,
398 expected.getChecksums().c_str(),
399 actual.getChecksums().c_str());
400 return false;
401 }
402 }
403
404 return true;
405 }
406
GenerateComponents(const std::vector<std::string> & jars) const407 std::vector<art_apex::Component> GenerateComponents(const std::vector<std::string>& jars) const {
408 std::vector<art_apex::Component> components;
409
410 ArtDexFileLoader loader;
411 for (const std::string& path : jars) {
412 struct stat sb;
413 if (stat(path.c_str(), &sb) == -1) {
414 PLOG(ERROR) << "Failed to get component: " << QuotePath(path);
415 return {};
416 }
417
418 std::vector<uint32_t> checksums;
419 std::vector<std::string> dex_locations;
420 std::string error_msg;
421 if (!loader.GetMultiDexChecksums(path.c_str(), &checksums, &dex_locations, &error_msg)) {
422 LOG(ERROR) << "Failed to get components: " << error_msg;
423 return {};
424 }
425
426 std::ostringstream oss;
427 for (size_t i = 0; i < checksums.size(); ++i) {
428 if (i != 0) {
429 oss << ';';
430 }
431 oss << android::base::StringPrintf("%08x", checksums[i]);
432 }
433 const std::string checksum = oss.str();
434
435 components.emplace_back(
436 art_apex::Component{path, static_cast<uint64_t>(sb.st_size), checksum});
437 }
438
439 return components;
440 }
441
GenerateBootExtensionComponents() const442 std::vector<art_apex::Component> GenerateBootExtensionComponents() const {
443 return GenerateComponents(boot_extension_compilable_jars_);
444 }
445
GenerateSystemServerComponents() const446 std::vector<art_apex::Component> GenerateSystemServerComponents() const {
447 return GenerateComponents(systemserver_compilable_jars_);
448 }
449
450 // Checks whether a group of artifacts exists. Returns true if all are present, false otherwise.
ArtifactsExist(const OdrArtifacts & artifacts,std::string * error_msg)451 static bool ArtifactsExist(const OdrArtifacts& artifacts, /*out*/ std::string* error_msg) {
452 const auto paths = {
453 artifacts.ImagePath().c_str(), artifacts.OatPath().c_str(), artifacts.VdexPath().c_str()};
454 for (const char* path : paths) {
455 if (!OS::FileExists(path)) {
456 if (errno == EACCES) {
457 PLOG(ERROR) << "Failed to stat() " << path;
458 }
459 *error_msg = "Missing file: " + QuotePath(path);
460 return false;
461 }
462 }
463 return true;
464 }
465
466 // Checks whether all boot extension artifacts are present on /data. Returns true if all are
467 // present, false otherwise.
BootExtensionArtifactsExistOnData(const InstructionSet isa,std::string * error_msg) const468 WARN_UNUSED bool BootExtensionArtifactsExistOnData(const InstructionSet isa,
469 /*out*/ std::string* error_msg) const {
470 const std::string apexdata_image_location = GetBootImageExtensionImagePath(isa);
471 const OdrArtifacts artifacts = OdrArtifacts::ForBootImageExtension(apexdata_image_location);
472 return ArtifactsExist(artifacts, error_msg);
473 }
474
475 // Checks whether all system_server artifacts are present on /data. The artifacts are checked in
476 // their order of compilation. Returns true if all are present, false otherwise.
SystemServerArtifactsExistOnData(std::string * error_msg) const477 WARN_UNUSED bool SystemServerArtifactsExistOnData(/*out*/ std::string* error_msg) const {
478 for (const std::string& jar_path : systemserver_compilable_jars_) {
479 const std::string image_location = GetSystemServerImagePath(/*on_system=*/false, jar_path);
480 const OdrArtifacts artifacts = OdrArtifacts::ForSystemServer(image_location);
481 if (!ArtifactsExist(artifacts, error_msg)) {
482 return false;
483 }
484 }
485 return true;
486 }
487
CheckArtifactsAreUpToDate(OdrMetrics & metrics)488 WARN_UNUSED ExitCode CheckArtifactsAreUpToDate(OdrMetrics& metrics) {
489 metrics.SetStage(OdrMetrics::Stage::kCheck);
490
491 // Clean-up helper used to simplify clean-ups and handling failures there.
492 auto cleanup_return = [this](ExitCode exit_code) {
493 return CleanApexdataDirectory() ? exit_code : ExitCode::kCleanupFailed;
494 };
495
496 const auto apex_info = GetArtApexInfo();
497 if (!apex_info.has_value()) {
498 // This should never happen, further up-to-date checks are not possible if it does.
499 LOG(ERROR) << "Could not get ART APEX info.";
500 metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
501 return cleanup_return(ExitCode::kCompilationRequired);
502 }
503
504 // Generate current module info for the current ART APEX.
505 const auto current_info = GenerateArtModuleInfo();
506 if (!current_info.has_value()) {
507 // This should never happen, further up-to-date checks are not possible if it does.
508 LOG(ERROR) << "Failed to generate cache provenance.";
509 metrics.SetTrigger(OdrMetrics::Trigger::kUnknown);
510 return cleanup_return(ExitCode::kCompilationRequired);
511 }
512
513 // Record ART APEX version for metrics reporting.
514 metrics.SetArtApexVersion(current_info->getVersionCode());
515
516 // Record ART APEX last update milliseconds (used in compilation log).
517 metrics.SetArtApexLastUpdateMillis(current_info->getLastUpdateMillis());
518
519 if (apex_info->getIsFactory()) {
520 // Remove any artifacts on /data as they are not necessary and no compilation is necessary.
521 LOG(INFO) << "Factory APEX mounted.";
522 return cleanup_return(ExitCode::kOkay);
523 }
524
525 if (!OS::FileExists(cache_info_filename_.c_str())) {
526 // If the cache info file does not exist, assume compilation is required because the
527 // file is missing and because the current ART APEX is not factory installed.
528 PLOG(ERROR) << "No prior cache-info file: " << QuotePath(cache_info_filename_);
529 metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
530 return cleanup_return(ExitCode::kCompilationRequired);
531 }
532
533 // Get and parse the ART APEX cache info file.
534 std::optional<art_apex::CacheInfo> cache_info = ReadCacheInfo();
535 if (!cache_info.has_value()) {
536 // This should never happen, further up-to-date checks are not possible if it does.
537 PLOG(ERROR) << "Failed to read cache-info file: " << QuotePath(cache_info_filename_);
538 metrics.SetTrigger(OdrMetrics::Trigger::kUnknown);
539 return cleanup_return(ExitCode::kCompilationRequired);
540 }
541
542 // Check whether the current cache ART module info differs from the current ART module info.
543 // Always check APEX version.
544 const auto cached_info = cache_info->getFirstArtModuleInfo();
545
546 if (cached_info->getVersionCode() != current_info->getVersionCode()) {
547 LOG(INFO) << "ART APEX version code mismatch ("
548 << cached_info->getVersionCode()
549 << " != " << current_info->getVersionCode() << ").";
550 metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
551 return cleanup_return(ExitCode::kCompilationRequired);
552 }
553
554 if (cached_info->getVersionName() != current_info->getVersionName()) {
555 LOG(INFO) << "ART APEX version name mismatch ("
556 << cached_info->getVersionName()
557 << " != " << current_info->getVersionName() << ").";
558 metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
559 return cleanup_return(ExitCode::kCompilationRequired);
560 }
561
562 // Check lastUpdateMillis for samegrade installs. If `cached_info` is missing lastUpdateMillis
563 // then it is not current with the schema used by this binary so treat it as a samegrade
564 // update. Otherwise check whether the lastUpdateMillis changed.
565 if (!cached_info->hasLastUpdateMillis() ||
566 cached_info->getLastUpdateMillis() != current_info->getLastUpdateMillis()) {
567 LOG(INFO) << "ART APEX last update time mismatch ("
568 << cached_info->getLastUpdateMillis()
569 << " != " << current_info->getLastUpdateMillis() << ").";
570 metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
571 return cleanup_return(ExitCode::kCompilationRequired);
572 }
573
574 // Check boot class components.
575 //
576 // This checks the size and checksums of odrefresh compilable files on the DEX2OATBOOTCLASSPATH
577 // (the Odrefresh constructor determines which files are compilable). If the number of files
578 // there changes, or their size or checksums change then compilation will be triggered.
579 //
580 // The boot class components may change unexpectedly, for example an OTA could update
581 // framework.jar.
582 const std::vector<art_apex::Component> expected_bcp_components =
583 GenerateBootExtensionComponents();
584 if (expected_bcp_components.size() != 0 &&
585 (!cache_info->hasDex2oatBootClasspath() ||
586 !cache_info->getFirstDex2oatBootClasspath()->hasComponent())) {
587 LOG(INFO) << "Missing Dex2oatBootClasspath components.";
588 metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
589 return cleanup_return(ExitCode::kCompilationRequired);
590 }
591
592 std::string error_msg;
593 const std::vector<art_apex::Component>& bcp_components =
594 cache_info->getFirstDex2oatBootClasspath()->getComponent();
595 if (!CheckComponents(expected_bcp_components, bcp_components, &error_msg)) {
596 LOG(INFO) << "Dex2OatClasspath components mismatch: " << error_msg;
597 metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
598 return cleanup_return(ExitCode::kCompilationRequired);
599 }
600
601 // Check system server components.
602 //
603 // This checks the size and checksums of odrefresh compilable files on the
604 // SYSTEMSERVERCLASSPATH (the Odrefresh constructor determines which files are compilable). If
605 // the number of files there changes, or their size or checksums change then compilation will be
606 // triggered.
607 //
608 // The system_server components may change unexpectedly, for example an OTA could update
609 // services.jar.
610 auto cleanup_system_server_return = [this](ExitCode exit_code) {
611 return RemoveSystemServerArtifactsFromData() ? exit_code : ExitCode::kCleanupFailed;
612 };
613
614 const std::vector<art_apex::Component> expected_system_server_components =
615 GenerateSystemServerComponents();
616 if (expected_system_server_components.size() != 0 &&
617 (!cache_info->hasSystemServerClasspath() ||
618 !cache_info->getFirstSystemServerClasspath()->hasComponent())) {
619 LOG(INFO) << "Missing SystemServerClasspath components.";
620 metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
621 return cleanup_system_server_return(ExitCode::kCompilationRequired);
622 }
623
624 const std::vector<art_apex::Component>& system_server_components =
625 cache_info->getFirstSystemServerClasspath()->getComponent();
626 if (!CheckComponents(expected_system_server_components, system_server_components, &error_msg)) {
627 LOG(INFO) << "SystemServerClasspath components mismatch: " << error_msg;
628 metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
629 return cleanup_system_server_return(ExitCode::kCompilationRequired);
630 }
631
632 // Cache info looks good, check all compilation artifacts exist.
633 auto cleanup_boot_extensions_return = [this](ExitCode exit_code, InstructionSet isa) {
634 return RemoveBootExtensionArtifactsFromData(isa) ? exit_code : ExitCode::kCleanupFailed;
635 };
636
637 for (const InstructionSet isa : config_.GetBootExtensionIsas()) {
638 if (!BootExtensionArtifactsExistOnData(isa, &error_msg)) {
639 LOG(INFO) << "Incomplete boot extension artifacts. " << error_msg;
640 metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
641 return cleanup_boot_extensions_return(ExitCode::kCompilationRequired, isa);
642 }
643 }
644
645 if (!SystemServerArtifactsExistOnData(&error_msg)) {
646 LOG(INFO) << "Incomplete system_server artifacts. " << error_msg;
647 // No clean-up is required here: we have boot extension artifacts. The method
648 // `SystemServerArtifactsExistOnData()` checks in compilation order so it is possible some of
649 // the artifacts are here. We likely ran out of space compiling the system_server artifacts.
650 // Any artifacts present are usable.
651 metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
652 return ExitCode::kCompilationRequired;
653 }
654
655 return ExitCode::kOkay;
656 }
657
AddDex2OatCommonOptions(std::vector<std::string> * args)658 static void AddDex2OatCommonOptions(/*inout*/ std::vector<std::string>* args) {
659 args->emplace_back("--android-root=out/empty");
660 args->emplace_back("--abort-on-hard-verifier-error");
661 args->emplace_back("--no-abort-on-soft-verifier-error");
662 args->emplace_back("--compilation-reason=boot");
663 args->emplace_back("--image-format=lz4");
664 args->emplace_back("--force-determinism");
665 args->emplace_back("--resolve-startup-const-strings=true");
666 }
667
AddDex2OatConcurrencyArguments(std::vector<std::string> * args)668 static void AddDex2OatConcurrencyArguments(/*inout*/ std::vector<std::string>* args) {
669 static constexpr std::pair<const char*, const char*> kPropertyArgPairs[] = {
670 std::make_pair("dalvik.vm.boot-dex2oat-cpu-set", "--cpu-set="),
671 std::make_pair("dalvik.vm.boot-dex2oat-threads", "-j"),
672 };
673 for (auto property_arg_pair : kPropertyArgPairs) {
674 auto [property, arg] = property_arg_pair;
675 std::string value = android::base::GetProperty(property, {});
676 if (!value.empty()) {
677 args->push_back(arg + value);
678 }
679 }
680 }
681
AddDex2OatDebugInfo(std::vector<std::string> * args)682 static void AddDex2OatDebugInfo(/*inout*/ std::vector<std::string>* args) {
683 args->emplace_back("--generate-mini-debug-info");
684 args->emplace_back("--strip");
685 }
686
AddDex2OatInstructionSet(std::vector<std::string> * args,InstructionSet isa)687 static void AddDex2OatInstructionSet(/*inout*/ std::vector<std::string>* args,
688 InstructionSet isa) {
689 const char* isa_str = GetInstructionSetString(isa);
690 args->emplace_back(Concatenate({"--instruction-set=", isa_str}));
691 }
692
693
AddDex2OatProfileAndCompilerFilter(std::vector<std::string> * args,const std::string & profile_file)694 static void AddDex2OatProfileAndCompilerFilter(/*inout*/ std::vector<std::string>* args,
695 const std::string& profile_file) {
696 if (OS::FileExists(profile_file.c_str(), /*check_file_type=*/true)) {
697 args->emplace_back(Concatenate({"--profile-file=", profile_file}));
698 args->emplace_back("--compiler-filter=speed-profile");
699 } else {
700 args->emplace_back("--compiler-filter=speed");
701 }
702 }
703
VerifySystemServerArtifactsAreUpToDate(bool on_system) const704 WARN_UNUSED bool VerifySystemServerArtifactsAreUpToDate(bool on_system) const {
705 std::vector<std::string> classloader_context;
706 for (const std::string& jar_path : systemserver_compilable_jars_) {
707 std::vector<std::string> args;
708 args.emplace_back(config_.GetDexOptAnalyzer());
709 args.emplace_back("--dex-file=" + jar_path);
710
711 const std::string image_location = GetSystemServerImagePath(on_system, jar_path);
712
713 // odrefresh produces app-image files, but these are not guaranteed for those pre-installed
714 // on /system.
715 if (!on_system && !OS::FileExists(image_location.c_str(), true)) {
716 LOG(INFO) << "Missing image file: " << QuotePath(image_location);
717 return false;
718 }
719
720 // Generate set of artifacts that are output by compilation.
721 OdrArtifacts artifacts = OdrArtifacts::ForSystemServer(image_location);
722 if (!on_system) {
723 CHECK_EQ(artifacts.OatPath(),
724 GetApexDataOdexFilename(jar_path, config_.GetSystemServerIsa()));
725 CHECK_EQ(artifacts.ImagePath(),
726 GetApexDataDalvikCacheFilename(jar_path, config_.GetSystemServerIsa(), "art"));
727 CHECK_EQ(artifacts.OatPath(),
728 GetApexDataDalvikCacheFilename(jar_path, config_.GetSystemServerIsa(), "odex"));
729 CHECK_EQ(artifacts.VdexPath(),
730 GetApexDataDalvikCacheFilename(jar_path, config_.GetSystemServerIsa(), "vdex"));
731 }
732
733 // Associate inputs and outputs with dexoptanalyzer arguments.
734 std::pair<const std::string, const char*> location_args[] = {
735 std::make_pair(artifacts.OatPath(), "--oat-fd="),
736 std::make_pair(artifacts.VdexPath(), "--vdex-fd="),
737 std::make_pair(jar_path, "--zip-fd=")
738 };
739
740 // Open file descriptors for dexoptanalyzer file inputs and add to the command-line.
741 std::vector<std::unique_ptr<File>> files;
742 for (const auto& location_arg : location_args) {
743 auto& [location, arg] = location_arg;
744 std::unique_ptr<File> file(OS::OpenFileForReading(location.c_str()));
745 if (file == nullptr) {
746 PLOG(ERROR) << "Failed to open \"" << location << "\"";
747 return false;
748 }
749 args.emplace_back(android::base::StringPrintf("%s%d", arg, file->Fd()));
750 files.emplace_back(file.release());
751 }
752
753 const std::string basename(android::base::Basename(jar_path));
754 const std::string root = GetAndroidRoot();
755 const std::string profile_file = Concatenate({root, "/framework/", basename, ".prof"});
756 if (OS::FileExists(profile_file.c_str())) {
757 args.emplace_back("--compiler-filter=speed-profile");
758 } else {
759 args.emplace_back("--compiler-filter=speed");
760 }
761
762 args.emplace_back(
763 Concatenate({"--image=", GetBootImage(), ":", GetBootImageExtensionImage(on_system)}));
764 args.emplace_back(
765 Concatenate({"--isa=", GetInstructionSetString(config_.GetSystemServerIsa())}));
766 args.emplace_back("--runtime-arg");
767 args.emplace_back(Concatenate({"-Xbootclasspath:", config_.GetDex2oatBootClasspath()}));
768 args.emplace_back(Concatenate(
769 {"--class-loader-context=PCL[", android::base::Join(classloader_context, ':'), "]"}));
770
771 classloader_context.emplace_back(jar_path);
772
773 LOG(INFO) << "Checking " << jar_path << ": " << android::base::Join(args, ' ');
774 std::string error_msg;
775 bool timed_out = false;
776 const time_t timeout = GetSubprocessTimeout();
777 const int dexoptanalyzer_result = ExecAndReturnCode(args, timeout, &timed_out, &error_msg);
778 if (dexoptanalyzer_result == -1) {
779 LOG(ERROR) << "Unexpected exit from dexoptanalyzer: " << error_msg;
780 if (timed_out) {
781 // TODO(oth): record metric for timeout.
782 }
783 return false;
784 }
785 LOG(INFO) << "dexoptanalyzer returned " << dexoptanalyzer_result;
786
787 bool unexpected_result = true;
788 switch (static_cast<dexoptanalyzer::ReturnCode>(dexoptanalyzer_result)) {
789 case art::dexoptanalyzer::ReturnCode::kNoDexOptNeeded:
790 unexpected_result = false;
791 break;
792
793 // Recompile needed
794 case art::dexoptanalyzer::ReturnCode::kDex2OatFromScratch:
795 case art::dexoptanalyzer::ReturnCode::kDex2OatForBootImageOat:
796 case art::dexoptanalyzer::ReturnCode::kDex2OatForFilterOat:
797 case art::dexoptanalyzer::ReturnCode::kDex2OatForBootImageOdex:
798 case art::dexoptanalyzer::ReturnCode::kDex2OatForFilterOdex:
799 return false;
800
801 // Unexpected issues (note no default-case here to catch missing enum values, but the
802 // return code from dexoptanalyzer may also be outside expected values, such as a
803 // process crash.
804 case art::dexoptanalyzer::ReturnCode::kFlattenClassLoaderContextSuccess:
805 case art::dexoptanalyzer::ReturnCode::kErrorInvalidArguments:
806 case art::dexoptanalyzer::ReturnCode::kErrorCannotCreateRuntime:
807 case art::dexoptanalyzer::ReturnCode::kErrorUnknownDexOptNeeded:
808 break;
809 }
810
811 if (unexpected_result) {
812 LOG(ERROR) << "Unexpected result from dexoptanalyzer: " << dexoptanalyzer_result;
813 return false;
814 }
815 }
816 return true;
817 }
818
RemoveSystemServerArtifactsFromData() const819 WARN_UNUSED bool RemoveSystemServerArtifactsFromData() const {
820 if (config_.GetDryRun()) {
821 LOG(INFO) << "Removal of system_server artifacts on /data skipped (dry-run).";
822 return true;
823 }
824
825 bool success = true;
826 for (const std::string& jar_path : systemserver_compilable_jars_) {
827 const std::string image_location =
828 GetSystemServerImagePath(/*on_system=*/false, jar_path);
829 const OdrArtifacts artifacts = OdrArtifacts::ForSystemServer(image_location);
830 LOG(INFO) << "Removing system_server artifacts on /data for " << QuotePath(jar_path);
831 success &= RemoveArtifacts(artifacts);
832 }
833 return success;
834 }
835
836 // Verify the validity of system server artifacts on both /system and /data.
837 // This method has the side-effect of removing system server artifacts on /data, if there are
838 // valid artifacts on /system, or if the artifacts on /data are not valid.
839 // Returns true if valid artifacts are found.
VerifySystemServerArtifactsAreUpToDate() const840 WARN_UNUSED bool VerifySystemServerArtifactsAreUpToDate() const {
841 bool system_ok = VerifySystemServerArtifactsAreUpToDate(/*on_system=*/true);
842 LOG(INFO) << "system_server artifacts on /system are " << (system_ok ? "ok" : "stale");
843 bool data_ok = VerifySystemServerArtifactsAreUpToDate(/*on_system=*/false);
844 LOG(INFO) << "system_server artifacts on /data are " << (data_ok ? "ok" : "stale");
845 return system_ok || data_ok;
846 }
847
848 // Check the validity of boot class path extension artifacts.
849 //
850 // Returns true if artifacts exist and are valid according to dexoptanalyzer.
VerifyBootExtensionArtifactsAreUpToDate(const InstructionSet isa,bool on_system) const851 WARN_UNUSED bool VerifyBootExtensionArtifactsAreUpToDate(const InstructionSet isa,
852 bool on_system) const {
853 const std::string dex_file = boot_extension_compilable_jars_.front();
854 const std::string image_location = GetBootImageExtensionImage(on_system);
855
856 std::vector<std::string> args;
857 args.emplace_back(config_.GetDexOptAnalyzer());
858 args.emplace_back("--validate-bcp");
859 args.emplace_back(Concatenate({"--image=", GetBootImage(), ":", image_location}));
860 args.emplace_back(Concatenate({"--isa=", GetInstructionSetString(isa)}));
861 args.emplace_back("--runtime-arg");
862 args.emplace_back(Concatenate({"-Xbootclasspath:", config_.GetDex2oatBootClasspath()}));
863
864 LOG(INFO) << "Checking " << dex_file << ": " << android::base::Join(args, ' ');
865
866 std::string error_msg;
867 bool timed_out = false;
868 const time_t timeout = GetSubprocessTimeout();
869 const int dexoptanalyzer_result = ExecAndReturnCode(args, timeout, &timed_out, &error_msg);
870 if (dexoptanalyzer_result == -1) {
871 LOG(ERROR) << "Unexpected exit from dexoptanalyzer: " << error_msg;
872 if (timed_out) {
873 // TODO(oth): record metric for timeout.
874 }
875 return false;
876 }
877 auto rc = static_cast<dexoptanalyzer::ReturnCode>(dexoptanalyzer_result);
878 if (rc == dexoptanalyzer::ReturnCode::kNoDexOptNeeded) {
879 return true;
880 }
881 return false;
882 }
883
884 // Remove boot extension artifacts from /data.
RemoveBootExtensionArtifactsFromData(InstructionSet isa) const885 WARN_UNUSED bool RemoveBootExtensionArtifactsFromData(InstructionSet isa) const {
886 if (config_.GetDryRun()) {
887 LOG(INFO) << "Removal of bcp extension artifacts on /data skipped (dry-run).";
888 return true;
889 }
890
891 bool success = true;
892 if (isa == config_.GetSystemServerIsa()) {
893 // system_server artifacts are invalid without boot extension artifacts.
894 success &= RemoveSystemServerArtifactsFromData();
895 }
896
897 const std::string apexdata_image_location = GetBootImageExtensionImagePath(isa);
898 LOG(INFO) << "Removing boot class path artifacts on /data for "
899 << QuotePath(apexdata_image_location);
900 success &= RemoveArtifacts(OdrArtifacts::ForBootImageExtension(apexdata_image_location));
901 return success;
902 }
903
904 // Verify whether boot extension artifacts for `isa` are valid on system partition or in apexdata.
905 // This method has the side-effect of removing boot classpath extension artifacts on /data,
906 // if there are valid artifacts on /system, or if the artifacts on /data are not valid.
907 // Returns true if valid boot externsion artifacts are valid.
VerifyBootExtensionArtifactsAreUpToDate(InstructionSet isa) const908 WARN_UNUSED bool VerifyBootExtensionArtifactsAreUpToDate(InstructionSet isa) const {
909 bool system_ok = VerifyBootExtensionArtifactsAreUpToDate(isa, /*on_system=*/true);
910 LOG(INFO) << "Boot extension artifacts on /system are " << (system_ok ? "ok" : "stale");
911 bool data_ok = VerifyBootExtensionArtifactsAreUpToDate(isa, /*on_system=*/false);
912 LOG(INFO) << "Boot extension artifacts on /data are " << (data_ok ? "ok" : "stale");
913 return system_ok || data_ok;
914 }
915
916 // Verify all artifacts are up-to-date.
917 //
918 // This method checks artifacts can be loaded by the runtime.
919 //
920 // Returns ExitCode::kOkay if artifacts are up-to-date, ExitCode::kCompilationRequired otherwise.
921 //
922 // NB This is the main function used by the --check command-line option. When invoked with
923 // --compile, we only recompile the out-of-date artifacts, not all (see `Odrefresh::Compile`).
VerifyArtifactsAreUpToDate()924 WARN_UNUSED ExitCode VerifyArtifactsAreUpToDate() {
925 ExitCode exit_code = ExitCode::kOkay;
926 for (const InstructionSet isa : config_.GetBootExtensionIsas()) {
927 if (!VerifyBootExtensionArtifactsAreUpToDate(isa)) {
928 if (!RemoveBootExtensionArtifactsFromData(isa)) {
929 return ExitCode::kCleanupFailed;
930 }
931 exit_code = ExitCode::kCompilationRequired;
932 }
933 }
934 if (!VerifySystemServerArtifactsAreUpToDate()) {
935 if (!RemoveSystemServerArtifactsFromData()) {
936 return ExitCode::kCleanupFailed;
937 }
938 exit_code = ExitCode::kCompilationRequired;
939 }
940 return exit_code;
941 }
942
CleanApexdataDirectory() const943 WARN_UNUSED bool CleanApexdataDirectory() const {
944 const std::string& apex_data_path = GetArtApexData();
945 if (config_.GetDryRun()) {
946 LOG(INFO) << "Files under `" << QuotePath(apex_data_path) << " would be removed (dry-run).";
947 return true;
948 }
949 return CleanDirectory(apex_data_path);
950 }
951
RemoveArtifacts(const OdrArtifacts & artifacts) const952 WARN_UNUSED bool RemoveArtifacts(const OdrArtifacts& artifacts) const {
953 bool success = true;
954 for (const auto& location :
955 {artifacts.ImagePath(), artifacts.OatPath(), artifacts.VdexPath()}) {
956 if (config_.GetDryRun()) {
957 LOG(INFO) << "Removing " << QuotePath(location) << " (dry-run).";
958 continue;
959 }
960
961 if (OS::FileExists(location.c_str()) && unlink(location.c_str()) != 0) {
962 PLOG(ERROR) << "Failed to remove: " << QuotePath(location);
963 success = false;
964 }
965 }
966 return success;
967 }
968
GetBootImage()969 static std::string GetBootImage() {
970 // Typically "/apex/com.android.art/javalib/boot.art".
971 return GetArtRoot() + "/javalib/boot.art";
972 }
973
GetBootImageExtensionImage(bool on_system) const974 std::string GetBootImageExtensionImage(bool on_system) const {
975 CHECK(!boot_extension_compilable_jars_.empty());
976 const std::string leading_jar = boot_extension_compilable_jars_[0];
977 if (on_system) {
978 const std::string jar_name = android::base::Basename(leading_jar);
979 const std::string image_name = ReplaceFileExtension(jar_name, "art");
980 // Typically "/system/framework/boot-framework.art".
981 return Concatenate({GetAndroidRoot(), "/framework/boot-", image_name});
982 } else {
983 // Typically "/data/misc/apexdata/com.android.art/dalvik-cache/boot-framework.art".
984 return GetApexDataBootImage(leading_jar);
985 }
986 }
987
GetBootImageExtensionImagePath(const InstructionSet isa) const988 std::string GetBootImageExtensionImagePath(const InstructionSet isa) const {
989 // Typically "/data/misc/apexdata/com.android.art/dalvik-cache/<isa>/boot-framework.art".
990 return GetSystemImageFilename(GetBootImageExtensionImage(/*on_system=*/false).c_str(), isa);
991 }
992
GetSystemServerImagePath(bool on_system,const std::string & jar_path) const993 std::string GetSystemServerImagePath(bool on_system, const std::string& jar_path) const {
994 if (on_system) {
995 const std::string jar_name = android::base::Basename(jar_path);
996 const std::string image_name = ReplaceFileExtension(jar_name, "art");
997 const char* isa_str = GetInstructionSetString(config_.GetSystemServerIsa());
998 // Typically "/system/framework/oat/<isa>/services.art".
999 return Concatenate({GetAndroidRoot(), "/framework/oat/", isa_str, "/", image_name});
1000 } else {
1001 // Typically
1002 // "/data/misc/apexdata/.../dalvik-cache/<isa>/system@framework@services.jar@classes.art".
1003 const std::string image = GetApexDataImage(jar_path.c_str());
1004 return GetSystemImageFilename(image.c_str(), config_.GetSystemServerIsa());
1005 }
1006 }
1007
GetStagingLocation(const std::string & staging_dir,const std::string & path) const1008 std::string GetStagingLocation(const std::string& staging_dir, const std::string& path) const {
1009 return Concatenate({staging_dir, "/", android::base::Basename(path)});
1010 }
1011
CompileBootExtensionArtifacts(const InstructionSet isa,const std::string & staging_dir,OdrMetrics & metrics,uint32_t * dex2oat_invocation_count,std::string * error_msg) const1012 WARN_UNUSED bool CompileBootExtensionArtifacts(const InstructionSet isa,
1013 const std::string& staging_dir,
1014 OdrMetrics& metrics,
1015 uint32_t* dex2oat_invocation_count,
1016 std::string* error_msg) const {
1017 ScopedOdrCompilationTimer compilation_timer(metrics);
1018 std::vector<std::string> args;
1019 args.push_back(config_.GetDex2Oat());
1020
1021 AddDex2OatCommonOptions(&args);
1022 AddDex2OatConcurrencyArguments(&args);
1023 AddDex2OatDebugInfo(&args);
1024 AddDex2OatInstructionSet(&args, isa);
1025 const std::string boot_profile_file(GetAndroidRoot() + "/etc/boot-image.prof");
1026 AddDex2OatProfileAndCompilerFilter(&args, boot_profile_file);
1027
1028 // Compile as a single image for fewer files and slightly less memory overhead.
1029 args.emplace_back("--single-image");
1030
1031 // Set boot-image and expectation of compiling boot classpath extensions.
1032 args.emplace_back("--boot-image=" + GetBootImage());
1033
1034 const std::string dirty_image_objects_file(GetAndroidRoot() + "/etc/dirty-image-objects");
1035 if (OS::FileExists(dirty_image_objects_file.c_str())) {
1036 args.emplace_back(Concatenate({"--dirty-image-objects=", dirty_image_objects_file}));
1037 } else {
1038 LOG(WARNING) << "Missing dirty objects file : " << QuotePath(dirty_image_objects_file);
1039 }
1040
1041 // Add boot extensions to compile.
1042 for (const std::string& component : boot_extension_compilable_jars_) {
1043 args.emplace_back("--dex-file=" + component);
1044 }
1045
1046 args.emplace_back("--runtime-arg");
1047 args.emplace_back(Concatenate({"-Xbootclasspath:", config_.GetDex2oatBootClasspath()}));
1048
1049 const std::string image_location = GetBootImageExtensionImagePath(isa);
1050 const OdrArtifacts artifacts = OdrArtifacts::ForBootImageExtension(image_location);
1051 CHECK_EQ(GetApexDataOatFilename(boot_extension_compilable_jars_.front().c_str(), isa),
1052 artifacts.OatPath());
1053
1054 args.emplace_back("--oat-location=" + artifacts.OatPath());
1055 const std::pair<const std::string, const char*> location_kind_pairs[] = {
1056 std::make_pair(artifacts.ImagePath(), "image"),
1057 std::make_pair(artifacts.OatPath(), "oat"),
1058 std::make_pair(artifacts.VdexPath(), "output-vdex")
1059 };
1060
1061 std::vector<std::unique_ptr<File>> staging_files;
1062 for (const auto& location_kind_pair : location_kind_pairs) {
1063 auto& [location, kind] = location_kind_pair;
1064 const std::string staging_location = GetStagingLocation(staging_dir, location);
1065 std::unique_ptr<File> staging_file(OS::CreateEmptyFile(staging_location.c_str()));
1066 if (staging_file == nullptr) {
1067 PLOG(ERROR) << "Failed to create " << kind << " file: " << staging_location;
1068 metrics.SetStatus(OdrMetrics::Status::kIoError);
1069 EraseFiles(staging_files);
1070 return false;
1071 }
1072
1073 if (fchmod(staging_file->Fd(), S_IRUSR | S_IWUSR) != 0) {
1074 PLOG(ERROR) << "Could not set file mode on " << QuotePath(staging_location);
1075 metrics.SetStatus(OdrMetrics::Status::kIoError);
1076 EraseFiles(staging_files);
1077 return false;
1078 }
1079
1080 args.emplace_back(android::base::StringPrintf("--%s-fd=%d", kind, staging_file->Fd()));
1081 staging_files.emplace_back(std::move(staging_file));
1082 }
1083
1084 const std::string install_location = android::base::Dirname(image_location);
1085 if (!EnsureDirectoryExists(install_location)) {
1086 metrics.SetStatus(OdrMetrics::Status::kIoError);
1087 return false;
1088 }
1089
1090 const time_t timeout = GetSubprocessTimeout();
1091 const std::string cmd_line = android::base::Join(args, ' ');
1092 LOG(INFO) << "Compiling boot extensions (" << isa << "): " << cmd_line
1093 << " [timeout " << timeout << "s]";
1094 if (config_.GetDryRun()) {
1095 LOG(INFO) << "Compilation skipped (dry-run).";
1096 return true;
1097 }
1098
1099 bool timed_out = false;
1100 int dex2oat_exit_code = ExecAndReturnCode(args, timeout, &timed_out, error_msg);
1101 if (dex2oat_exit_code != 0) {
1102 if (timed_out) {
1103 metrics.SetStatus(OdrMetrics::Status::kTimeLimitExceeded);
1104 } else {
1105 metrics.SetStatus(OdrMetrics::Status::kDex2OatError);
1106 }
1107 EraseFiles(staging_files);
1108 return false;
1109 }
1110
1111 if (!MoveOrEraseFiles(staging_files, install_location)) {
1112 metrics.SetStatus(OdrMetrics::Status::kInstallFailed);
1113 return false;
1114 }
1115
1116 *dex2oat_invocation_count = *dex2oat_invocation_count + 1;
1117 ReportNextBootAnimationProgress(*dex2oat_invocation_count);
1118
1119 return true;
1120 }
1121
CompileSystemServerArtifacts(const std::string & staging_dir,OdrMetrics & metrics,uint32_t * dex2oat_invocation_count,std::string * error_msg) const1122 WARN_UNUSED bool CompileSystemServerArtifacts(const std::string& staging_dir,
1123 OdrMetrics& metrics,
1124 uint32_t* dex2oat_invocation_count,
1125 std::string* error_msg) const {
1126 ScopedOdrCompilationTimer compilation_timer(metrics);
1127 std::vector<std::string> classloader_context;
1128
1129 const std::string dex2oat = config_.GetDex2Oat();
1130 const InstructionSet isa = config_.GetSystemServerIsa();
1131 for (const std::string& jar : systemserver_compilable_jars_) {
1132 std::vector<std::string> args;
1133 args.emplace_back(dex2oat);
1134 args.emplace_back("--dex-file=" + jar);
1135
1136 AddDex2OatCommonOptions(&args);
1137 AddDex2OatConcurrencyArguments(&args);
1138 AddDex2OatDebugInfo(&args);
1139 AddDex2OatInstructionSet(&args, isa);
1140 const std::string jar_name(android::base::Basename(jar));
1141 const std::string profile = Concatenate({GetAndroidRoot(), "/framework/", jar_name, ".prof"});
1142 std::string compiler_filter =
1143 android::base::GetProperty("dalvik.vm.systemservercompilerfilter", "speed");
1144 if (compiler_filter == "speed-profile") {
1145 AddDex2OatProfileAndCompilerFilter(&args, profile);
1146 } else {
1147 args.emplace_back("--compiler-filter=" + compiler_filter);
1148 }
1149
1150 const std::string image_location = GetSystemServerImagePath(/*on_system=*/false, jar);
1151 const std::string install_location = android::base::Dirname(image_location);
1152 if (classloader_context.empty()) {
1153 // All images are in the same directory, we only need to check on the first iteration.
1154 if (!EnsureDirectoryExists(install_location)) {
1155 metrics.SetStatus(OdrMetrics::Status::kIoError);
1156 return false;
1157 }
1158 }
1159
1160 OdrArtifacts artifacts = OdrArtifacts::ForSystemServer(image_location);
1161 CHECK_EQ(artifacts.OatPath(), GetApexDataOdexFilename(jar.c_str(), isa));
1162
1163 const std::pair<const std::string, const char*> location_kind_pairs[] = {
1164 std::make_pair(artifacts.ImagePath(), "app-image"),
1165 std::make_pair(artifacts.OatPath(), "oat"),
1166 std::make_pair(artifacts.VdexPath(), "output-vdex")
1167 };
1168
1169 std::vector<std::unique_ptr<File>> staging_files;
1170 for (const auto& location_kind_pair : location_kind_pairs) {
1171 auto& [location, kind] = location_kind_pair;
1172 const std::string staging_location = GetStagingLocation(staging_dir, location);
1173 std::unique_ptr<File> staging_file(OS::CreateEmptyFile(staging_location.c_str()));
1174 if (staging_file == nullptr) {
1175 PLOG(ERROR) << "Failed to create " << kind << " file: " << staging_location;
1176 metrics.SetStatus(OdrMetrics::Status::kIoError);
1177 EraseFiles(staging_files);
1178 return false;
1179 }
1180 args.emplace_back(android::base::StringPrintf("--%s-fd=%d", kind, staging_file->Fd()));
1181 staging_files.emplace_back(std::move(staging_file));
1182 }
1183 args.emplace_back("--oat-location=" + artifacts.OatPath());
1184
1185 if (!config_.GetUpdatableBcpPackagesFile().empty()) {
1186 const std::string& bcp_packages = config_.GetUpdatableBcpPackagesFile();
1187 if (!OS::FileExists(bcp_packages.c_str())) {
1188 *error_msg = "Cannot compile system_server JARs: missing " + QuotePath(bcp_packages);
1189 metrics.SetStatus(OdrMetrics::Status::kIoError);
1190 EraseFiles(staging_files);
1191 return false;
1192 }
1193 args.emplace_back("--updatable-bcp-packages-file=" + bcp_packages);
1194 }
1195
1196 args.emplace_back("--runtime-arg");
1197 args.emplace_back(Concatenate({"-Xbootclasspath:", config_.GetDex2oatBootClasspath()}));
1198 const std::string context_path = android::base::Join(classloader_context, ':');
1199 args.emplace_back(Concatenate({"--class-loader-context=PCL[", context_path, "]"}));
1200 const std::string extension_image = GetBootImageExtensionImage(/*on_system=*/false);
1201 args.emplace_back(Concatenate({"--boot-image=", GetBootImage(), ":", extension_image}));
1202
1203 const time_t timeout = GetSubprocessTimeout();
1204 const std::string cmd_line = android::base::Join(args, ' ');
1205 LOG(INFO) << "Compiling " << jar << ": " << cmd_line << " [timeout " << timeout << "s]";
1206 if (config_.GetDryRun()) {
1207 LOG(INFO) << "Compilation skipped (dry-run).";
1208 return true;
1209 }
1210
1211 bool timed_out = false;
1212 int dex2oat_exit_code = ExecAndReturnCode(args, timeout, &timed_out, error_msg);
1213 if (dex2oat_exit_code != 0) {
1214 if (timed_out) {
1215 metrics.SetStatus(OdrMetrics::Status::kTimeLimitExceeded);
1216 } else {
1217 metrics.SetStatus(OdrMetrics::Status::kDex2OatError);
1218 }
1219 EraseFiles(staging_files);
1220 return false;
1221 }
1222
1223 if (!MoveOrEraseFiles(staging_files, install_location)) {
1224 metrics.SetStatus(OdrMetrics::Status::kInstallFailed);
1225 return false;
1226 }
1227
1228 *dex2oat_invocation_count = *dex2oat_invocation_count + 1;
1229 ReportNextBootAnimationProgress(*dex2oat_invocation_count);
1230 classloader_context.emplace_back(jar);
1231 }
1232
1233 return true;
1234 }
1235
ReportNextBootAnimationProgress(uint32_t current_compilation) const1236 void ReportNextBootAnimationProgress(uint32_t current_compilation) const {
1237 uint32_t number_of_compilations =
1238 config_.GetBootExtensionIsas().size() + systemserver_compilable_jars_.size();
1239 // We arbitrarily show progress until 90%, expecting that our compilations
1240 // take a large chunk of boot time.
1241 uint32_t value = (90 * current_compilation) / number_of_compilations;
1242 android::base::SetProperty("service.bootanim.progress", std::to_string(value));
1243 }
1244
CheckCompilationSpace() const1245 WARN_UNUSED bool CheckCompilationSpace() const {
1246 // Check the available storage space against an arbitrary threshold because dex2oat does not
1247 // report when it runs out of storage space and we do not want to completely fill
1248 // the users data partition.
1249 //
1250 // We do not have a good way of pre-computing the required space for a compilation step, but
1251 // typically observe 16MB as the largest size of an AOT artifact. Since there are three
1252 // AOT artifacts per compilation step - an image file, executable file, and a verification
1253 // data file - the threshold is three times 16MB.
1254 static constexpr uint64_t kMinimumSpaceForCompilation = 3 * 16 * 1024 * 1024;
1255
1256 uint64_t bytes_available;
1257 const std::string& art_apex_data_path = GetArtApexData();
1258 if (!GetFreeSpace(art_apex_data_path, &bytes_available)) {
1259 return false;
1260 }
1261
1262 if (bytes_available < kMinimumSpaceForCompilation) {
1263 LOG(WARNING) << "Low space for " << QuotePath(art_apex_data_path) << " (" << bytes_available
1264 << " bytes)";
1265 return false;
1266 }
1267
1268 return true;
1269 }
1270
Compile(OdrMetrics & metrics,bool force_compile) const1271 WARN_UNUSED ExitCode Compile(OdrMetrics& metrics, bool force_compile) const {
1272 const char* staging_dir = nullptr;
1273 metrics.SetStage(OdrMetrics::Stage::kPreparation);
1274 // Clean-up existing files.
1275 if (force_compile && !CleanApexdataDirectory()) {
1276 metrics.SetStatus(OdrMetrics::Status::kIoError);
1277 return ExitCode::kCleanupFailed;
1278 }
1279
1280 // Create staging area and assign label for generating compilation artifacts.
1281 if (PaletteCreateOdrefreshStagingDirectory(&staging_dir) != PALETTE_STATUS_OK) {
1282 metrics.SetStatus(OdrMetrics::Status::kStagingFailed);
1283 return ExitCode::kCleanupFailed;
1284 }
1285
1286 // Emit cache info before compiling. This can be used to throttle compilation attempts later.
1287 WriteCacheInfo();
1288
1289 std::string error_msg;
1290
1291 uint32_t dex2oat_invocation_count = 0;
1292 ReportNextBootAnimationProgress(dex2oat_invocation_count);
1293
1294 const auto& bcp_instruction_sets = config_.GetBootExtensionIsas();
1295 DCHECK(!bcp_instruction_sets.empty() && bcp_instruction_sets.size() <= 2);
1296 for (const InstructionSet isa : bcp_instruction_sets) {
1297 auto stage = (isa == bcp_instruction_sets.front()) ?
1298 OdrMetrics::Stage::kPrimaryBootClasspath :
1299 OdrMetrics::Stage::kSecondaryBootClasspath;
1300 metrics.SetStage(stage);
1301 if (force_compile || !BootExtensionArtifactsExistOnData(isa, &error_msg)) {
1302 // Remove artifacts we are about to generate. Ordinarily these are removed in the checking
1303 // step, but this is not always run (e.g. during manual testing).
1304 if (!RemoveBootExtensionArtifactsFromData(isa)) {
1305 return ExitCode::kCleanupFailed;
1306 }
1307
1308 if (!CheckCompilationSpace()) {
1309 metrics.SetStatus(OdrMetrics::Status::kNoSpace);
1310 // Return kOkay so odsign will keep and sign whatever we have been able to compile.
1311 return ExitCode::kOkay;
1312 }
1313
1314 if (!CompileBootExtensionArtifacts(
1315 isa, staging_dir, metrics, &dex2oat_invocation_count, &error_msg)) {
1316 LOG(ERROR) << "Compilation of BCP failed: " << error_msg;
1317 if (!config_.GetDryRun() && !CleanDirectory(staging_dir)) {
1318 return ExitCode::kCleanupFailed;
1319 }
1320 return ExitCode::kCompilationFailed;
1321 }
1322 }
1323 }
1324
1325 if (force_compile || !SystemServerArtifactsExistOnData(&error_msg)) {
1326 metrics.SetStage(OdrMetrics::Stage::kSystemServerClasspath);
1327
1328 if (!CheckCompilationSpace()) {
1329 metrics.SetStatus(OdrMetrics::Status::kNoSpace);
1330 // Return kOkay so odsign will keep and sign whatever we have been able to compile.
1331 return ExitCode::kOkay;
1332 }
1333
1334 if (!CompileSystemServerArtifacts(
1335 staging_dir, metrics, &dex2oat_invocation_count, &error_msg)) {
1336 LOG(ERROR) << "Compilation of system_server failed: " << error_msg;
1337 if (!config_.GetDryRun() && !CleanDirectory(staging_dir)) {
1338 return ExitCode::kCleanupFailed;
1339 }
1340 return ExitCode::kCompilationFailed;
1341 }
1342 }
1343
1344 metrics.SetStage(OdrMetrics::Stage::kComplete);
1345 return ExitCode::kCompilationSuccess;
1346 }
1347
ArgumentMatches(std::string_view argument,std::string_view prefix,std::string * value)1348 static bool ArgumentMatches(std::string_view argument,
1349 std::string_view prefix,
1350 std::string* value) {
1351 if (StartsWith(argument, prefix)) {
1352 *value = std::string(argument.substr(prefix.size()));
1353 return true;
1354 }
1355 return false;
1356 }
1357
ArgumentEquals(std::string_view argument,std::string_view expected)1358 static bool ArgumentEquals(std::string_view argument, std::string_view expected) {
1359 return argument == expected;
1360 }
1361
InitializeCommonConfig(std::string_view argument,OdrConfig * config)1362 static bool InitializeCommonConfig(std::string_view argument, OdrConfig* config) {
1363 static constexpr std::string_view kDryRunArgument{"--dry-run"};
1364 if (ArgumentEquals(argument, kDryRunArgument)) {
1365 config->SetDryRun();
1366 return true;
1367 }
1368 return false;
1369 }
1370
InitializeHostConfig(int argc,const char ** argv,OdrConfig * config)1371 static int InitializeHostConfig(int argc, const char** argv, OdrConfig* config) {
1372 __android_log_set_logger(__android_log_stderr_logger);
1373
1374 std::string current_binary;
1375 if (argv[0][0] == '/') {
1376 current_binary = argv[0];
1377 } else {
1378 std::vector<char> buf(PATH_MAX);
1379 if (getcwd(buf.data(), buf.size()) == nullptr) {
1380 PLOG(FATAL) << "Failed getwd()";
1381 }
1382 current_binary = Concatenate({buf.data(), "/", argv[0]});
1383 }
1384 config->SetArtBinDir(android::base::Dirname(current_binary));
1385
1386 int n = 1;
1387 for (; n < argc - 1; ++n) {
1388 const char* arg = argv[n];
1389 std::string value;
1390 if (ArgumentMatches(arg, "--android-root=", &value)) {
1391 setenv("ANDROID_ROOT", value.c_str(), 1);
1392 } else if (ArgumentMatches(arg, "--android-art-root=", &value)) {
1393 setenv("ANDROID_ART_ROOT", value.c_str(), 1);
1394 } else if (ArgumentMatches(arg, "--apex-info-list=", &value)) {
1395 config->SetApexInfoListFile(value);
1396 } else if (ArgumentMatches(arg, "--art-apex-data=", &value)) {
1397 setenv("ART_APEX_DATA", value.c_str(), 1);
1398 } else if (ArgumentMatches(arg, "--dex2oat-bootclasspath=", &value)) {
1399 config->SetDex2oatBootclasspath(value);
1400 } else if (ArgumentMatches(arg, "--isa=", &value)) {
1401 config->SetIsa(GetInstructionSetFromString(value.c_str()));
1402 } else if (ArgumentMatches(arg, "--system-server-classpath=", &value)) {
1403 config->SetSystemServerClasspath(arg);
1404 } else if (ArgumentMatches(arg, "--updatable-bcp-packages-file=", &value)) {
1405 config->SetUpdatableBcpPackagesFile(value);
1406 } else if (ArgumentMatches(arg, "--zygote-arch=", &value)) {
1407 ZygoteKind zygote_kind;
1408 if (!ParseZygoteKind(value.c_str(), &zygote_kind)) {
1409 ArgumentError("Unrecognized zygote kind: '%s'", value.c_str());
1410 }
1411 config->SetZygoteKind(zygote_kind);
1412 } else if (!InitializeCommonConfig(arg, config)) {
1413 UsageError("Unrecognized argument: '%s'", arg);
1414 }
1415 }
1416 return n;
1417 }
1418
InitializeTargetConfig(int argc,const char ** argv,OdrConfig * config)1419 static int InitializeTargetConfig(int argc, const char** argv, OdrConfig* config) {
1420 config->SetApexInfoListFile("/apex/apex-info-list.xml");
1421 config->SetArtBinDir(GetArtBinDir());
1422 config->SetDex2oatBootclasspath(GetEnvironmentVariableOrDie("DEX2OATBOOTCLASSPATH"));
1423 config->SetSystemServerClasspath(GetEnvironmentVariableOrDie("SYSTEMSERVERCLASSPATH"));
1424 config->SetIsa(kRuntimeISA);
1425
1426 const std::string zygote = android::base::GetProperty("ro.zygote", {});
1427 ZygoteKind zygote_kind;
1428 if (!ParseZygoteKind(zygote.c_str(), &zygote_kind)) {
1429 LOG(FATAL) << "Unknown zygote: " << QuotePath(zygote);
1430 }
1431 config->SetZygoteKind(zygote_kind);
1432
1433 const std::string updatable_packages =
1434 android::base::GetProperty("dalvik.vm.dex2oat-updatable-bcp-packages-file", {});
1435 config->SetUpdatableBcpPackagesFile(updatable_packages);
1436
1437 int n = 1;
1438 for (; n < argc - 1; ++n) {
1439 if (!InitializeCommonConfig(argv[n], config)) {
1440 UsageError("Unrecognized argument: '%s'", argv[n]);
1441 }
1442 }
1443 return n;
1444 }
1445
InitializeConfig(int argc,const char ** argv,OdrConfig * config)1446 static int InitializeConfig(int argc, const char** argv, OdrConfig* config) {
1447 if (kIsTargetBuild) {
1448 return InitializeTargetConfig(argc, argv, config);
1449 } else {
1450 return InitializeHostConfig(argc, argv, config);
1451 }
1452 }
1453
main(int argc,const char ** argv)1454 static int main(int argc, const char** argv) {
1455 OdrConfig config(argv[0]);
1456 int n = InitializeConfig(argc, argv, &config);
1457 argv += n;
1458 argc -= n;
1459 if (argc != 1) {
1460 UsageError("Expected 1 argument, but have %d.", argc);
1461 }
1462
1463 OdrMetrics metrics(kOdrefreshArtifactDirectory);
1464 OnDeviceRefresh odr(config);
1465 for (int i = 0; i < argc; ++i) {
1466 std::string_view action(argv[i]);
1467 if (action == "--check") {
1468 // Fast determination of whether artifacts are up to date.
1469 return odr.CheckArtifactsAreUpToDate(metrics);
1470 } else if (action == "--compile") {
1471 const ExitCode exit_code = odr.CheckArtifactsAreUpToDate(metrics);
1472 if (exit_code != ExitCode::kCompilationRequired) {
1473 return exit_code;
1474 }
1475 OdrCompilationLog compilation_log;
1476 if (!compilation_log.ShouldAttemptCompile(metrics.GetArtApexVersion(),
1477 metrics.GetArtApexLastUpdateMillis(),
1478 metrics.GetTrigger())) {
1479 return ExitCode::kOkay;
1480 }
1481 ExitCode compile_result = odr.Compile(metrics, /*force_compile=*/false);
1482 compilation_log.Log(metrics.GetArtApexVersion(),
1483 metrics.GetArtApexLastUpdateMillis(),
1484 metrics.GetTrigger(),
1485 compile_result);
1486 return compile_result;
1487 } else if (action == "--force-compile") {
1488 return odr.Compile(metrics, /*force_compile=*/true);
1489 } else if (action == "--verify") {
1490 // Slow determination of whether artifacts are up to date. These are too slow for checking
1491 // during boot (b/181689036).
1492 return odr.VerifyArtifactsAreUpToDate();
1493 } else if (action == "--help") {
1494 UsageHelp(argv[0]);
1495 } else {
1496 UsageError("Unknown argument: ", argv[i]);
1497 }
1498 }
1499 return ExitCode::kOkay;
1500 }
1501
1502 DISALLOW_COPY_AND_ASSIGN(OnDeviceRefresh);
1503 };
1504
1505 } // namespace odrefresh
1506 } // namespace art
1507
main(int argc,const char ** argv)1508 int main(int argc, const char** argv) {
1509 // odrefresh is launched by `init` which sets the umask of forked processed to
1510 // 077 (S_IRWXG | S_IRWXO). This blocks the ability to make files and directories readable
1511 // by others and prevents system_server from loading generated artifacts.
1512 umask(S_IWGRP | S_IWOTH);
1513 return art::odrefresh::OnDeviceRefresh::main(argc, argv);
1514 }
1515