UsageError(" Example: --instruction-set-features=div");
UsageError(" Default: default");
UsageError("");
+ UsageError(" --compile-pic: Force indirect use of code, methods, and classes");
+ UsageError(" Default: disabled");
+ UsageError("");
UsageError(" --compiler-backend=(Quick|Optimizing|Portable): select compiler backend");
UsageError(" set.");
UsageError(" Example: --compiler-backend=Portable");
~Dex2Oat() {
delete runtime_;
- LogCompletionTime();
}
- void LogCompletionTime() {
+ void LogCompletionTime(const CompilerDriver* compiler) {
LOG(INFO) << "dex2oat took " << PrettyDuration(NanoTime() - start_ns_)
- << " (threads: " << thread_count_ << ")";
+ << " (threads: " << thread_count_ << ") " << compiler->GetMemoryUsageString();
}
const std::string& bitcode_filename,
bool image,
std::unique_ptr<std::set<std::string>>& image_classes,
+ std::unique_ptr<std::set<std::string>>& compiled_classes,
bool dump_stats,
bool dump_passes,
TimingLogger& timings,
instruction_set_features_,
image,
image_classes.release(),
+ compiled_classes.release(),
thread_count_,
dump_stats,
dump_passes,
t2.NewTiming("Writing ELF");
if (!driver->WriteElf(android_root, is_host, dex_files, &oat_writer, oat_file)) {
LOG(ERROR) << "Failed to write ELF file " << oat_file->GetPath();
+ oat_file->Erase();
+ return nullptr;
+ }
+
+ // Flush result to disk. Patching code will re-open the file (mmap), so ensure that our view
+ // of the file already made it there and won't be re-ordered with writes from PatchOat or
+ // image patching.
+ if (oat_file->Flush() != 0) {
+ PLOG(ERROR) << "Failed flushing oat file " << oat_file->GetPath();
+ oat_file->Erase();
return nullptr;
}
std::string error_msg;
if (!PatchOatCode(driver.get(), oat_file, oat_location, &error_msg)) {
LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath() << ": " << error_msg;
+ oat_file->Erase();
return nullptr;
}
}
{
// ImageWriter is scoped so it can free memory before doing FixupElf
ImageWriter image_writer(compiler);
- if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location)) {
+ if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location,
+ compiler_options_->GetCompilePic())) {
LOG(ERROR) << "Failed to create image file " << image_filename;
return false;
}
oat_data_begin = image_writer.GetOatDataBegin();
}
- std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
- if (oat_file.get() == nullptr) {
- PLOG(ERROR) << "Failed to open ELF file: " << oat_filename;
- return false;
- }
- if (!ElfFixup::Fixup(oat_file.get(), oat_data_begin)) {
- LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
- return false;
+
+ // Do not fix up the ELF file if we are --compile-pic
+ if (!compiler_options_->GetCompilePic()) {
+ std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
+ if (oat_file.get() == nullptr) {
+ PLOG(ERROR) << "Failed to open ELF file: " << oat_filename;
+ return false;
+ }
+
+ if (!ElfFixup::Fixup(oat_file.get(), oat_data_begin)) {
+ LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
+ oat_file->Erase();
+ return false;
+ }
+
+ if (oat_file->FlushCloseOrErase() != 0) {
+ PLOG(ERROR) << "Failed to flush and close patched oat file " << oat_filename;
+ return false;
+ }
}
+
return true;
}
// during development when fatal aborts lead to a cascade of failures
// that result in a deadlock.
class WatchDog {
-// WatchDog defines its own CHECK_PTHREAD_CALL to avoid using Log which uses locks
+// WatchDog defines its own CHECK_PTHREAD_CALL to avoid using LOG which uses locks
#undef CHECK_PTHREAD_CALL
#define CHECK_WATCH_DOG_PTHREAD_CALL(call, args, what) \
do { \
message.c_str());
}
- static void Warn(const std::string& message) {
- Message('W', message);
- }
-
static void Fatal(const std::string& message) {
Message('F', message);
exit(1);
}
void Wait() {
- bool warning = true;
- CHECK_GT(kWatchDogTimeoutSeconds, kWatchDogWarningSeconds);
// TODO: tune the multiplier for GC verification, the following is just to make the timeout
// large.
int64_t multiplier = kVerifyObjectSupport > kVerifyObjectModeFast ? 100 : 1;
- timespec warning_ts;
- InitTimeSpec(true, CLOCK_REALTIME, multiplier * kWatchDogWarningSeconds * 1000, 0, &warning_ts);
timespec timeout_ts;
InitTimeSpec(true, CLOCK_REALTIME, multiplier * kWatchDogTimeoutSeconds * 1000, 0, &timeout_ts);
const char* reason = "dex2oat watch dog thread waiting";
CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_lock, (&mutex_), reason);
while (!shutting_down_) {
- int rc = TEMP_FAILURE_RETRY(pthread_cond_timedwait(&cond_, &mutex_,
- warning ? &warning_ts
- : &timeout_ts));
+ int rc = TEMP_FAILURE_RETRY(pthread_cond_timedwait(&cond_, &mutex_, &timeout_ts));
if (rc == ETIMEDOUT) {
- std::string message(StringPrintf("dex2oat did not finish after %d seconds",
- warning ? kWatchDogWarningSeconds
- : kWatchDogTimeoutSeconds));
- if (warning) {
- Warn(message.c_str());
- warning = false;
- } else {
- Fatal(message.c_str());
- }
+ Fatal(StringPrintf("dex2oat did not finish after %d seconds", kWatchDogTimeoutSeconds));
} else if (rc != 0) {
std::string message(StringPrintf("pthread_cond_timedwait failed: %s",
strerror(errno)));
// Debug builds are slower so they have larger timeouts.
static const unsigned int kSlowdownFactor = kIsDebugBuild ? 5U : 1U;
#if ART_USE_PORTABLE_COMPILER
- // 2 minutes scaled by kSlowdownFactor.
- static const unsigned int kWatchDogWarningSeconds = kSlowdownFactor * 2 * 60;
// 30 minutes scaled by kSlowdownFactor.
static const unsigned int kWatchDogTimeoutSeconds = kSlowdownFactor * 30 * 60;
#else
- // 1 minutes scaled by kSlowdownFactor.
- static const unsigned int kWatchDogWarningSeconds = kSlowdownFactor * 1 * 60;
// 6 minutes scaled by kSlowdownFactor.
static const unsigned int kWatchDogTimeoutSeconds = kSlowdownFactor * 6 * 60;
#endif
pthread_attr_t attr_;
pthread_t pthread_;
};
-const unsigned int WatchDog::kWatchDogWarningSeconds;
const unsigned int WatchDog::kWatchDogTimeoutSeconds;
// Given a set of instruction features from the build, parse it. The
*parsed_value = value;
}
-static int dex2oat(int argc, char** argv) {
+static void b13564922() {
#if defined(__linux__) && defined(__arm__)
int major, minor;
struct utsname uts;
}
}
#endif
+}
+
+static int dex2oat(int argc, char** argv) {
+ b13564922();
original_argc = argc;
original_argv = argv;
std::string bitcode_filename;
const char* image_classes_zip_filename = nullptr;
const char* image_classes_filename = nullptr;
+ const char* compiled_classes_zip_filename = nullptr;
+ const char* compiled_classes_filename = nullptr;
std::string image_filename;
std::string boot_image_filename;
uintptr_t image_base = 0;
? Compiler::kPortable
: Compiler::kQuick;
const char* compiler_filter_string = nullptr;
+ bool compile_pic = false;
int huge_method_threshold = CompilerOptions::kDefaultHugeMethodThreshold;
int large_method_threshold = CompilerOptions::kDefaultLargeMethodThreshold;
int small_method_threshold = CompilerOptions::kDefaultSmallMethodThreshold;
image_classes_filename = option.substr(strlen("--image-classes=")).data();
} else if (option.starts_with("--image-classes-zip=")) {
image_classes_zip_filename = option.substr(strlen("--image-classes-zip=")).data();
+ } else if (option.starts_with("--compiled-classes=")) {
+ compiled_classes_filename = option.substr(strlen("--compiled-classes=")).data();
+ } else if (option.starts_with("--compiled-classes-zip=")) {
+ compiled_classes_zip_filename = option.substr(strlen("--compiled-classes-zip=")).data();
} else if (option.starts_with("--base=")) {
const char* image_base_str = option.substr(strlen("--base=")).data();
char* end;
}
} else if (option.starts_with("--compiler-filter=")) {
compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
+ } else if (option == "--compile-pic") {
+ compile_pic = true;
} else if (option.starts_with("--huge-method-max=")) {
const char* threshold = option.substr(strlen("--huge-method-max=")).data();
if (!ParseInt(threshold, &huge_method_threshold)) {
include_debug_symbols = true;
} else if (option == "--no-include-debug-symbols" || option == "--strip-symbols") {
include_debug_symbols = false;
+ generate_gdb_information = false; // Depends on debug symbols, see above.
} else if (option.starts_with("--profile-file=")) {
profile_file = option.substr(strlen("--profile-file=")).data();
VLOG(compiler) << "dex2oat: profile file is " << profile_file;
Usage("--image-classes-zip should be used with --image-classes");
}
+ if (compiled_classes_filename != nullptr && !image) {
+ Usage("--compiled-classes should only be used with --image");
+ }
+
+ if (compiled_classes_filename != nullptr && !boot_image_option.empty()) {
+ Usage("--compiled-classes should not be used with --boot-image");
+ }
+
+ if (compiled_classes_zip_filename != nullptr && compiled_classes_filename == nullptr) {
+ Usage("--compiled-classes-zip should be used with --compiled-classes");
+ }
+
if (dex_filenames.empty() && zip_fd == -1) {
Usage("Input must be supplied with either --dex-file or --zip-fd");
}
include_debug_symbols,
implicit_null_checks,
implicit_so_checks,
- implicit_suspend_checks
+ implicit_suspend_checks,
+ compile_pic
#ifdef ART_SEA_IR_MODE
, compiler_options.sea_ir_ =
true;
oat_location = oat_filename;
}
} else {
- oat_file.reset(new File(oat_fd, oat_location));
+ oat_file.reset(new File(oat_fd, oat_location, true));
oat_file->DisableAutoClose();
+ if (oat_file->SetLength(0)) { // Only warn for truncation error.
+ PLOG(WARNING) << "Truncating oat file " << oat_location << " failed.";
+ }
}
if (oat_file.get() == nullptr) {
PLOG(ERROR) << "Failed to create oat file: " << oat_location;
}
if (create_file && fchmod(oat_file->Fd(), 0644) != 0) {
PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location;
+ oat_file->Erase();
return EXIT_FAILURE;
}
RuntimeOptions runtime_options;
std::vector<const DexFile*> boot_class_path;
+ art::MemMap::Init(); // For ZipEntry::ExtractToMemMap.
if (boot_image_option.empty()) {
size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, boot_class_path);
if (failure_count > 0) {
LOG(ERROR) << "Failed to open some dex files: " << failure_count;
+ oat_file->Erase();
return EXIT_FAILURE;
}
runtime_options.push_back(std::make_pair("bootclasspath", &boot_class_path));
&method_inliner_map,
thread_count)) {
LOG(ERROR) << "Failed to create dex2oat";
+ timings.EndTiming();
+ oat_file->Erase();
return EXIT_FAILURE;
}
std::unique_ptr<Dex2Oat> dex2oat(p_dex2oat);
if (image_classes.get() == nullptr) {
LOG(ERROR) << "Failed to create list of image classes from '" << image_classes_filename <<
"': " << error_msg;
+ timings.EndTiming();
+ oat_file->Erase();
return EXIT_FAILURE;
}
} else if (image) {
image_classes.reset(new std::set<std::string>);
}
+ // If --compiled-classes was specified, calculate the full list of classes to compile in the
+ // image.
+ std::unique_ptr<std::set<std::string>> compiled_classes(nullptr);
+ if (compiled_classes_filename != nullptr) {
+ std::string error_msg;
+ if (compiled_classes_zip_filename != nullptr) {
+ compiled_classes.reset(dex2oat->ReadImageClassesFromZip(compiled_classes_zip_filename,
+ compiled_classes_filename,
+ &error_msg));
+ } else {
+ compiled_classes.reset(dex2oat->ReadImageClassesFromFile(compiled_classes_filename));
+ }
+ if (compiled_classes.get() == nullptr) {
+ LOG(ERROR) << "Failed to create list of compiled classes from '" << compiled_classes_filename
+ << "': " << error_msg;
+ timings.EndTiming();
+ oat_file->Erase();
+ return EXIT_FAILURE;
+ }
+ } else if (image) {
+ compiled_classes.reset(nullptr); // By default compile everything.
+ }
std::vector<const DexFile*> dex_files;
if (boot_image_option.empty()) {
if (zip_archive.get() == nullptr) {
LOG(ERROR) << "Failed to open zip from file descriptor for '" << zip_location << "': "
<< error_msg;
+ timings.EndTiming();
+ oat_file->Erase();
return EXIT_FAILURE;
}
if (!DexFile::OpenFromZip(*zip_archive.get(), zip_location, &error_msg, &dex_files)) {
LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location
<< "': " << error_msg;
+ timings.EndTiming();
+ oat_file->Erase();
return EXIT_FAILURE;
}
ATRACE_END();
size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, dex_files);
if (failure_count > 0) {
LOG(ERROR) << "Failed to open some dex files: " << failure_count;
+ timings.EndTiming();
+ oat_file->Erase();
return EXIT_FAILURE;
}
}
<< ". Try: adb shell chmod 777 /data/local/tmp";
continue;
}
- tmp_file->WriteFully(dex_file->Begin(), dex_file->Size());
+ // This is just dumping files for debugging. Ignore errors, and leave remnants.
+ UNUSED(tmp_file->WriteFully(dex_file->Begin(), dex_file->Size()));
+ UNUSED(tmp_file->Flush());
+ UNUSED(tmp_file->Close());
LOG(INFO) << "Wrote input to " << tmp_file_name;
}
}
new SafeMap<std::string, std::string>());
// Insert some compiler things.
- std::ostringstream oss;
- for (int i = 0; i < argc; ++i) {
- if (i > 0) {
- oss << ' ';
+ {
+ std::ostringstream oss;
+ for (int i = 0; i < argc; ++i) {
+ if (i > 0) {
+ oss << ' ';
+ }
+ oss << argv[i];
}
- oss << argv[i];
+ key_value_store->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
+ oss.str(""); // Reset.
+ oss << kRuntimeISA;
+ key_value_store->Put(OatHeader::kDex2OatHostKey, oss.str());
+ key_value_store->Put(OatHeader::kPicKey, compile_pic ? "true" : "false");
}
- key_value_store->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
- oss.str(""); // Reset.
- oss << kRuntimeISA;
- key_value_store->Put(OatHeader::kDex2OatHostKey, oss.str());
std::unique_ptr<const CompilerDriver> compiler(dex2oat->CreateOatFile(boot_image_option,
android_root,
bitcode_filename,
image,
image_classes,
+ compiled_classes,
dump_stats,
dump_passes,
timings,
key_value_store.get()));
if (compiler.get() == nullptr) {
LOG(ERROR) << "Failed to create oat file: " << oat_location;
+ timings.EndTiming();
return EXIT_FAILURE;
}
- VLOG(compiler) << "Oat file written successfully (unstripped): " << oat_location;
+ if (!kUsePortableCompiler) {
+ if (oat_file->FlushCloseOrErase() != 0) {
+ PLOG(ERROR) << "Failed to flush and close oat file: " << oat_location;
+ timings.EndTiming();
+ return EXIT_FAILURE;
+ }
+ oat_file.reset();
+ }
+ VLOG(compiler) << "Oat file written successfully (unstripped): " << oat_location;
// Notes on the interleaving of creating the image and oat file to
// ensure the references between the two are correct.
//
oat_location,
*compiler.get());
if (!image_creation_success) {
+ timings.EndTiming();
return EXIT_FAILURE;
}
VLOG(compiler) << "Image written successfully: " << image_filename;
// We need to strip after image creation because FixupElf needs to use .strtab.
if (oat_unstripped != oat_stripped) {
TimingLogger::ScopedTiming t("dex2oat OatFile copy", &timings);
- oat_file.reset();
- std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped.c_str()));
+ if (kUsePortableCompiler) {
+ if (oat_file->FlushCloseOrErase() != 0) {
+ PLOG(ERROR) << "Failed to flush and close oat file: " << oat_location;
+ return EXIT_FAILURE;
+ }
+ oat_file.reset();
+ }
+ std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped.c_str()));
std::unique_ptr<File> out(OS::CreateEmptyFile(oat_stripped.c_str()));
size_t buffer_size = 8192;
std::unique_ptr<uint8_t> buffer(new uint8_t[buffer_size]);
VLOG(compiler) << "Oat file copied successfully (stripped): " << oat_stripped;
}
-#if ART_USE_PORTABLE_COMPILER // We currently only generate symbols on Portable
- if (!compiler_options.GetIncludeDebugSymbols()) {
- timings.NewSplit("dex2oat ElfStripper");
- // Strip unneeded sections for target
- off_t seek_actual = lseek(oat_file->Fd(), 0, SEEK_SET);
- CHECK_EQ(0, seek_actual);
- std::string error_msg;
- CHECK(ElfStripper::Strip(oat_file.get(), &error_msg)) << error_msg;
+ if (kUsePortableCompiler) {
+ if (!compiler_options->GetIncludeDebugSymbols()) {
+ timings.NewTiming("dex2oat ElfStripper");
+ // Strip unneeded sections for target
+ off_t seek_actual = lseek(oat_file->Fd(), 0, SEEK_SET);
+ CHECK_EQ(0, seek_actual);
+ std::string error_msg;
+ CHECK(ElfStripper::Strip(oat_file.get(), &error_msg)) << error_msg;
- // We wrote the oat file successfully, and want to keep it.
- VLOG(compiler) << "Oat file written successfully (stripped): " << oat_location;
- } else {
- VLOG(compiler) << "Oat file written successfully without stripping: " << oat_location;
+ // We wrote the oat file successfully, and want to keep it.
+ VLOG(compiler) << "Oat file written successfully (stripped): " << oat_location;
+ } else {
+ VLOG(compiler) << "Oat file written successfully without stripping: " << oat_location;
+ }
+ if (oat_file->FlushCloseOrErase() != 0) {
+ LOG(ERROR) << "Failed to flush and close oat file: " << oat_location;
+ return EXIT_FAILURE;
+ }
+ oat_file.reset(nullptr);
+ }
+
+ if (oat_file.get() != nullptr) {
+ if (oat_file->FlushCloseOrErase() != 0) {
+ PLOG(ERROR) << "Failed to flush and close oat file: " << oat_location << "/" << oat_filename;
+ return EXIT_FAILURE;
+ }
}
-#endif // ART_USE_PORTABLE_COMPILER
timings.EndTiming();
LOG(INFO) << Dumpable<CumulativeLogger>(compiler_phases_timings);
}
+ dex2oat->LogCompletionTime(compiler.get());
// Everything was successfully written, do an explicit exit here to avoid running Runtime
// destructors that take time (bug 10645725) unless we're a debug build or running on valgrind.
if (!kIsDebugBuild && (RUNNING_ON_VALGRIND == 0)) {
- dex2oat->LogCompletionTime();
exit(EXIT_SUCCESS);
}