diff options
| -rw-r--r-- | compiler/jit/jit_compiler.cc | 8 | ||||
| -rw-r--r-- | compiler/jit/jit_compiler.h | 2 | ||||
| -rw-r--r-- | compiler/optimizing/intrinsics.h | 3 | ||||
| -rw-r--r-- | libdexfile/Android.bp | 65 | ||||
| -rw-r--r-- | libdexfile/dex/art_dex_file_loader.cc | 20 | ||||
| -rw-r--r-- | libdexfile/dex/art_dex_file_loader.h | 18 | ||||
| -rw-r--r-- | libdexfile/dex/dex_file_loader.cc | 20 | ||||
| -rw-r--r-- | libdexfile/dex/dex_file_loader.h | 18 | ||||
| -rw-r--r-- | libdexfile/external/dex_file_ext.cc | 346 | ||||
| -rw-r--r-- | libdexfile/external/dex_file_supp.cc | 34 | ||||
| -rw-r--r-- | libdexfile/external/include/art_api/ext_dex_file.h | 248 | ||||
| -rw-r--r-- | libdexfile/external/libdexfile_external.map.txt | 13 | ||||
| -rw-r--r-- | runtime/Android.bp | 2 | ||||
| -rw-r--r-- | runtime/jit/jit.cc | 9 | ||||
| -rw-r--r-- | runtime/jit/jit.h | 4 | ||||
| -rw-r--r-- | test/566-polymorphic-inlining/polymorphic_inline.cc | 2 | ||||
| -rw-r--r-- | test/570-checker-osr/osr.cc | 2 | ||||
| -rw-r--r-- | test/common/runtime_state.cc | 2 |
18 files changed, 768 insertions, 48 deletions
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 9b8bb3e90e..e57bbfa1e1 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -126,11 +126,11 @@ extern "C" void jit_unload(void* handle) { } extern "C" bool jit_compile_method( - void* handle, ArtMethod* method, Thread* self, bool osr) + void* handle, ArtMethod* method, Thread* self, bool baseline, bool osr) REQUIRES_SHARED(Locks::mutator_lock_) { auto* jit_compiler = reinterpret_cast<JitCompiler*>(handle); DCHECK(jit_compiler != nullptr); - return jit_compiler->CompileMethod(self, method, osr); + return jit_compiler->CompileMethod(self, method, baseline, osr); } extern "C" void jit_types_loaded(void* handle, mirror::Class** types, size_t count) @@ -181,7 +181,7 @@ JitCompiler::~JitCompiler() { } } -bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool osr) { +bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool baseline, bool osr) { SCOPED_TRACE << "JIT compiling " << method->PrettyMethod(); DCHECK(!method->IsProxyMethod()); @@ -198,7 +198,7 @@ bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool osr) { TimingLogger::ScopedTiming t2("Compiling", &logger); JitCodeCache* const code_cache = runtime->GetJit()->GetCodeCache(); success = compiler_driver_->GetCompiler()->JitCompile( - self, code_cache, method, /* baseline= */ false, osr, jit_logger_.get()); + self, code_cache, method, baseline, osr, jit_logger_.get()); } // Trim maps to reduce memory usage. diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h index d201611d79..29d2761348 100644 --- a/compiler/jit/jit_compiler.h +++ b/compiler/jit/jit_compiler.h @@ -37,7 +37,7 @@ class JitCompiler { virtual ~JitCompiler(); // Compilation entrypoint. Returns whether the compilation succeeded. - bool CompileMethod(Thread* self, ArtMethod* method, bool osr) + bool CompileMethod(Thread* self, ArtMethod* method, bool baseline, bool osr) REQUIRES_SHARED(Locks::mutator_lock_); const CompilerOptions& GetCompilerOptions() const { diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 5bd1122698..50b13c842b 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -243,7 +243,8 @@ void IntrinsicCodeGenerator ## Arch::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNU // compilation. #define UNREACHABLE_INTRINSIC(Arch, Name) \ void IntrinsicLocationsBuilder ## Arch::Visit ## Name(HInvoke* invoke) { \ - if (!codegen_->GetCompilerOptions().IsBaseline()) { \ + if (Runtime::Current()->IsAotCompiler() && \ + !codegen_->GetCompilerOptions().IsBaseline()) { \ LOG(FATAL) << "Unreachable: intrinsic " << invoke->GetIntrinsic() \ << " should have been converted to HIR"; \ } \ diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp index 7f25f02dea..4d6aa5c8ac 100644 --- a/libdexfile/Android.bp +++ b/libdexfile/Android.bp @@ -131,6 +131,71 @@ art_cc_library { ], } +cc_library_headers { + name: "libdexfile_external_headers", + host_supported: true, + header_libs: ["libbase_headers"], + export_header_lib_headers: ["libbase_headers"], + export_include_dirs: ["external/include"], + + target: { + windows: { + enabled: true, + }, + }, +} + +cc_library { + name: "libdexfile_external", + host_supported: true, + srcs: [ + "external/dex_file_ext.cc", + ], + header_libs: ["libdexfile_external_headers"], + shared_libs: [ + "libbase", + "libdexfile", + ], + + // TODO(b/120670568): Enable this when linking bug is fixed. + // stubs: { + // symbol_file: "external/libdexfile_external.map.txt", + // versions: ["1"], + // }, + + // Hide symbols using version scripts for targets that support it, i.e. all + // but Darwin. + // TODO(b/120670568): Clean this up when stubs above is enabled. + target: { + android: { + version_script: "external/libdexfile_external.map.txt", + }, + linux_bionic: { + version_script: "external/libdexfile_external.map.txt", + }, + linux_glibc: { + version_script: "external/libdexfile_external.map.txt", + }, + windows: { + version_script: "external/libdexfile_external.map.txt", + }, + }, +} + +// Support library with a C++ API for accessing the libdexfile API for external +// (non-ART) users. They should link to their own instance of this (either +// statically or through linker namespaces). +cc_library { + name: "libdexfile_support", + host_supported: true, + srcs: [ + "external/dex_file_supp.cc", + ], + header_libs: ["libdexfile_external_headers"], + shared_libs: ["libdexfile_external"], + export_header_lib_headers: ["libdexfile_external_headers"], +} + art_cc_test { name: "art_libdexfile_tests", defaults: [ diff --git a/libdexfile/dex/art_dex_file_loader.cc b/libdexfile/dex/art_dex_file_loader.cc index 20a519bf99..ae1322d059 100644 --- a/libdexfile/dex/art_dex_file_loader.cc +++ b/libdexfile/dex/art_dex_file_loader.cc @@ -156,14 +156,16 @@ bool ArtDexFileLoader::GetMultiDexChecksums(const char* filename, return false; } -std::unique_ptr<const DexFile> ArtDexFileLoader::Open(const uint8_t* base, - size_t size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - bool verify, - bool verify_checksum, - std::string* error_msg) const { +std::unique_ptr<const DexFile> ArtDexFileLoader::Open( + const uint8_t* base, + size_t size, + const std::string& location, + uint32_t location_checksum, + const OatDexFile* oat_dex_file, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::unique_ptr<DexFileContainer> container) const { ScopedTrace trace(std::string("Open dex file from RAM ") + location); return OpenCommon(base, size, @@ -175,7 +177,7 @@ std::unique_ptr<const DexFile> ArtDexFileLoader::Open(const uint8_t* base, verify, verify_checksum, error_msg, - /*container=*/ nullptr, + std::move(container), /*verify_result=*/ nullptr); } diff --git a/libdexfile/dex/art_dex_file_loader.h b/libdexfile/dex/art_dex_file_loader.h index 40d4673625..d41eac5329 100644 --- a/libdexfile/dex/art_dex_file_loader.h +++ b/libdexfile/dex/art_dex_file_loader.h @@ -54,14 +54,16 @@ class ArtDexFileLoader : public DexFileLoader { bool* only_contains_uncompressed_dex = nullptr) const override; // Opens .dex file, backed by existing memory - std::unique_ptr<const DexFile> Open(const uint8_t* base, - size_t size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - bool verify, - bool verify_checksum, - std::string* error_msg) const override; + std::unique_ptr<const DexFile> Open( + const uint8_t* base, + size_t size, + const std::string& location, + uint32_t location_checksum, + const OatDexFile* oat_dex_file, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::unique_ptr<DexFileContainer> container = nullptr) const override; // Opens .dex file that has been memory-mapped by the caller. std::unique_ptr<const DexFile> Open(const std::string& location, diff --git a/libdexfile/dex/dex_file_loader.cc b/libdexfile/dex/dex_file_loader.cc index 3667c8c289..1884bcf3f8 100644 --- a/libdexfile/dex/dex_file_loader.cc +++ b/libdexfile/dex/dex_file_loader.cc @@ -212,14 +212,16 @@ bool DexFileLoader::GetMultiDexChecksums( return false; } -std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base, - size_t size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - bool verify, - bool verify_checksum, - std::string* error_msg) const { +std::unique_ptr<const DexFile> DexFileLoader::Open( + const uint8_t* base, + size_t size, + const std::string& location, + uint32_t location_checksum, + const OatDexFile* oat_dex_file, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::unique_ptr<DexFileContainer> container) const { return OpenCommon(base, size, /*data_base=*/ nullptr, @@ -230,7 +232,7 @@ std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base, verify, verify_checksum, error_msg, - /*container=*/ nullptr, + std::move(container), /*verify_result=*/ nullptr); } diff --git a/libdexfile/dex/dex_file_loader.h b/libdexfile/dex/dex_file_loader.h index 8fc836e0f5..49e177fce6 100644 --- a/libdexfile/dex/dex_file_loader.h +++ b/libdexfile/dex/dex_file_loader.h @@ -121,14 +121,16 @@ class DexFileLoader { bool* zip_file_only_contains_uncompress_dex = nullptr) const; // Opens .dex file, backed by existing memory - virtual std::unique_ptr<const DexFile> Open(const uint8_t* base, - size_t size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - bool verify, - bool verify_checksum, - std::string* error_msg) const; + virtual std::unique_ptr<const DexFile> Open( + const uint8_t* base, + size_t size, + const std::string& location, + uint32_t location_checksum, + const OatDexFile* oat_dex_file, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::unique_ptr<DexFileContainer> container = nullptr) const; // Open a dex file with a separate data section. virtual std::unique_ptr<const DexFile> OpenWithDataSection( diff --git a/libdexfile/external/dex_file_ext.cc b/libdexfile/external/dex_file_ext.cc new file mode 100644 index 0000000000..3c193f4eb5 --- /dev/null +++ b/libdexfile/external/dex_file_ext.cc @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <inttypes.h> +#include <stdint.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <cerrno> +#include <cstring> +#include <map> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include <android-base/logging.h> +#include <android-base/macros.h> +#include <android-base/mapped_file.h> +#include <android-base/stringprintf.h> + +#include <dex/class_accessor-inl.h> +#include <dex/code_item_accessors-inl.h> +#include <dex/dex_file-inl.h> +#include <dex/dex_file_loader.h> + +#include "art_api/ext_dex_file.h" + +extern "C" class ExtDexFileString { + public: + const std::string str_; +}; + +namespace art { +namespace { + +const ExtDexFileString empty_string{""}; + +struct MethodCacheEntry { + int32_t offset; // Offset relative to the start of the dex file header. + int32_t len; + int32_t index; // Method index. + std::string name; // Method name. Not filled in for all cache entries. +}; + +class MappedFileContainer : public DexFileContainer { + public: + explicit MappedFileContainer(std::unique_ptr<android::base::MappedFile>&& map) + : map_(std::move(map)) {} + ~MappedFileContainer() override {} + int GetPermissions() override { return 0; } + bool IsReadOnly() override { return true; } + bool EnableWrite() override { return false; } + bool DisableWrite() override { return false; } + + private: + std::unique_ptr<android::base::MappedFile> map_; + DISALLOW_COPY_AND_ASSIGN(MappedFileContainer); +}; + +} // namespace +} // namespace art + +extern "C" { + +const ExtDexFileString* ExtDexFileMakeString(const char* str) { + if (str[0] == '\0') { + return &art::empty_string; + } + return new ExtDexFileString{str}; +} + +const char* ExtDexFileGetString(const ExtDexFileString* ext_string, /*out*/ size_t* size) { + DCHECK(ext_string != nullptr); + *size = ext_string->str_.size(); + return ext_string->str_.data(); +} + +void ExtDexFileFreeString(const ExtDexFileString* ext_string) { + DCHECK(ext_string != nullptr); + if (ext_string != &art::empty_string) { + delete (ext_string); + } +} + +// Wraps DexFile to add the caching needed by the external interface. This is +// what gets passed over as ExtDexFile*. +class ExtDexFile { + // Method cache for GetMethodInfoForOffset. This is populated as we iterate + // sequentially through the class defs. MethodCacheEntry.name is only set for + // methods returned by GetMethodInfoForOffset. + std::map<int32_t, art::MethodCacheEntry> method_cache_; + + // Index of first class def for which method_cache_ isn't complete. + uint32_t class_def_index_ = 0; + + public: + std::unique_ptr<const art::DexFile> dex_file_; + explicit ExtDexFile(std::unique_ptr<const art::DexFile>&& dex_file) + : dex_file_(std::move(dex_file)) {} + + art::MethodCacheEntry* GetMethodCacheEntryForOffset(int64_t dex_offset) { + // First look in the method cache. + auto it = method_cache_.upper_bound(dex_offset); + if (it != method_cache_.end() && dex_offset >= it->second.offset) { + return &it->second; + } + + for (; class_def_index_ < dex_file_->NumClassDefs(); class_def_index_++) { + art::ClassAccessor accessor(*dex_file_, class_def_index_); + + for (const art::ClassAccessor::Method& method : accessor.GetMethods()) { + art::CodeItemInstructionAccessor code = method.GetInstructions(); + if (!code.HasCodeItem()) { + continue; + } + + int32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin(); + int32_t len = code.InsnsSizeInBytes(); + int32_t index = method.GetIndex(); + auto res = + method_cache_.emplace(offset + len, art::MethodCacheEntry{offset, len, index, ""}); + if (offset <= dex_offset && dex_offset < offset + len) { + return &res.first->second; + } + } + } + + return nullptr; + } + + const std::string& GetMethodName(art::MethodCacheEntry& entry) { + if (entry.name.empty()) { + entry.name = dex_file_->PrettyMethod(entry.index, false); + } + return entry.name; + } +}; + +int ExtDexFileOpenFromMemory(const void* addr, + /*inout*/ size_t* size, + const char* location, + /*out*/ const ExtDexFileString** ext_error_msg, + /*out*/ ExtDexFile** ext_dex_file) { + if (*size < sizeof(art::DexFile::Header)) { + *size = sizeof(art::DexFile::Header); + *ext_error_msg = nullptr; + return false; + } + + const art::DexFile::Header* header = reinterpret_cast<const art::DexFile::Header*>(addr); + uint32_t file_size = header->file_size_; + if (art::CompactDexFile::IsMagicValid(header->magic_)) { + // Compact dex files store the data section separately so that it can be shared. + // Therefore we need to extend the read memory range to include it. + // TODO: This might be wasteful as we might read data in between as well. + // In practice, this should be fine, as such sharing only happens on disk. + uint32_t computed_file_size; + if (__builtin_add_overflow(header->data_off_, header->data_size_, &computed_file_size)) { + *ext_error_msg = new ExtDexFileString{ + android::base::StringPrintf("Corrupt CompactDexFile header in '%s'", location)}; + return false; + } + if (computed_file_size > file_size) { + file_size = computed_file_size; + } + } else if (!art::StandardDexFile::IsMagicValid(header->magic_)) { + *ext_error_msg = new ExtDexFileString{ + android::base::StringPrintf("Unrecognized dex file header in '%s'", location)}; + return false; + } + + if (*size < file_size) { + *size = file_size; + *ext_error_msg = nullptr; + return false; + } + + std::string loc_str(location); + art::DexFileLoader loader; + std::string error_msg; + std::unique_ptr<const art::DexFile> dex_file = loader.Open(static_cast<const uint8_t*>(addr), + *size, + loc_str, + header->checksum_, + /*oat_dex_file=*/nullptr, + /*verify=*/false, + /*verify_checksum=*/false, + &error_msg); + if (dex_file == nullptr) { + *ext_error_msg = new ExtDexFileString{std::move(error_msg)}; + return false; + } + + *ext_dex_file = new ExtDexFile(std::move(dex_file)); + return true; +} + +int ExtDexFileOpenFromFd(int fd, + off_t offset, + const char* location, + /*out*/ const ExtDexFileString** ext_error_msg, + /*out*/ ExtDexFile** ext_dex_file) { + size_t length; + { + struct stat sbuf; + std::memset(&sbuf, 0, sizeof(sbuf)); + if (fstat(fd, &sbuf) == -1) { + *ext_error_msg = new ExtDexFileString{ + android::base::StringPrintf("fstat '%s' failed: %s", location, std::strerror(errno))}; + return false; + } + if (S_ISDIR(sbuf.st_mode)) { + *ext_error_msg = new ExtDexFileString{ + android::base::StringPrintf("Attempt to mmap directory '%s'", location)}; + return false; + } + length = sbuf.st_size; + } + + if (length < offset + sizeof(art::DexFile::Header)) { + *ext_error_msg = new ExtDexFileString{android::base::StringPrintf( + "Offset %" PRId64 " too large for '%s' of size %zu", int64_t{offset}, location, length)}; + return false; + } + + // Cannot use MemMap in libartbase here, because it pulls in dlopen which we + // can't have when being compiled statically. + std::unique_ptr<android::base::MappedFile> map = + android::base::MappedFile::FromFd(fd, offset, length, PROT_READ); + if (map == nullptr) { + *ext_error_msg = new ExtDexFileString{ + android::base::StringPrintf("mmap '%s' failed: %s", location, std::strerror(errno))}; + return false; + } + + const art::DexFile::Header* header = reinterpret_cast<const art::DexFile::Header*>(map->data()); + uint32_t file_size; + if (__builtin_add_overflow(offset, header->file_size_, &file_size)) { + *ext_error_msg = + new ExtDexFileString{android::base::StringPrintf("Corrupt header in '%s'", location)}; + return false; + } + if (length < file_size) { + *ext_error_msg = new ExtDexFileString{ + android::base::StringPrintf("Dex file '%s' too short: expected %" PRIu32 ", got %" PRIu64, + location, + file_size, + uint64_t{length})}; + return false; + } + + void* addr = map->data(); + size_t size = map->size(); + auto container = std::make_unique<art::MappedFileContainer>(std::move(map)); + + std::string loc_str(location); + std::string error_msg; + art::DexFileLoader loader; + std::unique_ptr<const art::DexFile> dex_file = loader.Open(reinterpret_cast<const uint8_t*>(addr), + size, + loc_str, + header->checksum_, + /*oat_dex_file=*/nullptr, + /*verify=*/false, + /*verify_checksum=*/false, + &error_msg, + std::move(container)); + if (dex_file == nullptr) { + *ext_error_msg = new ExtDexFileString{std::move(error_msg)}; + return false; + } + *ext_dex_file = new ExtDexFile(std::move(dex_file)); + return true; +} + +int ExtDexFileGetMethodInfoForOffset(ExtDexFile* ext_dex_file, + int64_t dex_offset, + /*out*/ ExtDexFileMethodInfo* method_info) { + if (!ext_dex_file->dex_file_->IsInDataSection(ext_dex_file->dex_file_->Begin() + dex_offset)) { + return false; // The DEX offset is not within the bytecode of this dex file. + } + + if (ext_dex_file->dex_file_->IsCompactDexFile()) { + // The data section of compact dex files might be shared. + // Check the subrange unique to this compact dex. + const art::CompactDexFile::Header& cdex_header = + ext_dex_file->dex_file_->AsCompactDexFile()->GetHeader(); + uint32_t begin = cdex_header.data_off_ + cdex_header.OwnedDataBegin(); + uint32_t end = cdex_header.data_off_ + cdex_header.OwnedDataEnd(); + if (dex_offset < begin || dex_offset >= end) { + return false; // The DEX offset is not within the bytecode of this dex file. + } + } + + art::MethodCacheEntry* entry = ext_dex_file->GetMethodCacheEntryForOffset(dex_offset); + if (entry != nullptr) { + method_info->offset = entry->offset; + method_info->len = entry->len; + method_info->name = new ExtDexFileString{ext_dex_file->GetMethodName(*entry)}; + return true; + } + + return false; +} + +void ExtDexFileGetAllMethodInfos(ExtDexFile* ext_dex_file, + int with_signature, + ExtDexFileMethodInfoCallback* method_info_cb, + void* user_data) { + for (art::ClassAccessor accessor : ext_dex_file->dex_file_->GetClasses()) { + for (const art::ClassAccessor::Method& method : accessor.GetMethods()) { + art::CodeItemInstructionAccessor code = method.GetInstructions(); + if (!code.HasCodeItem()) { + continue; + } + + ExtDexFileMethodInfo method_info; + method_info.offset = static_cast<int32_t>(reinterpret_cast<const uint8_t*>(code.Insns()) - + ext_dex_file->dex_file_->Begin()); + method_info.len = code.InsnsSizeInBytes(); + method_info.name = new ExtDexFileString{ + ext_dex_file->dex_file_->PrettyMethod(method.GetIndex(), with_signature)}; + method_info_cb(&method_info, user_data); + } + } +} + +void ExtDexFileFree(ExtDexFile* ext_dex_file) { delete (ext_dex_file); } + +} // extern "C" diff --git a/libdexfile/external/dex_file_supp.cc b/libdexfile/external/dex_file_supp.cc new file mode 100644 index 0000000000..6514c8abf3 --- /dev/null +++ b/libdexfile/external/dex_file_supp.cc @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "art_api/ext_dex_file.h" + +namespace art_api { +namespace dex { + +DexFile::~DexFile() { ExtDexFileFree(ext_dex_file_); } + +MethodInfo DexFile::AbsorbMethodInfo(const ExtDexFileMethodInfo& ext_method_info) { + return {ext_method_info.offset, ext_method_info.len, DexString(ext_method_info.name)}; +} + +void DexFile::AddMethodInfoCallback(const ExtDexFileMethodInfo* ext_method_info, void* ctx) { + auto vect = static_cast<MethodInfoVector*>(ctx); + vect->emplace_back(AbsorbMethodInfo(*ext_method_info)); +} + +} // namespace dex +} // namespace art_api diff --git a/libdexfile/external/include/art_api/ext_dex_file.h b/libdexfile/external/include/art_api/ext_dex_file.h new file mode 100644 index 0000000000..5f64ab1f9e --- /dev/null +++ b/libdexfile/external/include/art_api/ext_dex_file.h @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_EXT_DEX_FILE_H_ +#define ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_EXT_DEX_FILE_H_ + +// Dex file external API + +#include <sys/types.h> + +#include <cstring> +#include <memory> +#include <string> +#include <string_view> +#include <vector> + +#include <android-base/macros.h> + +extern "C" { + +// This is the stable C ABI that backs art_api::dex below. Structs and functions +// may only be added here. +// TODO(b/120978655): Move this to a separate pure C header. +// +// Clients should use the C++ wrappers in art_api::dex instead. + +// Opaque wrapper for an std::string allocated in libdexfile which must be freed +// using ExtDexFileFreeString. +class ExtDexFileString; + +// Returns an ExtDexFileString initialized to the given string. +const ExtDexFileString* ExtDexFileMakeString(const char* str); + +// Returns a pointer to the underlying null-terminated character array and its +// size for the given ExtDexFileString. +const char* ExtDexFileGetString(const ExtDexFileString* ext_string, /*out*/ size_t* size); + +// Frees an ExtDexFileString. +void ExtDexFileFreeString(const ExtDexFileString* ext_string); + +struct ExtDexFileMethodInfo { + int32_t offset; + int32_t len; + const ExtDexFileString* name; +}; + +class ExtDexFile; + +// See art_api::dex::DexFile::OpenFromMemory. Returns true on success. +int ExtDexFileOpenFromMemory(const void* addr, + /*inout*/ size_t* size, + const char* location, + /*out*/ const ExtDexFileString** error_msg, + /*out*/ ExtDexFile** ext_dex_file); + +// See art_api::dex::DexFile::OpenFromFd. Returns true on success. +int ExtDexFileOpenFromFd(int fd, + off_t offset, + const char* location, + /*out*/ const ExtDexFileString** error_msg, + /*out*/ ExtDexFile** ext_dex_file); + +// See art_api::dex::DexFile::GetMethodInfoForOffset. Returns true on success. +int ExtDexFileGetMethodInfoForOffset(ExtDexFile* ext_dex_file, + int64_t dex_offset, + /*out*/ ExtDexFileMethodInfo* method_info); + +typedef void ExtDexFileMethodInfoCallback(const ExtDexFileMethodInfo* ext_method_info, + void* user_data); + +// See art_api::dex::DexFile::GetAllMethodInfos. +void ExtDexFileGetAllMethodInfos(ExtDexFile* ext_dex_file, + int with_signature, + ExtDexFileMethodInfoCallback* method_info_cb, + void* user_data); + +// Frees an ExtDexFile. +void ExtDexFileFree(ExtDexFile* ext_dex_file); + +} // extern "C" + +namespace art_api { +namespace dex { + +// Minimal std::string look-alike for a string returned from libdexfile. +class DexString final { + public: + DexString(DexString&& dex_str) { ReplaceExtString(std::move(dex_str)); } + explicit DexString(const char* str = "") : ext_string_(ExtDexFileMakeString(str)) {} + ~DexString() { ExtDexFileFreeString(ext_string_); } + + DexString& operator=(DexString&& dex_str) { + ReplaceExtString(std::move(dex_str)); + return *this; + } + + const char* data() const { + size_t ignored; + return ExtDexFileGetString(ext_string_, &ignored); + } + const char* c_str() const { return data(); } + + size_t size() const { + size_t len; + (void)ExtDexFileGetString(ext_string_, &len); + return len; + } + size_t length() const { return size(); } + + operator std::string_view() const { + size_t len; + const char* chars = ExtDexFileGetString(ext_string_, &len); + return std::string_view(chars, len); + } + + private: + friend class DexFile; + friend bool operator==(const DexString&, const DexString&); + explicit DexString(const ExtDexFileString* ext_string) : ext_string_(ext_string) {} + const ExtDexFileString* ext_string_; // Owned instance. Never nullptr. + + void ReplaceExtString(DexString&& dex_str) { + ext_string_ = dex_str.ext_string_; + dex_str.ext_string_ = ExtDexFileMakeString(""); + } + + DISALLOW_COPY_AND_ASSIGN(DexString); +}; + +inline bool operator==(const DexString& s1, const DexString& s2) { + size_t l1, l2; + const char* str1 = ExtDexFileGetString(s1.ext_string_, &l1); + const char* str2 = ExtDexFileGetString(s2.ext_string_, &l2); + // Use memcmp to avoid assumption about absence of null characters in the strings. + return l1 == l2 && !std::memcmp(str1, str2, l1); +} + +struct MethodInfo { + int32_t offset; // Code offset relative to the start of the dex file header + int32_t len; // Code length + DexString name; +}; + +inline bool operator==(const MethodInfo& s1, const MethodInfo& s2) { + return s1.offset == s2.offset && s1.len == s2.len && s1.name == s2.name; +} + +// External stable API to access ordinary dex files and CompactDex. This wraps +// the stable C ABI and handles instance ownership. Thread-compatible but not +// thread-safe. +class DexFile { + public: + DexFile(DexFile&& dex_file) { + ext_dex_file_ = dex_file.ext_dex_file_; + dex_file.ext_dex_file_ = nullptr; + } + virtual ~DexFile(); + + // Interprets a chunk of memory as a dex file. As long as *size is too small, + // returns nullptr, sets *size to a new size to try again with, and sets + // *error_msg to "". That might happen repeatedly. Also returns nullptr + // on error in which case *error_msg is set to a nonempty string. + // + // location is a string that describes the dex file, and is preferably its + // path. It is mostly used to make error messages better, and may be "". + // + // The caller must retain the memory. + static std::unique_ptr<DexFile> OpenFromMemory(const void* addr, + size_t* size, + const std::string& location, + /*out*/ std::string* error_msg) { + ExtDexFile* ext_dex_file; + const ExtDexFileString* ext_error_msg = nullptr; + if (ExtDexFileOpenFromMemory(addr, size, location.c_str(), &ext_error_msg, &ext_dex_file)) { + return std::unique_ptr<DexFile>(new DexFile(ext_dex_file)); + } + *error_msg = (ext_error_msg == nullptr) ? "" : std::string(DexString(ext_error_msg)); + return nullptr; + } + + // mmaps the given file offset in the open fd and reads a dexfile from there. + // Returns nullptr on error in which case *error_msg is set. + // + // location is a string that describes the dex file, and is preferably its + // path. It is mostly used to make error messages better, and may be "". + static std::unique_ptr<DexFile> OpenFromFd(int fd, + off_t offset, + const std::string& location, + /*out*/ std::string* error_msg) { + ExtDexFile* ext_dex_file; + const ExtDexFileString* ext_error_msg = nullptr; + if (ExtDexFileOpenFromFd(fd, offset, location.c_str(), &ext_error_msg, &ext_dex_file)) { + return std::unique_ptr<DexFile>(new DexFile(ext_dex_file)); + } + *error_msg = std::string(DexString(ext_error_msg)); + return nullptr; + } + + // Given an offset relative to the start of the dex file header, if there is a + // method whose instruction range includes that offset then returns info about + // it, otherwise returns a struct with offset == 0. + MethodInfo GetMethodInfoForOffset(int64_t dex_offset) { + ExtDexFileMethodInfo ext_method_info; + if (ExtDexFileGetMethodInfoForOffset(ext_dex_file_, dex_offset, &ext_method_info)) { + return AbsorbMethodInfo(ext_method_info); + } + return {/*offset=*/0, /*len=*/0, /*name=*/DexString()}; + } + + // Returns info structs about all methods in the dex file. MethodInfo.name + // receives the full function signature if with_signature is set, otherwise it + // gets the class and method name only. + std::vector<MethodInfo> GetAllMethodInfos(bool with_signature = true) { + MethodInfoVector res; + ExtDexFileGetAllMethodInfos( + ext_dex_file_, with_signature, AddMethodInfoCallback, static_cast<void*>(&res)); + return res; + } + + private: + explicit DexFile(ExtDexFile* ext_dex_file) : ext_dex_file_(ext_dex_file) {} + ExtDexFile* ext_dex_file_; // Owned instance. nullptr only in moved-from zombies. + + typedef std::vector<MethodInfo> MethodInfoVector; + + static MethodInfo AbsorbMethodInfo(const ExtDexFileMethodInfo& ext_method_info); + static void AddMethodInfoCallback(const ExtDexFileMethodInfo* ext_method_info, void* user_data); + + DISALLOW_COPY_AND_ASSIGN(DexFile); +}; + +} // namespace dex +} // namespace art_api + +#endif // ART_LIBDEXFILE_EXTERNAL_INCLUDE_ART_API_EXT_DEX_FILE_H_ diff --git a/libdexfile/external/libdexfile_external.map.txt b/libdexfile/external/libdexfile_external.map.txt new file mode 100644 index 0000000000..450b633d32 --- /dev/null +++ b/libdexfile/external/libdexfile_external.map.txt @@ -0,0 +1,13 @@ +LIBDEXFILE_EXTERNAL_1 { + global: + ExtDexFileFree; + ExtDexFileFreeString; + ExtDexFileGetAllMethodInfos; + ExtDexFileGetMethodInfoForOffset; + ExtDexFileGetString; + ExtDexFileMakeString; + ExtDexFileOpenFromFd; + ExtDexFileOpenFromMemory; + local: + *; +}; diff --git a/runtime/Android.bp b/runtime/Android.bp index 60f1af1aea..4780f16910 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -420,6 +420,8 @@ libart_static_cc_defaults { "libbacktrace", "libbase", "libcutils", + "libdexfile_external", // libunwindstack dependency + "libdexfile_support", // libunwindstack dependency "liblog", "libnativebridge", "libnativeloader", diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index de4dd82ef9..03c97f47f0 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -58,7 +58,7 @@ void* Jit::jit_library_handle_ = nullptr; void* Jit::jit_compiler_handle_ = nullptr; void* (*Jit::jit_load_)(void) = nullptr; void (*Jit::jit_unload_)(void*) = nullptr; -bool (*Jit::jit_compile_method_)(void*, ArtMethod*, Thread*, bool) = nullptr; +bool (*Jit::jit_compile_method_)(void*, ArtMethod*, Thread*, bool, bool) = nullptr; void (*Jit::jit_types_loaded_)(void*, mirror::Class**, size_t count) = nullptr; bool (*Jit::jit_generate_debug_info_)(void*) = nullptr; void (*Jit::jit_update_options_)(void*) = nullptr; @@ -242,7 +242,7 @@ bool Jit::LoadCompilerLibrary(std::string* error_msg) { return true; } -bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) { +bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool baseline, bool osr) { DCHECK(Runtime::Current()->UseJitCompilation()); DCHECK(!method->IsRuntimeMethod()); @@ -272,7 +272,7 @@ bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) { VLOG(jit) << "Compiling method " << ArtMethod::PrettyMethod(method_to_compile) << " osr=" << std::boolalpha << osr; - bool success = jit_compile_method_(jit_compiler_handle_, method_to_compile, self, osr); + bool success = jit_compile_method_(jit_compiler_handle_, method_to_compile, self, baseline, osr); code_cache_->DoneCompiling(method_to_compile, self, osr); if (!success) { VLOG(jit) << "Failed to compile method " @@ -555,6 +555,7 @@ class JitCompileTask final : public Task { enum class TaskKind { kAllocateProfile, kCompile, + kCompileBaseline, kCompileOsr, }; @@ -574,10 +575,12 @@ class JitCompileTask final : public Task { ScopedObjectAccess soa(self); switch (kind_) { case TaskKind::kCompile: + case TaskKind::kCompileBaseline: case TaskKind::kCompileOsr: { Runtime::Current()->GetJit()->CompileMethod( method_, self, + /* baseline= */ (kind_ == TaskKind::kCompileBaseline), /* osr= */ (kind_ == TaskKind::kCompileOsr)); break; } diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index 1cfbb9c282..e5c976669e 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -161,7 +161,7 @@ class Jit { // Create JIT itself. static Jit* Create(JitCodeCache* code_cache, JitOptions* options); - bool CompileMethod(ArtMethod* method, Thread* self, bool osr) + bool CompileMethod(ArtMethod* method, Thread* self, bool baseline, bool osr) REQUIRES_SHARED(Locks::mutator_lock_); const JitCodeCache* GetCodeCache() const { @@ -305,7 +305,7 @@ class Jit { static void* jit_compiler_handle_; static void* (*jit_load_)(void); static void (*jit_unload_)(void*); - static bool (*jit_compile_method_)(void*, ArtMethod*, Thread*, bool); + static bool (*jit_compile_method_)(void*, ArtMethod*, Thread*, bool, bool); static void (*jit_types_loaded_)(void*, mirror::Class**, size_t count); static void (*jit_update_options_)(void*); static bool (*jit_generate_debug_info_)(void*); diff --git a/test/566-polymorphic-inlining/polymorphic_inline.cc b/test/566-polymorphic-inlining/polymorphic_inline.cc index 17ccd9a781..00827cf8d5 100644 --- a/test/566-polymorphic-inlining/polymorphic_inline.cc +++ b/test/566-polymorphic-inlining/polymorphic_inline.cc @@ -46,7 +46,7 @@ static void do_checks(jclass cls, const char* method_name) { usleep(1000); } // Will either ensure it's compiled or do the compilation itself. - jit->CompileMethod(method, soa.Self(), /* osr */ false); + jit->CompileMethod(method, soa.Self(), /*baseline=*/ false, /*osr=*/ false); } CodeInfo info(header); diff --git a/test/570-checker-osr/osr.cc b/test/570-checker-osr/osr.cc index b2b363447f..dc0e94cbc7 100644 --- a/test/570-checker-osr/osr.cc +++ b/test/570-checker-osr/osr.cc @@ -128,7 +128,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_ensureHasOsrCode(JNIEnv* env, // Sleep to yield to the compiler thread. usleep(1000); // Will either ensure it's compiled or do the compilation itself. - jit->CompileMethod(m, Thread::Current(), /* osr */ true); + jit->CompileMethod(m, Thread::Current(), /*baseline=*/ false, /*osr=*/ true); } }); } diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index 65127fcab1..55631a9651 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -227,7 +227,7 @@ static void ForceJitCompiled(Thread* self, ArtMethod* method) REQUIRES(!Locks::m // Make sure there is a profiling info, required by the compiler. ProfilingInfo::Create(self, method, /* retry_allocation */ true); // Will either ensure it's compiled or do the compilation itself. - jit->CompileMethod(method, self, /* osr */ false); + jit->CompileMethod(method, self, /*baseline=*/ false, /*osr=*/ false); } } } |