Rewrite profile file format.
The new format contains one mandatory section and several
optional sections. This allows extending the profile with
new sections that shall be ignored by old versions of ART.
We add an "extra descriptors" section to support class
references without a `dex::TypeId` in the referencing dex
file. Type indexes between the dex file's `NumTypeIds()`
and `DexFile::kDexNoIndex16` are used to index these extra
descriptors. This prepares for collecting array classes
which shall be tied to the element type's dex file even
when the array type is not needed by that dex file and has
been used only from another dex file. It also allows inline
caches to be self-contained, so we can remove the profile
index from data structures and serialized data.
The creation of the the binary profile from text files is
updated to correctly allow array types to be stored as the
profiled classes using the "extra descriptors". However,
the interface for filling in inline caches remains unchanged
for now, so we require a `TypeId` in one of the processed
dex files. The data collection by JIT has not been updated.
(cherry picked from commit c63d9672264e894d8d409e8d582b4e086b26abca)
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing --speed-profile
Test: boots.
Test: atest BootImageProfileTest
Bug: 148067697
Merged-In: Idd5f709bdc0ab4a3c7480d69d1dfac72d6e818fc
Change-Id: I99b314c3de4e4bc0c515b86336ecbb283107ee38
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 9e1965d..1d9520d 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -666,58 +666,23 @@
}
DCHECK_LE(dex_pc_data.classes.size(), InlineCache::kIndividualCacheSize);
- Thread* self = Thread::Current();
- // We need to resolve the class relative to the containing dex file.
- // So first, build a mapping from the index of dex file in the profile to
- // its dex cache. This will avoid repeating the lookup when walking over
- // the inline cache types.
- ScopedArenaAllocator allocator(graph_->GetArenaStack());
- ScopedArenaVector<ObjPtr<mirror::DexCache>> dex_profile_index_to_dex_cache(
- pci->GetNumberOfDexFiles(), nullptr, allocator.Adapter(kArenaAllocMisc));
- const std::vector<const DexFile*>& dex_files =
- codegen_->GetCompilerOptions().GetDexFilesForOatFile();
- for (const ProfileCompilationInfo::ClassReference& class_ref : dex_pc_data.classes) {
- if (dex_profile_index_to_dex_cache[class_ref.dex_profile_index] == nullptr) {
- ProfileCompilationInfo::ProfileIndexType profile_index = class_ref.dex_profile_index;
- const DexFile* dex_file = pci->FindDexFileForProfileIndex(profile_index, dex_files);
- if (dex_file == nullptr) {
- VLOG(compiler) << "Could not find profiled dex file: "
- << pci->DumpDexReference(profile_index);
- return kInlineCacheMissingTypes;
- }
- dex_profile_index_to_dex_cache[class_ref.dex_profile_index] =
- caller_compilation_unit_.GetClassLinker()->FindDexCache(self, *dex_file);
- DCHECK(dex_profile_index_to_dex_cache[class_ref.dex_profile_index] != nullptr);
- }
- }
-
- // Walk over the classes and resolve them. If we cannot find a type we return
- // kInlineCacheMissingTypes.
- for (const ProfileCompilationInfo::ClassReference& class_ref : dex_pc_data.classes) {
- ObjPtr<mirror::DexCache> dex_cache =
- dex_profile_index_to_dex_cache[class_ref.dex_profile_index];
- DCHECK(dex_cache != nullptr);
-
- if (!dex_cache->GetDexFile()->IsTypeIndexValid(class_ref.type_index)) {
- VLOG(compiler) << "Profile data corrupt: type index " << class_ref.type_index
- << "is invalid in location" << dex_cache->GetDexFile()->GetLocation();
- return kInlineCacheNoData;
- }
- ObjPtr<mirror::Class> clazz = caller_compilation_unit_.GetClassLinker()->LookupResolvedType(
- class_ref.type_index,
- dex_cache,
- caller_compilation_unit_.GetClassLoader().Get());
- if (clazz != nullptr) {
- DCHECK_NE(classes->RemainingSlots(), 0u);
- classes->NewHandle(clazz);
- } else {
- VLOG(compiler) << "Could not resolve class from inline cache in AOT mode "
+ // Walk over the class descriptors and look up the actual classes.
+ // If we cannot find a type we return kInlineCacheMissingTypes.
+ ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
+ for (const dex::TypeIndex& type_index : dex_pc_data.classes) {
+ const DexFile* dex_file = caller_compilation_unit_.GetDexFile();
+ const char* descriptor = pci->GetTypeDescriptor(dex_file, type_index);
+ ObjPtr<mirror::ClassLoader> class_loader = caller_compilation_unit_.GetClassLoader().Get();
+ ObjPtr<mirror::Class> clazz = class_linker->LookupResolvedType(descriptor, class_loader);
+ if (clazz == nullptr) {
+ VLOG(compiler) << "Could not find class from inline cache in AOT mode "
<< invoke_instruction->GetMethodReference().PrettyMethod()
<< " : "
- << caller_compilation_unit_
- .GetDexFile()->StringByTypeIdx(class_ref.type_index);
+ << descriptor;
return kInlineCacheMissingTypes;
}
+ DCHECK_NE(classes->RemainingSlots(), 0u);
+ classes->NewHandle(clazz);
}
return GetInlineCacheType(*classes);
diff --git a/libartbase/base/bit_memory_region.h b/libartbase/base/bit_memory_region.h
index 5d54445..6168488 100644
--- a/libartbase/base/bit_memory_region.h
+++ b/libartbase/base/bit_memory_region.h
@@ -172,6 +172,24 @@
StoreBits(bit_offset + bit, src.LoadBits(bit, num_bits), num_bits);
}
+ // Or bits from other bit region.
+ ALWAYS_INLINE void OrBits(size_t bit_offset, const BitMemoryRegion& src, size_t bit_length) {
+ // TODO: Load `size_t` chunks (instead of `uint32_t`) from aligned
+ // addresses except for the leading and trailing bits. Refactor to
+ // share code with StoreBits() and maybe other functions.
+ DCHECK_LE(bit_offset, bit_size_);
+ DCHECK_LE(bit_length, bit_size_ - bit_offset);
+ size_t bit = 0;
+ constexpr size_t kNumBits = BitSizeOf<uint32_t>();
+ for (; bit + kNumBits <= bit_length; bit += kNumBits) {
+ size_t old_bits = LoadBits(bit_offset + bit, kNumBits);
+ StoreBits(bit_offset + bit, old_bits | src.LoadBits(bit, kNumBits), kNumBits);
+ }
+ size_t num_bits = bit_length - bit;
+ size_t old_bits = LoadBits(bit_offset + bit, num_bits);
+ StoreBits(bit_offset + bit, old_bits | src.LoadBits(bit, num_bits), num_bits);
+ }
+
// Count the number of set bits within the given bit range.
ALWAYS_INLINE size_t PopCount(size_t bit_offset, size_t bit_length) const {
DCHECK_LE(bit_offset, bit_size_);
@@ -186,6 +204,23 @@
return count;
}
+ // Check if there is any bit set within the given bit range.
+ ALWAYS_INLINE bool HasSomeBitSet(size_t bit_offset, size_t bit_length) const {
+ // TODO: Load `size_t` chunks (instead of `uint32_t`) from aligned
+ // addresses except for the leading and trailing bits. Refactor to
+ // share code with PopCount() and maybe also Compare().
+ DCHECK_LE(bit_offset, bit_size_);
+ DCHECK_LE(bit_length, bit_size_ - bit_offset);
+ size_t bit = 0;
+ constexpr size_t kNumBits = BitSizeOf<uint32_t>();
+ for (; bit + kNumBits <= bit_length; bit += kNumBits) {
+ if (LoadBits(bit_offset + bit, kNumBits) != 0u) {
+ return true;
+ }
+ }
+ return LoadBits(bit_offset + bit, bit_length - bit) != 0u;
+ }
+
static int Compare(const BitMemoryRegion& lhs, const BitMemoryRegion& rhs) {
if (lhs.size_in_bits() != rhs.size_in_bits()) {
return (lhs.size_in_bits() < rhs.size_in_bits()) ? -1 : 1;
diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc
index 3d1acb0..f32c122 100644
--- a/libprofile/profile/profile_compilation_info.cc
+++ b/libprofile/profile/profile_compilation_info.cc
@@ -35,6 +35,7 @@
#include "android-base/file.h"
#include "base/arena_allocator.h"
+#include "base/bit_utils.h"
#include "base/dumpable.h"
#include "base/file_utils.h"
#include "base/logging.h" // For VLOG.
@@ -48,16 +49,15 @@
#include "base/unix_file/fd_file.h"
#include "base/utils.h"
#include "base/zip_archive.h"
+#include "dex/descriptors_names.h"
#include "dex/dex_file_loader.h"
namespace art {
const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
-// Last profile version: merge profiles directly from the file without creating
-// profile_compilation_info object. All the profile line headers are now placed together
-// before corresponding method_encodings and class_ids.
-const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '1', '0', '\0' };
-const uint8_t ProfileCompilationInfo::kProfileVersionForBootImage[] = { '0', '1', '2', '\0' };
+// Last profile version: New extensible profile format.
+const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '1', '3', '\0' };
+const uint8_t ProfileCompilationInfo::kProfileVersionForBootImage[] = { '0', '1', '4', '\0' };
static_assert(sizeof(ProfileCompilationInfo::kProfileVersion) == 4,
"Invalid profile version size");
@@ -77,7 +77,16 @@
static constexpr char kSampleMetadataSeparator = ':';
-static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
+// Note: This used to be PATH_MAX (usually 4096) but that seems excessive
+// and we do not want to rely on that external constant anyway.
+static constexpr uint16_t kMaxDexFileKeyLength = 1024;
+
+// According to dex file specification, there can be more than 2^16 valid method indexes
+// but bytecode uses only 16 bits, so higher method indexes are not very useful (though
+// such methods could be reached through virtual or interface dispatch). Consequently,
+// dex files with more than 2^16 method indexes are not really used and the profile file
+// format does not support higher method indexes.
+static constexpr uint32_t kMaxSupportedMethodIndex = 0xffffu;
// Debug flag to ignore checksums when testing if a method or a class is present in the profile.
// Used to facilitate testing profile guided compilation across a large number of apps
@@ -175,6 +184,163 @@
} // anonymous namespace
+enum class ProfileCompilationInfo::ProfileLoadStatus : uint32_t {
+ kSuccess,
+ kIOError,
+ kBadMagic,
+ kVersionMismatch,
+ kBadData,
+ kMergeError, // Merging failed. There are too many extra descriptors
+ // or classes without TypeId referenced by a dex file.
+};
+
+enum class ProfileCompilationInfo::FileSectionType : uint32_t {
+ // The values of section enumerators and data format for individual sections
+ // must not be changed without changing the profile file version. New sections
+ // can be added at the end and they shall be ignored by old versions of ART.
+
+ // The list of the dex files included in the profile.
+ // There must be exactly one dex file section and it must be first.
+ kDexFiles = 0,
+
+ // Extra descriptors for referencing classes that do not have a `dex::TypeId`
+ // in the referencing dex file, such as classes from a different dex file
+ // (even outside of the dex files in the profile) or array classes that were
+ // used from other dex files or created through reflection.
+ kExtraDescriptors = 1,
+
+ // Classes included in the profile.
+ kClasses = 2,
+
+ // Methods included in the profile, their hotness flags and inline caches.
+ kMethods = 3,
+
+ // The number of known sections.
+ kNumberOfSections = 4
+};
+
+class ProfileCompilationInfo::FileSectionInfo {
+ public:
+ // Constructor for reading from a `ProfileSource`. Data shall be filled from the source.
+ FileSectionInfo() {}
+
+ // Constructor for writing to a file.
+ FileSectionInfo(FileSectionType type,
+ uint32_t file_offset,
+ uint32_t file_size,
+ uint32_t inflated_size)
+ : type_(type),
+ file_offset_(file_offset),
+ file_size_(file_size),
+ inflated_size_(inflated_size) {}
+
+ void SetFileOffset(uint32_t file_offset) {
+ DCHECK_EQ(file_offset_, 0u);
+ DCHECK_NE(file_offset, 0u);
+ file_offset_ = file_offset;
+ }
+
+ FileSectionType GetType() const {
+ return type_;
+ }
+
+ uint32_t GetFileOffset() const {
+ return file_offset_;
+ }
+
+ uint32_t GetFileSize() const {
+ return file_size_;
+ }
+
+ uint32_t GetInflatedSize() const {
+ return inflated_size_;
+ }
+
+ uint32_t GetMemSize() const {
+ return inflated_size_ != 0u ? inflated_size_ : file_size_;
+ }
+
+ private:
+ FileSectionType type_;
+ uint32_t file_offset_;
+ uint32_t file_size_;
+ uint32_t inflated_size_; // If 0, do not inflate and use data from file directly.
+};
+
+// The file header.
+class ProfileCompilationInfo::FileHeader {
+ public:
+ // Constructor for reading from a `ProfileSource`. Data shall be filled from the source.
+ FileHeader() {
+ DCHECK(!IsValid());
+ }
+
+ // Constructor for writing to a file.
+ FileHeader(const uint8_t* version, uint32_t file_section_count)
+ : file_section_count_(file_section_count) {
+ static_assert(sizeof(magic_) == sizeof(kProfileMagic));
+ static_assert(sizeof(version_) == sizeof(kProfileVersion));
+ static_assert(sizeof(version_) == sizeof(kProfileVersionForBootImage));
+ memcpy(magic_, kProfileMagic, sizeof(kProfileMagic));
+ memcpy(version_, version, sizeof(version_));
+ DCHECK_LE(file_section_count, kMaxFileSectionCount);
+ DCHECK(IsValid());
+ }
+
+ bool IsValid() const {
+ return memcmp(magic_, kProfileMagic, sizeof(kProfileMagic)) == 0 &&
+ (memcmp(version_, kProfileVersion, kProfileVersionSize) == 0 ||
+ memcmp(version_, kProfileVersionForBootImage, kProfileVersionSize) == 0) &&
+ file_section_count_ != 0u && // The dex files section is mandatory.
+ file_section_count_ <= kMaxFileSectionCount;
+ }
+
+ const uint8_t* GetVersion() const {
+ DCHECK(IsValid());
+ return version_;
+ }
+
+ ProfileLoadStatus InvalidHeaderMessage(/*out*/ std::string* error_msg) const;
+
+ uint32_t GetFileSectionCount() const {
+ DCHECK(IsValid());
+ return file_section_count_;
+ }
+
+ private:
+ // The upper bound for file section count is used to ensure that there
+ // shall be no arithmetic overflow when calculating size of the header
+ // with section information.
+ static const uint32_t kMaxFileSectionCount;
+
+ uint8_t magic_[4] = {0, 0, 0, 0};
+ uint8_t version_[4] = {0, 0, 0, 0};
+ uint32_t file_section_count_ = 0u;
+};
+
+const uint32_t ProfileCompilationInfo::FileHeader::kMaxFileSectionCount =
+ (std::numeric_limits<uint32_t>::max() - sizeof(FileHeader)) / sizeof(FileSectionInfo);
+
+ProfileCompilationInfo::ProfileLoadStatus
+ProfileCompilationInfo::FileHeader::InvalidHeaderMessage(/*out*/ std::string* error_msg) const {
+ if (memcmp(magic_, kProfileMagic, sizeof(kProfileMagic)) != 0) {
+ *error_msg = "Profile missing magic.";
+ return ProfileLoadStatus::kBadMagic;
+ }
+ if (memcmp(version_, kProfileVersion, sizeof(kProfileVersion)) != 0 &&
+ memcmp(version_, kProfileVersion, sizeof(kProfileVersionForBootImage)) != 0) {
+ *error_msg = "Profile version mismatch.";
+ return ProfileLoadStatus::kVersionMismatch;
+ }
+ if (file_section_count_ == 0u) {
+ *error_msg = "Missing mandatory dex files section.";
+ return ProfileLoadStatus::kBadData;
+ }
+ DCHECK_GT(file_section_count_, kMaxFileSectionCount);
+ *error_msg ="Too many sections.";
+ return ProfileLoadStatus::kBadData;
+}
+
/**
* Encapsulate the source of profile data for loading.
* The source can be either a plain file or a zip file.
@@ -201,12 +367,15 @@
return new ProfileSource(/*fd*/ -1, std::move(mem_map));
}
+ // Seek to the given offset in the source.
+ bool Seek(off_t offset);
+
/**
* Read bytes from this source.
* Reading will advance the current source position so subsequent
* invocations will read from the las position.
*/
- ProfileLoadStatus Read(uint8_t* buffer,
+ ProfileLoadStatus Read(void* buffer,
size_t byte_count,
const std::string& debug_stage,
std::string* error);
@@ -214,9 +383,6 @@
/** Return true if the source has 0 data. */
bool HasEmptyContent() const;
- /** Return true if all the information from this source has been read. */
- bool HasConsumedAllData() const;
-
private:
ProfileSource(int32_t fd, MemMap&& mem_map)
: fd_(fd), mem_map_(std::move(mem_map)), mem_map_cur_(0) {}
@@ -231,27 +397,25 @@
};
// A helper structure to make sure we don't read past our buffers in the loops.
+// Also used for writing but the buffer should be pre-sized correctly for that, so we
+// DCHECK() we do not write beyond the end, rather than returning `false` on failure.
class ProfileCompilationInfo::SafeBuffer {
public:
+ SafeBuffer()
+ : storage_(nullptr),
+ ptr_current_(nullptr),
+ ptr_end_(nullptr) {}
+
explicit SafeBuffer(size_t size)
: storage_(new uint8_t[size]),
ptr_current_(storage_.get()),
ptr_end_(ptr_current_ + size) {}
- // Reads the content of the descriptor at the current position.
- ProfileLoadStatus Fill(ProfileSource& source,
- const std::string& debug_stage,
- /*out*/std::string* error) {
- size_t byte_count = (ptr_end_ - ptr_current_) * sizeof(*ptr_current_);
- uint8_t* buffer = ptr_current_;
- return source.Read(buffer, byte_count, debug_stage, error);
- }
-
// Reads an uint value and advances the current pointer.
template <typename T>
bool ReadUintAndAdvance(/*out*/ T* value) {
static_assert(std::is_unsigned<T>::value, "Type is not unsigned");
- if (sizeof(T) > CountUnreadBytes()) {
+ if (sizeof(T) > GetAvailableBytes()) {
return false;
}
*value = 0;
@@ -262,10 +426,22 @@
return true;
}
+ // Reads a null-terminated string as `std::string_view` and advances the current pointer.
+ bool ReadStringAndAdvance(/*out*/ std::string_view* value) {
+ const void* null_char = memchr(GetCurrentPtr(), 0, GetAvailableBytes());
+ if (null_char == nullptr) {
+ return false;
+ }
+ size_t length = reinterpret_cast<const uint8_t*>(null_char) - GetCurrentPtr();
+ *value = std::string_view(reinterpret_cast<const char*>(GetCurrentPtr()), length);
+ Advance(length + 1u);
+ return true;
+ }
+
// Compares the given data with the content at the current pointer.
// If the contents are equal it advances the current pointer by data_size.
bool CompareAndAdvance(const uint8_t* data, size_t data_size) {
- if (data_size > CountUnreadBytes()) {
+ if (data_size > GetAvailableBytes()) {
return false;
}
if (memcmp(ptr_current_, data, data_size) == 0) {
@@ -275,20 +451,64 @@
return false;
}
+ void WriteAndAdvance(const void* data, size_t data_size) {
+ DCHECK_LE(data_size, GetAvailableBytes());
+ memcpy(ptr_current_, data, data_size);
+ ptr_current_ += data_size;
+ }
+
+ template <typename T>
+ void WriteUintAndAdvance(T value) {
+ static_assert(std::is_integral_v<T>);
+ WriteAndAdvance(&value, sizeof(value));
+ }
+
+ // Deflate a filled buffer. Replaces the internal buffer with a new one, also filled.
+ bool Deflate() {
+ DCHECK_EQ(GetAvailableBytes(), 0u);
+ DCHECK_NE(Size(), 0u);
+ ArrayRef<const uint8_t> in_buffer(Get(), Size());
+ uint32_t output_size = 0;
+ std::unique_ptr<uint8_t[]> compressed_buffer = DeflateBuffer(in_buffer, &output_size);
+ if (compressed_buffer == nullptr) {
+ return false;
+ }
+ storage_ = std::move(compressed_buffer);
+ ptr_current_ = storage_.get() + output_size;
+ ptr_end_ = ptr_current_;
+ return true;
+ }
+
+ // Inflate an unread buffer. Replaces the internal buffer with a new one, also unread.
+ bool Inflate(size_t uncompressed_data_size) {
+ DCHECK(ptr_current_ == storage_.get());
+ DCHECK_NE(Size(), 0u);
+ ArrayRef<const uint8_t> in_buffer(Get(), Size());
+ SafeBuffer uncompressed_buffer(uncompressed_data_size);
+ ArrayRef<uint8_t> out_buffer(uncompressed_buffer.Get(), uncompressed_data_size);
+ int ret = InflateBuffer(in_buffer, out_buffer);
+ if (ret != Z_STREAM_END) {
+ return false;
+ }
+ Swap(uncompressed_buffer);
+ DCHECK(ptr_current_ == storage_.get());
+ return true;
+ }
+
// Advances current pointer by data_size.
void Advance(size_t data_size) {
- DCHECK_LE(data_size, CountUnreadBytes());
+ DCHECK_LE(data_size, GetAvailableBytes());
ptr_current_ += data_size;
}
// Returns the count of unread bytes.
- size_t CountUnreadBytes() {
+ size_t GetAvailableBytes() const {
DCHECK_LE(static_cast<void*>(ptr_current_), static_cast<void*>(ptr_end_));
return (ptr_end_ - ptr_current_) * sizeof(*ptr_current_);
}
// Returns the current pointer.
- const uint8_t* GetCurrentPtr() {
+ uint8_t* GetCurrentPtr() {
return ptr_current_;
}
@@ -297,6 +517,17 @@
return storage_.get();
}
+ // Get the size of the raw buffer.
+ size_t Size() const {
+ return ptr_end_ - storage_.get();
+ }
+
+ void Swap(SafeBuffer& other) {
+ std::swap(storage_, other.storage_);
+ std::swap(ptr_current_, other.ptr_current_);
+ std::swap(ptr_end_, other.ptr_end_);
+ }
+
private:
std::unique_ptr<uint8_t[]> storage_;
uint8_t* ptr_current_;
@@ -307,8 +538,10 @@
: default_arena_pool_(),
allocator_(custom_arena_pool),
info_(allocator_.Adapter(kArenaAllocProfile)),
- profile_key_map_(std::less<const std::string_view>(),
- allocator_.Adapter(kArenaAllocProfile)) {
+ profile_key_map_(std::less<const std::string_view>(), allocator_.Adapter(kArenaAllocProfile)),
+ extra_descriptors_(),
+ extra_descriptors_indexes_(ExtraDescriptorHash(&extra_descriptors_),
+ ExtraDescriptorEquals(&extra_descriptors_)) {
memcpy(version_,
for_boot_image ? kProfileVersionForBootImage : kProfileVersion,
kProfileVersionSize);
@@ -327,8 +560,7 @@
VLOG(profiler) << Dumpable<MemStats>(allocator_.GetMemStats());
}
-void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx,
- const dex::TypeIndex& type_idx) {
+void ProfileCompilationInfo::DexPcData::AddClass(const dex::TypeIndex& type_idx) {
if (is_megamorphic || is_missing_types) {
return;
}
@@ -337,9 +569,8 @@
// element. We do this because emplace() allocates the node before doing the
// lookup and if it then finds an identical element, it shall deallocate the
// node. For Arena allocations, that's essentially a leak.
- ClassReference ref(dex_profile_idx, type_idx);
- auto it = classes.find(ref);
- if (it != classes.end()) {
+ auto lb = classes.lower_bound(type_idx);
+ if (lb != classes.end() && *lb == type_idx) {
// The type index exists.
return;
}
@@ -352,7 +583,7 @@
}
// The type does not exist and the inline cache will not be megamorphic.
- classes.insert(ref);
+ classes.emplace_hint(lb, type_idx);
}
// Transform the actual dex location into a key used to index the dex file in the profile.
@@ -427,6 +658,72 @@
return true;
}
+dex::TypeIndex ProfileCompilationInfo::FindOrCreateTypeIndex(const DexFile& dex_file,
+ TypeReference class_ref) {
+ DCHECK(class_ref.dex_file != nullptr);
+ DCHECK_LT(class_ref.TypeIndex().index_, class_ref.dex_file->NumTypeIds());
+ if (class_ref.dex_file == &dex_file) {
+ // We can use the type index from the `class_ref` as it's a valid index in the `dex_file`.
+ return class_ref.TypeIndex();
+ }
+ // Try to find a `TypeId` in the method's dex file.
+ const char* descriptor = class_ref.dex_file->StringByTypeIdx(class_ref.TypeIndex());
+ return FindOrCreateTypeIndex(dex_file, descriptor);
+}
+
+dex::TypeIndex ProfileCompilationInfo::FindOrCreateTypeIndex(const DexFile& dex_file,
+ const char* descriptor) {
+ const dex::TypeId* type_id = dex_file.FindTypeId(descriptor);
+ if (type_id != nullptr) {
+ return dex_file.GetIndexForTypeId(*type_id);
+ }
+ // Try to find an existing extra descriptor.
+ uint32_t num_type_ids = dex_file.NumTypeIds();
+ uint32_t max_artificial_ids = DexFile::kDexNoIndex16 - num_type_ids;
+ std::string_view descriptor_view(descriptor);
+ auto it = extra_descriptors_indexes_.find(descriptor_view);
+ if (it != extra_descriptors_indexes_.end()) {
+ return (*it < max_artificial_ids) ? dex::TypeIndex(num_type_ids + *it) : dex::TypeIndex();
+ }
+ // Check if inserting the extra descriptor yields a valid artificial type index.
+ if (UNLIKELY(extra_descriptors_.size() >= max_artificial_ids)) {
+ return dex::TypeIndex(); // Invalid.
+ }
+ // Add the descriptor to extra descriptors and return the artificial type index.
+ ExtraDescriptorIndex new_extra_descriptor_index = AddExtraDescriptor(descriptor_view);
+ DCHECK_LT(new_extra_descriptor_index, max_artificial_ids);
+ return dex::TypeIndex(num_type_ids + new_extra_descriptor_index);
+}
+
+bool ProfileCompilationInfo::AddClass(const DexFile& dex_file,
+ dex::TypeIndex type_index,
+ const ProfileSampleAnnotation& annotation) {
+ DCHECK(type_index.IsValid());
+ DCHECK(type_index.index_ <= dex_file.NumTypeIds() ||
+ type_index.index_ - dex_file.NumTypeIds() < extra_descriptors_.size());
+ DexFileData* const data = GetOrAddDexFileData(&dex_file, annotation);
+ if (data == nullptr) { // checksum mismatch
+ return false;
+ }
+ data->class_set.insert(type_index);
+ return true;
+}
+
+bool ProfileCompilationInfo::AddClass(const DexFile& dex_file,
+ const char* descriptor,
+ const ProfileSampleAnnotation& annotation) {
+ DexFileData* const data = GetOrAddDexFileData(&dex_file, annotation);
+ if (data == nullptr) { // checksum mismatch
+ return false;
+ }
+ dex::TypeIndex type_index = FindOrCreateTypeIndex(dex_file, descriptor);
+ if (!type_index.IsValid()) {
+ return false;
+ }
+ data->class_set.insert(type_index);
+ return true;
+}
+
bool ProfileCompilationInfo::MergeWith(const std::string& filename) {
std::string error;
#ifdef _WIN32
@@ -485,7 +782,8 @@
}
if (clear_if_invalid &&
- ((status == ProfileLoadStatus::kVersionMismatch) ||
+ ((status == ProfileLoadStatus::kBadMagic) ||
+ (status == ProfileLoadStatus::kVersionMismatch) ||
(status == ProfileLoadStatus::kBadData))) {
LOG(WARNING) << "Clearing bad or obsolete profile data from file "
<< filename << ": " << error;
@@ -547,278 +845,250 @@
}
// Returns true if all the bytes were successfully written to the file descriptor.
-static bool WriteBuffer(int fd, const uint8_t* buffer, size_t byte_count) {
+static bool WriteBuffer(int fd, const void* buffer, size_t byte_count) {
while (byte_count > 0) {
int bytes_written = TEMP_FAILURE_RETRY(write(fd, buffer, byte_count));
if (bytes_written == -1) {
return false;
}
byte_count -= bytes_written; // Reduce the number of remaining bytes.
- buffer += bytes_written; // Move the buffer forward.
+ reinterpret_cast<const uint8_t*&>(buffer) += bytes_written; // Move the buffer forward.
}
return true;
}
-// Add the string bytes to the buffer.
-static void AddStringToBuffer(std::vector<uint8_t>* buffer, const std::string& value) {
- buffer->insert(buffer->end(), value.begin(), value.end());
-}
-
-// Insert each byte, from low to high into the buffer.
-template <typename T>
-static void AddUintToBuffer(std::vector<uint8_t>* buffer, T value) {
- for (size_t i = 0; i < sizeof(T); i++) {
- buffer->push_back((value >> (i * kBitsPerByte)) & 0xff);
- }
-}
-
-static constexpr size_t kLineHeaderSize =
- 2 * sizeof(uint16_t) + // class_set.size + dex_location.size
- 3 * sizeof(uint32_t); // method_map.size + checksum + num_method_ids
-
/**
* Serialization format:
- * [profile_header, zipped[[profile_line_header1, profile_line_header2...],[profile_line_data1,
- * profile_line_data2...]]
- * profile_header:
- * magic,version,number_of_dex_files,uncompressed_size_of_zipped_data,compressed_data_size
- * profile_line_header:
- * profile_key,number_of_classes,methods_region_size,dex_location_checksum,num_method_ids
- * profile_line_data:
- * method_encoding_1,method_encoding_2...,class_id1,class_id2...,method_flags bitmap,
- * The method_encoding is:
- * method_id,number_of_inline_caches,inline_cache1,inline_cache2...
- * The inline_cache is:
- * dex_pc,[M|dex_map_size], dex_profile_index,class_id1,class_id2...,dex_profile_index2,...
- * dex_map_size is the number of dex_indeces that follows.
- * Classes are grouped per their dex files and the line
- * `dex_profile_index,class_id1,class_id2...,dex_profile_index2,...` encodes the
- * mapping from `dex_profile_index` to the set of classes `class_id1,class_id2...`
- * M stands for megamorphic or missing types and it's encoded as either
- * the byte kIsMegamorphicEncoding or kIsMissingTypesEncoding.
- * When present, there will be no class ids following.
+ *
+ * The file starts with a header and section information:
+ * FileHeader
+ * FileSectionInfo[]
+ * The first FileSectionInfo must be for the DexFiles section.
+ *
+ * The rest of the file is allowed to contain different sections in any order,
+ * at arbitrary offsets, with any gaps betweeen them and each section can be
+ * either plaintext or separately zipped. However, we're writing sections
+ * without any gaps with the following order and compression:
+ * DexFiles - mandatory, plaintext
+ * ExtraDescriptors - optional, zipped
+ * Classes - optional, zipped
+ * Methods - optional, zipped
+ *
+ * DexFiles:
+ * number_of_dex_files
+ * (checksum,num_type_ids,num_method_ids,profile_key)[number_of_dex_files]
+ * where `profile_key` is a NULL-terminated string.
+ *
+ * ExtraDescriptors:
+ * number_of_extra_descriptors
+ * (extra_descriptor)[number_of_extra_descriptors]
+ * where `extra_descriptor` is a NULL-terminated string.
+ *
+ * Classes contains records for any number of dex files, each consisting of:
+ * profile_index
+ * number_of_classes
+ * type_index_diff[number_of_classes]
+ * where instead of storing plain sorted type indexes, we store their differences
+ * as smaller numbers are likely to compress better.
+ *
+ * Methods contains records for any number of dex files, each consisting of:
+ * profile_index
+ * following_data_size // For easy skipping of remaining data when dex file is filtered out.
+ * method_flags
+ * bitmap_data
+ * method_encoding[] // Until the size indicated by `following_data_size`.
+ * where `bitmap_data` contains `num_method_ids` bits for each bit set in `method_flags` other
+ * than "hot" (the size of `bitmap_data` is rounded up to whole bytes) and `method_encoding[]`
+ * contains data for hot methods. The `method_encoding` is:
+ * method_index_diff
+ * number_of_inline_caches
+ * inline_cache_encoding[number_of_inline_caches]
+ * where differences in method indexes are used for better compression,
+ * and the `inline_cache_encoding` is
+ * dex_pc
+ * (M|dex_map_size)
+ * type_index_diff[dex_map_size]
+ * where `M` stands for special encodings indicating missing types (kIsMissingTypesEncoding)
+ * or memamorphic call (kIsMegamorphicEncoding) which both imply `dex_map_size == 0`.
**/
bool ProfileCompilationInfo::Save(int fd) {
uint64_t start = NanoTime();
ScopedTrace trace(__PRETTY_FUNCTION__);
DCHECK_GE(fd, 0);
- // Use a vector wrapper to avoid keeping track of offsets when we add elements.
- std::vector<uint8_t> buffer;
- if (!WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic))) {
- return false;
+ // Collect uncompressed section sizes.
+ // Use `uint64_t` and assume this cannot overflow as we would have run out of memory.
+ uint64_t extra_descriptors_section_size = 0u;
+ if (!extra_descriptors_.empty()) {
+ extra_descriptors_section_size += sizeof(uint16_t); // Number of descriptors.
+ for (const std::string& descriptor : extra_descriptors_) {
+ extra_descriptors_section_size += descriptor.size() + 1u; // Null-terminated string.
+ }
}
- if (!WriteBuffer(fd, version_, sizeof(version_))) {
- return false;
- }
-
+ uint64_t dex_files_section_size = sizeof(ProfileIndexType); // Number of dex files.
+ uint64_t classes_section_size = 0u;
+ uint64_t methods_section_size = 0u;
DCHECK_LE(info_.size(), MaxProfileIndex());
- WriteProfileIndex(&buffer, static_cast<ProfileIndexType>(info_.size()));
-
- uint32_t required_capacity = 0;
- for (const std::unique_ptr<DexFileData>& dex_data_ptr : info_) {
- const DexFileData& dex_data = *dex_data_ptr;
- uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
- required_capacity += kLineHeaderSize +
- dex_data.profile_key.size() +
- sizeof(uint16_t) * dex_data.class_set.size() +
- methods_region_size +
- dex_data.bitmap_storage.size();
- }
- // Allow large profiles for non target builds for the case where we are merging many profiles
- // to generate a boot image profile.
- VLOG(profiler) << "Required capacity: " << required_capacity << " bytes.";
- if (required_capacity > GetSizeErrorThresholdBytes()) {
- LOG(ERROR) << "Profile data size exceeds "
- << GetSizeErrorThresholdBytes()
- << " bytes. Profile will not be written to disk."
- << " It requires " << required_capacity << " bytes.";
- return false;
- }
- AddUintToBuffer(&buffer, required_capacity);
- if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
- return false;
- }
- // Make sure that the buffer has enough capacity to avoid repeated resizings
- // while we add data.
- buffer.reserve(required_capacity);
- buffer.clear();
-
- // Dex files must be written in the order of their profile index. This
- // avoids writing the index in the output file and simplifies the parsing logic.
- // Write profile line headers.
- for (const std::unique_ptr<DexFileData>& dex_data_ptr : info_) {
- const DexFileData& dex_data = *dex_data_ptr;
-
- if (dex_data.profile_key.size() >= kMaxDexFileKeyLength) {
+ for (const std::unique_ptr<DexFileData>& dex_data : info_) {
+ if (dex_data->profile_key.size() > kMaxDexFileKeyLength) {
LOG(WARNING) << "DexFileKey exceeds allocated limit";
return false;
}
-
- uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
-
- DCHECK_LE(dex_data.profile_key.size(), std::numeric_limits<uint16_t>::max());
- DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max());
- // Write profile line header.
- AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.profile_key.size()));
- AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.class_set.size()));
- AddUintToBuffer(&buffer, methods_region_size); // uint32_t
- AddUintToBuffer(&buffer, dex_data.checksum); // uint32_t
- AddUintToBuffer(&buffer, dex_data.num_method_ids); // uint32_t
-
- AddStringToBuffer(&buffer, dex_data.profile_key);
+ dex_files_section_size +=
+ 3 * sizeof(uint32_t) + // Checksum, num_type_ids, num_method_ids.
+ dex_data->profile_key.size() + 1u; // Null-terminated key.
+ classes_section_size += dex_data->ClassesDataSize();
+ methods_section_size += dex_data->MethodsDataSize();
}
- for (const std::unique_ptr<DexFileData>& dex_data_ptr : info_) {
- const DexFileData& dex_data = *dex_data_ptr;
+ const uint32_t file_section_count =
+ /* dex files */ 1u +
+ /* extra descriptors */ (extra_descriptors_section_size != 0u ? 1u : 0u) +
+ /* classes */ (classes_section_size != 0u ? 1u : 0u) +
+ /* methods */ (methods_section_size != 0u ? 1u : 0u);
+ uint64_t header_and_infos_size =
+ sizeof(FileHeader) + file_section_count * sizeof(FileSectionInfo);
- // Note that we allow dex files without any methods or classes, so that
- // inline caches can refer valid dex files.
-
- uint16_t last_method_index = 0;
- for (const auto& method_it : dex_data.method_map) {
- // Store the difference between the method indices. The SafeMap is ordered by
- // method_id, so the difference will always be non negative.
- DCHECK_GE(method_it.first, last_method_index);
- uint16_t diff_with_last_method_index = method_it.first - last_method_index;
- last_method_index = method_it.first;
- AddUintToBuffer(&buffer, diff_with_last_method_index);
- AddInlineCacheToBuffer(&buffer, method_it.second);
- }
-
- uint16_t last_class_index = 0;
- for (const auto& class_id : dex_data.class_set) {
- // Store the difference between the class indices. The set is ordered by
- // class_id, so the difference will always be non negative.
- DCHECK_GE(class_id.index_, last_class_index);
- uint16_t diff_with_last_class_index = class_id.index_ - last_class_index;
- last_class_index = class_id.index_;
- AddUintToBuffer(&buffer, diff_with_last_class_index);
- }
-
- buffer.insert(buffer.end(),
- dex_data.bitmap_storage.begin(),
- dex_data.bitmap_storage.end());
+ // Check size limit. Allow large profiles for non target builds for the case
+ // where we are merging many profiles to generate a boot image profile.
+ uint64_t total_uncompressed_size =
+ header_and_infos_size +
+ dex_files_section_size +
+ extra_descriptors_section_size +
+ classes_section_size +
+ methods_section_size;
+ VLOG(profiler) << "Required capacity: " << total_uncompressed_size << " bytes.";
+ if (total_uncompressed_size > GetSizeErrorThresholdBytes()) {
+ LOG(ERROR) << "Profile data size exceeds "
+ << GetSizeErrorThresholdBytes()
+ << " bytes. Profile will not be written to disk."
+ << " It requires " << total_uncompressed_size << " bytes.";
+ return false;
}
- ArrayRef<const uint8_t> in_buffer(buffer.data(), required_capacity);
- uint32_t output_size = 0;
- std::unique_ptr<uint8_t[]> compressed_buffer = DeflateBuffer(in_buffer, &output_size);
+ // Start with an invalid file header and section infos.
+ DCHECK_EQ(lseek(fd, 0, SEEK_CUR), 0);
+ constexpr uint32_t kMaxNumberOfSections = enum_cast<uint32_t>(FileSectionType::kNumberOfSections);
+ constexpr uint64_t kMaxHeaderAndInfosSize =
+ sizeof(FileHeader) + kMaxNumberOfSections * sizeof(FileSectionInfo);
+ DCHECK_LE(header_and_infos_size, kMaxHeaderAndInfosSize);
+ std::array<uint8_t, kMaxHeaderAndInfosSize> placeholder;
+ memset(placeholder.data(), 0, header_and_infos_size);
+ if (!WriteBuffer(fd, placeholder.data(), header_and_infos_size)) {
+ return false;
+ }
- if (output_size > GetSizeWarningThresholdBytes()) {
+ std::array<FileSectionInfo, kMaxNumberOfSections> section_infos;
+ size_t section_index = 0u;
+ uint32_t file_offset = header_and_infos_size;
+ auto add_section_info = [&](FileSectionType type, uint32_t file_size, uint32_t inflated_size) {
+ DCHECK_LT(section_index, section_infos.size());
+ section_infos[section_index] = FileSectionInfo(type, file_offset, file_size, inflated_size);
+ file_offset += file_size;
+ section_index += 1u;
+ };
+
+ // Write the dex files section.
+ {
+ SafeBuffer buffer(dex_files_section_size);
+ buffer.WriteUintAndAdvance(dchecked_integral_cast<ProfileIndexType>(info_.size()));
+ for (const std::unique_ptr<DexFileData>& dex_data : info_) {
+ buffer.WriteUintAndAdvance(dex_data->checksum);
+ buffer.WriteUintAndAdvance(dex_data->num_type_ids);
+ buffer.WriteUintAndAdvance(dex_data->num_method_ids);
+ buffer.WriteAndAdvance(dex_data->profile_key.c_str(), dex_data->profile_key.size() + 1u);
+ }
+ DCHECK_EQ(buffer.GetAvailableBytes(), 0u);
+ // Write the dex files section uncompressed.
+ if (!WriteBuffer(fd, buffer.Get(), dex_files_section_size)) {
+ return false;
+ }
+ add_section_info(FileSectionType::kDexFiles, dex_files_section_size, /*inflated_size=*/ 0u);
+ }
+
+ // Write the extra descriptors section.
+ if (extra_descriptors_section_size != 0u) {
+ SafeBuffer buffer(extra_descriptors_section_size);
+ buffer.WriteUintAndAdvance(dchecked_integral_cast<uint16_t>(extra_descriptors_.size()));
+ for (const std::string& descriptor : extra_descriptors_) {
+ buffer.WriteAndAdvance(descriptor.c_str(), descriptor.size() + 1u);
+ }
+ if (!buffer.Deflate()) {
+ return false;
+ }
+ if (!WriteBuffer(fd, buffer.Get(), buffer.Size())) {
+ return false;
+ }
+ add_section_info(
+ FileSectionType::kExtraDescriptors, buffer.Size(), extra_descriptors_section_size);
+ }
+
+ // Write the classes section.
+ if (classes_section_size != 0u) {
+ SafeBuffer buffer(classes_section_size);
+ for (const std::unique_ptr<DexFileData>& dex_data : info_) {
+ dex_data->WriteClasses(buffer);
+ }
+ if (!buffer.Deflate()) {
+ return false;
+ }
+ if (!WriteBuffer(fd, buffer.Get(), buffer.Size())) {
+ return false;
+ }
+ add_section_info(FileSectionType::kClasses, buffer.Size(), classes_section_size);
+ }
+
+ // Write the methods section.
+ if (methods_section_size != 0u) {
+ SafeBuffer buffer(methods_section_size);
+ for (const std::unique_ptr<DexFileData>& dex_data : info_) {
+ dex_data->WriteMethods(buffer);
+ }
+ if (!buffer.Deflate()) {
+ return false;
+ }
+ if (!WriteBuffer(fd, buffer.Get(), buffer.Size())) {
+ return false;
+ }
+ add_section_info(FileSectionType::kMethods, buffer.Size(), methods_section_size);
+ }
+
+ if (file_offset > GetSizeWarningThresholdBytes()) {
LOG(WARNING) << "Profile data size exceeds "
<< GetSizeWarningThresholdBytes()
- << " It has " << output_size << " bytes";
+ << " It has " << file_offset << " bytes";
}
- buffer.clear();
- AddUintToBuffer(&buffer, output_size);
+ // Write section infos.
+ if (lseek64(fd, sizeof(FileHeader), SEEK_SET) != sizeof(FileHeader)) {
+ return false;
+ }
+ if (!WriteBuffer(fd, section_infos.data(), section_index * sizeof(FileSectionInfo))) {
+ return false;
+ }
- if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
+ // Write header.
+ FileHeader header(version_, section_index);
+ if (lseek(fd, 0, SEEK_SET) != 0) {
return false;
}
- if (!WriteBuffer(fd, compressed_buffer.get(), output_size)) {
+ if (!WriteBuffer(fd, &header, sizeof(FileHeader))) {
return false;
}
+
uint64_t total_time = NanoTime() - start;
VLOG(profiler) << "Compressed from "
- << std::to_string(required_capacity)
+ << std::to_string(total_uncompressed_size)
<< " to "
- << std::to_string(output_size);
+ << std::to_string(file_offset);
VLOG(profiler) << "Time to save profile: " << std::to_string(total_time);
return true;
}
-void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector<uint8_t>* buffer,
- const InlineCacheMap& inline_cache_map) {
- // Add inline cache map size.
- AddUintToBuffer(buffer, static_cast<uint16_t>(inline_cache_map.size()));
- if (inline_cache_map.size() == 0) {
- return;
- }
- for (const auto& inline_cache_it : inline_cache_map) {
- uint16_t dex_pc = inline_cache_it.first;
- const DexPcData dex_pc_data = inline_cache_it.second;
- const ClassSet& classes = dex_pc_data.classes;
-
- // Add the dex pc.
- AddUintToBuffer(buffer, dex_pc);
-
- // Add the megamorphic/missing_types encoding if needed and continue.
- // In either cases we don't add any classes to the profiles and so there's
- // no point to continue.
- // TODO(calin): in case we miss types there is still value to add the
- // rest of the classes. They can be added without bumping the profile version.
- if (dex_pc_data.is_missing_types) {
- DCHECK(!dex_pc_data.is_megamorphic); // at this point the megamorphic flag should not be set.
- DCHECK_EQ(classes.size(), 0u);
- AddUintToBuffer(buffer, kIsMissingTypesEncoding);
- continue;
- } else if (dex_pc_data.is_megamorphic) {
- DCHECK_EQ(classes.size(), 0u);
- AddUintToBuffer(buffer, kIsMegamorphicEncoding);
- continue;
- }
-
- DCHECK_LT(classes.size(), ProfileCompilationInfo::kIndividualInlineCacheSize);
- DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes";
-
- SafeMap<ProfileIndexType, std::vector<dex::TypeIndex>> dex_to_classes_map;
- // Group the classes by dex. We expect that most of the classes will come from
- // the same dex, so this will be more efficient than encoding the dex index
- // for each class reference.
- GroupClassesByDex(classes, &dex_to_classes_map);
- // Add the dex map size.
- AddUintToBuffer(buffer, static_cast<uint8_t>(dex_to_classes_map.size()));
- for (const auto& dex_it : dex_to_classes_map) {
- ProfileIndexType dex_profile_index = dex_it.first;
- const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
- // Add the dex profile index.
- WriteProfileIndex(buffer, dex_profile_index);
- // Add the the number of classes for each dex profile index.
- AddUintToBuffer(buffer, static_cast<uint8_t>(dex_classes.size()));
- for (size_t i = 0; i < dex_classes.size(); i++) {
- // Add the type index of the classes.
- AddUintToBuffer(buffer, dex_classes[i].index_);
- }
- }
- }
-}
-
-uint32_t ProfileCompilationInfo::GetMethodsRegionSize(const DexFileData& dex_data) {
- // ((uint16_t)method index + (uint16_t)inline cache size) * number of methods
- uint32_t size = 2 * sizeof(uint16_t) * dex_data.method_map.size();
- for (const auto& method_it : dex_data.method_map) {
- const InlineCacheMap& inline_cache = method_it.second;
- size += sizeof(uint16_t) * inline_cache.size(); // dex_pc
- for (const auto& inline_cache_it : inline_cache) {
- const ClassSet& classes = inline_cache_it.second.classes;
- SafeMap<ProfileIndexType, std::vector<dex::TypeIndex>> dex_to_classes_map;
- GroupClassesByDex(classes, &dex_to_classes_map);
- size += sizeof(uint8_t); // dex_to_classes_map size
- for (const auto& dex_it : dex_to_classes_map) {
- size += SizeOfProfileIndexType(); // dex profile index
- size += sizeof(uint8_t); // number of classes
- const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
- size += sizeof(uint16_t) * dex_classes.size(); // the actual classes
- }
- }
- }
- return size;
-}
-
-void ProfileCompilationInfo::GroupClassesByDex(
- const ClassSet& classes,
- /*out*/SafeMap<ProfileIndexType, std::vector<dex::TypeIndex>>* dex_to_classes_map) {
- for (const auto& classes_it : classes) {
- auto dex_it = dex_to_classes_map->FindOrAdd(classes_it.dex_profile_index);
- dex_it->second.push_back(classes_it.type_index);
- }
-}
-
ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
const std::string& profile_key,
uint32_t checksum,
+ uint32_t num_type_ids,
uint32_t num_method_ids) {
DCHECK_EQ(profile_key_map_.size(), info_.size());
auto profile_index_it = profile_key_map_.lower_bound(profile_key);
@@ -829,9 +1099,7 @@
// Allow only a limited number dex files to be profiled. This allows us to save bytes
// when encoding. For regular profiles this 2^8, and for boot profiles is 2^16
// (well above what we expect for normal applications).
- if (kIsDebugBuild) {
- LOG(ERROR) << "Exceeded the maximum number of dex file. Something went wrong";
- }
+ LOG(ERROR) << "Exceeded the maximum number of dex file. Something went wrong";
return nullptr;
}
ProfileIndexType new_profile_index = dchecked_integral_cast<ProfileIndexType>(info_.size());
@@ -840,6 +1108,7 @@
profile_key,
checksum,
new_profile_index,
+ num_type_ids,
num_method_ids,
IsForBootImage()));
// Record the new data in `profile_key_map_` and `info_`.
@@ -864,11 +1133,11 @@
DCHECK_EQ(profile_key, result->profile_key);
DCHECK_EQ(profile_index, result->profile_index);
- if (num_method_ids != result->num_method_ids) {
+ if (num_type_ids != result->num_type_ids || num_method_ids != result->num_method_ids) {
// This should not happen... added to help investigating b/65812889.
- LOG(ERROR) << "num_method_ids mismatch for dex " << profile_key
- << ", expected=" << num_method_ids
- << ", actual=" << result->num_method_ids;
+ LOG(ERROR) << "num_type_ids or num_method_ids mismatch for dex " << profile_key
+ << ", types: expected=" << num_type_ids << " v. actual=" << result->num_type_ids
+ << ", methods: expected=" << num_method_ids << " actual=" << result->num_method_ids;
return nullptr;
}
@@ -928,6 +1197,20 @@
}
}
+ProfileCompilationInfo::ExtraDescriptorIndex ProfileCompilationInfo::AddExtraDescriptor(
+ std::string_view extra_descriptor) {
+ DCHECK(extra_descriptors_indexes_.find(extra_descriptor) == extra_descriptors_indexes_.end());
+ ExtraDescriptorIndex new_extra_descriptor_index = extra_descriptors_.size();
+ DCHECK_LE(new_extra_descriptor_index, kMaxExtraDescriptors);
+ if (UNLIKELY(new_extra_descriptor_index == kMaxExtraDescriptors)) {
+ return kMaxExtraDescriptors; // Cannot add another extra descriptor.
+ }
+ // Add the extra descriptor and record the new index.
+ extra_descriptors_.emplace_back(extra_descriptor);
+ extra_descriptors_indexes_.insert(new_extra_descriptor_index);
+ return new_extra_descriptor_index;
+}
+
bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi,
MethodHotness::Flag flags,
const ProfileSampleAnnotation& annotation) {
@@ -957,307 +1240,23 @@
continue;
}
for (const TypeReference& class_ref : cache.classes) {
- DexFileData* class_dex_data = GetOrAddDexFileData(class_ref.dex_file, annotation);
- if (class_dex_data == nullptr) { // checksum mismatch
- return false;
- }
DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, cache.dex_pc);
if (dex_pc_data->is_missing_types || dex_pc_data->is_megamorphic) {
// Don't bother adding classes if we are missing types or already megamorphic.
break;
}
- dex_pc_data->AddClass(class_dex_data->profile_index, class_ref.TypeIndex());
- }
- }
- return true;
-}
-
-#define READ_UINT(type, buffer, dest, error) \
- do { \
- if (!(buffer).ReadUintAndAdvance<type>(&(dest))) { \
- *(error) = "Could not read "#dest; \
- return false; \
- } \
- } \
- while (false)
-
-bool ProfileCompilationInfo::ReadInlineCache(
- SafeBuffer& buffer,
- ProfileIndexType number_of_dex_files,
- const SafeMap<ProfileIndexType, ProfileIndexType>& dex_profile_index_remap,
- /*out*/ InlineCacheMap* inline_cache,
- /*out*/ std::string* error) {
- uint16_t inline_cache_size;
- READ_UINT(uint16_t, buffer, inline_cache_size, error);
- for (; inline_cache_size > 0; inline_cache_size--) {
- uint16_t dex_pc;
- uint8_t dex_to_classes_map_size;
- READ_UINT(uint16_t, buffer, dex_pc, error);
- READ_UINT(uint8_t, buffer, dex_to_classes_map_size, error);
- DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, dex_pc);
- if (dex_to_classes_map_size == kIsMissingTypesEncoding) {
- dex_pc_data->SetIsMissingTypes();
- continue;
- }
- if (dex_to_classes_map_size == kIsMegamorphicEncoding) {
- dex_pc_data->SetIsMegamorphic();
- continue;
- }
- for (; dex_to_classes_map_size > 0; dex_to_classes_map_size--) {
- ProfileIndexType dex_profile_index;
- uint8_t dex_classes_size;
- if (!ReadProfileIndex(buffer, &dex_profile_index)) {
- *error = "Cannot read profile index";
- return false;
- }
- READ_UINT(uint8_t, buffer, dex_classes_size, error);
- if (dex_profile_index >= number_of_dex_files) {
- *error = "dex_profile_index out of bounds ";
- *error += std::to_string(dex_profile_index) + " " + std::to_string(number_of_dex_files);
- return false;
- }
- for (; dex_classes_size > 0; dex_classes_size--) {
- uint16_t type_index;
- READ_UINT(uint16_t, buffer, type_index, error);
- auto it = dex_profile_index_remap.find(dex_profile_index);
- if (it == dex_profile_index_remap.end()) {
- // If we don't have an index that's because the dex file was filtered out when loading.
- // Set missing types on the dex pc data.
- dex_pc_data->SetIsMissingTypes();
- } else {
- dex_pc_data->AddClass(it->second, dex::TypeIndex(type_index));
- }
+ dex::TypeIndex type_index = FindOrCreateTypeIndex(*pmi.ref.dex_file, class_ref);
+ if (type_index.IsValid()) {
+ dex_pc_data->AddClass(type_index);
+ } else {
+ // Could not create artificial type index.
+ dex_pc_data->SetIsMissingTypes();
}
}
}
return true;
}
-bool ProfileCompilationInfo::ReadMethods(
- SafeBuffer& buffer,
- ProfileIndexType number_of_dex_files,
- const ProfileLineHeader& line_header,
- const SafeMap<ProfileIndexType, ProfileIndexType>& dex_profile_index_remap,
- /*out*/std::string* error) {
- uint32_t unread_bytes_before_operation = buffer.CountUnreadBytes();
- if (unread_bytes_before_operation < line_header.method_region_size_bytes) {
- *error += "Profile EOF reached prematurely for ReadMethod";
- return false;
- }
- size_t expected_unread_bytes_after_operation = buffer.CountUnreadBytes()
- - line_header.method_region_size_bytes;
- uint16_t last_method_index = 0;
- while (buffer.CountUnreadBytes() > expected_unread_bytes_after_operation) {
- DexFileData* const data = GetOrAddDexFileData(line_header.profile_key,
- line_header.checksum,
- line_header.num_method_ids);
- uint16_t diff_with_last_method_index;
- READ_UINT(uint16_t, buffer, diff_with_last_method_index, error);
- uint16_t method_index = last_method_index + diff_with_last_method_index;
- last_method_index = method_index;
- InlineCacheMap* inline_cache = data->FindOrAddHotMethod(method_index);
- if (inline_cache == nullptr) {
- return false;
- }
- if (!ReadInlineCache(buffer,
- number_of_dex_files,
- dex_profile_index_remap,
- inline_cache,
- error)) {
- return false;
- }
- }
- uint32_t total_bytes_read = unread_bytes_before_operation - buffer.CountUnreadBytes();
- if (total_bytes_read != line_header.method_region_size_bytes) {
- *error += "Profile data inconsistent for ReadMethods";
- return false;
- }
- return true;
-}
-
-bool ProfileCompilationInfo::ReadClasses(SafeBuffer& buffer,
- const ProfileLineHeader& line_header,
- /*out*/std::string* error) {
- size_t unread_bytes_before_op = buffer.CountUnreadBytes();
- if (unread_bytes_before_op < line_header.class_set_size) {
- *error += "Profile EOF reached prematurely for ReadClasses";
- return false;
- }
-
- uint16_t last_class_index = 0;
- for (uint16_t i = 0; i < line_header.class_set_size; i++) {
- uint16_t diff_with_last_class_index;
- READ_UINT(uint16_t, buffer, diff_with_last_class_index, error);
- uint16_t type_index = last_class_index + diff_with_last_class_index;
- last_class_index = type_index;
-
- DexFileData* const data = GetOrAddDexFileData(line_header.profile_key,
- line_header.checksum,
- line_header.num_method_ids);
- if (data == nullptr) {
- return false;
- }
- data->class_set.insert(dex::TypeIndex(type_index));
- }
- size_t total_bytes_read = unread_bytes_before_op - buffer.CountUnreadBytes();
- uint32_t expected_bytes_read = line_header.class_set_size * sizeof(uint16_t);
- if (total_bytes_read != expected_bytes_read) {
- *error += "Profile data inconsistent for ReadClasses";
- return false;
- }
- return true;
-}
-
-// Tests for EOF by trying to read 1 byte from the descriptor.
-// Returns:
-// 0 if the descriptor is at the EOF,
-// -1 if there was an IO error
-// 1 if the descriptor has more content to read
-static int testEOF(int fd) {
- uint8_t buffer[1];
- return TEMP_FAILURE_RETRY(read(fd, buffer, 1));
-}
-
-ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::ReadProfileHeader(
- ProfileSource& source,
- /*out*/ProfileIndexType* number_of_dex_files,
- /*out*/uint32_t* uncompressed_data_size,
- /*out*/uint32_t* compressed_data_size,
- /*out*/std::string* error) {
- // Read magic and version
- const size_t kMagicVersionSize =
- sizeof(kProfileMagic) +
- kProfileVersionSize;
- SafeBuffer safe_buffer_version(kMagicVersionSize);
-
- ProfileLoadStatus status = safe_buffer_version.Fill(source, "ReadProfileHeaderVersion", error);
- if (status != ProfileLoadStatus::kSuccess) {
- return status;
- }
-
- if (!safe_buffer_version.CompareAndAdvance(kProfileMagic, sizeof(kProfileMagic))) {
- *error = "Profile missing magic";
- return ProfileLoadStatus::kVersionMismatch;
- }
- if (safe_buffer_version.CountUnreadBytes() < kProfileVersionSize) {
- *error = "Cannot read profile version";
- return ProfileLoadStatus::kBadData;
- }
- memcpy(version_, safe_buffer_version.GetCurrentPtr(), kProfileVersionSize);
- if ((memcmp(version_, kProfileVersion, kProfileVersionSize) != 0) &&
- (memcmp(version_, kProfileVersionForBootImage, kProfileVersionSize) != 0)) {
- *error = "Profile version mismatch";
- return ProfileLoadStatus::kVersionMismatch;
- }
-
- const size_t kProfileHeaderDataSize =
- SizeOfProfileIndexType() + // number of dex files
- sizeof(uint32_t) + // size of uncompressed profile data
- sizeof(uint32_t); // size of compressed profile data
- SafeBuffer safe_buffer_header_data(kProfileHeaderDataSize);
-
- status = safe_buffer_header_data.Fill(source, "ReadProfileHeaderData", error);
- if (status != ProfileLoadStatus::kSuccess) {
- return status;
- }
-
- if (!ReadProfileIndex(safe_buffer_header_data, number_of_dex_files)) {
- *error = "Cannot read the number of dex files";
- return ProfileLoadStatus::kBadData;
- }
- if (!safe_buffer_header_data.ReadUintAndAdvance<uint32_t>(uncompressed_data_size)) {
- *error = "Cannot read the size of uncompressed data";
- return ProfileLoadStatus::kBadData;
- }
- if (!safe_buffer_header_data.ReadUintAndAdvance<uint32_t>(compressed_data_size)) {
- *error = "Cannot read the size of compressed data";
- return ProfileLoadStatus::kBadData;
- }
- return ProfileLoadStatus::kSuccess;
-}
-
-bool ProfileCompilationInfo::ReadProfileLineHeaderElements(SafeBuffer& buffer,
- /*out*/uint16_t* profile_key_size,
- /*out*/ProfileLineHeader* line_header,
- /*out*/std::string* error) {
- READ_UINT(uint16_t, buffer, *profile_key_size, error);
- READ_UINT(uint16_t, buffer, line_header->class_set_size, error);
- READ_UINT(uint32_t, buffer, line_header->method_region_size_bytes, error);
- READ_UINT(uint32_t, buffer, line_header->checksum, error);
- READ_UINT(uint32_t, buffer, line_header->num_method_ids, error);
- return true;
-}
-
-ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::ReadProfileLineHeader(
- SafeBuffer& buffer,
- /*out*/ProfileLineHeader* line_header,
- /*out*/std::string* error) {
- if (buffer.CountUnreadBytes() < kLineHeaderSize) {
- *error += "Profile EOF reached prematurely for ReadProfileLineHeader";
- return ProfileLoadStatus::kBadData;
- }
-
- uint16_t profile_key_size;
- if (!ReadProfileLineHeaderElements(buffer, &profile_key_size, line_header, error)) {
- return ProfileLoadStatus::kBadData;
- }
-
- if (profile_key_size == 0 || profile_key_size > kMaxDexFileKeyLength) {
- *error = "ProfileKey has an invalid size: " +
- std::to_string(static_cast<uint32_t>(profile_key_size));
- return ProfileLoadStatus::kBadData;
- }
-
- if (buffer.CountUnreadBytes() < profile_key_size) {
- *error += "Profile EOF reached prematurely for ReadProfileHeaderDexLocation";
- return ProfileLoadStatus::kBadData;
- }
- const uint8_t* base_ptr = buffer.GetCurrentPtr();
- line_header->profile_key.assign(
- reinterpret_cast<const char*>(base_ptr), profile_key_size);
- buffer.Advance(profile_key_size);
- return ProfileLoadStatus::kSuccess;
-}
-
-ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::ReadProfileLine(
- SafeBuffer& buffer,
- ProfileIndexType number_of_dex_files,
- const ProfileLineHeader& line_header,
- const SafeMap<ProfileIndexType, ProfileIndexType>& dex_profile_index_remap,
- bool merge_classes,
- /*out*/std::string* error) {
- DexFileData* data = GetOrAddDexFileData(line_header.profile_key,
- line_header.checksum,
- line_header.num_method_ids);
- if (data == nullptr) {
- *error = "Error when reading profile file line header: checksum mismatch for "
- + line_header.profile_key;
- return ProfileLoadStatus::kBadData;
- }
-
- if (!ReadMethods(buffer, number_of_dex_files, line_header, dex_profile_index_remap, error)) {
- return ProfileLoadStatus::kBadData;
- }
-
- if (merge_classes) {
- if (!ReadClasses(buffer, line_header, error)) {
- return ProfileLoadStatus::kBadData;
- }
- }
-
- // Read method bitmap.
- const size_t bytes = data->bitmap_storage.size();
- if (buffer.CountUnreadBytes() < bytes) {
- *error += "Profile EOF reached prematurely for method bitmap";
- return ProfileLoadStatus::kBadData;
- }
- const uint8_t* base_ptr = buffer.GetCurrentPtr();
- std::copy_n(base_ptr, bytes, data->bitmap_storage.data());
- buffer.Advance(bytes);
-
- return ProfileLoadStatus::kSuccess;
-}
-
// TODO(calin): Fix this API. ProfileCompilationInfo::Load should be static and
// return a unique pointer to a ProfileCompilationInfo upon success.
bool ProfileCompilationInfo::Load(
@@ -1296,71 +1295,45 @@
return false;
}
- if (dex_data->num_method_ids != dex_file->NumMethodIds()) {
- LOG(ERROR) << "Number of method ids in dex file and profile don't match."
- << "dex location " << dex_location << " NumMethodId in DexFile"
- << dex_file->NumMethodIds() << ", NumMethodId in profile"
- << dex_data->num_method_ids;
+ if (dex_data->num_method_ids != dex_file->NumMethodIds() ||
+ dex_data->num_type_ids != dex_file->NumTypeIds()) {
+ LOG(ERROR) << "Number of type or method ids in dex file and profile don't match."
+ << "dex location " << dex_location
+ << " dex_file.NumTypeIds=" << dex_file->NumTypeIds()
+ << " .v dex_data.num_type_ids=" << dex_data->num_type_ids
+ << ", dex_file.NumMethodIds=" << dex_file->NumMethodIds()
+ << " v. dex_data.num_method_ids=" << dex_data->num_method_ids;
return false;
}
- // Verify method_encoding.
- for (const auto& method_it : dex_data->method_map) {
- size_t method_id = (size_t)(method_it.first);
- if (method_id >= dex_file->NumMethodIds()) {
- LOG(ERROR) << "Invalid method id in profile file. dex location="
- << dex_location << " method_id=" << method_id << " NumMethodIds="
- << dex_file->NumMethodIds();
- return false;
- }
+ // Class and method data should be valid. Verify only in debug builds.
+ if (kIsDebugBuild) {
+ // Verify method_encoding.
+ for (const auto& method_it : dex_data->method_map) {
+ CHECK_LT(method_it.first, dex_data->num_method_ids);
- // Verify class indices of inline caches.
- const InlineCacheMap &inline_cache_map = method_it.second;
- for (const auto& inline_cache_it : inline_cache_map) {
- const DexPcData dex_pc_data = inline_cache_it.second;
- if (dex_pc_data.is_missing_types || dex_pc_data.is_megamorphic) {
- // No class indices to verify.
- continue;
- }
-
- const ClassSet &classes = dex_pc_data.classes;
- SafeMap<ProfileIndexType, std::vector<dex::TypeIndex>> dex_to_classes_map;
- // Group the classes by dex. We expect that most of the classes will come from
- // the same dex, so this will be more efficient than encoding the dex index
- // for each class reference.
- GroupClassesByDex(classes, &dex_to_classes_map);
- for (const auto &dex_it : dex_to_classes_map) {
- ProfileIndexType dex_profile_index = dex_it.first;
- const auto dex_file_inline_cache_it = key_to_dex_file.find(
- info_[dex_profile_index]->profile_key);
- if (dex_file_inline_cache_it == key_to_dex_file.end()) {
- // It is okay if profile contains data for additional dex files.
+ // Verify class indices of inline caches.
+ const InlineCacheMap &inline_cache_map = method_it.second;
+ for (const auto& inline_cache_it : inline_cache_map) {
+ const DexPcData& dex_pc_data = inline_cache_it.second;
+ if (dex_pc_data.is_missing_types || dex_pc_data.is_megamorphic) {
+ // No class indices to verify.
+ CHECK(dex_pc_data.classes.empty());
continue;
}
- const DexFile *dex_file_for_inline_cache_check = dex_file_inline_cache_it->second;
- const std::vector<dex::TypeIndex> &dex_classes = dex_it.second;
- for (size_t i = 0; i < dex_classes.size(); i++) {
- if (dex_classes[i].index_ >= dex_file_for_inline_cache_check->NumTypeIds()) {
- LOG(ERROR) << "Invalid inline cache in profile file. dex location="
- << dex_location << " method_id=" << method_id
- << " dex_profile_index="
- << static_cast<uint16_t >(dex_profile_index) << " type_index="
- << dex_classes[i].index_
- << " NumTypeIds="
- << dex_file_for_inline_cache_check->NumTypeIds();
- return false;
+
+ for (const dex::TypeIndex& type_index : dex_pc_data.classes) {
+ if (type_index.index_ >= dex_data->num_type_ids) {
+ CHECK_LT(type_index.index_ - dex_data->num_type_ids, extra_descriptors_.size());
}
}
}
}
- }
- // Verify class_ids.
- for (const auto& class_id : dex_data->class_set) {
- if (class_id.index_ >= dex_file->NumTypeIds()) {
- LOG(ERROR) << "Invalid class id in profile file. dex_file location "
- << dex_location << " class_id=" << class_id.index_ << " NumClassIds="
- << dex_file->NumClassDefs();
- return false;
+ // Verify class_ids.
+ for (const dex::TypeIndex& type_index : dex_data->class_set) {
+ if (type_index.index_ >= dex_data->num_type_ids) {
+ CHECK_LT(type_index.index_ - dex_data->num_type_ids, extra_descriptors_.size());
+ }
}
}
}
@@ -1409,19 +1382,34 @@
}
}
+bool ProfileCompilationInfo::ProfileSource::Seek(off_t offset) {
+ DCHECK_GE(offset, 0);
+ if (IsMemMap()) {
+ if (offset > static_cast<int64_t>(mem_map_.Size())) {
+ return false;
+ }
+ mem_map_cur_ = offset;
+ return true;
+ } else {
+ if (lseek64(fd_, offset, SEEK_SET) != offset) {
+ return false;
+ }
+ return true;
+ }
+}
+
ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::ProfileSource::Read(
- uint8_t* buffer,
+ void* buffer,
size_t byte_count,
const std::string& debug_stage,
std::string* error) {
if (IsMemMap()) {
- if (mem_map_cur_ + byte_count > mem_map_.Size()) {
+ DCHECK_LE(mem_map_cur_, mem_map_.Size());
+ if (byte_count > mem_map_.Size() - mem_map_cur_) {
return ProfileLoadStatus::kBadData;
}
- for (size_t i = 0; i < byte_count; i++) {
- buffer[i] = *(mem_map_.Begin() + mem_map_cur_);
- mem_map_cur_++;
- }
+ memcpy(buffer, mem_map_.Begin() + mem_map_cur_, byte_count);
+ mem_map_cur_ += byte_count;
} else {
while (byte_count > 0) {
int bytes_read = TEMP_FAILURE_RETRY(read(fd_, buffer, byte_count));;
@@ -1433,17 +1421,12 @@
return ProfileLoadStatus::kIOError;
}
byte_count -= bytes_read;
- buffer += bytes_read;
+ reinterpret_cast<uint8_t*&>(buffer) += bytes_read;
}
}
return ProfileLoadStatus::kSuccess;
}
-bool ProfileCompilationInfo::ProfileSource::HasConsumedAllData() const {
- return IsMemMap()
- ? (!mem_map_.IsValid() || mem_map_cur_ == mem_map_.Size())
- : (testEOF(fd_) == 0);
-}
bool ProfileCompilationInfo::ProfileSource::HasEmptyContent() const {
if (IsMemMap()) {
@@ -1457,12 +1440,228 @@
}
}
+ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::ReadSectionData(
+ ProfileSource& source,
+ const FileSectionInfo& section_info,
+ /*out*/ SafeBuffer* buffer,
+ /*out*/ std::string* error) {
+ DCHECK_EQ(buffer->Size(), 0u);
+ if (!source.Seek(section_info.GetFileOffset())) {
+ *error = "Failed to seek to section data.";
+ return ProfileLoadStatus::kIOError;
+ }
+ SafeBuffer temp_buffer(section_info.GetFileSize());
+ ProfileLoadStatus status = source.Read(
+ temp_buffer.GetCurrentPtr(), temp_buffer.GetAvailableBytes(), "ReadSectionData", error);
+ if (status != ProfileLoadStatus::kSuccess) {
+ return status;
+ }
+ if (section_info.GetInflatedSize() != 0u &&
+ !temp_buffer.Inflate(section_info.GetInflatedSize())) {
+ *error += "Error uncompressing section data.";
+ return ProfileLoadStatus::kBadData;
+ }
+ buffer->Swap(temp_buffer);
+ return ProfileLoadStatus::kSuccess;
+}
+
+ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::ReadDexFilesSection(
+ ProfileSource& source,
+ const FileSectionInfo& section_info,
+ const ProfileLoadFilterFn& filter_fn,
+ /*out*/ dchecked_vector<ProfileIndexType>* dex_profile_index_remap,
+ /*out*/ std::string* error) {
+ DCHECK(section_info.GetType() == FileSectionType::kDexFiles);
+ SafeBuffer buffer;
+ ProfileLoadStatus status = ReadSectionData(source, section_info, &buffer, error);
+ if (status != ProfileLoadStatus::kSuccess) {
+ return status;
+ }
+
+ ProfileIndexType num_dex_files;
+ if (!buffer.ReadUintAndAdvance(&num_dex_files)) {
+ *error = "Error reading number of dex files.";
+ return ProfileLoadStatus::kBadData;
+ }
+ if (num_dex_files >= MaxProfileIndex()) {
+ *error = "Too many dex files.";
+ return ProfileLoadStatus::kBadData;
+ }
+
+ DCHECK(dex_profile_index_remap->empty());
+ for (ProfileIndexType i = 0u; i != num_dex_files; ++i) {
+ uint32_t checksum, num_type_ids, num_method_ids;
+ if (!buffer.ReadUintAndAdvance(&checksum) ||
+ !buffer.ReadUintAndAdvance(&num_type_ids) ||
+ !buffer.ReadUintAndAdvance(&num_method_ids)) {
+ *error = "Error reading dex file data.";
+ return ProfileLoadStatus::kBadData;
+ }
+ std::string_view profile_key_view;
+ if (!buffer.ReadStringAndAdvance(&profile_key_view)) {
+ *error += "Missing terminating null character for profile key.";
+ return ProfileLoadStatus::kBadData;
+ }
+ if (profile_key_view.size() == 0u || profile_key_view.size() > kMaxDexFileKeyLength) {
+ *error = "ProfileKey has an invalid size: " + std::to_string(profile_key_view.size());
+ return ProfileLoadStatus::kBadData;
+ }
+ std::string profile_key(profile_key_view);
+ if (!filter_fn(profile_key, checksum)) {
+ // Do not load data for this key. Store invalid index to `dex_profile_index_remap`.
+ VLOG(compiler) << "Profile: Filtered out " << profile_key << " 0x" << std::hex << checksum;
+ dex_profile_index_remap->push_back(MaxProfileIndex());
+ continue;
+ }
+ DexFileData* data = GetOrAddDexFileData(profile_key, checksum, num_type_ids, num_method_ids);
+ if (data == nullptr) {
+ if (UNLIKELY(profile_key_map_.size() == MaxProfileIndex()) &&
+ profile_key_map_.find(profile_key) == profile_key_map_.end()) {
+ *error = "Too many dex files.";
+ } else {
+ *error = "Checksum, NumTypeIds, or NumMethodIds mismatch for " + profile_key;
+ }
+ return ProfileLoadStatus::kBadData;
+ }
+ dex_profile_index_remap->push_back(data->profile_index);
+ }
+ if (buffer.GetAvailableBytes() != 0u) {
+ *error = "Unexpected data at end of dex files section.";
+ return ProfileLoadStatus::kBadData;
+ }
+ return ProfileLoadStatus::kSuccess;
+}
+
+ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::ReadExtraDescriptorsSection(
+ ProfileSource& source,
+ const FileSectionInfo& section_info,
+ /*out*/ dchecked_vector<ExtraDescriptorIndex>* extra_descriptors_remap,
+ /*out*/ std::string* error) {
+ DCHECK(section_info.GetType() == FileSectionType::kExtraDescriptors);
+ SafeBuffer buffer;
+ ProfileLoadStatus status = ReadSectionData(source, section_info, &buffer, error);
+ if (status != ProfileLoadStatus::kSuccess) {
+ return status;
+ }
+
+ uint16_t num_extra_descriptors;
+ if (!buffer.ReadUintAndAdvance(&num_extra_descriptors)) {
+ *error = "Error reading number of extra descriptors.";
+ return ProfileLoadStatus::kBadData;
+ }
+
+ // Note: We allow multiple extra descriptors sections in a single profile file
+ // but that can lead to `kMergeError` if there are too many extra descriptors.
+ // Other sections can reference only extra descriptors from preceding sections.
+ extra_descriptors_remap->reserve(
+ std::min<size_t>(extra_descriptors_remap->size() + num_extra_descriptors,
+ std::numeric_limits<uint16_t>::max()));
+ for (uint16_t i = 0; i != num_extra_descriptors; ++i) {
+ const char* descriptor = reinterpret_cast<const char*>(buffer.GetCurrentPtr());
+ std::string_view extra_descriptor;
+ if (!buffer.ReadStringAndAdvance(&extra_descriptor)) {
+ *error += "Missing terminating null character for extra descriptor.";
+ return ProfileLoadStatus::kBadData;
+ }
+ if (!IsValidDescriptor(descriptor)) {
+ *error += "Invalid extra descriptor.";
+ return ProfileLoadStatus::kBadData;
+ }
+ // Try to match an existing extra descriptor.
+ auto it = extra_descriptors_indexes_.find(extra_descriptor);
+ if (it != extra_descriptors_indexes_.end()) {
+ extra_descriptors_remap->push_back(*it);
+ continue;
+ }
+ // Try to insert a new extra descriptor.
+ ExtraDescriptorIndex extra_descriptor_index = AddExtraDescriptor(extra_descriptor);
+ if (extra_descriptor_index == kMaxExtraDescriptors) {
+ *error = "Too many extra descriptors.";
+ return ProfileLoadStatus::kMergeError;
+ }
+ extra_descriptors_remap->push_back(extra_descriptor_index);
+ }
+ return ProfileLoadStatus::kSuccess;
+}
+
+ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::ReadClassesSection(
+ ProfileSource& source,
+ const FileSectionInfo& section_info,
+ const dchecked_vector<ProfileIndexType>& dex_profile_index_remap,
+ const dchecked_vector<ExtraDescriptorIndex>& extra_descriptors_remap,
+ /*out*/ std::string* error) {
+ DCHECK(section_info.GetType() == FileSectionType::kClasses);
+ SafeBuffer buffer;
+ ProfileLoadStatus status = ReadSectionData(source, section_info, &buffer, error);
+ if (status != ProfileLoadStatus::kSuccess) {
+ return status;
+ }
+
+ while (buffer.GetAvailableBytes() != 0u) {
+ ProfileIndexType profile_index;
+ if (!buffer.ReadUintAndAdvance(&profile_index)) {
+ *error = "Error profile index in classes section.";
+ return ProfileLoadStatus::kBadData;
+ }
+ if (profile_index >= dex_profile_index_remap.size()) {
+ *error = "Invalid profile index in classes section.";
+ return ProfileLoadStatus::kBadData;
+ }
+ profile_index = dex_profile_index_remap[profile_index];
+ if (profile_index == MaxProfileIndex()) {
+ status = DexFileData::SkipClasses(buffer, error);
+ } else {
+ status = info_[profile_index]->ReadClasses(buffer, extra_descriptors_remap, error);
+ }
+ if (status != ProfileLoadStatus::kSuccess) {
+ return status;
+ }
+ }
+ return ProfileLoadStatus::kSuccess;
+}
+
+ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::ReadMethodsSection(
+ ProfileSource& source,
+ const FileSectionInfo& section_info,
+ const dchecked_vector<ProfileIndexType>& dex_profile_index_remap,
+ const dchecked_vector<ExtraDescriptorIndex>& extra_descriptors_remap,
+ /*out*/ std::string* error) {
+ DCHECK(section_info.GetType() == FileSectionType::kMethods);
+ SafeBuffer buffer;
+ ProfileLoadStatus status = ReadSectionData(source, section_info, &buffer, error);
+ if (status != ProfileLoadStatus::kSuccess) {
+ return status;
+ }
+
+ while (buffer.GetAvailableBytes() != 0u) {
+ ProfileIndexType profile_index;
+ if (!buffer.ReadUintAndAdvance(&profile_index)) {
+ *error = "Error profile index in methods section.";
+ return ProfileLoadStatus::kBadData;
+ }
+ if (profile_index >= dex_profile_index_remap.size()) {
+ *error = "Invalid profile index in methods section.";
+ return ProfileLoadStatus::kBadData;
+ }
+ profile_index = dex_profile_index_remap[profile_index];
+ if (profile_index == MaxProfileIndex()) {
+ status = DexFileData::SkipMethods(buffer, error);
+ } else {
+ status = info_[profile_index]->ReadMethods(buffer, extra_descriptors_remap, error);
+ }
+ if (status != ProfileLoadStatus::kSuccess) {
+ return status;
+ }
+ }
+ return ProfileLoadStatus::kSuccess;
+}
+
// TODO(calin): fail fast if the dex checksums don't match.
ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::LoadInternal(
- int32_t fd,
- std::string* error,
- bool merge_classes,
- const ProfileLoadFilterFn& filter_fn) {
+ int32_t fd,
+ std::string* error,
+ bool merge_classes,
+ const ProfileLoadFilterFn& filter_fn) {
ScopedTrace trace(__PRETTY_FUNCTION__);
DCHECK_GE(fd, 0);
@@ -1479,19 +1678,48 @@
return ProfileLoadStatus::kSuccess;
}
- // Read profile header: magic + version + number_of_dex_files.
- ProfileIndexType number_of_dex_files;
- uint32_t uncompressed_data_size;
- uint32_t compressed_data_size;
- status = ReadProfileHeader(*source,
- &number_of_dex_files,
- &uncompressed_data_size,
- &compressed_data_size,
- error);
-
+ // Read file header.
+ FileHeader header;
+ status = source->Read(&header, sizeof(FileHeader), "ReadProfileHeader", error);
if (status != ProfileLoadStatus::kSuccess) {
return status;
}
+ if (!header.IsValid()) {
+ return header.InvalidHeaderMessage(error);
+ }
+ if (memcmp(header.GetVersion(), version_, kProfileVersionSize) != 0) {
+ *error = IsForBootImage() ? "Expected boot profile, got app profile."
+ : "Expected app profile, got boot profile.";
+ return ProfileLoadStatus::kMergeError;
+ }
+
+ // Check if there are too many section infos.
+ uint32_t section_count = header.GetFileSectionCount();
+ uint32_t uncompressed_data_size = sizeof(FileHeader) + section_count * sizeof(FileSectionInfo);
+ if (uncompressed_data_size > GetSizeErrorThresholdBytes()) {
+ LOG(ERROR) << "Profile data size exceeds " << GetSizeErrorThresholdBytes()
+ << " bytes. It has " << uncompressed_data_size << " bytes.";
+ return ProfileLoadStatus::kBadData;
+ }
+
+ // Read section infos.
+ dchecked_vector<FileSectionInfo> section_infos(section_count);
+ status = source->Read(
+ section_infos.data(), section_count * sizeof(FileSectionInfo), "ReadSectionInfos", error);
+ if (status != ProfileLoadStatus::kSuccess) {
+ return status;
+ }
+
+ // Finish uncompressed data size calculation.
+ for (const FileSectionInfo& section_info : section_infos) {
+ uint32_t mem_size = section_info.GetMemSize();
+ if (UNLIKELY(mem_size > std::numeric_limits<uint32_t>::max() - uncompressed_data_size)) {
+ *error = "Total memory size overflow.";
+ return ProfileLoadStatus::kBadData;
+ }
+ uncompressed_data_size += mem_size;
+ }
+
// Allow large profiles for non target builds for the case where we are merging many profiles
// to generate a boot image profile.
if (uncompressed_data_size > GetSizeErrorThresholdBytes()) {
@@ -1506,117 +1734,61 @@
<< " bytes. It has " << uncompressed_data_size << " bytes.";
}
- std::unique_ptr<uint8_t[]> compressed_data(new uint8_t[compressed_data_size]);
- status = source->Read(compressed_data.get(), compressed_data_size, "ReadContent", error);
+ // Process the mandatory dex files section.
+ DCHECK_NE(section_count, 0u); // Checked by `header.IsValid()` above.
+ const FileSectionInfo& dex_files_section_info = section_infos[0];
+ if (dex_files_section_info.GetType() != FileSectionType::kDexFiles) {
+ *error = "First section is not dex files section.";
+ return ProfileLoadStatus::kBadData;
+ }
+ dchecked_vector<ProfileIndexType> dex_profile_index_remap;
+ status = ReadDexFilesSection(
+ *source, dex_files_section_info, filter_fn, &dex_profile_index_remap, error);
if (status != ProfileLoadStatus::kSuccess) {
- *error += "Unable to read compressed profile data";
+ DCHECK(!error->empty());
return status;
}
- if (!source->HasConsumedAllData()) {
- *error += "Unexpected data in the profile file.";
- return ProfileLoadStatus::kBadData;
- }
-
- SafeBuffer uncompressed_data(uncompressed_data_size);
-
- ArrayRef<const uint8_t> in_buffer(compressed_data.get(), compressed_data_size);
- ArrayRef<uint8_t> out_buffer(uncompressed_data.Get(), uncompressed_data_size);
- int ret = InflateBuffer(in_buffer, out_buffer);
-
- if (ret != Z_STREAM_END) {
- *error += "Error reading uncompressed profile data";
- return ProfileLoadStatus::kBadData;
- }
-
- std::vector<ProfileLineHeader> profile_line_headers;
- // Read profile line headers.
- for (ProfileIndexType k = 0; k < number_of_dex_files; k++) {
- ProfileLineHeader line_header;
-
- // First, read the line header to get the amount of data we need to read.
- status = ReadProfileLineHeader(uncompressed_data, &line_header, error);
+ // Process all other sections.
+ dchecked_vector<ExtraDescriptorIndex> extra_descriptors_remap;
+ for (uint32_t i = 1u; i != section_count; ++i) {
+ const FileSectionInfo& section_info = section_infos[i];
+ DCHECK(status == ProfileLoadStatus::kSuccess);
+ switch (section_info.GetType()) {
+ case FileSectionType::kDexFiles:
+ *error = "Unsupported additional dex files section.";
+ status = ProfileLoadStatus::kBadData;
+ break;
+ case FileSectionType::kExtraDescriptors:
+ status = ReadExtraDescriptorsSection(
+ *source, section_info, &extra_descriptors_remap, error);
+ break;
+ case FileSectionType::kClasses:
+ // Skip if all dex files were filtered out.
+ if (!info_.empty() && merge_classes) {
+ status = ReadClassesSection(
+ *source, section_info, dex_profile_index_remap, extra_descriptors_remap, error);
+ }
+ break;
+ case FileSectionType::kMethods:
+ // Skip if all dex files were filtered out.
+ if (!info_.empty()) {
+ status = ReadMethodsSection(
+ *source, section_info, dex_profile_index_remap, extra_descriptors_remap, error);
+ }
+ break;
+ default:
+ // Unknown section. Skip it. New versions of ART are allowed
+ // to add sections that shall be ignored by old versions.
+ break;
+ }
if (status != ProfileLoadStatus::kSuccess) {
+ DCHECK(!error->empty());
return status;
}
- profile_line_headers.push_back(line_header);
}
- SafeMap<ProfileIndexType, ProfileIndexType> dex_profile_index_remap;
- if (!RemapProfileIndex(profile_line_headers, filter_fn, &dex_profile_index_remap)) {
- return ProfileLoadStatus::kBadData;
- }
-
- for (ProfileIndexType k = 0; k < number_of_dex_files; k++) {
- if (!filter_fn(profile_line_headers[k].profile_key, profile_line_headers[k].checksum)) {
- // We have to skip the line. Advanced the current pointer of the buffer.
- size_t profile_line_size =
- profile_line_headers[k].class_set_size * sizeof(uint16_t) +
- profile_line_headers[k].method_region_size_bytes +
- DexFileData::ComputeBitmapStorage(IsForBootImage(),
- profile_line_headers[k].num_method_ids);
- uncompressed_data.Advance(profile_line_size);
- } else {
- // Now read the actual profile line.
- status = ReadProfileLine(uncompressed_data,
- number_of_dex_files,
- profile_line_headers[k],
- dex_profile_index_remap,
- merge_classes,
- error);
- if (status != ProfileLoadStatus::kSuccess) {
- return status;
- }
- }
- }
-
- // Check that we read everything and that profiles don't contain junk data.
- if (uncompressed_data.CountUnreadBytes() > 0) {
- *error = "Unexpected content in the profile file: " +
- std::to_string(uncompressed_data.CountUnreadBytes()) + " extra bytes";
- return ProfileLoadStatus::kBadData;
- } else {
- return ProfileLoadStatus::kSuccess;
- }
-}
-
-bool ProfileCompilationInfo::RemapProfileIndex(
- const std::vector<ProfileLineHeader>& profile_line_headers,
- const ProfileLoadFilterFn& filter_fn,
- /*out*/SafeMap<ProfileIndexType, ProfileIndexType>* dex_profile_index_remap) {
- // First verify that all checksums match. This will avoid adding garbage to
- // the current profile info.
- // Note that the number of elements should be very small, so this should not
- // be a performance issue.
- for (const ProfileLineHeader& other_profile_line_header : profile_line_headers) {
- if (!filter_fn(other_profile_line_header.profile_key, other_profile_line_header.checksum)) {
- continue;
- }
- // verify_checksum is false because we want to differentiate between a missing dex data and
- // a mismatched checksum.
- const DexFileData* dex_data = FindDexData(other_profile_line_header.profile_key,
- /* checksum= */ 0u,
- /* verify_checksum= */ false);
- if ((dex_data != nullptr) && (dex_data->checksum != other_profile_line_header.checksum)) {
- LOG(WARNING) << "Checksum mismatch for dex " << other_profile_line_header.profile_key;
- return false;
- }
- }
- // All checksums match. Import the data.
- uint32_t num_dex_files = static_cast<uint32_t>(profile_line_headers.size());
- for (uint32_t i = 0; i < num_dex_files; i++) {
- if (!filter_fn(profile_line_headers[i].profile_key, profile_line_headers[i].checksum)) {
- continue;
- }
- const DexFileData* dex_data = GetOrAddDexFileData(profile_line_headers[i].profile_key,
- profile_line_headers[i].checksum,
- profile_line_headers[i].num_method_ids);
- if (dex_data == nullptr) {
- return false; // Could happen if we exceed the number of allowed dex files.
- }
- dex_profile_index_remap->Put(i, dex_data->profile_index);
- }
- return true;
+ return ProfileLoadStatus::kSuccess;
}
bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other,
@@ -1652,28 +1824,61 @@
// and one from split-B.
// First, build a mapping from other_dex_profile_index to this_dex_profile_index.
- // This will make sure that the ClassReferences will point to the correct dex file.
- SafeMap<ProfileIndexType, ProfileIndexType> dex_profile_index_remap;
+ dchecked_vector<ProfileIndexType> dex_profile_index_remap;
+ dex_profile_index_remap.reserve(other.info_.size());
for (const std::unique_ptr<DexFileData>& other_dex_data : other.info_) {
const DexFileData* dex_data = GetOrAddDexFileData(other_dex_data->profile_key,
other_dex_data->checksum,
+ other_dex_data->num_type_ids,
other_dex_data->num_method_ids);
if (dex_data == nullptr) {
- return false; // Could happen if we exceed the number of allowed dex files.
+ // Could happen if we exceed the number of allowed dex files or there is
+ // a mismatch in `num_type_ids` or `num_method_ids`.
+ return false;
}
- dex_profile_index_remap.Put(other_dex_data->profile_index, dex_data->profile_index);
+ DCHECK_EQ(other_dex_data->profile_index, dex_profile_index_remap.size());
+ dex_profile_index_remap.push_back(dex_data->profile_index);
+ }
+
+ // Then merge extra descriptors.
+ dchecked_vector<ExtraDescriptorIndex> extra_descriptors_remap;
+ extra_descriptors_remap.reserve(other.extra_descriptors_.size());
+ for (const std::string& other_extra_descriptor : other.extra_descriptors_) {
+ auto it = extra_descriptors_indexes_.find(std::string_view(other_extra_descriptor));
+ if (it != extra_descriptors_indexes_.end()) {
+ extra_descriptors_remap.push_back(*it);
+ } else {
+ ExtraDescriptorIndex extra_descriptor_index = AddExtraDescriptor(other_extra_descriptor);
+ if (extra_descriptor_index == kMaxExtraDescriptors) {
+ // Too many extra descriptors.
+ return false;
+ }
+ extra_descriptors_remap.push_back(extra_descriptor_index);
+ }
}
// Merge the actual profile data.
for (const std::unique_ptr<DexFileData>& other_dex_data : other.info_) {
- DexFileData* dex_data = const_cast<DexFileData*>(FindDexData(other_dex_data->profile_key,
- other_dex_data->checksum));
- DCHECK(dex_data != nullptr);
+ DexFileData* dex_data = info_[dex_profile_index_remap[other_dex_data->profile_index]].get();
+ DCHECK_EQ(dex_data, FindDexData(other_dex_data->profile_key, other_dex_data->checksum));
// Merge the classes.
+ uint32_t num_type_ids = dex_data->num_type_ids;
+ DCHECK_EQ(num_type_ids, other_dex_data->num_type_ids);
if (merge_classes) {
- dex_data->class_set.insert(other_dex_data->class_set.begin(),
- other_dex_data->class_set.end());
+ // Classes are ordered by the `TypeIndex`, so we have the classes with a `TypeId`
+ // in the dex file first, followed by classes using extra descriptors.
+ auto it = other_dex_data->class_set.lower_bound(dex::TypeIndex(num_type_ids));
+ dex_data->class_set.insert(other_dex_data->class_set.begin(), it);
+ for (auto end = other_dex_data->class_set.end(); it != end; ++it) {
+ ExtraDescriptorIndex new_extra_descriptor_index =
+ extra_descriptors_remap[it->index_ - num_type_ids];
+ if (new_extra_descriptor_index >= DexFile::kDexNoIndex16 - num_type_ids) {
+ // Cannot represent the type with new extra descriptor index.
+ return false;
+ }
+ dex_data->class_set.insert(dex::TypeIndex(num_type_ids + new_extra_descriptor_index));
+ }
}
// Merge the methods and the inline caches.
@@ -1686,16 +1891,24 @@
const auto& other_inline_cache = other_method_it.second;
for (const auto& other_ic_it : other_inline_cache) {
uint16_t other_dex_pc = other_ic_it.first;
- const ClassSet& other_class_set = other_ic_it.second.classes;
+ const ArenaSet<dex::TypeIndex>& other_class_set = other_ic_it.second.classes;
DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, other_dex_pc);
if (other_ic_it.second.is_missing_types) {
dex_pc_data->SetIsMissingTypes();
} else if (other_ic_it.second.is_megamorphic) {
dex_pc_data->SetIsMegamorphic();
} else {
- for (const auto& class_it : other_class_set) {
- dex_pc_data->AddClass(dex_profile_index_remap.Get(
- class_it.dex_profile_index), class_it.type_index);
+ for (dex::TypeIndex type_index : other_class_set) {
+ if (type_index.index_ >= num_type_ids) {
+ ExtraDescriptorIndex new_extra_descriptor_index =
+ extra_descriptors_remap[type_index.index_ - num_type_ids];
+ if (new_extra_descriptor_index >= DexFile::kDexNoIndex16 - num_type_ids) {
+ // Cannot represent the type with new extra descriptor index.
+ return false;
+ }
+ type_index = dex::TypeIndex(num_type_ids + new_extra_descriptor_index);
+ }
+ dex_pc_data->AddClass(type_index);
}
}
}
@@ -1758,6 +1971,14 @@
return os.str();
}
+ if (!extra_descriptors_.empty()) {
+ os << "\nextra descriptors:";
+ for (const std::string& str : extra_descriptors_) {
+ os << "\n\t" << str;
+ }
+ os << "\n";
+ }
+
const std::string kFirstDexFileKeySubstitute = "!classes.dex";
for (const std::unique_ptr<DexFileData>& dex_data : info_) {
@@ -1772,6 +1993,8 @@
}
os << " [index=" << static_cast<uint32_t>(dex_data->profile_index) << "]";
os << " [checksum=" << std::hex << dex_data->checksum << "]" << std::dec;
+ os << " [num_type_ids=" << dex_data->num_type_ids << "]";
+ os << " [num_method_ids=" << dex_data->num_method_ids << "]";
const DexFile* dex_file = nullptr;
for (const DexFile* current : dex_files) {
if (GetBaseKeyViewFromAugmentedKey(dex_data->profile_key) == current->GetLocation() &&
@@ -1795,9 +2018,10 @@
} else if (inline_cache_it.second.is_megamorphic) {
os << "MM";
} else {
- for (const ClassReference& class_ref : inline_cache_it.second.classes) {
- os << "(" << static_cast<uint32_t>(class_ref.dex_profile_index)
- << "," << class_ref.type_index.index_ << ")";
+ const char* separator = "";
+ for (dex::TypeIndex type_index : inline_cache_it.second.classes) {
+ os << separator << type_index.index_;
+ separator = ",";
}
}
os << "}";
@@ -1823,11 +2047,11 @@
startup = false;
}
os << "\n\tclasses: ";
- for (const auto class_it : dex_data->class_set) {
+ for (dex::TypeIndex type_index : dex_data->class_set) {
if (dex_file != nullptr) {
- os << "\n\t\t" << dex_file->PrettyType(class_it);
+ os << "\n\t\t" << PrettyDescriptor(GetTypeDescriptor(dex_file, type_index));
} else {
- os << class_it.index_ << ",";
+ os << type_index.index_ << ",";
}
}
}
@@ -1897,9 +2121,9 @@
const std::string base_dex_location = "base.apk";
ProfileCompilationInfo info;
// The limits are defined by the dex specification.
- const uint16_t max_method = std::numeric_limits<uint16_t>::max();
+ const uint16_t max_methods = std::numeric_limits<uint16_t>::max();
const uint16_t max_classes = std::numeric_limits<uint16_t>::max();
- uint16_t number_of_methods = max_method * method_percentage / 100;
+ uint16_t number_of_methods = max_methods * method_percentage / 100;
uint16_t number_of_classes = max_classes * class_percentage / 100;
std::srand(random_seed);
@@ -1913,9 +2137,10 @@
std::string dex_location = DexFileLoader::GetMultiDexLocation(i, base_dex_location.c_str());
std::string profile_key = info.GetProfileDexFileBaseKey(dex_location);
- DexFileData* const data = info.GetOrAddDexFileData(profile_key, /*checksum=*/ 0, max_method);
+ DexFileData* const data =
+ info.GetOrAddDexFileData(profile_key, /*checksum=*/ 0, max_classes, max_methods);
for (uint16_t m = 0; m < number_of_methods; m++) {
- uint16_t method_idx = rand() % max_method;
+ uint16_t method_idx = rand() % max_methods;
if (m < (number_of_methods / kFavorSplit)) {
method_idx %= kFavorFirstN;
}
@@ -1966,7 +2191,7 @@
uint32_t classes_required_in_profile = (number_of_classes * class_percentage) / 100;
DexFileData* const data = info.GetOrAddDexFileData(
- profile_key, checksum, dex_file->NumMethodIds());
+ profile_key, checksum, dex_file->NumTypeIds(), dex_file->NumMethodIds());
for (uint32_t class_index : create_shuffled_range(classes_required_in_profile,
number_of_classes)) {
data->class_set.insert(dex_file->GetClassDef(class_index).class_idx_);
@@ -1988,7 +2213,7 @@
}
bool ProfileCompilationInfo::IsEmpty() const {
- DCHECK_EQ(info_.empty(), profile_key_map_.empty());
+ DCHECK_EQ(info_.size(), profile_key_map_.size());
return info_.empty();
}
@@ -2005,8 +2230,9 @@
// Mark a method as executed at least once.
bool ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, size_t index) {
- if (index >= num_method_ids) {
- LOG(ERROR) << "Invalid method index " << index << ". num_method_ids=" << num_method_ids;
+ if (index >= num_method_ids || index > kMaxSupportedMethodIndex) {
+ LOG(ERROR) << "Invalid method index " << index << ". num_method_ids=" << num_method_ids
+ << ", max: " << kMaxSupportedMethodIndex;
return false;
}
@@ -2019,9 +2245,8 @@
return true;
}
-void ProfileCompilationInfo::DexFileData::SetMethodHotness(size_t index,
- MethodHotness::Flag flags) {
- DCHECK_LT(index, num_method_ids);
+template <typename Fn>
+ALWAYS_INLINE void ProfileCompilationInfo::DexFileData::ForMethodBitmapHotnessFlags(Fn fn) const {
uint32_t lastFlag = is_for_boot_image
? MethodHotness::kFlagLastBoot
: MethodHotness::kFlagLastRegular;
@@ -2031,28 +2256,30 @@
// We store the hotness by recording the method in the method list.
continue;
}
+ fn(enum_cast<MethodHotness::Flag>(flag));
+ }
+}
+
+void ProfileCompilationInfo::DexFileData::SetMethodHotness(size_t index,
+ MethodHotness::Flag flags) {
+ DCHECK_LT(index, num_method_ids);
+ ForMethodBitmapHotnessFlags([&](MethodHotness::Flag flag) {
if ((flags & flag) != 0) {
method_bitmap.StoreBit(MethodFlagBitmapIndex(
static_cast<MethodHotness::Flag>(flag), index), /*value=*/ true);
}
- }
+ });
}
ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::DexFileData::GetHotnessInfo(
uint32_t dex_method_index) const {
MethodHotness ret;
- uint32_t lastFlag = is_for_boot_image
- ? MethodHotness::kFlagLastBoot
- : MethodHotness::kFlagLastRegular;
- for (uint32_t flag = MethodHotness::kFlagFirst; flag <= lastFlag; flag = flag << 1) {
- if (flag == MethodHotness::kFlagHot) {
- continue;
- }
+ ForMethodBitmapHotnessFlags([&](MethodHotness::Flag flag) {
if (method_bitmap.LoadBit(MethodFlagBitmapIndex(
static_cast<MethodHotness::Flag>(flag), dex_method_index))) {
ret.AddFlag(static_cast<MethodHotness::Flag>(flag));
}
- }
+ });
auto it = method_map.find(dex_method_index);
if (it != method_map.end()) {
ret.SetInlineCacheMap(&it->second);
@@ -2098,9 +2325,20 @@
return WhichPowerOf2(static_cast<uint32_t>(flag)) - 1;
}
+uint16_t ProfileCompilationInfo::DexFileData::GetUsedBitmapFlags() const {
+ uint32_t used_flags = 0u;
+ ForMethodBitmapHotnessFlags([&](MethodHotness::Flag flag) {
+ size_t index = FlagBitmapIndex(static_cast<MethodHotness::Flag>(flag));
+ if (method_bitmap.HasSomeBitSet(index * num_method_ids, num_method_ids)) {
+ used_flags |= flag;
+ }
+ });
+ return dchecked_integral_cast<uint16_t>(used_flags);
+}
+
ProfileCompilationInfo::DexPcData*
ProfileCompilationInfo::FindOrAddDexPc(InlineCacheMap* inline_cache, uint32_t dex_pc) {
- return &(inline_cache->FindOrAdd(dex_pc, DexPcData(&allocator_))->second);
+ return &(inline_cache->FindOrAdd(dex_pc, DexPcData(inline_cache->get_allocator()))->second);
}
HashSet<std::string> ProfileCompilationInfo::GetClassDescriptors(
@@ -2111,14 +2349,7 @@
const DexFileData* data = FindDexDataUsingAnnotations(dex_file, annotation);
if (data != nullptr) {
for (dex::TypeIndex type_idx : data->class_set) {
- if (!dex_file->IsTypeIndexValid(type_idx)) {
- // Something went bad. The profile is probably corrupted. Abort and return an emtpy set.
- LOG(WARNING) << "Corrupted profile: invalid type index "
- << type_idx.index_ << " in dex " << dex_file->GetLocation();
- return HashSet<std::string>();
- }
- const dex::TypeId& type_id = dex_file->GetTypeId(type_idx);
- ret.insert(dex_file->GetTypeDescriptor(type_id));
+ ret.insert(GetTypeDescriptor(dex_file, type_idx));
}
} else {
VLOG(compiler) << "Failed to find profile data for " << dex_file->GetLocation();
@@ -2161,8 +2392,9 @@
const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
for (const std::unique_ptr<DexFileData>& dex_data : info_) {
- if (dex_data->checksum == dex_file->GetLocationChecksum()
- && dex_data->num_method_ids == dex_file->NumMethodIds()) {
+ if (dex_data->checksum == dex_file->GetLocationChecksum() &&
+ dex_data->num_type_ids == dex_file->NumTypeIds() &&
+ dex_data->num_method_ids == dex_file->NumMethodIds()) {
std::string new_profile_key = GetProfileDexFileBaseKey(dex_file->GetLocation());
std::string dex_data_base_key = GetBaseKeyFromAugmentedKey(dex_data->profile_key);
if (dex_data_base_key != new_profile_key) {
@@ -2193,6 +2425,8 @@
void ProfileCompilationInfo::ClearData() {
profile_key_map_.clear();
info_.clear();
+ extra_descriptors_indexes_.clear();
+ extra_descriptors_.clear();
}
void ProfileCompilationInfo::ClearDataAndAdjustVersion(bool for_boot_image) {
@@ -2210,10 +2444,396 @@
return version_;
}
-bool ProfileCompilationInfo::DexFileData::ContainsClass(const dex::TypeIndex type_index) const {
+bool ProfileCompilationInfo::DexFileData::ContainsClass(dex::TypeIndex type_index) const {
return class_set.find(type_index) != class_set.end();
}
+uint32_t ProfileCompilationInfo::DexFileData::ClassesDataSize() const {
+ return class_set.empty()
+ ? 0u
+ : sizeof(ProfileIndexType) + // Which dex file.
+ sizeof(uint16_t) + // Number of classes.
+ sizeof(uint16_t) * class_set.size(); // Type index diffs.
+}
+
+void ProfileCompilationInfo::DexFileData::WriteClasses(SafeBuffer& buffer) const {
+ if (class_set.empty()) {
+ return;
+ }
+ buffer.WriteUintAndAdvance(profile_index);
+ buffer.WriteUintAndAdvance(dchecked_integral_cast<uint16_t>(class_set.size()));
+ WriteClassSet(buffer, class_set);
+}
+
+ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::DexFileData::ReadClasses(
+ SafeBuffer& buffer,
+ const dchecked_vector<ExtraDescriptorIndex>& extra_descriptors_remap,
+ std::string* error) {
+ uint16_t classes_size;
+ if (!buffer.ReadUintAndAdvance(&classes_size)) {
+ *error = "Error reading classes size.";
+ return ProfileLoadStatus::kBadData;
+ }
+ uint16_t num_valid_type_indexes = dchecked_integral_cast<uint16_t>(
+ std::min<size_t>(num_type_ids + extra_descriptors_remap.size(), DexFile::kDexNoIndex16));
+ uint16_t type_index = 0u;
+ for (size_t i = 0; i != classes_size; ++i) {
+ uint16_t type_index_diff;
+ if (!buffer.ReadUintAndAdvance(&type_index_diff)) {
+ *error = "Error reading class type index diff.";
+ return ProfileLoadStatus::kBadData;
+ }
+ if (type_index_diff == 0u && i != 0u) {
+ *error = "Duplicate type index.";
+ return ProfileLoadStatus::kBadData;
+ }
+ if (type_index_diff >= num_valid_type_indexes - type_index) {
+ *error = "Invalid type index.";
+ return ProfileLoadStatus::kBadData;
+ }
+ type_index += type_index_diff;
+ if (type_index >= num_type_ids) {
+ uint32_t new_extra_descriptor_index = extra_descriptors_remap[type_index - num_type_ids];
+ if (new_extra_descriptor_index >= DexFile::kDexNoIndex16 - num_type_ids) {
+ *error = "Remapped type index out of range.";
+ return ProfileLoadStatus::kMergeError;
+ }
+ type_index = num_type_ids + new_extra_descriptor_index;
+ }
+ class_set.insert(dex::TypeIndex(type_index));
+ }
+ return ProfileLoadStatus::kSuccess;
+}
+
+ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::DexFileData::SkipClasses(
+ SafeBuffer& buffer,
+ std::string* error) {
+ uint16_t classes_size;
+ if (!buffer.ReadUintAndAdvance(&classes_size)) {
+ *error = "Error reading classes size to skip.";
+ return ProfileLoadStatus::kBadData;
+ }
+ size_t following_data_size = static_cast<size_t>(classes_size) * sizeof(uint16_t);
+ if (following_data_size > buffer.GetAvailableBytes()) {
+ *error = "Classes data size to skip exceeds remaining data.";
+ return ProfileLoadStatus::kBadData;
+ }
+ buffer.Advance(following_data_size);
+ return ProfileLoadStatus::kSuccess;
+}
+
+uint32_t ProfileCompilationInfo::DexFileData::MethodsDataSize(
+ /*out*/ uint16_t* method_flags,
+ /*out*/ size_t* saved_bitmap_bit_size) const {
+ uint16_t local_method_flags = GetUsedBitmapFlags();
+ size_t local_saved_bitmap_bit_size = POPCOUNT(local_method_flags) * num_method_ids;
+ if (!method_map.empty()) {
+ local_method_flags |= enum_cast<uint16_t>(MethodHotness::kFlagHot);
+ }
+ size_t size = 0u;
+ if (local_method_flags != 0u) {
+ size_t num_hot_methods = method_map.size();
+ size_t num_dex_pc_entries = 0u;
+ size_t num_class_entries = 0u;
+ for (const auto& method_entry : method_map) {
+ const InlineCacheMap& inline_cache_map = method_entry.second;
+ num_dex_pc_entries += inline_cache_map.size();
+ for (const auto& inline_cache_entry : inline_cache_map) {
+ const DexPcData& dex_pc_data = inline_cache_entry.second;
+ num_class_entries += dex_pc_data.classes.size();
+ }
+ }
+
+ constexpr size_t kPerHotMethodSize =
+ sizeof(uint16_t) + // Method index diff.
+ sizeof(uint16_t); // Inline cache size.
+ constexpr size_t kPerDexPcEntrySize =
+ sizeof(uint16_t) + // Dex PC.
+ sizeof(uint8_t); // Number of inline cache classes.
+ constexpr size_t kPerClassEntrySize =
+ sizeof(uint16_t); // Type index diff.
+
+ size_t saved_bitmap_byte_size = BitsToBytesRoundUp(local_saved_bitmap_bit_size);
+ size = sizeof(ProfileIndexType) + // Which dex file.
+ sizeof(uint32_t) + // Total size of following data.
+ sizeof(uint16_t) + // Method flags.
+ saved_bitmap_byte_size + // Bitmap data.
+ num_hot_methods * kPerHotMethodSize + // Data for hot methods.
+ num_dex_pc_entries * kPerDexPcEntrySize + // Data for dex pc entries.
+ num_class_entries * kPerClassEntrySize; // Data for inline cache class entries.
+ }
+ if (method_flags != nullptr) {
+ *method_flags = local_method_flags;
+ }
+ if (saved_bitmap_bit_size != nullptr) {
+ *saved_bitmap_bit_size = local_saved_bitmap_bit_size;
+ }
+ return size;
+}
+
+void ProfileCompilationInfo::DexFileData::WriteMethods(SafeBuffer& buffer) const {
+ uint16_t method_flags;
+ size_t saved_bitmap_bit_size;
+ uint32_t methods_data_size = MethodsDataSize(&method_flags, &saved_bitmap_bit_size);
+ if (methods_data_size == 0u) {
+ return; // No data to write.
+ }
+ DCHECK_GE(buffer.GetAvailableBytes(), methods_data_size);
+ uint32_t expected_available_bytes_at_end = buffer.GetAvailableBytes() - methods_data_size;
+
+ // Write the profile index.
+ buffer.WriteUintAndAdvance(profile_index);
+ // Write the total size of the following methods data (without the profile index
+ // and the total size itself) for easy skipping when the dex file is filtered out.
+ uint32_t following_data_size = methods_data_size - sizeof(ProfileIndexType) - sizeof(uint32_t);
+ buffer.WriteUintAndAdvance(following_data_size);
+ // Write the used method flags.
+ buffer.WriteUintAndAdvance(method_flags);
+
+ // Write the bitmap data.
+ size_t saved_bitmap_byte_size = BitsToBytesRoundUp(saved_bitmap_bit_size);
+ DCHECK_LE(saved_bitmap_byte_size, buffer.GetAvailableBytes());
+ BitMemoryRegion saved_bitmap(buffer.GetCurrentPtr(), /*bit_start=*/ 0, saved_bitmap_bit_size);
+ size_t saved_bitmap_index = 0u;
+ ForMethodBitmapHotnessFlags([&](MethodHotness::Flag flag) {
+ if ((method_flags & flag) != 0u) {
+ size_t index = FlagBitmapIndex(static_cast<MethodHotness::Flag>(flag));
+ BitMemoryRegion src = method_bitmap.Subregion(index * num_method_ids, num_method_ids);
+ saved_bitmap.StoreBits(saved_bitmap_index * num_method_ids, src, num_method_ids);
+ ++saved_bitmap_index;
+ }
+ });
+ DCHECK_EQ(saved_bitmap_index * num_method_ids, saved_bitmap_bit_size);
+ buffer.Advance(saved_bitmap_byte_size);
+
+ uint16_t last_method_index = 0;
+ for (const auto& method_entry : method_map) {
+ uint16_t method_index = method_entry.first;
+ const InlineCacheMap& inline_cache_map = method_entry.second;
+
+ // Store the difference between the method indices for better compression.
+ // The SafeMap is ordered by method_id, so the difference will always be non negative.
+ DCHECK_GE(method_index, last_method_index);
+ uint16_t diff_with_last_method_index = method_index - last_method_index;
+ last_method_index = method_index;
+ buffer.WriteUintAndAdvance(diff_with_last_method_index);
+
+ // Add inline cache map size.
+ buffer.WriteUintAndAdvance(dchecked_integral_cast<uint16_t>(inline_cache_map.size()));
+
+ // Add inline cache entries.
+ for (const auto& inline_cache_entry : inline_cache_map) {
+ uint16_t dex_pc = inline_cache_entry.first;
+ const DexPcData& dex_pc_data = inline_cache_entry.second;
+ const ArenaSet<dex::TypeIndex>& classes = dex_pc_data.classes;
+
+ // Add the dex pc.
+ buffer.WriteUintAndAdvance(dex_pc);
+
+ // Add the megamorphic/missing_types encoding if needed and continue.
+ // In either cases we don't add any classes to the profiles and so there's
+ // no point to continue.
+ // TODO: in case we miss types there is still value to add the rest of the
+ // classes. (This requires changing profile version or using a new section type.)
+ if (dex_pc_data.is_missing_types) {
+ // At this point the megamorphic flag should not be set.
+ DCHECK(!dex_pc_data.is_megamorphic);
+ DCHECK_EQ(classes.size(), 0u);
+ buffer.WriteUintAndAdvance(kIsMissingTypesEncoding);
+ continue;
+ } else if (dex_pc_data.is_megamorphic) {
+ DCHECK_EQ(classes.size(), 0u);
+ buffer.WriteUintAndAdvance(kIsMegamorphicEncoding);
+ continue;
+ }
+
+ DCHECK_LT(classes.size(), ProfileCompilationInfo::kIndividualInlineCacheSize);
+ DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes";
+
+ // Add the number of classes for the dex PC.
+ buffer.WriteUintAndAdvance(dchecked_integral_cast<uint8_t>(classes.size()));
+ // Store the class set.
+ WriteClassSet(buffer, classes);
+ }
+ }
+
+ // Check if we've written the right number of bytes.
+ DCHECK_EQ(buffer.GetAvailableBytes(), expected_available_bytes_at_end);
+}
+
+ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::DexFileData::ReadMethods(
+ SafeBuffer& buffer,
+ const dchecked_vector<ExtraDescriptorIndex>& extra_descriptors_remap,
+ std::string* error) {
+ uint32_t following_data_size;
+ if (!buffer.ReadUintAndAdvance(&following_data_size)) {
+ *error = "Error reading methods data size.";
+ return ProfileLoadStatus::kBadData;
+ }
+ if (following_data_size > buffer.GetAvailableBytes()) {
+ *error = "Methods data size exceeds available data size.";
+ return ProfileLoadStatus::kBadData;
+ }
+ uint32_t expected_available_bytes_at_end = buffer.GetAvailableBytes() - following_data_size;
+
+ // Read method flags.
+ uint16_t method_flags;
+ if (!buffer.ReadUintAndAdvance(&method_flags)) {
+ *error = "Error reading method flags.";
+ return ProfileLoadStatus::kBadData;
+ }
+ if (!is_for_boot_image && method_flags >= (MethodHotness::kFlagLastRegular << 1)) {
+ // The profile we're loading contains data for boot image.
+ *error = "Method flags contain boot image profile flags for non-boot image profile.";
+ return ProfileLoadStatus::kBadData;
+ }
+
+ // Read method bitmap.
+ size_t saved_bitmap_bit_size = POPCOUNT(method_flags & ~MethodHotness::kFlagHot) * num_method_ids;
+ size_t saved_bitmap_byte_size = BitsToBytesRoundUp(saved_bitmap_bit_size);
+ if (sizeof(uint16_t) + saved_bitmap_byte_size > following_data_size) {
+ *error = "Insufficient available data for method bitmap.";
+ return ProfileLoadStatus::kBadData;
+ }
+ BitMemoryRegion saved_bitmap(buffer.GetCurrentPtr(), /*bit_start=*/ 0, saved_bitmap_bit_size);
+ size_t saved_bitmap_index = 0u;
+ ForMethodBitmapHotnessFlags([&](MethodHotness::Flag flag) {
+ if ((method_flags & flag) != 0u) {
+ size_t index = FlagBitmapIndex(static_cast<MethodHotness::Flag>(flag));
+ BitMemoryRegion src =
+ saved_bitmap.Subregion(saved_bitmap_index * num_method_ids, num_method_ids);
+ method_bitmap.OrBits(index * num_method_ids, src, num_method_ids);
+ ++saved_bitmap_index;
+ }
+ });
+ buffer.Advance(saved_bitmap_byte_size);
+
+ // Load hot methods.
+ if ((method_flags & MethodHotness::kFlagHot) != 0u) {
+ uint32_t num_valid_method_indexes =
+ std::min<uint32_t>(kMaxSupportedMethodIndex + 1u, num_method_ids);
+ uint16_t num_valid_type_indexes = dchecked_integral_cast<uint16_t>(
+ std::min<size_t>(num_type_ids + extra_descriptors_remap.size(), DexFile::kDexNoIndex16));
+ uint16_t method_index = 0;
+ bool first_diff = true;
+ while (buffer.GetAvailableBytes() > expected_available_bytes_at_end) {
+ uint16_t diff_with_last_method_index;
+ if (!buffer.ReadUintAndAdvance(&diff_with_last_method_index)) {
+ *error = "Error reading method index diff.";
+ return ProfileLoadStatus::kBadData;
+ }
+ if (diff_with_last_method_index == 0u && !first_diff) {
+ *error = "Duplicate method index.";
+ return ProfileLoadStatus::kBadData;
+ }
+ first_diff = false;
+ if (diff_with_last_method_index >= num_valid_method_indexes - method_index) {
+ *error = "Invalid method index.";
+ return ProfileLoadStatus::kBadData;
+ }
+ method_index += diff_with_last_method_index;
+ InlineCacheMap* inline_cache = FindOrAddHotMethod(method_index);
+ DCHECK(inline_cache != nullptr);
+
+ // Load inline cache map size.
+ uint16_t inline_cache_size;
+ if (!buffer.ReadUintAndAdvance(&inline_cache_size)) {
+ *error = "Error reading inline cache size.";
+ return ProfileLoadStatus::kBadData;
+ }
+ for (uint16_t ic_index = 0; ic_index != inline_cache_size; ++ic_index) {
+ // Load dex pc.
+ uint16_t dex_pc;
+ if (!buffer.ReadUintAndAdvance(&dex_pc)) {
+ *error = "Error reading inline cache dex pc.";
+ return ProfileLoadStatus::kBadData;
+ }
+ DexPcData* dex_pc_data = FindOrAddDexPc(inline_cache, dex_pc);
+ DCHECK(dex_pc_data != nullptr);
+
+ // Load inline cache classes.
+ uint8_t inline_cache_classes_size;
+ if (!buffer.ReadUintAndAdvance(&inline_cache_classes_size)) {
+ *error = "Error reading inline cache classes size.";
+ return ProfileLoadStatus::kBadData;
+ }
+ if (inline_cache_classes_size == kIsMissingTypesEncoding) {
+ dex_pc_data->SetIsMissingTypes();
+ } else if (inline_cache_classes_size == kIsMegamorphicEncoding) {
+ dex_pc_data->SetIsMegamorphic();
+ } else if (inline_cache_classes_size >= kIndividualInlineCacheSize) {
+ *error = "Inline cache size too large.";
+ return ProfileLoadStatus::kBadData;
+ } else {
+ uint16_t type_index = 0u;
+ for (size_t i = 0; i != inline_cache_classes_size; ++i) {
+ uint16_t type_index_diff;
+ if (!buffer.ReadUintAndAdvance(&type_index_diff)) {
+ *error = "Error reading inline cache type index diff.";
+ return ProfileLoadStatus::kBadData;
+ }
+ if (type_index_diff == 0u && i != 0u) {
+ *error = "Duplicate inline cache type index.";
+ return ProfileLoadStatus::kBadData;
+ }
+ if (type_index_diff >= num_valid_type_indexes - type_index) {
+ *error = "Invalid inline cache type index.";
+ return ProfileLoadStatus::kBadData;
+ }
+ type_index += type_index_diff;
+ if (type_index >= num_type_ids) {
+ ExtraDescriptorIndex new_extra_descriptor_index =
+ extra_descriptors_remap[type_index - num_type_ids];
+ if (new_extra_descriptor_index >= DexFile::kDexNoIndex16 - num_type_ids) {
+ *error = "Remapped inline cache type index out of range.";
+ return ProfileLoadStatus::kMergeError;
+ }
+ type_index = num_type_ids + new_extra_descriptor_index;
+ }
+ dex_pc_data->AddClass(dex::TypeIndex(type_index));
+ }
+ }
+ }
+ }
+ }
+
+ if (buffer.GetAvailableBytes() != expected_available_bytes_at_end) {
+ *error = "Methods data did not end at expected position.";
+ return ProfileLoadStatus::kBadData;
+ }
+
+ return ProfileLoadStatus::kSuccess;
+}
+
+ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::DexFileData::SkipMethods(
+ SafeBuffer& buffer,
+ std::string* error) {
+ uint32_t following_data_size;
+ if (!buffer.ReadUintAndAdvance(&following_data_size)) {
+ *error = "Error reading methods data size to skip.";
+ return ProfileLoadStatus::kBadData;
+ }
+ if (following_data_size > buffer.GetAvailableBytes()) {
+ *error = "Methods data size to skip exceeds remaining data.";
+ return ProfileLoadStatus::kBadData;
+ }
+ buffer.Advance(following_data_size);
+ return ProfileLoadStatus::kSuccess;
+}
+
+void ProfileCompilationInfo::DexFileData::WriteClassSet(
+ SafeBuffer& buffer,
+ const ArenaSet<dex::TypeIndex>& class_set) {
+ // Store the difference between the type indexes for better compression.
+ uint16_t last_type_index = 0u;
+ for (const dex::TypeIndex& type_index : class_set) {
+ DCHECK_GE(type_index.index_, last_type_index);
+ uint16_t diff_with_last_type_index = type_index.index_ - last_type_index;
+ last_type_index = type_index.index_;
+ buffer.WriteUintAndAdvance(diff_with_last_type_index);
+ }
+}
+
size_t ProfileCompilationInfo::GetSizeWarningThresholdBytes() const {
return IsForBootImage() ? kSizeWarningThresholdBootBytes : kSizeWarningThresholdBytes;
}
@@ -2226,44 +2846,12 @@
ProfileCompilationInfo::DexReferenceDumper dumper) {
stream << "[profile_key=" << dumper.GetProfileKey()
<< ",dex_checksum=" << std::hex << dumper.GetDexChecksum() << std::dec
+ << ",num_type_ids=" << dumper.GetNumTypeIds()
<< ",num_method_ids=" << dumper.GetNumMethodIds()
<< "]";
return stream;
}
-void ProfileCompilationInfo::WriteProfileIndex(
- std::vector<uint8_t>* buffer, ProfileIndexType value) const {
- if (IsForBootImage()) {
- AddUintToBuffer(buffer, value);
- } else {
- AddUintToBuffer(buffer, static_cast<ProfileIndexTypeRegular>(value));
- }
-}
-
-bool ProfileCompilationInfo::ReadProfileIndex(
- SafeBuffer& safe_buffer, ProfileIndexType* value) const {
- if (IsForBootImage()) {
- return safe_buffer.ReadUintAndAdvance<ProfileIndexType>(value);
- } else {
- ProfileIndexTypeRegular out;
- bool result = safe_buffer.ReadUintAndAdvance<ProfileIndexTypeRegular>(&out);
- *value = out;
- return result;
- }
-}
-
-ProfileCompilationInfo::ProfileIndexType ProfileCompilationInfo::MaxProfileIndex() const {
- return IsForBootImage()
- ? std::numeric_limits<ProfileIndexType>::max()
- : std::numeric_limits<ProfileIndexTypeRegular>::max();
-}
-
-uint32_t ProfileCompilationInfo::SizeOfProfileIndexType() const {
- return IsForBootImage()
- ? sizeof(ProfileIndexType)
- : sizeof(ProfileIndexTypeRegular);
-}
-
FlattenProfileData::FlattenProfileData() :
max_aggregation_for_methods_(0),
max_aggregation_for_classes_(0) {
@@ -2320,6 +2908,12 @@
// Check which classes from the current dex files are in the profile.
for (const dex::TypeIndex& type_index : dex_data->class_set) {
+ if (type_index.index_ >= dex_file->NumTypeIds()) {
+ // Not a valid `dex::TypeIndex` for `TypeReference`.
+ // TODO: Rewrite the API to use descriptors or the `ProfileCompilationInfo` directly
+ // instead of the `FlattenProfileData` helper class.
+ continue;
+ }
TypeReference ref(dex_file.get(), type_index);
FlattenProfileData::ItemMetadata& metadata =
result->class_metadata_.GetOrCreate(ref, create_metadata_fn);
diff --git a/libprofile/profile/profile_compilation_info.h b/libprofile/profile/profile_compilation_info.h
index 1cf8da5..b1bbe1b 100644
--- a/libprofile/profile/profile_compilation_info.h
+++ b/libprofile/profile/profile_compilation_info.h
@@ -28,6 +28,7 @@
#include "base/array_ref.h"
#include "base/atomic.h"
#include "base/bit_memory_region.h"
+#include "base/hash_map.h"
#include "base/hash_set.h"
#include "base/malloc_arena_pool.h"
#include "base/mem_map.h"
@@ -57,6 +58,9 @@
const uint32_t dex_pc;
const bool is_missing_types;
+ // TODO: Replace `TypeReference` with `dex::TypeIndex` and allow artificial
+ // type indexes for types without a `dex::TypeId` in any dex file processed
+ // by the profman. See `ProfileCompilationInfo::FindOrCreateTypeIndex()`.
const std::vector<TypeReference> classes;
const bool is_megamorphic;
};
@@ -94,20 +98,9 @@
// This is exposed as public in order to make it available to dex2oat compilations
// (see compiler/optimizing/inliner.cc).
- // The types used to manipulate the profile index of dex files.
- // They set an upper limit to how many dex files a given profile can record.
- //
- // Boot profiles have more needs than regular profiles as they contain data from
- // many apps merged together. As such they set the default type for data manipulation.
- //
- // Regular profiles don't record a lot of dex files, and use a smaller data type
- // in order to save disk and ram.
- //
- // In-memory all profiles will use ProfileIndexType to represent the indices. However,
- // when serialized, the profile type (boot or regular) will determine which data type
- // is used to write the data.
+ // The type used to manipulate the profile index of dex files.
+ // It sets an upper limit to how many dex files a given profile can record.
using ProfileIndexType = uint16_t;
- using ProfileIndexTypeRegular = uint8_t;
// Encodes a class reference in the profile.
// The owning dex file is encoded as the index (dex_profile_index) it has in the
@@ -136,18 +129,17 @@
dex::TypeIndex type_index; // the type index of the class
};
- // The set of classes that can be found at a given dex pc.
- using ClassSet = ArenaSet<ClassReference>;
-
// Encodes the actual inline cache for a given dex pc (whether or not the receiver is
// megamorphic and its possible types).
// If the receiver is megamorphic or is missing types the set of classes will be empty.
struct DexPcData : public ArenaObject<kArenaAllocProfile> {
explicit DexPcData(ArenaAllocator* allocator)
+ : DexPcData(allocator->Adapter(kArenaAllocProfile)) {}
+ explicit DexPcData(const ArenaAllocatorAdapter<void>& allocator)
: is_missing_types(false),
is_megamorphic(false),
- classes(std::less<ClassReference>(), allocator->Adapter(kArenaAllocProfile)) {}
- void AddClass(uint16_t dex_profile_idx, const dex::TypeIndex& type_idx);
+ classes(std::less<dex::TypeIndex>(), allocator) {}
+ void AddClass(const dex::TypeIndex& type_idx);
void SetIsMegamorphic() {
if (is_missing_types) return;
is_megamorphic = true;
@@ -169,7 +161,7 @@
// encoded. When types are missing this field will be set to true.
bool is_missing_types;
bool is_megamorphic;
- ClassSet classes;
+ ArenaSet<dex::TypeIndex> classes;
};
// The inline cache map: DexPc -> DexPcData.
@@ -311,6 +303,39 @@
MethodHotness::Flag flags,
const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone);
+ // Find a type index in the `dex_file` if there is a `TypeId` for it. Otherwise,
+ // find or insert the descriptor in "extra descriptors" and return an artificial
+ // type index beyond `dex_file.NumTypeIds()`. This fails if the artificial index
+ // would be kDexNoIndex16 (0xffffu) or higher, returning an invalid type index.
+ // The returned type index can be used, if valid, for `AddClass()` or (TODO) as
+ // a type index for inline caches.
+ dex::TypeIndex FindOrCreateTypeIndex(const DexFile& dex_file, TypeReference class_ref);
+ dex::TypeIndex FindOrCreateTypeIndex(const DexFile& dex_file, const char* descriptor);
+
+ // Add a class with the specified `type_index` to the profile. The `type_index`
+ // can be either a normal index for a `TypeId` in the dex file, or an artificial
+ // type index created by `FindOrCreateTypeIndex()`.
+ // Returns `true` on success, `false` on failure.
+ bool AddClass(const DexFile& dex_file,
+ dex::TypeIndex type_index,
+ const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone);
+
+ // Add a class with the specified `descriptor` to the profile.
+ // Returns `true` on success, `false` on failure.
+ bool AddClass(const DexFile& dex_file,
+ const char* descriptor,
+ const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone);
+ bool AddClass(const DexFile& dex_file,
+ const std::string& descriptor,
+ const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) {
+ return AddClass(dex_file, descriptor.c_str(), annotation);
+ }
+ bool AddClass(const DexFile& dex_file,
+ std::string_view descriptor,
+ const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) {
+ return AddClass(dex_file, std::string(descriptor).c_str(), annotation);
+ }
+
// Add multiple type ids for classes in a single dex file. Iterator is for type_ids not
// class_defs.
//
@@ -556,6 +581,19 @@
// Returns true if the profile is configured to store aggregation counters.
bool IsForBootImage() const;
+ // Get type descriptor for a valid type index, whether a normal type index
+ // referencing a `dex::TypeId` in the dex file, or an artificial type index
+ // referencing an "extra descriptor".
+ const char* GetTypeDescriptor(const DexFile* dex_file, dex::TypeIndex type_index) const {
+ DCHECK(type_index.IsValid());
+ uint32_t num_type_ids = dex_file->NumTypeIds();
+ if (type_index.index_ < num_type_ids) {
+ return dex_file->StringByTypeIdx(type_index);
+ } else {
+ return extra_descriptors_[type_index.index_ - num_type_ids].c_str();
+ }
+ }
+
// Return the version of this profile.
const uint8_t* GetVersion() const;
@@ -570,16 +608,71 @@
private:
// Helper classes.
+ class FileHeader;
+ class FileSectionInfo;
+ enum class FileSectionType : uint32_t;
+ enum class ProfileLoadStatus : uint32_t;
class ProfileSource;
class SafeBuffer;
- enum class ProfileLoadStatus : uint32_t {
- kSuccess,
- kIOError,
- kVersionMismatch,
- kBadData,
+ // Extra descriptors are used to reference classes with `TypeIndex` between the dex
+ // file's `NumTypeIds()` and the `DexFile::kDexNoIndex16`. The range of usable
+ // extra descriptor indexes is therefore also limited by `DexFile::kDexNoIndex16`.
+ using ExtraDescriptorIndex = uint16_t;
+ static constexpr ExtraDescriptorIndex kMaxExtraDescriptors = DexFile::kDexNoIndex16;
+
+ class ExtraDescriptorIndexEmpty {
+ public:
+ void MakeEmpty(ExtraDescriptorIndex& index) const {
+ index = kMaxExtraDescriptors;
+ }
+ bool IsEmpty(const ExtraDescriptorIndex& index) const {
+ return index == kMaxExtraDescriptors;
+ }
};
+ class ExtraDescriptorHash {
+ public:
+ explicit ExtraDescriptorHash(const dchecked_vector<std::string>* extra_descriptors)
+ : extra_descriptors_(extra_descriptors) {}
+
+ size_t operator()(const ExtraDescriptorIndex& index) const {
+ std::string_view str = (*extra_descriptors_)[index];
+ return (*this)(str);
+ }
+
+ size_t operator()(std::string_view str) const {
+ return DataHash()(str);
+ }
+
+ private:
+ const dchecked_vector<std::string>* extra_descriptors_;
+ };
+
+ class ExtraDescriptorEquals {
+ public:
+ explicit ExtraDescriptorEquals(const dchecked_vector<std::string>* extra_descriptors)
+ : extra_descriptors_(extra_descriptors) {}
+
+ size_t operator()(const ExtraDescriptorIndex& lhs, const ExtraDescriptorIndex& rhs) const {
+ DCHECK_EQ(lhs == rhs, (*this)(lhs, (*extra_descriptors_)[rhs]));
+ return lhs == rhs;
+ }
+
+ size_t operator()(const ExtraDescriptorIndex& lhs, std::string_view rhs_str) const {
+ std::string_view lhs_str = (*extra_descriptors_)[lhs];
+ return lhs_str == rhs_str;
+ }
+
+ private:
+ const dchecked_vector<std::string>* extra_descriptors_;
+ };
+
+ using ExtraDescriptorHashSet = HashSet<ExtraDescriptorIndex,
+ ExtraDescriptorIndexEmpty,
+ ExtraDescriptorHash,
+ ExtraDescriptorEquals>;
+
// Internal representation of the profile information belonging to a dex file.
// Note that we could do without the profile_index (the index of the dex file
// in the profile) field in this struct because we can infer it from
@@ -590,6 +683,7 @@
const std::string& key,
uint32_t location_checksum,
uint16_t index,
+ uint32_t num_types,
uint32_t num_methods,
bool for_boot_image)
: allocator_(allocator),
@@ -598,6 +692,7 @@
checksum(location_checksum),
method_map(std::less<uint16_t>(), allocator->Adapter(kArenaAllocProfile)),
class_set(std::less<dex::TypeIndex>(), allocator->Adapter(kArenaAllocProfile)),
+ num_type_ids(num_types),
num_method_ids(num_methods),
bitmap_storage(allocator->Adapter(kArenaAllocProfile)),
is_for_boot_image(for_boot_image) {
@@ -644,7 +739,24 @@
void SetMethodHotness(size_t index, MethodHotness::Flag flags);
MethodHotness GetHotnessInfo(uint32_t dex_method_index) const;
- bool ContainsClass(const dex::TypeIndex type_index) const;
+ bool ContainsClass(dex::TypeIndex type_index) const;
+
+ uint32_t ClassesDataSize() const;
+ void WriteClasses(SafeBuffer& buffer) const;
+ ProfileLoadStatus ReadClasses(
+ SafeBuffer& buffer,
+ const dchecked_vector<ExtraDescriptorIndex>& extra_descriptors_remap,
+ std::string* error);
+ static ProfileLoadStatus SkipClasses(SafeBuffer& buffer, std::string* error);
+
+ uint32_t MethodsDataSize(/*out*/ uint16_t* method_flags = nullptr,
+ /*out*/ size_t* saved_bitmap_bit_size = nullptr) const;
+ void WriteMethods(SafeBuffer& buffer) const;
+ ProfileLoadStatus ReadMethods(
+ SafeBuffer& buffer,
+ const dchecked_vector<ExtraDescriptorIndex>& extra_descriptors_remap,
+ std::string* error);
+ static ProfileLoadStatus SkipMethods(SafeBuffer& buffer, std::string* error);
// The allocator used to allocate new inline cache maps.
ArenaAllocator* const allocator_;
@@ -662,6 +774,8 @@
// Find the inline caches of the the given method index. Add an empty entry if
// no previous data is found.
InlineCacheMap* FindOrAddHotMethod(uint16_t method_index);
+ // Num type ids.
+ uint32_t num_type_ids;
// Num method ids.
uint32_t num_method_ids;
ArenaVector<uint8_t> bitmap_storage;
@@ -669,20 +783,28 @@
bool is_for_boot_image;
private:
+ template <typename Fn>
+ void ForMethodBitmapHotnessFlags(Fn fn) const;
+
+ static void WriteClassSet(SafeBuffer& buffer, const ArenaSet<dex::TypeIndex>& class_set);
size_t MethodFlagBitmapIndex(MethodHotness::Flag flag, size_t method_index) const;
static size_t FlagBitmapIndex(MethodHotness::Flag flag);
+
+ uint16_t GetUsedBitmapFlags() const;
};
// Return the profile data for the given profile key or null if the dex location
// already exists but has a different checksum
DexFileData* GetOrAddDexFileData(const std::string& profile_key,
uint32_t checksum,
+ uint32_t num_type_ids,
uint32_t num_method_ids);
DexFileData* GetOrAddDexFileData(const DexFile* dex_file,
const ProfileSampleAnnotation& annotation) {
return GetOrAddDexFileData(GetProfileDexFileAugmentedKey(dex_file->GetLocation(), annotation),
dex_file->GetLocationChecksum(),
+ dex_file->NumTypeIds(),
dex_file->NumMethodIds());
}
@@ -705,21 +827,47 @@
const DexFile* dex_file,
/*out*/ std::vector<const ProfileCompilationInfo::DexFileData*>* result) const;
- // Parsing functionality.
+ // Add a new extra descriptor. Returns kMaxExtraDescriptors on failure.
+ ExtraDescriptorIndex AddExtraDescriptor(std::string_view extra_descriptor);
- // The information present in the header of each profile line.
- struct ProfileLineHeader {
- std::string profile_key;
- uint16_t class_set_size;
- uint32_t method_region_size_bytes;
- uint32_t checksum;
- uint32_t num_method_ids;
- };
+ // Parsing functionality.
ProfileLoadStatus OpenSource(int32_t fd,
/*out*/ std::unique_ptr<ProfileSource>* source,
/*out*/ std::string* error);
+ ProfileLoadStatus ReadSectionData(ProfileSource& source,
+ const FileSectionInfo& section_info,
+ /*out*/ SafeBuffer* buffer,
+ /*out*/ std::string* error);
+
+ ProfileLoadStatus ReadDexFilesSection(
+ ProfileSource& source,
+ const FileSectionInfo& section_info,
+ const ProfileLoadFilterFn& filter_fn,
+ /*out*/ dchecked_vector<ProfileIndexType>* dex_profile_index_remap,
+ /*out*/ std::string* error);
+
+ ProfileLoadStatus ReadExtraDescriptorsSection(
+ ProfileSource& source,
+ const FileSectionInfo& section_info,
+ /*out*/ dchecked_vector<ExtraDescriptorIndex>* extra_descriptors_remap,
+ /*out*/ std::string* error);
+
+ ProfileLoadStatus ReadClassesSection(
+ ProfileSource& source,
+ const FileSectionInfo& section_info,
+ const dchecked_vector<ProfileIndexType>& dex_profile_index_remap,
+ const dchecked_vector<ExtraDescriptorIndex>& extra_descriptors_remap,
+ /*out*/ std::string* error);
+
+ ProfileLoadStatus ReadMethodsSection(
+ ProfileSource& source,
+ const FileSectionInfo& section_info,
+ const dchecked_vector<ProfileIndexType>& dex_profile_index_remap,
+ const dchecked_vector<ExtraDescriptorIndex>& extra_descriptors_remap,
+ /*out*/ std::string* error);
+
// Entry point for profile loading functionality.
ProfileLoadStatus LoadInternal(
int32_t fd,
@@ -727,77 +875,9 @@
bool merge_classes = true,
const ProfileLoadFilterFn& filter_fn = ProfileFilterFnAcceptAll);
- // Read the profile header from the given fd and store the number of profile
- // lines into number_of_dex_files.
- ProfileLoadStatus ReadProfileHeader(ProfileSource& source,
- /*out*/ProfileIndexType* number_of_dex_files,
- /*out*/uint32_t* size_uncompressed_data,
- /*out*/uint32_t* size_compressed_data,
- /*out*/std::string* error);
-
- // Read the header of a profile line from the given fd.
- ProfileLoadStatus ReadProfileLineHeader(SafeBuffer& buffer,
- /*out*/ProfileLineHeader* line_header,
- /*out*/std::string* error);
-
- // Read individual elements from the profile line header.
- bool ReadProfileLineHeaderElements(SafeBuffer& buffer,
- /*out*/uint16_t* dex_location_size,
- /*out*/ProfileLineHeader* line_header,
- /*out*/std::string* error);
-
- // Read a single profile line from the given fd.
- ProfileLoadStatus ReadProfileLine(
- SafeBuffer& buffer,
- ProfileIndexType number_of_dex_files,
- const ProfileLineHeader& line_header,
- const SafeMap<ProfileIndexType, ProfileIndexType>& dex_profile_index_remap,
- bool merge_classes,
- /*out*/std::string* error);
-
- // Read all the classes from the buffer into the profile `info_` structure.
- bool ReadClasses(SafeBuffer& buffer,
- const ProfileLineHeader& line_header,
- /*out*/std::string* error);
-
- // Read all the methods from the buffer into the profile `info_` structure.
- bool ReadMethods(SafeBuffer& buffer,
- ProfileIndexType number_of_dex_files,
- const ProfileLineHeader& line_header,
- const SafeMap<ProfileIndexType, ProfileIndexType>& dex_profile_index_remap,
- /*out*/std::string* error);
-
- // The method generates mapping of profile indices while merging a new profile
- // data into current data. It returns true, if the mapping was successful.
- bool RemapProfileIndex(
- const std::vector<ProfileLineHeader>& profile_line_headers,
- const ProfileLoadFilterFn& filter_fn,
- /*out*/SafeMap<ProfileIndexType, ProfileIndexType>* dex_profile_index_remap);
-
- // Read the inline cache encoding from line_bufer into inline_cache.
- bool ReadInlineCache(SafeBuffer& buffer,
- ProfileIndexType number_of_dex_files,
- const SafeMap<ProfileIndexType, ProfileIndexType>& dex_profile_index_remap,
- /*out*/InlineCacheMap* inline_cache,
- /*out*/std::string* error);
-
- // Encode the inline cache into the given buffer.
- void AddInlineCacheToBuffer(std::vector<uint8_t>* buffer,
- const InlineCacheMap& inline_cache);
-
- // Return the number of bytes needed to encode the profile information
- // for the methods in dex_data.
- uint32_t GetMethodsRegionSize(const DexFileData& dex_data);
-
- // Group `classes` by their owning dex profile index and put the result in
- // `dex_to_classes_map`.
- void GroupClassesByDex(
- const ClassSet& classes,
- /*out*/SafeMap<ProfileIndexType, std::vector<dex::TypeIndex>>* dex_to_classes_map);
-
// Find the data for the dex_pc in the inline cache. Adds an empty entry
// if no previous data exists.
- DexPcData* FindOrAddDexPc(InlineCacheMap* inline_cache, uint32_t dex_pc);
+ static DexPcData* FindOrAddDexPc(InlineCacheMap* inline_cache, uint32_t dex_pc);
// Initializes the profile version to the desired one.
void InitProfileVersionInternal(const uint8_t version[]);
@@ -826,17 +906,10 @@
static std::string MigrateAnnotationInfo(const std::string& base_key,
const std::string& augmented_key);
- // Returns the maximum value for the profile index. It depends on the profile type.
- // Boot profiles can store more dex files than regular profiles.
- ProfileIndexType MaxProfileIndex() const;
- // Returns the size of the profile index type used for serialization.
- uint32_t SizeOfProfileIndexType() const;
- // Writes the profile index to the buffer. The type of profile will determine the
- // number of bytes used for serialization.
- void WriteProfileIndex(std::vector<uint8_t>* buffer, ProfileIndexType value) const;
- // Read the profile index from the buffer. The type of profile will determine the
- // number of bytes used for serialization.
- bool ReadProfileIndex(SafeBuffer& safe_buffer, ProfileIndexType* value) const;
+ // Returns the maximum value for the profile index.
+ static constexpr ProfileIndexType MaxProfileIndex() {
+ return std::numeric_limits<ProfileIndexType>::max();
+ }
friend class ProfileCompilationInfoTest;
friend class CompilerDriverProfileTest;
@@ -857,6 +930,10 @@
// The backing storage for the `string_view` is the associated `DexFileData`.
ArenaSafeMap<const std::string_view, ProfileIndexType> profile_key_map_;
+ // Additional descriptors for referencing types not present in a dex files's `TypeId`s.
+ dchecked_vector<std::string> extra_descriptors_;
+ ExtraDescriptorHashSet extra_descriptors_indexes_;
+
// The version of the profile.
uint8_t version_[kProfileVersionSize];
};
@@ -944,6 +1021,10 @@
return dex_file_data->checksum;
}
+ uint32_t GetNumTypeIds() const {
+ return dex_file_data->num_type_ids;
+ }
+
uint32_t GetNumMethodIds() const {
return dex_file_data->num_method_ids;
}
diff --git a/libprofile/profile/profile_compilation_info_test.cc b/libprofile/profile/profile_compilation_info_test.cc
index c6b80d9..8c9d0df 100644
--- a/libprofile/profile/profile_compilation_info_test.cc
+++ b/libprofile/profile/profile_compilation_info_test.cc
@@ -32,7 +32,6 @@
namespace art {
-using ProfileIndexTypeRegular = ProfileCompilationInfo::ProfileIndexTypeRegular;
using ItemMetadata = FlattenProfileData::ItemMetadata;
class ProfileCompilationInfoTest : public CommonArtTest, public ProfileTestHelper {
@@ -210,7 +209,7 @@
ASSERT_TRUE(boot_profile.Save(GetFd(boot_file)));
ASSERT_TRUE(reg_profile.Save(GetFd(reg_file)));
- ProfileCompilationInfo loaded_boot;
+ ProfileCompilationInfo loaded_boot(/*for_boot_image=*/ true);
ProfileCompilationInfo loaded_reg;
ASSERT_TRUE(loaded_boot.Load(GetFd(boot_file)));
ASSERT_TRUE(loaded_reg.Load(GetFd(reg_file)));
@@ -245,6 +244,39 @@
std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches;
};
+TEST_F(ProfileCompilationInfoTest, AddClasses) {
+ ProfileCompilationInfo info;
+
+ // Add all classes with a `TypeId` in `dex1`.
+ uint32_t num_type_ids1 = dex1->NumTypeIds();
+ for (uint32_t type_index = 0; type_index != num_type_ids1; ++type_index) {
+ ASSERT_TRUE(info.AddClass(*dex1, dex::TypeIndex(type_index)));
+ }
+ // Add classes without `TypeId` in `dex1`.
+ for (uint32_t type_index = num_type_ids1; type_index != DexFile::kDexNoIndex16; ++type_index) {
+ std::string descriptor = "LX" + std::to_string(type_index) + ";";
+ ASSERT_TRUE(info.AddClass(*dex1, descriptor));
+ }
+ // Fail to add another class without `TypeId` in `dex1` as we have
+ // run out of available artificial type indexes.
+ ASSERT_FALSE(info.AddClass(*dex1, "LCannotAddThis;"));
+
+ // Add all classes with a `TypeId` in `dex2`.
+ uint32_t num_type_ids2 = dex2->NumTypeIds();
+ for (uint32_t type_index = 0; type_index != num_type_ids2; ++type_index) {
+ ASSERT_TRUE(info.AddClass(*dex2, dex::TypeIndex(type_index)));
+ }
+ // Fail to add another class without `TypeId` in `dex2` as we have
+ // run out of available artificial type indexes when adding types for `dex1`.
+ ASSERT_FALSE(info.AddClass(*dex2, "LCannotAddThis;"));
+ // Add classes without `TypeId` in `dex2` for which we already have articial indexes.
+ ASSERT_EQ(num_type_ids1, num_type_ids2);
+ for (uint32_t type_index = num_type_ids2; type_index != DexFile::kDexNoIndex16; ++type_index) {
+ std::string descriptor = "LX" + std::to_string(type_index) + ";";
+ ASSERT_TRUE(info.AddClass(*dex2, descriptor));
+ }
+}
+
TEST_F(ProfileCompilationInfoTest, SaveFd) {
ScratchFile profile;
@@ -395,9 +427,9 @@
ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
ASSERT_TRUE(profile.GetFile()->WriteFully(
ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
- // Write that we have at least one line.
- uint8_t line_number[] = { 0, 1 };
- ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
+ // Write that we have one section info.
+ const uint32_t file_section_count = 1u;
+ ASSERT_TRUE(profile.GetFile()->WriteFully(&file_section_count, sizeof(file_section_count)));
ASSERT_EQ(0, profile.GetFile()->Flush());
ProfileCompilationInfo loaded_info;
@@ -410,14 +442,38 @@
ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
ASSERT_TRUE(profile.GetFile()->WriteFully(
ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
- // Write that we have at least one line.
- uint8_t line_number[] = { 0, 1 };
- ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
+ // Write that we have one section info.
+ const uint32_t file_section_count = 1u;
+ ASSERT_TRUE(profile.GetFile()->WriteFully(&file_section_count, sizeof(file_section_count)));
- // dex_location_size, methods_size, classes_size, checksum.
- // Dex location size is too big and should be rejected.
- uint8_t line[] = { 255, 255, 0, 1, 0, 1, 0, 0, 0, 0 };
- ASSERT_TRUE(profile.GetFile()->WriteFully(line, sizeof(line)));
+ constexpr size_t kInvalidDexFileLocationLength = 1025u;
+ constexpr uint32_t kDexFilesOffset =
+ kProfileMagicSize + kProfileVersionSize + sizeof(file_section_count) + 4u * sizeof(uint32_t);
+ constexpr uint32_t kDexFilesSize =
+ sizeof(ProfileIndexType) + // number of dex files
+ 3u * sizeof(uint32_t) + // numeric data
+ kInvalidDexFileLocationLength + 1u; // null-terminated string
+ const uint32_t section_info[] = {
+ 0u, // type = kDexFiles
+ kDexFilesOffset,
+ kDexFilesSize,
+ 0u, // inflated size = 0
+ };
+ ASSERT_TRUE(profile.GetFile()->WriteFully(section_info, sizeof(section_info)));
+
+ ProfileIndexType num_dex_files = 1u;
+ ASSERT_TRUE(profile.GetFile()->WriteFully(&num_dex_files, sizeof(num_dex_files)));
+
+ uint32_t numeric_data[3] = {
+ 1234u, // checksum
+ 1u, // num_type_ids
+ 2u, // num_method_ids
+ };
+ ASSERT_TRUE(profile.GetFile()->WriteFully(numeric_data, sizeof(numeric_data)));
+
+ std::string dex_location(kInvalidDexFileLocationLength, 'a');
+ ASSERT_TRUE(profile.GetFile()->WriteFully(dex_location.c_str(), dex_location.size() + 1u));
+
ASSERT_EQ(0, profile.GetFile()->Flush());
ProfileCompilationInfo loaded_info;
@@ -434,13 +490,17 @@
ASSERT_TRUE(saved_info.Save(GetFd(profile)));
uint8_t random_data[] = { 1, 2, 3};
- ASSERT_TRUE(profile.GetFile()->WriteFully(random_data, sizeof(random_data)));
+ int64_t file_length = profile.GetFile()->GetLength();
+ ASSERT_GT(file_length, 0);
+ ASSERT_TRUE(profile.GetFile()->PwriteFully(random_data, sizeof(random_data), file_length));
ASSERT_EQ(0, profile.GetFile()->Flush());
+ ASSERT_EQ(profile.GetFile()->GetLength(),
+ file_length + static_cast<int64_t>(sizeof(random_data)));
- // Check that we fail because of unexpected data at the end of the file.
+ // Extra data at the end of the file is OK, loading the profile should succeed.
ProfileCompilationInfo loaded_info;
- ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
}
TEST_F(ProfileCompilationInfoTest, SaveInlineCaches) {
@@ -470,11 +530,11 @@
ProfileCompilationInfo::MethodHotness loaded_hotness1 =
GetMethod(loaded_info, dex1, /*method_idx=*/ 3);
ASSERT_TRUE(loaded_hotness1.IsHot());
- ASSERT_TRUE(EqualInlineCaches(inline_caches, loaded_hotness1, loaded_info));
+ ASSERT_TRUE(EqualInlineCaches(inline_caches, dex1, loaded_hotness1, loaded_info));
ProfileCompilationInfo::MethodHotness loaded_hotness2 =
GetMethod(loaded_info, dex4, /*method_idx=*/ 3);
ASSERT_TRUE(loaded_hotness2.IsHot());
- ASSERT_TRUE(EqualInlineCaches(inline_caches, loaded_hotness2, loaded_info));
+ ASSERT_TRUE(EqualInlineCaches(inline_caches, dex4, loaded_hotness2, loaded_info));
}
TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCaches) {
@@ -515,7 +575,7 @@
GetMethod(loaded_info, dex1, /*method_idx=*/ 3);
ASSERT_TRUE(loaded_hotness1.IsHot());
- ASSERT_TRUE(EqualInlineCaches(inline_caches_extra, loaded_hotness1, loaded_info));
+ ASSERT_TRUE(EqualInlineCaches(inline_caches_extra, dex1, loaded_hotness1, loaded_info));
}
TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCaches) {
@@ -563,7 +623,7 @@
ProfileCompilationInfo::MethodHotness loaded_hotness1 =
GetMethod(loaded_info, dex1, /*method_idx=*/ 3);
ASSERT_TRUE(loaded_hotness1.IsHot());
- ASSERT_TRUE(EqualInlineCaches(missing_types, loaded_hotness1, loaded_info));
+ ASSERT_TRUE(EqualInlineCaches(missing_types, dex1, loaded_hotness1, loaded_info));
}
TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCache) {
@@ -578,7 +638,44 @@
types->front().dex_file = dex1_checksum_missmatch;
ASSERT_TRUE(AddMethod(&info, dex1, /*method_idx=*/ 0, inline_caches1));
- ASSERT_FALSE(AddMethod(&info, dex2, /*method_idx=*/ 0, inline_caches2));
+
+ // The dex files referenced in inline infos do not matter. We are recoding class
+ // references across dex files by looking up the descriptor in the referencing
+ // method's dex file. If not found, we create an artificial type index.
+ ASSERT_TRUE(AddMethod(&info, dex2, /*method_idx=*/ 0, inline_caches2));
+}
+
+TEST_F(ProfileCompilationInfoTest, InlineCacheAcrossDexFiles) {
+ ScratchFile profile;
+
+ const char kDex1Class[] = "LUnique1;";
+ const dex::TypeId* dex1_tid = dex1->FindTypeId(kDex1Class);
+ ASSERT_TRUE(dex1_tid != nullptr);
+ dex::TypeIndex dex1_tidx = dex1->GetIndexForTypeId(*dex1_tid);
+ ASSERT_FALSE(dex2->FindTypeId(kDex1Class) != nullptr);
+
+ const uint16_t dex_pc = 33u;
+ std::vector<TypeReference> types = {TypeReference(dex1, dex1_tidx)};
+ std::vector<ProfileInlineCache> inline_caches {
+ ProfileInlineCache(dex_pc, /*missing_types=*/ false, types)
+ };
+
+ ProfileCompilationInfo info;
+ ASSERT_TRUE(AddMethod(&info, dex2, /*method_idx=*/ 0, inline_caches));
+ Hotness hotness = GetMethod(info, dex2, /*method_idx=*/ 0);
+ ASSERT_TRUE(hotness.IsHot());
+ ASSERT_TRUE(EqualInlineCaches(inline_caches, dex2, hotness, info));
+ const ProfileCompilationInfo::InlineCacheMap* inline_cache_map = hotness.GetInlineCacheMap();
+ ASSERT_TRUE(inline_cache_map != nullptr);
+ ASSERT_EQ(1u, inline_cache_map->size());
+ ASSERT_EQ(dex_pc, inline_cache_map->begin()->first);
+ const ProfileCompilationInfo::DexPcData& dex_pc_data = inline_cache_map->begin()->second;
+ ASSERT_FALSE(dex_pc_data.is_missing_types);
+ ASSERT_FALSE(dex_pc_data.is_megamorphic);
+ ASSERT_EQ(1u, dex_pc_data.classes.size());
+ dex::TypeIndex type_index = *dex_pc_data.classes.begin();
+ ASSERT_FALSE(dex2->IsTypeIndexValid(type_index));
+ ASSERT_STREQ(kDex1Class, info.GetTypeDescriptor(dex2, type_index));
}
// Verify that profiles behave correctly even if the methods are added in a different
@@ -624,17 +721,17 @@
for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
ProfileCompilationInfo::MethodHotness loaded_hotness1 = GetMethod(info, dex1, method_idx);
ASSERT_TRUE(loaded_hotness1.IsHot());
- ASSERT_TRUE(EqualInlineCaches(inline_caches, loaded_hotness1, info));
+ ASSERT_TRUE(EqualInlineCaches(inline_caches, dex1, loaded_hotness1, info));
ProfileCompilationInfo::MethodHotness loaded_hotness2 = GetMethod(info, dex2, method_idx);
ASSERT_TRUE(loaded_hotness2.IsHot());
- ASSERT_TRUE(EqualInlineCaches(inline_caches, loaded_hotness2, info));
+ ASSERT_TRUE(EqualInlineCaches(inline_caches, dex2, loaded_hotness2, info));
}
}
TEST_F(ProfileCompilationInfoTest, AddMoreDexFileThanLimitRegular) {
ProfileCompilationInfo info;
// Save a few methods.
- for (uint16_t i = 0; i < std::numeric_limits<ProfileIndexTypeRegular>::max(); i++) {
+ for (uint16_t i = 0; i < std::numeric_limits<ProfileIndexType>::max(); i++) {
std::string location = std::to_string(i);
const DexFile* dex = BuildDex(location, /*checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
ASSERT_TRUE(AddMethod(&info, dex, /*method_idx=*/ 0));
@@ -812,9 +909,9 @@
ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
ASSERT_TRUE(profile.GetFile()->WriteFully(
ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
- // Write that we have at least one line.
- uint8_t line_number[] = { 0, 1 };
- ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
+ // Write that we have one section info.
+ const uint32_t file_section_count = 1u;
+ ASSERT_TRUE(profile.GetFile()->WriteFully(&file_section_count, sizeof(file_section_count)));
ASSERT_EQ(0, profile.GetFile()->Flush());
// Prepare the profile content for zipping.
@@ -966,7 +1063,8 @@
ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
// Verify that we filtered out locations during load.
- ASSERT_EQ(2u, loaded_info.GetNumberOfDexFiles());
+ // Note that `dex3` did not have any data recorded in the profile.
+ ASSERT_EQ(1u, loaded_info.GetNumberOfDexFiles());
// Dex location 2 and 4 should have been filtered out
for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
@@ -982,46 +1080,11 @@
GetMethod(loaded_info, dex1, method_idx);
ASSERT_TRUE(loaded_hotness1.IsHot());
- // Verify the inline cache.
- // Everything should be as constructed by GetTestInlineCaches with the exception
- // of the inline caches referring types from dex_location2.
- // These should be set to IsMissingType.
- std::vector<ProfileInlineCache> expected_ics;
-
- // Monomorphic types should remain the same as dex_location1 was kept.
- for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
- std::vector<TypeReference> types = {TypeReference(dex1, dex::TypeIndex(0))};
- expected_ics.push_back(ProfileInlineCache(dex_pc, /*missing_types=*/ false, types));
- }
-
- // Polymorphic inline cache should have been transformed to IsMissingType due to
- // the removal of dex_location2.
- for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
- std::vector<TypeReference> types;
- expected_ics.push_back(ProfileInlineCache(dex_pc, /*missing_types=*/ true, types));
- }
-
- // Megamorphic are not affected by removal of dex files.
- for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
- // We need 5 types to make the cache megamorphic.
- // The `is_megamorphic` flag shall be `false`; it is not used for testing.
- std::vector<TypeReference> types = {
- TypeReference(dex1, dex::TypeIndex(0)),
- TypeReference(dex1, dex::TypeIndex(1)),
- TypeReference(dex1, dex::TypeIndex(2)),
- TypeReference(dex1, dex::TypeIndex(3)),
- TypeReference(dex1, dex::TypeIndex(4))};
- expected_ics.push_back(ProfileInlineCache(dex_pc, /*missing_types=*/ false, types));
- }
-
- // Missing types are not affected be removal of dex files.
- for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
- std::vector<TypeReference> types;
- expected_ics.push_back(ProfileInlineCache(dex_pc, /*missing_types=*/ true, types));
- }
-
- // Now check that we get back what we expect.
- ASSERT_TRUE(EqualInlineCaches(expected_ics, loaded_hotness1, loaded_info));
+ // Verify the inline cache. Note that references to other dex files are translated
+ // to use type indexes within the referencing dex file and artificial type indexes
+ // referencing "extra descriptors" are used when there is no `dex::TypeId` for
+ // these types. `EqualInlineCaches()` compares descriptors when necessary.
+ ASSERT_TRUE(EqualInlineCaches(inline_caches, dex1, loaded_hotness1, loaded_info));
}
}
@@ -1088,13 +1151,13 @@
ProfileCompilationInfo::MethodHotness loaded_hotness1 =
GetMethod(loaded_info, dex1, method_idx);
ASSERT_TRUE(loaded_hotness1.IsHot());
- ASSERT_TRUE(EqualInlineCaches(inline_caches, loaded_hotness1, loaded_info));
+ ASSERT_TRUE(EqualInlineCaches(inline_caches, dex1, loaded_hotness1, loaded_info));
}
for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
ProfileCompilationInfo::MethodHotness loaded_hotness2 =
GetMethod(loaded_info, dex4, method_idx);
ASSERT_TRUE(loaded_hotness2.IsHot());
- ASSERT_TRUE(EqualInlineCaches(inline_caches, loaded_hotness2, loaded_info));
+ ASSERT_TRUE(EqualInlineCaches(inline_caches, dex4, loaded_hotness2, loaded_info));
}
}
@@ -1183,7 +1246,6 @@
ASSERT_FALSE(info.IsForBootImage());
ProfileCompilationInfo info1(/*for_boot_image=*/ true);
-
ASSERT_EQ(
memcmp(info1.GetVersion(),
ProfileCompilationInfo::kProfileVersionForBootImage,
@@ -1221,7 +1283,7 @@
ASSERT_EQ(0, profile.GetFile()->Flush());
// Load the profile and make sure we can read the data and it matches what we expect.
- ProfileCompilationInfo loaded_info;
+ ProfileCompilationInfo loaded_info(/*for_boot_image=*/ true);
ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
run_test(loaded_info);
}
@@ -1249,7 +1311,7 @@
ASSERT_EQ(0, profile.GetFile()->Flush());
// Load the profile and make sure we can read the data and it matches what we expect.
- ProfileCompilationInfo loaded_info;
+ ProfileCompilationInfo loaded_info(/*for_boot_image=*/ true);
ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
run_test(loaded_info);
}
@@ -1318,7 +1380,7 @@
ASSERT_EQ(0, profile.GetFile()->Flush());
// Load the profile and make sure we can read the data and it matches what we expect.
- ProfileCompilationInfo loaded_info;
+ ProfileCompilationInfo loaded_info(/*for_boot_image=*/ true);
ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
run_test(loaded_info);
}
@@ -1632,7 +1694,7 @@
/*missing_types=*/ false,
/*profile_classes=*/ cls_pc15) };
EXPECT_EQ(loaded_ic_12.GetInlineCacheMap()->size(), expected.size());
- EXPECT_TRUE(EqualInlineCaches(expected, loaded_ic_12, info_12)) << i;
+ EXPECT_TRUE(EqualInlineCaches(expected, dex1, loaded_ic_12, info_12)) << i;
}
}
diff --git a/libprofile/profile/profile_test_helper.h b/libprofile/profile/profile_test_helper.h
index 859c8fb..9410620 100644
--- a/libprofile/profile/profile_test_helper.h
+++ b/libprofile/profile/profile_test_helper.h
@@ -76,8 +76,7 @@
const DexFile* dex,
dex::TypeIndex type_index,
const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) {
- std::vector<dex::TypeIndex> classes = {type_index};
- return info->AddClassesForDex(dex, classes.begin(), classes.end(), annotation);
+ return info->AddClass(*dex, type_index, annotation);
}
static bool ProfileIndexMatchesDexFile(const ProfileCompilationInfo& info,
@@ -90,6 +89,7 @@
// Compare different representations of inline caches for equality.
static bool EqualInlineCaches(const std::vector<ProfileMethodInfo::ProfileInlineCache>& expected,
+ const DexFile* dex_file,
const ProfileCompilationInfo::MethodHotness& actual_hotness,
const ProfileCompilationInfo& info) {
CHECK(actual_hotness.IsHot());
@@ -126,14 +126,18 @@
if (dex_pc_data.classes.size() != expected_it->classes.size()) {
return false;
}
- for (const ProfileCompilationInfo::ClassReference& class_ref : dex_pc_data.classes) {
+ for (dex::TypeIndex type_index : dex_pc_data.classes) {
if (std::none_of(expected_it->classes.begin(),
expected_it->classes.end(),
[&](const TypeReference& type_ref) {
- return (class_ref.type_index == type_ref.TypeIndex()) &&
- ProfileIndexMatchesDexFile(info,
- class_ref.dex_profile_index,
- type_ref.dex_file);
+ if (type_ref.dex_file == dex_file) {
+ return type_index == type_ref.TypeIndex();
+ } else {
+ const char* expected_descriptor =
+ type_ref.dex_file->StringByTypeIdx(type_ref.TypeIndex());
+ const char* descriptor = info.GetTypeDescriptor(dex_file, type_index);
+ return strcmp(expected_descriptor, descriptor) == 0;
+ }
})) {
return false;
}
@@ -151,18 +155,23 @@
size_t num_method_ids,
size_t num_class_ids = kNumSharedTypes + 1u) {
TestDexFileBuilder builder;
- for (size_t shared_type_index = 0; shared_type_index != kNumSharedTypes; ++shared_type_index) {
+ builder.AddType(class_descriptor);
+ CHECK_NE(num_class_ids, 0u);
+ size_t num_shared_ids = std::min(num_class_ids - 1u, kNumSharedTypes);
+ for (size_t shared_type_index = 0; shared_type_index != num_shared_ids; ++shared_type_index) {
builder.AddType("LSharedType" + std::to_string(shared_type_index) + ";");
}
- builder.AddType(class_descriptor);
- for (size_t i = kNumSharedTypes + 1u; i < num_class_ids; ++i) {
+ for (size_t i = 1u + num_shared_ids; i < num_class_ids; ++i) {
builder.AddType("LFiller" + std::to_string(i) + ";");
}
for (size_t method_index = 0; method_index != num_method_ids; ++method_index) {
- // Keep the number of protos and names low even for the maximum number of methods.
- size_t return_type_index = method_index % kNumSharedTypes;
- size_t arg_type_index = (method_index / kNumSharedTypes) % kNumSharedTypes;
- size_t method_name_index = (method_index / kNumSharedTypes) / kNumSharedTypes;
+ // Some tests add the maximum number of methods (`num_method_ids` is 2^16) and we
+ // do not want to waste memory with that many unique name strings (with identical
+ // proto id). So create up to num_shared_ids^2 proto ids and only
+ // num_method_ids/num_shared_ids^2 names.
+ size_t return_type_index = method_index % num_shared_ids;
+ size_t arg_type_index = (method_index / num_shared_ids) % num_shared_ids;
+ size_t method_name_index = (method_index / num_shared_ids) / num_shared_ids;
std::string return_type = "LSharedType" + std::to_string(return_type_index) + ";";
std::string arg_type = "LSharedType" + std::to_string(arg_type_index) + ";";
std::string signature = "(" + arg_type + ")" + return_type;
diff --git a/profman/boot_image_profile.cc b/profman/boot_image_profile.cc
index 7596367..fc20f20 100644
--- a/profman/boot_image_profile.cc
+++ b/profman/boot_image_profile.cc
@@ -210,7 +210,7 @@
std::unique_ptr<FlattenProfileData> flattend_data(new FlattenProfileData());
for (const std::string& profile_file : profile_files) {
- ProfileCompilationInfo profile;
+ ProfileCompilationInfo profile(/*for_boot_image=*/ true);
if (!profile.Load(profile_file, /*clear_if_invalid=*/ false)) {
LOG(ERROR) << "Profile is not a valid: " << profile_file;
return false;
diff --git a/profman/profile_assistant.cc b/profman/profile_assistant.cc
index ba5be4d..614a736 100644
--- a/profman/profile_assistant.cc
+++ b/profman/profile_assistant.cc
@@ -53,7 +53,7 @@
// Merge all current profiles.
for (size_t i = 0; i < profile_files.size(); i++) {
- ProfileCompilationInfo cur_info;
+ ProfileCompilationInfo cur_info(options.IsBootImageMerge());
if (!cur_info.Load(profile_files[i]->Fd(), /*merge_classes=*/ true, filter_fn)) {
LOG(WARNING) << "Could not load profile file at index " << i;
if (options.IsForceMerge()) {
@@ -62,22 +62,12 @@
// cleared lazily.
continue;
}
- return kErrorBadProfiles;
- }
-
- // Check version mismatch.
- // This may happen during profile analysis if one profile is regular and
- // the other one is for the boot image. For example when switching on-off
- // the boot image profiles.
- if (!info.SameVersion(cur_info)) {
- if (options.IsForceMerge()) {
- // If we have to merge forcefully, ignore the current profile and
- // continue to the next one.
- continue;
- } else {
- // Otherwise, return an error.
+ // TODO: Do we really need to use a different error code for version mismatch?
+ ProfileCompilationInfo wrong_info(!options.IsBootImageMerge());
+ if (wrong_info.Load(profile_files[i]->Fd(), /*merge_classes=*/ true, filter_fn)) {
return kErrorDifferentVersions;
}
+ return kErrorBadProfiles;
}
if (!info.MergeWith(cur_info)) {
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index cf7ada3..dc105bf 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -338,7 +338,7 @@
void AssertInlineCaches(ArtMethod* method,
uint16_t dex_pc,
- const TypeReferenceSet& expected_clases,
+ const TypeReferenceSet& expected_classes,
const ProfileCompilationInfo& info,
bool is_megamorphic,
bool is_missing_types)
@@ -348,14 +348,15 @@
ASSERT_TRUE(hotness.IsHot());
const ProfileCompilationInfo::InlineCacheMap* inline_caches = hotness.GetInlineCacheMap();
ASSERT_TRUE(inline_caches->find(dex_pc) != inline_caches->end());
- AssertInlineCaches(expected_clases,
+ AssertInlineCaches(expected_classes,
info,
+ method,
inline_caches->find(dex_pc)->second,
is_megamorphic,
is_missing_types);
}
void AssertInlineCaches(ArtMethod* method,
- const TypeReferenceSet& expected_clases,
+ const TypeReferenceSet& expected_classes,
const ProfileCompilationInfo& info,
bool is_megamorphic,
bool is_missing_types)
@@ -365,8 +366,9 @@
ASSERT_TRUE(hotness.IsHot());
const ProfileCompilationInfo::InlineCacheMap* inline_caches = hotness.GetInlineCacheMap();
ASSERT_EQ(inline_caches->size(), 1u);
- AssertInlineCaches(expected_clases,
+ AssertInlineCaches(expected_classes,
info,
+ method,
inline_caches->begin()->second,
is_megamorphic,
is_missing_types);
@@ -374,6 +376,7 @@
void AssertInlineCaches(const TypeReferenceSet& expected_clases,
const ProfileCompilationInfo& info,
+ ArtMethod* method,
const ProfileCompilationInfo::DexPcData& dex_pc_data,
bool is_megamorphic,
bool is_missing_types)
@@ -381,12 +384,26 @@
ASSERT_EQ(dex_pc_data.is_megamorphic, is_megamorphic);
ASSERT_EQ(dex_pc_data.is_missing_types, is_missing_types);
ASSERT_EQ(expected_clases.size(), dex_pc_data.classes.size());
+ const DexFile* dex_file = method->GetDexFile();
size_t found = 0;
for (const TypeReference& type_ref : expected_clases) {
- for (const auto& class_ref : dex_pc_data.classes) {
- if (class_ref.type_index == type_ref.TypeIndex() &&
- ProfileIndexMatchesDexFile(info, class_ref.dex_profile_index, type_ref.dex_file)) {
- found++;
+ if (type_ref.dex_file == dex_file) {
+ CHECK_LT(type_ref.TypeIndex().index_, dex_file->NumTypeIds());
+ for (dex::TypeIndex type_index : dex_pc_data.classes) {
+ ASSERT_TRUE(type_index.IsValid());
+ if (type_ref.TypeIndex() == type_index) {
+ ++found;
+ }
+ }
+ } else {
+ // Match by descriptor.
+ const char* expected_descriptor = type_ref.dex_file->StringByTypeIdx(type_ref.TypeIndex());
+ for (dex::TypeIndex type_index : dex_pc_data.classes) {
+ ASSERT_TRUE(type_index.IsValid());
+ const char* descriptor = info.GetTypeDescriptor(dex_file, type_index);
+ if (strcmp(expected_descriptor, descriptor) == 0) {
+ ++found;
+ }
}
}
}
@@ -424,6 +441,12 @@
uint16_t classes_in_ref_profile,
const std::vector<const std::string>& extra_args =
std::vector<const std::string>()) {
+ uint16_t max_classes = std::max(classes_in_cur_profile, classes_in_ref_profile);
+ const DexFile* dex1_x = BuildDex(
+ "location1_x", /*checksum=*/ 0x101, "LUnique1_x;", /*num_method_ids=*/ 0, max_classes);
+ const DexFile* dex2_x = BuildDex(
+ "location2_x", /*checksum=*/ 0x102, "LUnique2_x;", /*num_method_ids=*/ 0, max_classes);
+
ScratchFile profile;
ScratchFile reference_profile;
@@ -431,9 +454,9 @@
int reference_profile_fd = GetFd(reference_profile);
ProfileCompilationInfo info1;
- SetupProfile(dex1, dex2, 0, classes_in_cur_profile, profile, &info1);
+ SetupProfile(dex1_x, dex2_x, 0, classes_in_cur_profile, profile, &info1);
ProfileCompilationInfo info2;
- SetupProfile(dex1, dex2, 0, classes_in_ref_profile, reference_profile, &info2);
+ SetupProfile(dex1_x, dex2_x, 0, classes_in_ref_profile, reference_profile, &info2);
return ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
}
@@ -481,6 +504,18 @@
// TODO(calin): Add more tests for classes.
TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferencesBecauseOfClasses) {
+ const uint16_t kNumberOfClassesToEnableCompilation = 100;
+ const DexFile* dex1_100 = BuildDex("location1_100",
+ /*checksum=*/ 101,
+ "LUnique1_100;",
+ /*num_method_ids=*/ 0,
+ /*num_type_ids=*/ 100);
+ const DexFile* dex2_100 = BuildDex("location2_100",
+ /*checksum=*/ 102,
+ "LUnique2_100;",
+ /*num_method_ids=*/ 0,
+ /*num_type_ids=*/ 100);
+
ScratchFile profile1;
ScratchFile reference_profile;
@@ -488,9 +523,8 @@
GetFd(profile1)});
int reference_profile_fd = GetFd(reference_profile);
- const uint16_t kNumberOfClassesToEnableCompilation = 100;
ProfileCompilationInfo info1;
- SetupProfile(dex1, dex2, 0, kNumberOfClassesToEnableCompilation, profile1, &info1);
+ SetupProfile(dex1_100, dex2_100, 0, kNumberOfClassesToEnableCompilation, profile1, &info1);
// We should advise compilation.
ASSERT_EQ(ProfileAssistant::kCompile,
@@ -621,7 +655,7 @@
kNumberOfMethodsInRefProfile));
}
-TEST_F(ProfileAssistantTest, DoNotdviseCompilationClassPercentage) {
+TEST_F(ProfileAssistantTest, DoNotAdviseCompilationClassPercentage) {
const uint16_t kNumberOfClassesInRefProfile = 6000;
const uint16_t kNumberOfClassesInCurProfile = 6110; // Threshold is 2%.
std::vector<const std::string> extra_args({"--min-new-classes-percent-change=2"});
@@ -741,6 +775,8 @@
"Ljava/lang/Math;",
"Ljava/lang/Object;",
"SPLjava/lang/Comparable;->compareTo(Ljava/lang/Object;)I",
+ "[[[[[[[[I", // No `TypeId`s in core-oj with this many array dimensions,
+ "[[[[[[[[Ljava/lang/Object;", // "extra descriptors" shall be used for these array classes.
};
std::string file_contents;
for (std::string& class_name : class_names) {
@@ -1509,7 +1545,7 @@
const ProfileCompilationInfo::DexPcData& dex_pc_data = inline_caches->begin()->second;
dex::TypeIndex target_type_index(dex_file->GetIndexForTypeId(*dex_file->FindTypeId("LSubA;")));
ASSERT_EQ(1u, dex_pc_data.classes.size());
- ASSERT_EQ(target_type_index, dex_pc_data.classes.begin()->type_index);
+ ASSERT_EQ(target_type_index, *dex_pc_data.classes.begin());
// Verify that the method is present in subclass but there are no
// inline-caches (since there is no code).
@@ -1564,9 +1600,9 @@
TEST_F(ProfileAssistantTest, TestProfileCreateWithInvalidData) {
// Create the profile content.
std::vector<std::string> profile_methods = {
- "HLTestInline;->inlineMonomorphic(LSuper;)I+invalid_class",
- "HLTestInline;->invalid_method",
- "invalid_class"
+ "HLTestInline;->inlineMonomorphic(LSuper;)I+invalid_class", // Invalid descriptor for IC.
+ "HLTestInline;->invalid_method", // Invalid method spec (no signature).
+ "invalid_class", // Invalid descriptor.
};
std::string input_file_contents;
for (std::string& m : profile_methods) {
@@ -1594,34 +1630,24 @@
"inlineMonomorphic");
const DexFile* dex_file = inline_monomorphic->GetDexFile();
- // Verify that the inline cache contains the invalid type.
+ // Invalid descriptor in IC results in rejection of the entire line.
ProfileCompilationInfo::MethodHotness hotness =
info.GetMethodHotness(MethodReference(dex_file, inline_monomorphic->GetDexMethodIndex()));
- ASSERT_TRUE(hotness.IsHot());
- const ProfileCompilationInfo::InlineCacheMap* inline_caches = hotness.GetInlineCacheMap();
- ASSERT_EQ(inline_caches->size(), 1u);
- const ProfileCompilationInfo::DexPcData& dex_pc_data = inline_caches->begin()->second;
- dex::TypeIndex invalid_class_index(std::numeric_limits<uint16_t>::max() - 1);
- ASSERT_EQ(1u, dex_pc_data.classes.size());
- ASSERT_EQ(invalid_class_index, dex_pc_data.classes.begin()->type_index);
+ ASSERT_FALSE(hotness.IsHot());
- // Verify that the start-up classes contain the invalid class.
+ // No data was recorded, so the dex file does not appear in the profile.
+ // TODO: Record all dex files passed to `profman` in the profile. Note that
+ // this makes sense only if there are no annotations, otherwise we do not
+ // know what annotation to use with each dex file.
std::set<dex::TypeIndex> classes;
std::set<uint16_t> hot_methods;
std::set<uint16_t> startup_methods;
std::set<uint16_t> post_start_methods;
- ASSERT_TRUE(info.GetClassesAndMethods(*dex_file,
- &classes,
- &hot_methods,
- &startup_methods,
- &post_start_methods));
- ASSERT_EQ(1u, classes.size());
- ASSERT_TRUE(classes.find(invalid_class_index) != classes.end());
-
- // Verify that the invalid method did not get in the profile.
- ASSERT_EQ(1u, hot_methods.size());
- uint16_t invalid_method_index = std::numeric_limits<uint16_t>::max() - 1;
- ASSERT_FALSE(hot_methods.find(invalid_method_index) != hot_methods.end());
+ ASSERT_FALSE(info.GetClassesAndMethods(*dex_file,
+ &classes,
+ &hot_methods,
+ &startup_methods,
+ &post_start_methods));
}
TEST_F(ProfileAssistantTest, DumpOnly) {
@@ -1771,10 +1797,10 @@
ProfileCompilationInfo info1;
uint16_t num_methods_to_add = std::min(d1.NumMethodIds(), d2.NumMethodIds());
- const DexFile* dex_to_be_updated1 =
- BuildDex("fake-location1", d1.GetLocationChecksum(), "LC;", d1.NumMethodIds());
- const DexFile* dex_to_be_updated2 =
- BuildDex("fake-location2", d2.GetLocationChecksum(), "LC;", d2.NumMethodIds());
+ const DexFile* dex_to_be_updated1 = BuildDex(
+ "fake-location1", d1.GetLocationChecksum(), "LC;", d1.NumMethodIds(), d1.NumTypeIds());
+ const DexFile* dex_to_be_updated2 = BuildDex(
+ "fake-location2", d2.GetLocationChecksum(), "LC;", d2.NumMethodIds(), d2.NumTypeIds());
SetupProfile(dex_to_be_updated1,
dex_to_be_updated2,
num_methods_to_add,
@@ -1827,7 +1853,7 @@
for (size_t i = 0; i < num_methods; ++i) {
hot_methods_ref.push_back(i);
}
- ProfileCompilationInfo info1;
+ ProfileCompilationInfo info1(/*for_boot_image=*/ true);
SetupBasicProfile(dex1, hot_methods_cur, empty_vector, empty_vector,
profile, &info1);
ProfileCompilationInfo info2(/*for_boot_image=*/true);
@@ -1842,7 +1868,7 @@
// Verify the result: it should be equal to info2 since info1 is a regular profile
// and should be ignored.
- ProfileCompilationInfo result;
+ ProfileCompilationInfo result(/*for_boot_image=*/ true);
ASSERT_TRUE(result.Load(reference_profile.GetFd()));
ASSERT_TRUE(result.Equals(info2));
}
@@ -1855,6 +1881,17 @@
const uint16_t kNumberOfClassesInRefProfile = 6000;
const uint16_t kNumberOfClassesInCurProfile = 6110; // Threshold is 2%.
+ const DexFile* dex1_7000 = BuildDex("location1_7000",
+ /*checksum=*/ 7001,
+ "LUnique1_7000;",
+ /*num_method_ids=*/ 0,
+ /*num_type_ids=*/ 7000);
+ const DexFile* dex2_7000 = BuildDex("location2_7000",
+ /*checksum=*/ 7002,
+ "LUnique2_7000;",
+ /*num_method_ids=*/ 0,
+ /*num_type_ids=*/ 7000);
+
ScratchFile profile;
ScratchFile reference_profile;
@@ -1862,9 +1899,9 @@
int reference_profile_fd = GetFd(reference_profile);
ProfileCompilationInfo info1;
- SetupProfile(dex1, dex2, 0, kNumberOfClassesInRefProfile, profile, &info1);
+ SetupProfile(dex1_7000, dex2_7000, 0, kNumberOfClassesInRefProfile, profile, &info1);
ProfileCompilationInfo info2;
- SetupProfile(dex1, dex2, 0, kNumberOfClassesInCurProfile, reference_profile, &info2);
+ SetupProfile(dex1_7000, dex2_7000, 0, kNumberOfClassesInCurProfile, reference_profile, &info2);
std::vector<const std::string> extra_args({"--force-merge"});
int return_code = ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
@@ -1917,7 +1954,7 @@
EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfileAssistant::kSuccess) << error;
// Verify that we can load the result and that it equals to what we saved.
- ProfileCompilationInfo result;
+ ProfileCompilationInfo result(/*for_boot_image=*/ true);
ASSERT_TRUE(result.Load(reference_profile_fd));
ASSERT_TRUE(info.Equals(result));
}
@@ -1934,12 +1971,17 @@
std::vector<int> profile_fds({ GetFd(profile1)});
int reference_profile_fd = GetFd(profile2);
- ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd),
+ std::vector<const std::string> boot_image_args({"--boot-image-merge"});
+ ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, boot_image_args),
ProfileAssistant::kErrorDifferentVersions);
+ ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd),
+ ProfileAssistant::kErrorBadProfiles);
// Reverse the order of the profiles to verify we get the same behaviour.
profile_fds[0] = GetFd(profile2);
reference_profile_fd = GetFd(profile1);
+ ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, boot_image_args),
+ ProfileAssistant::kErrorBadProfiles);
ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd),
ProfileAssistant::kErrorDifferentVersions);
}
@@ -1962,16 +2004,18 @@
int reference_profile_fd = GetFd(profile2);
// With force-merge we should merge successfully.
- std::vector<const std::string> extra_args({"--force-merge"});
+ std::vector<const std::string> extra_args({"--force-merge", "--boot-image-merge"});
ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, extra_args),
ProfileAssistant::kSuccess);
- ProfileCompilationInfo result;
+ ProfileCompilationInfo result(/*for_boot_image=*/ true);
ASSERT_TRUE(result.Load(reference_profile_fd));
ASSERT_TRUE(info2.Equals(result));
// Without force-merge we should fail.
- ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd),
+ std::vector<const std::string> extra_args2({"--boot-image-merge"});
+ ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, extra_args2),
ProfileAssistant::kErrorBadProfiles);
}
+
} // namespace art
diff --git a/profman/profman.cc b/profman/profman.cc
index d4b8433..56c504b 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -54,6 +54,7 @@
#include "dex/class_accessor-inl.h"
#include "dex/class_reference.h"
#include "dex/code_item_accessors-inl.h"
+#include "dex/descriptors_names.h"
#include "dex/dex_file.h"
#include "dex/dex_file_loader.h"
#include "dex/dex_file_structs.h"
@@ -152,7 +153,7 @@
UsageError("");
UsageError(" --apk-fd=<number>: file descriptor containing an open APK to");
UsageError(" search for dex files");
- UsageError(" --apk-=<filename>: an APK to search for dex files");
+ UsageError(" --apk=<filename>: an APK to search for dex files");
UsageError(" --skip-apk-verification: do not attempt to verify APKs");
UsageError("");
UsageError(" --generate-boot-image-profile: Generate a boot image profile based on input");
@@ -205,8 +206,6 @@
static const std::string kMethodSep = "->"; // NOLINT [runtime/string] [4]
static const std::string kMissingTypesMarker = "missing_types"; // NOLINT [runtime/string] [4]
static const std::string kMegamorphicTypesMarker = "megamorphic_types"; // NOLINT [runtime/string] [4]
-static const std::string kInvalidClassDescriptor = "invalid_class"; // NOLINT [runtime/string] [4]
-static const std::string kInvalidMethod = "invalid_method"; // NOLINT [runtime/string] [4]
static const std::string kClassAllMethods = "*"; // NOLINT [runtime/string] [4]
static constexpr char kAnnotationStart = '{';
static constexpr char kAnnotationEnd = '}';
@@ -665,7 +664,9 @@
#endif
}
- std::unique_ptr<const ProfileCompilationInfo> LoadProfile(const std::string& filename, int fd) {
+ std::unique_ptr<const ProfileCompilationInfo> LoadProfile(const std::string& filename,
+ int fd,
+ bool for_boot_image) {
if (!filename.empty()) {
#ifdef _WIN32
int flags = O_RDWR;
@@ -678,7 +679,7 @@
return nullptr;
}
}
- std::unique_ptr<ProfileCompilationInfo> info(new ProfileCompilationInfo);
+ std::unique_ptr<ProfileCompilationInfo> info(new ProfileCompilationInfo(for_boot_image));
if (!info->Load(fd)) {
LOG(ERROR) << "Cannot load profile info from fd=" << fd << "\n";
return nullptr;
@@ -691,7 +692,12 @@
int fd,
const std::vector<std::unique_ptr<const DexFile>>* dex_files,
std::string* dump) {
- std::unique_ptr<const ProfileCompilationInfo> info(LoadProfile(filename, fd));
+ // For dumping, try loading as app profile and if that fails try loading as boot profile.
+ std::unique_ptr<const ProfileCompilationInfo> info =
+ LoadProfile(filename, fd, /*for_boot_image=*/ false);
+ if (info == nullptr) {
+ info = LoadProfile(filename, fd, /*for_boot_image=*/ true);
+ }
if (info == nullptr) {
LOG(ERROR) << "Cannot load profile info from filename=" << filename << " fd=" << fd;
return -1;
@@ -786,7 +792,6 @@
// followed by an IC description matching the format described by ProcessLine
// below. Note that this will collapse all ICs with the same receiver type.
std::string GetInlineCacheLine(const ProfileCompilationInfo& profile_info,
- std::vector<std::unique_ptr<const DexFile>>* dex_files,
const dex::MethodId& id,
const DexFile* dex_file,
uint16_t dex_method_idx) {
@@ -800,7 +805,7 @@
struct IcLineInfo {
bool is_megamorphic_ = false;
bool is_missing_types_ = false;
- std::set<TypeReference> classes_;
+ std::set<dex::TypeIndex> classes_;
};
std::unordered_map<dex::TypeIndex, IcLineInfo> ics;
CodeItemInstructionAccessor accessor(
@@ -823,14 +828,8 @@
if (ic_data.is_missing_types) {
val->second.is_missing_types_ = true;
}
- for (auto cls : ic_data.classes) {
- const DexFile* class_dex_file =
- profile_info.FindDexFileForProfileIndex(cls.dex_profile_index, *dex_files);
- if (class_dex_file == nullptr) {
- val->second.is_missing_types_ = true;
- continue;
- }
- val->second.classes_.insert({ class_dex_file, cls.type_index });
+ for (dex::TypeIndex type_index : ic_data.classes) {
+ val->second.classes_.insert(type_index);
}
}
if (ics.empty()) {
@@ -847,27 +846,21 @@
dump_ic << kMegamorphicTypesMarker;
} else {
bool first = true;
- for (const auto& klass : dex_data.classes_) {
+ for (dex::TypeIndex type_index : dex_data.classes_) {
if (!first) {
dump_ic << kProfileParsingTypeSep;
}
first = false;
- dump_ic << klass.dex_file->GetTypeDescriptor(
- klass.dex_file->GetTypeId(klass.TypeIndex()));
+ dump_ic << profile_info.GetTypeDescriptor(dex_file, type_index);
}
}
}
return dump_ic.str();
}
- bool GetClassNamesAndMethods(int fd,
+ bool GetClassNamesAndMethods(const ProfileCompilationInfo& profile_info,
std::vector<std::unique_ptr<const DexFile>>* dex_files,
std::set<std::string>* out_lines) {
- ProfileCompilationInfo profile_info;
- if (!profile_info.Load(fd)) {
- LOG(ERROR) << "Cannot load profile info";
- return false;
- }
for (const std::unique_ptr<const DexFile>& dex_file : *dex_files) {
std::set<dex::TypeIndex> class_types;
std::set<uint16_t> hot_methods;
@@ -880,8 +873,7 @@
&startup_methods,
&post_startup_methods)) {
for (const dex::TypeIndex& type_index : class_types) {
- const dex::TypeId& type_id = dex_file->GetTypeId(type_index);
- out_lines->insert(std::string(dex_file->GetTypeDescriptor(type_id)));
+ out_lines->insert(profile_info.GetTypeDescriptor(dex_file.get(), type_index));
}
combined_methods = hot_methods;
combined_methods.insert(startup_methods.begin(), startup_methods.end());
@@ -902,7 +894,7 @@
flags_string += kMethodFlagStringPostStartup;
}
std::string inline_cache_string =
- GetInlineCacheLine(profile_info, dex_files, id, dex_file.get(), dex_method_idx);
+ GetInlineCacheLine(profile_info, id, dex_file.get(), dex_method_idx);
out_lines->insert(flags_string + type_string + kMethodSep + method_name +
signature_string + inline_cache_string);
}
@@ -911,6 +903,20 @@
return true;
}
+ bool GetClassNamesAndMethods(int fd,
+ std::vector<std::unique_ptr<const DexFile>>* dex_files,
+ std::set<std::string>* out_lines) {
+ // For dumping, try loading as app profile and if that fails try loading as boot profile.
+ for (bool for_boot_image : {false, true}) {
+ ProfileCompilationInfo profile_info(for_boot_image);
+ if (profile_info.Load(fd)) {
+ return GetClassNamesAndMethods(profile_info, dex_files, out_lines);
+ }
+ }
+ LOG(ERROR) << "Cannot load profile info";
+ return false;
+ }
+
bool GetClassNamesAndMethods(const std::string& profile_file,
std::vector<std::unique_ptr<const DexFile>>* dex_files,
std::set<std::string>* out_lines) {
@@ -1029,81 +1035,56 @@
return output.release();
}
- // Find class klass_descriptor in the given dex_files and store its reference
- // in the out parameter class_ref.
- // Return true if the definition or a reference of the class was found in any
- // of the dex_files.
- bool FindClass(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
- const std::string_view& klass_descriptor,
- /*out*/ TypeReference* class_ref) {
- return FindClass(
- ArrayRef<const std::unique_ptr<const DexFile>>(dex_files), klass_descriptor, class_ref);
- }
-
- bool FindClass(ArrayRef<const std::unique_ptr<const DexFile>> dex_files,
- const std::string_view& klass_descriptor,
- /*out*/TypeReference* class_ref) {
- constexpr uint16_t kInvalidTypeIndex = std::numeric_limits<uint16_t>::max() - 1;
- for (const std::unique_ptr<const DexFile>& dex_file_ptr : dex_files) {
- const DexFile* dex_file = dex_file_ptr.get();
- if (klass_descriptor == kInvalidClassDescriptor) {
- if (kInvalidTypeIndex >= dex_file->NumTypeIds()) {
- // The dex file does not contain all possible type ids which leaves us room
- // to add an "invalid" type id.
- *class_ref = TypeReference(dex_file, dex::TypeIndex(kInvalidTypeIndex));
- return true;
- } else {
- // The dex file contains all possible type ids. We don't have any free type id
- // that we can use as invalid.
- continue;
+ // Find class definition for a descriptor.
+ const dex::ClassDef* FindClassDef(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
+ std::string_view klass_descriptor,
+ /*out*/ TypeReference* class_ref) {
+ for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
+ const dex::TypeId* type_id = dex_file->FindTypeId(klass_descriptor);
+ if (type_id != nullptr) {
+ dex::TypeIndex type_index = dex_file->GetIndexForTypeId(*type_id);
+ const dex::ClassDef* class_def = dex_file->FindClassDef(type_index);
+ if (class_def != nullptr) {
+ *class_ref = TypeReference(dex_file.get(), type_index);
+ return class_def;
}
}
-
- const dex::TypeId* type_id = dex_file->FindTypeId(klass_descriptor);
- if (type_id == nullptr) {
- continue;
- }
- dex::TypeIndex type_index = dex_file->GetIndexForTypeId(*type_id);
- *class_ref = TypeReference(dex_file, type_index);
-
- if (dex_file->FindClassDef(type_index) == nullptr) {
- // Class is only referenced in the current dex file but not defined in it.
- // We use its current type reference, but keep looking for its
- // definition.
- // Note that array classes fall into that category, as they do not have
- // a class definition.
- continue;
- }
- return true;
}
- // If we arrive here, we haven't found a class definition. If the dex file
- // of the class reference is not null, then we have found a type reference,
- // and we return that to the caller.
- return (class_ref->dex_file != nullptr);
+ return nullptr;
+ }
+
+ // Find class klass_descriptor in the given dex_files and store its reference
+ // in the out parameter class_ref.
+ // Return true if a reference of the class was found in any of the dex_files.
+ bool FindClass(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
+ std::string_view klass_descriptor,
+ /*out*/ TypeReference* class_ref) {
+ for (const std::unique_ptr<const DexFile>& dex_file_ptr : dex_files) {
+ const DexFile* dex_file = dex_file_ptr.get();
+ const dex::TypeId* type_id = dex_file->FindTypeId(klass_descriptor);
+ if (type_id != nullptr) {
+ *class_ref = TypeReference(dex_file, dex_file->GetIndexForTypeId(*type_id));
+ return true;
+ }
+ }
+ return false;
}
// Find the method specified by method_spec in the class class_ref.
uint32_t FindMethodIndex(const TypeReference& class_ref,
- const std::string& method_spec) {
+ std::string_view method_spec) {
const DexFile* dex_file = class_ref.dex_file;
- if (method_spec == kInvalidMethod) {
- constexpr uint16_t kInvalidMethodIndex = std::numeric_limits<uint16_t>::max() - 1;
- return kInvalidMethodIndex >= dex_file->NumMethodIds()
- ? kInvalidMethodIndex
- : dex::kDexNoIndex;
- }
- std::vector<std::string> name_and_signature;
- Split(method_spec, kProfileParsingFirstCharInSignature, &name_and_signature);
- if (name_and_signature.size() != 2) {
- LOG(ERROR) << "Invalid method name and signature " << method_spec;
+ size_t signature_start = method_spec.find(kProfileParsingFirstCharInSignature);
+ if (signature_start == std::string_view::npos) {
+ LOG(ERROR) << "Invalid method name and signature: " << method_spec;
return dex::kDexNoIndex;
}
- const std::string& name = name_and_signature[0];
- const std::string& signature = kProfileParsingFirstCharInSignature + name_and_signature[1];
+ const std::string_view name = method_spec.substr(0u, signature_start);
+ const std::string_view signature = method_spec.substr(signature_start);
- const dex::StringId* name_id = dex_file->FindStringId(name.c_str());
+ const dex::StringId* name_id = dex_file->FindStringId(std::string(name).c_str());
if (name_id == nullptr) {
LOG(WARNING) << "Could not find name: " << name;
return dex::kDexNoIndex;
@@ -1111,7 +1092,7 @@
dex::TypeIndex return_type_idx;
std::vector<dex::TypeIndex> param_type_idxs;
if (!dex_file->CreateTypeList(signature, &return_type_idx, ¶m_type_idxs)) {
- LOG(WARNING) << "Could not create type list" << signature;
+ LOG(WARNING) << "Could not create type list: " << signature;
return dex::kDexNoIndex;
}
const dex::ProtoId* proto_id = dex_file->FindProtoId(return_type_idx, param_type_idxs);
@@ -1348,28 +1329,24 @@
// The possible line formats are:
// "LJustTheClass;".
// "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;".
- // "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,invalid_class".
// "LTestInline;->inlineMissingTypes(LSuper;)I+missing_types".
// // Note no ',' after [LTarget;
// "LTestInline;->multiInlinePolymorphic(LSuper;)I+]LTarget1;LResA;,LResB;]LTarget2;LResC;,LResD;".
- // "LTestInline;->multiInlinePolymorphic(LSuper;)I+]LTarget1;LResA;,invalid_class]LTarget2;LResC;,LResD;".
// "LTestInline;->multiInlinePolymorphic(LSuper;)I+]LTarget1;missing_types]LTarget2;LResC;,LResD;".
// "{annotation}LTestInline;->inlineNoInlineCaches(LSuper;)I".
// "LTestInline;->*".
- // "invalid_class".
- // "LTestInline;->invalid_method".
// The method and classes are searched only in the given dex files.
bool ProcessLine(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
- const std::string& maybe_annotated_line,
+ std::string_view maybe_annotated_line,
/*out*/ProfileCompilationInfo* profile) {
// First, process the annotation.
if (maybe_annotated_line.empty()) {
return true;
}
// Working line variable which will contain the user input without the annotations.
- std::string line = maybe_annotated_line;
+ std::string_view line = maybe_annotated_line;
- std::string annotation_string;
+ std::string_view annotation_string;
if (maybe_annotated_line[0] == kAnnotationStart) {
size_t end_pos = maybe_annotated_line.find(kAnnotationEnd, 0);
if (end_pos == std::string::npos || end_pos == 0) {
@@ -1383,17 +1360,17 @@
ProfileSampleAnnotation annotation = annotation_string.empty()
? ProfileSampleAnnotation::kNone
- : ProfileSampleAnnotation(annotation_string);
+ : ProfileSampleAnnotation(std::string(annotation_string));
- // Now process the rest of the lines.
- std::string klass;
- std::string method_str;
+ // Now process the rest of the line.
+ std::string_view klass;
+ std::string_view method_str;
bool is_hot = false;
bool is_startup = false;
bool is_post_startup = false;
const size_t method_sep_index = line.find(kMethodSep, 0);
if (method_sep_index == std::string::npos) {
- klass = line.substr(0);
+ klass = line;
} else {
// The method prefix flags are only valid for method strings.
size_t start_index = 0;
@@ -1415,6 +1392,41 @@
method_str = line.substr(method_sep_index + kMethodSep.size());
}
+ if (!IsValidDescriptor(std::string(klass).c_str())) {
+ LOG(ERROR) << "Invalid descriptor: " << klass;
+ return false;
+ }
+
+ if (method_str.empty()) {
+ auto array_it = std::find_if(klass.begin(), klass.end(), [](char c) { return c != '['; });
+ size_t array_dim = std::distance(klass.begin(), array_it);
+ if (klass.size() == array_dim + 1u) {
+ // Attribute primitive types and their arrays to the first dex file.
+ profile->AddClass(*dex_files[0], klass, annotation);
+ return true;
+ }
+ // Attribute non-primitive classes and their arrays to the dex file with the definition.
+ TypeReference class_ref(/* dex_file= */ nullptr, dex::TypeIndex());
+ if (FindClassDef(dex_files, klass.substr(array_dim), &class_ref) == nullptr) {
+ LOG(WARNING) << "Could not find class definition: " << klass.substr(array_dim);
+ return false;
+ }
+ if (array_dim != 0) {
+ // Let the ProfileCompilationInfo find the type index or add an extra descriptor.
+ return profile->AddClass(*class_ref.dex_file, klass, annotation);
+ } else {
+ return profile->AddClass(*class_ref.dex_file, class_ref.TypeIndex(), annotation);
+ }
+ }
+
+ DCHECK_NE(klass[0], '[');
+ TypeReference class_ref(/* dex_file= */ nullptr, dex::TypeIndex());
+ const dex::ClassDef* class_def = FindClassDef(dex_files, klass, &class_ref);
+ if (class_def == nullptr) {
+ LOG(WARNING) << "Could not find class definition: " << klass;
+ return false;
+ }
+
uint32_t flags = 0;
if (is_hot) {
flags |= ProfileCompilationInfo::MethodHotness::kFlagHot;
@@ -1426,33 +1438,21 @@
flags |= ProfileCompilationInfo::MethodHotness::kFlagPostStartup;
}
- TypeReference class_ref(/* dex_file= */ nullptr, dex::TypeIndex());
- if (!FindClass(dex_files, klass, &class_ref)) {
- LOG(WARNING) << "Could not find class: " << klass;
- return false;
- }
-
- if (method_str.empty() || method_str == kClassAllMethods) {
+ if (method_str == kClassAllMethods) {
// Start by adding the class.
- const DexFile* dex_file = class_ref.dex_file;
+ profile->AddClass(*class_ref.dex_file, class_ref.TypeIndex(), annotation);
+ uint16_t class_def_index = class_ref.dex_file->GetIndexForClassDef(*class_def);
+ ClassAccessor accessor(*class_ref.dex_file, class_def_index);
std::vector<ProfileMethodInfo> methods;
- if (method_str == kClassAllMethods) {
- ClassAccessor accessor(
- *dex_file,
- dex_file->GetIndexForClassDef(*dex_file->FindClassDef(class_ref.TypeIndex())));
- for (const ClassAccessor::Method& method : accessor.GetMethods()) {
- if (method.GetCodeItemOffset() != 0) {
- // Add all of the methods that have code to the profile.
- methods.push_back(ProfileMethodInfo(method.GetReference()));
- }
+ for (const ClassAccessor::Method& method : accessor.GetMethods()) {
+ if (method.GetCodeItemOffset() != 0) {
+ // Add all of the methods that have code to the profile.
+ methods.push_back(ProfileMethodInfo(method.GetReference()));
}
}
- // TODO: Check return values?
+ // TODO: Check return value?
profile->AddMethods(
methods, static_cast<ProfileCompilationInfo::MethodHotness::Flag>(flags), annotation);
- std::set<dex::TypeIndex> classes;
- classes.insert(class_ref.TypeIndex());
- profile->AddClassesForDex(dex_file, classes.begin(), classes.end(), annotation);
return true;
}
@@ -1460,11 +1460,12 @@
std::string method_spec;
// If none of the flags are set, default to hot.
+ // TODO: Why is this done after we have already calculated `flags`?
is_hot = is_hot || (!is_hot && !is_startup && !is_post_startup);
- std::vector<std::string> method_elems;
// Lifetime of segments is same as method_elems since it contains pointers into the string-data
std::vector<InlineCacheSegment> segments;
+ std::vector<std::string_view> method_elems;
Split(method_str, kProfileParsingInlineChacheSep, &method_elems);
if (method_elems.size() == 2) {
method_spec = method_elems[0];
@@ -1490,7 +1491,7 @@
// examine. If we couldn't resolve the method don't bother trying to create
// inline-caches.
if (resolved_class_method_ref) {
- for (const InlineCacheSegment &segment : segments) {
+ for (const InlineCacheSegment& segment : segments) {
std::vector<uint32_t> dex_pcs;
if (segment.IsSingleReceiver()) {
DCHECK_EQ(segments.size(), 1u);
@@ -1523,16 +1524,20 @@
bool missing_types = segment.GetIcTargets()[0] == kMissingTypesMarker;
bool megamorphic_types =
segment.GetIcTargets()[0] == kMegamorphicTypesMarker;
- std::vector<TypeReference> classes(
- missing_types || megamorphic_types ? 0u : segment.NumIcTargets(),
- TypeReference(/* dex_file= */ nullptr, dex::TypeIndex()));
+ std::vector<TypeReference> classes;
if (!missing_types && !megamorphic_types) {
- size_t class_it = 0;
- for (const std::string_view &ic_class : segment.GetIcTargets()) {
+ classes.reserve(segment.NumIcTargets());
+ for (const std::string_view& ic_class : segment.GetIcTargets()) {
if (ic_class.empty()) {
break;
}
- if (!FindClass(dex_files, ic_class, &(classes[class_it++]))) {
+ if (!IsValidDescriptor(std::string(ic_class).c_str())) {
+ LOG(ERROR) << "Invalid descriptor for inline cache: " << ic_class;
+ return false;
+ }
+ // TODO: Allow referencing classes without a `dex::TypeId` in any of the dex files.
+ TypeReference ic_class_ref(/* dex_file= */ nullptr, dex::TypeIndex());
+ if (!FindClass(dex_files, ic_class, &ic_class_ref)) {
LOG(segment.IsSingleReceiver() ? ERROR : WARNING)
<< "Could not find class: " << ic_class << " in " << segment;
if (segment.IsSingleReceiver()) {
@@ -1544,13 +1549,11 @@
break;
}
}
+ classes.push_back(ic_class_ref);
}
- // Make sure we are actually the correct size
- classes.resize(class_it, TypeReference(nullptr, dex::TypeIndex()));
}
for (size_t dex_pc : dex_pcs) {
- inline_caches.emplace_back(dex_pc, missing_types, classes,
- megamorphic_types);
+ inline_caches.emplace_back(dex_pc, missing_types, classes, megamorphic_types);
}
}
}
@@ -1599,15 +1602,19 @@
}
bool ProcessBootLine(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
- const std::string& line,
+ std::string_view line,
ProfileBootInfo* boot_profiling_info) {
const size_t method_sep_index = line.find(kMethodSep, 0);
- std::string klass_str = line.substr(0, method_sep_index);
- std::string method_str = line.substr(method_sep_index + kMethodSep.size());
+ if (method_sep_index == std::string_view::npos) {
+ LOG(ERROR) << "Invalid boot line: " << line;
+ return false;
+ }
+ std::string_view klass_str = line.substr(0, method_sep_index);
+ std::string_view method_str = line.substr(method_sep_index + kMethodSep.size());
TypeReference class_ref(/* dex_file= */ nullptr, dex::TypeIndex());
- if (!FindClass(dex_files, klass_str, &class_ref)) {
- LOG(WARNING) << "Could not find class: " << klass_str;
+ if (FindClassDef(dex_files, klass_str, &class_ref) == nullptr) {
+ LOG(WARNING) << "Could not find class definition: " << klass_str;
return false;
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0f5f139..2df7d23 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -8948,6 +8948,16 @@
ObjPtr<mirror::ClassLoader> class_loader) {
const DexFile& dex_file = *dex_cache->GetDexFile();
const char* descriptor = dex_file.StringByTypeIdx(type_idx);
+ ObjPtr<mirror::Class> type = LookupResolvedType(descriptor, class_loader);
+ if (type != nullptr) {
+ DCHECK(type->IsResolved());
+ dex_cache->SetResolvedType(type_idx, type);
+ }
+ return type;
+}
+
+ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(const char* descriptor,
+ ObjPtr<mirror::ClassLoader> class_loader) {
DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
ObjPtr<mirror::Class> type = nullptr;
if (descriptor[1] == '\0') {
@@ -8961,14 +8971,7 @@
// Find the class in the loaded classes table.
type = LookupClass(self, descriptor, hash, class_loader);
}
- if (type != nullptr) {
- if (type->IsResolved()) {
- dex_cache->SetResolvedType(type_idx, type);
- } else {
- type = nullptr;
- }
- }
- return type;
+ return (type != nullptr && type->IsResolved()) ? type : nullptr;
}
template <typename RefType>
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index a1b0b29..547753a 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -312,6 +312,11 @@
ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Look up a resolved type with the given descriptor associated with the given ClassLoader.
+ ObjPtr<mirror::Class> LookupResolvedType(const char* descriptor,
+ ObjPtr<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Determine whether a dex cache result should be trusted, or an IncompatibleClassChangeError
// check and IllegalAccessError check should be performed even after a hit.
enum class ResolveMode { // private.
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index dcc3d8d..ff711fa 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -551,20 +551,12 @@
total_number_of_code_cache_queries_++;
}
{
- ProfileCompilationInfo info(Runtime::Current()->GetArenaPool());
+ ProfileCompilationInfo info(Runtime::Current()->GetArenaPool(),
+ /*for_boot_image=*/ options_.GetProfileBootClassPath());
if (!info.Load(filename, /*clear_if_invalid=*/ true)) {
LOG(WARNING) << "Could not forcefully load profile " << filename;
continue;
}
- if (options_.GetProfileBootClassPath() != info.IsForBootImage()) {
- // If we enabled boot class path profiling but the profile is a regular one,
- // (or the opposite), clear the profile. We do not support cross-version merges.
- LOG(WARNING) << "Adjust profile version: for_boot_classpath="
- << options_.GetProfileBootClassPath();
- info.ClearDataAndAdjustVersion(options_.GetProfileBootClassPath());
- // For saving to ensure we persist the new version.
- force_save = true;
- }
uint64_t last_save_number_of_methods = info.GetNumberOfMethods();
uint64_t last_save_number_of_classes = info.GetNumberOfResolvedClasses();
VLOG(profiler) << "last_save_number_of_methods=" << last_save_number_of_methods
@@ -926,19 +918,6 @@
}
}
-bool ProfileSaver::HasSeenMethod(const std::string& profile, bool hot, MethodReference ref) {
- MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
- if (instance_ != nullptr) {
- ProfileCompilationInfo info(Runtime::Current()->GetArenaPool());
- if (!info.Load(profile, /*clear_if_invalid=*/false)) {
- return false;
- }
- const ProfileCompilationInfo::MethodHotness hotness = info.GetMethodHotness(ref);
- return hot ? hotness.IsHot() : hotness.IsInProfile();
- }
- return false;
-}
-
void ProfileSaver::ResolveTrackedLocations() {
SafeMap<std::string, std::set<std::string>> locations_to_be_resolved;
{
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 87a6de6..036e717 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -50,10 +50,6 @@
// For testing or manual purposes (SIGUSR1).
static void ForceProcessProfiles() REQUIRES(!Locks::profiler_lock_, !Locks::mutator_lock_);
- // Just for testing purposes.
- static bool HasSeenMethod(const std::string& profile, bool hot, MethodReference ref)
- REQUIRES(!Locks::profiler_lock_);
-
// Notify that startup has completed.
static void NotifyStartupCompleted() REQUIRES(!Locks::profiler_lock_, !instance_->wait_lock_);
diff --git a/runtime/jit/profiling_info_test.cc b/runtime/jit/profiling_info_test.cc
index 90162fd..ce0a30f 100644
--- a/runtime/jit/profiling_info_test.cc
+++ b/runtime/jit/profiling_info_test.cc
@@ -260,7 +260,8 @@
const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second;
ProfileCompilationInfo::MethodHotness offline_hotness = info.GetMethodHotness(method_ref);
ASSERT_TRUE(offline_hotness.IsHot());
- ASSERT_TRUE(ProfileTestHelper::EqualInlineCaches(pmi.inline_caches, offline_hotness, info));
+ ASSERT_TRUE(ProfileTestHelper::EqualInlineCaches(
+ pmi.inline_caches, method_ref.dex_file, offline_hotness, info));
}
}
}
diff --git a/test/595-profile-saving/profile-saving.cc b/test/595-profile-saving/profile-saving.cc
index 95aa7e9..bec4ae9 100644
--- a/test/595-profile-saving/profile-saving.cc
+++ b/test/595-profile-saving/profile-saving.cc
@@ -48,30 +48,38 @@
ProfileSaver::ForceProcessProfiles();
}
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(JNIEnv* env,
- jclass,
- jstring filename,
- jobject method) {
- ScopedUtfChars filename_chars(env, filename);
- CHECK(filename_chars.c_str() != nullptr);
- ScopedObjectAccess soa(env);
- ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
- ArtMethod* art_method = exec->GetArtMethod();
- return ProfileSaver::HasSeenMethod(std::string(filename_chars.c_str()),
- /*hot*/ true,
- MethodReference(art_method->GetDexFile(),
- art_method->GetDexMethodIndex()));
-}
-
extern "C" JNIEXPORT jboolean JNICALL Java_Main_isForBootImage(JNIEnv* env,
jclass,
jstring filename) {
ScopedUtfChars filename_chars(env, filename);
CHECK(filename_chars.c_str() != nullptr);
- ProfileCompilationInfo info;
- info.Load(std::string(filename_chars.c_str()), /*clear_if_invalid=*/ false);
- return info.IsForBootImage();
+ ProfileCompilationInfo info(/*for_boot_image=*/ true);
+ bool result = info.Load(std::string(filename_chars.c_str()), /*clear_if_invalid=*/ false);
+ return result ? JNI_TRUE : JNI_FALSE;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_presentInProfile(JNIEnv* env,
+ jclass c,
+ jstring filename,
+ jobject method) {
+ bool for_boot_image = Java_Main_isForBootImage(env, c, filename) == JNI_TRUE;
+ ScopedUtfChars filename_chars(env, filename);
+ CHECK(filename_chars.c_str() != nullptr);
+ ScopedObjectAccess soa(env);
+ ObjPtr<mirror::Executable> exec = soa.Decode<mirror::Executable>(method);
+ ArtMethod* art_method = exec->GetArtMethod();
+ MethodReference ref(art_method->GetDexFile(), art_method->GetDexMethodIndex());
+
+ ProfileCompilationInfo info(Runtime::Current()->GetArenaPool(), for_boot_image);
+ if (!info.Load(filename_chars.c_str(), /*clear_if_invalid=*/false)) {
+ LOG(ERROR) << "Failed to load profile from " << filename;
+ return JNI_FALSE;
+ }
+ const ProfileCompilationInfo::MethodHotness hotness = info.GetMethodHotness(ref);
+ // TODO: Why do we check `hotness.IsHot()` instead of `hotness.IsInProfile()`
+ // in a method named `presentInProfile()`?
+ return hotness.IsHot() ? JNI_TRUE : JNI_FALSE;
}
} // namespace
diff --git a/test/707-checker-invalid-profile/profile b/test/707-checker-invalid-profile/profile
index f142c40..171d656 100644
--- a/test/707-checker-invalid-profile/profile
+++ b/test/707-checker-invalid-profile/profile
@@ -1,4 +1,9 @@
SHLMain;->attemptInlineMonomorphic(LMain;)I+invalid_class
SHLMain;->attemptInlinePolymorphic(LMain;)I+LMain;,invalid_class
SHLMain;->invalid_method
-invalid_class
\ No newline at end of file
+invalid_class
+
+# Invalid descriptor in inline cache spec causes the entire line to be rejected
+# and this test requires the methods to be present in the profile.
+SHLMain;->attemptInlineMonomorphic(LMain;)I
+SHLMain;->attemptInlinePolymorphic(LMain;)I