Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  profile_compilation_info.cc

  Sprache: C
 

/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include "profile_compilation_info.h"

#include <fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <zlib.h>

#include <algorithm>
#include <cerrno>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <numeric>
#include <random>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "android-base/file.h"
#include "android-base/properties.h"
#include "android-base/scopeguard.h"
#include "android-base/strings.h"
#include "android-base/unique_fd.h"
#include "base/arena_allocator.h"
#include "base/bit_utils.h"
#include "base/dumpable.h"
#include "base/file_utils.h"
#include "base/globals.h"
#include "base/logging.h"  // For VLOG.
#include "base/calloc_arena_pool.h"
#include "base/os.h"
#include "base/safe_map.h"
#include "base/scoped_flock.h"
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
#include "base/utils.h"
#include "base/zip_archive.h"
#include "dex/code_item_accessors-inl.h"
#include "dex/descriptors_names.h"
#include "dex/dex_file_loader.h"
#include "dex/dex_instruction-inl.h"

#ifdef ART_TARGET_ANDROID
#include "android-modules-utils/sdk_level.h"
#endif

namespace art {

const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p''r''o''\0' };
// Last profile version: New extensible profile format.
const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0''1''5''\0' };
const uint8_t ProfileCompilationInfo::kProfileVersionForBootImage[] = { '0''1''6''\0' };

static_assert(sizeof(ProfileCompilationInfo::kProfileVersion) == 4,
              "Invalid profile version size");
static_assert(sizeof(ProfileCompilationInfo::kProfileVersionForBootImage) == 4,
              "Invalid profile version size");

// The name of the profile entry in the dex metadata file.
// DO NOT CHANGE THIS! (it's similar to classes.dex in the apk files).
const char ProfileCompilationInfo::kDexMetadataProfileEntry[] = "primary.prof";

// A synthetic annotations that can be used to denote that no annotation should
// be associated with the profile samples. We use the empty string for the package name
// because that's an invalid package name and should never occur in practice.
const ProfileCompilationInfo::ProfileSampleAnnotation
  ProfileCompilationInfo::ProfileSampleAnnotation::kNone =
      ProfileCompilationInfo::ProfileSampleAnnotation("");

static constexpr char kSampleMetadataSeparator = ':';

// 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;

// Extra descriptors are serialized with a `uint16_t` prefix. This defines the length limit.
static constexpr size_t kMaxExtraDescriptorLength = std::numeric_limits<uint16_t>::max();

// 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
// using the same test profile.
static constexpr bool kDebugIgnoreChecksum = false;

static constexpr uint8_t kIsMissingTypesEncoding = 6;
static constexpr uint8_t kIsMegamorphicEncoding = 7;

static_assert(sizeof(ProfileCompilationInfo::kIndividualInlineCacheSize) == sizeof(uint8_t),
              "InlineCache::kIndividualInlineCacheSize does not have the expect type size");
static_assert(ProfileCompilationInfo::kIndividualInlineCacheSize < kIsMegamorphicEncoding,
              "InlineCache::kIndividualInlineCacheSize is larger than expected");
static_assert(ProfileCompilationInfo::kIndividualInlineCacheSize < kIsMissingTypesEncoding,
              "InlineCache::kIndividualInlineCacheSize is larger than expected");

static constexpr uint32_t kSizeWarningThresholdBytes = 5000000U;
static constexpr uint32_t kSizeErrorThresholdBytes = 15000000U;

static constexpr uint32_t kSizeWarningThresholdBootBytes = 25000000U;
static constexpr uint32_t kSizeErrorThresholdBootBytes = 100000000U;

static bool ChecksumMatch(uint32_t dex_file_checksum, uint32_t checksum) {
  return kDebugIgnoreChecksum || dex_file_checksum == checksum;
}

namespace {

// Deflate the input buffer `in_buffer`. It returns a buffer of
// compressed data for the input buffer of `*compressed_data_size` size.
std::unique_ptr<uint8_t[]> DeflateBuffer(ArrayRef<const uint8_t> in_buffer,
                                         /*out*/ uint32_t* compressed_data_size) {
  z_stream strm;
  strm.zalloc = Z_NULL;
  strm.zfree = Z_NULL;
  strm.opaque = Z_NULL;
  int init_ret = deflateInit(&strm, 1);
  if (init_ret != Z_OK) {
    return nullptr;
  }

  uint32_t out_size = dchecked_integral_cast<uint32_t>(deflateBound(&strm, in_buffer.size()));

  std::unique_ptr<uint8_t[]> compressed_buffer(new uint8_t[out_size]);
  strm.avail_in = in_buffer.size();
  strm.next_in = const_cast<uint8_t*>(in_buffer.data());
  strm.avail_out = out_size;
  strm.next_out = &compressed_buffer[0];
  int ret = deflate(&strm, Z_FINISH);
  if (ret == Z_STREAM_ERROR) {
    return nullptr;
  }
  *compressed_data_size = out_size - strm.avail_out;

  int end_ret = deflateEnd(&strm);
  if (end_ret != Z_OK) {
    return nullptr;
  }

  return compressed_buffer;
}

// Inflate the data from `in_buffer` into `out_buffer`. The `out_buffer.size()`
// is the expected output size of the buffer. It returns Z_STREAM_END on success.
// On error, it returns Z_STREAM_ERROR if the compressed data is inconsistent
// and Z_DATA_ERROR if the stream ended prematurely or the stream has extra data.
int InflateBuffer(ArrayRef<const uint8_t> in_buffer, /*out*/ ArrayRef<uint8_t> out_buffer) {
  /* allocate inflate state */
  z_stream strm;
  strm.zalloc = Z_NULL;
  strm.zfree = Z_NULL;
  strm.opaque = Z_NULL;
  strm.avail_in = in_buffer.size();
  strm.next_in = const_cast<uint8_t*>(in_buffer.data());
  strm.avail_out = out_buffer.size();
  strm.next_out = out_buffer.data();

  int init_ret = inflateInit(&strm);
  if (init_ret != Z_OK) {
    return init_ret;
  }

  int ret = inflate(&strm, Z_NO_FLUSH);
  if (strm.avail_in != 0 || strm.avail_out != 0) {
    return Z_DATA_ERROR;
  }

  int end_ret = inflateEnd(&strm);
  if (end_ret != Z_OK) {
    return end_ret;
  }

  return ret;
}

}  // 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 aggregation counts of the profile, classes and methods. This section is
  // an optional reserved section not implemented on client yet.
  kAggregationCounts = 4,

  // Classes included in the profile that should not be initialized by zygote or dex2oat
  // (usually due to some logic in the class initializer that should not be shared between
  // processes, e.g. initializing random seed).
  kClassesNoPreload = 5,

  // The number of known sections.
  kNumberOfSections = 6
};

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] = {0000};
  uint8_t version_[4] = {0000};
  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_, kProfileVersionForBootImage, 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.
 * For zip files, the profile entry will be extracted to
 * the memory map.
 */

class ProfileCompilationInfo::ProfileSource {
 public:
  /**
   * Create a profile source for the given fd. The ownership of the fd
   * remains to the caller; as this class will not attempt to close it at any
   * point.
   */

  static ProfileSource* Create(int32_t fd) {
    DCHECK_GT(fd, -1);
    return new ProfileSource(fd, MemMap::Invalid());
  }

  /**
   * Create a profile source backed by a memory map. The map can be null in
   * which case it will the treated as an empty source.
   */

  static ProfileSource* Create(MemMap&& mem_map) {
    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(void* buffer,
                         size_t byte_count,
                         const std::string& debug_stage,
                         std::string* error);

  /** Return true if the source has 0 data. */
  bool HasEmptyContent() const;

 private:
  ProfileSource(int32_t fd, MemMap&& mem_map)
      : fd_(fd), mem_map_(std::move(mem_map)), mem_map_cur_(0) {}

  bool IsMemMap() const {
    return fd_ == -1;
  }

  int32_t fd_;  // The fd is not owned by this class.
  MemMap mem_map_;
  size_t mem_map_cur_;  // Current position in the map to read from.
};

// 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 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) > GetAvailableBytes()) {
      return false;
    }
    *value = 0;
    for (size_t i = 0; i < sizeof(T); i++) {
      *value += ptr_current_[i] << (i * kBitsPerByte);
    }
    ptr_current_ += sizeof(T);
    return true;
  }

  // Reads a length-prefixed string as `std::string_view` and advances the current pointer.
  // The length is `uint16_t`.
  bool ReadStringAndAdvance(/*out*/ std::string_view* value) {
    uint16_t length;
    if (!ReadUintAndAdvance(&length)) {
      return false;
    }
    if (length > GetAvailableBytes()) {
      return false;
    }
    const void* null_char = memchr(GetCurrentPtr(), 0, length);
    if (null_char != nullptr) {
      // Embedded nulls are invalid.
      return false;
    }
    *value = std::string_view(reinterpret_cast<const char*>(GetCurrentPtr()), length);
    Advance(length);
    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 > GetAvailableBytes()) {
      return false;
    }
    if (memcmp(ptr_current_, data, data_size) == 0) {
      ptr_current_ += data_size;
      return true;
    }
    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, GetAvailableBytes());
    ptr_current_ += data_size;
  }

  // Returns the count of unread bytes.
  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.
  uint8_t* GetCurrentPtr() {
    return ptr_current_;
  }

  // Get the underlying raw buffer.
  uint8_t* Get() {
    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_;
  uint8_t* ptr_end_;
};

ProfileCompilationInfo::ProfileCompilationInfo(ArenaPool* custom_arena_pool, bool for_boot_image)
    : default_arena_pool_(),
      allocator_(custom_arena_pool),
      info_(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);
}

ProfileCompilationInfo::ProfileCompilationInfo(ArenaPool* custom_arena_pool)
    : ProfileCompilationInfo(custom_arena_pool, /*for_boot_image=*/ false) { }

ProfileCompilationInfo::ProfileCompilationInfo()
    : ProfileCompilationInfo(/*for_boot_image=*/ false) { }

ProfileCompilationInfo::ProfileCompilationInfo(bool for_boot_image)
    : ProfileCompilationInfo(&default_arena_pool_, for_boot_image) { }

ProfileCompilationInfo::~ProfileCompilationInfo() {
  VLOG(profiler) << Dumpable<MemStats>(allocator_.GetMemStats());
}

void ProfileCompilationInfo::DexPcData::AddClass(const dex::TypeIndex& type_idx) {
  if (is_megamorphic || is_missing_types) {
    return;
  }

  // Perform an explicit lookup for the type instead of directly emplacing the
  // 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.
  auto lb = classes.lower_bound(type_idx);
  if (lb != classes.end() && *lb == type_idx) {
    // The type index exists.
    return;
  }

  // Check if the adding the type will cause the cache to become megamorphic.
  if (classes.size() + 1 >= ProfileCompilationInfo::kIndividualInlineCacheSize) {
    is_megamorphic = true;
    classes.clear();
    return;
  }

  // The type does not exist and the inline cache will not be megamorphic.
  classes.emplace_hint(lb, type_idx);
}

// Transform the actual dex location into a key used to index the dex file in the profile.
// See ProfileCompilationInfo#GetProfileDexFileBaseKey as well.
std::string ProfileCompilationInfo::GetProfileDexFileAugmentedKey(
    const DexFile* dex_file, const ProfileSampleAnnotation& annotation) {
  DCHECK(dex_file != nullptr);
  std::string base_key = GetProfileDexFileBaseKey(dex_file);
  return annotation == ProfileSampleAnnotation::kNone
      ? base_key
      : base_key + kSampleMetadataSeparator + annotation.GetOriginPackageName();
}

// Returns the basename of the location (e.g. "base.apk" from "/dir/base.apk").
std::string_view ProfileCompilationInfo::GetLocationBasename(std::string_view base_location) {
  DCHECK(!DexFileLoader::IsMultiDexLocation(base_location));
  size_t last_sep_index = base_location.find_last_of('/');
  return base_location.substr(last_sep_index == std::string_view::npos ? 0 : last_sep_index + 1);
}

// Transform the actual dex location into a base profile key (represented as relative paths).
// Note: this is OK because we don't store profiles of different apps into the same file.
// Apps with split apks don't cause trouble because each split has a different name and will not
// collide with other entries.
std::string ProfileCompilationInfo::GetProfileDexFileBaseKey(std::string_view base_location,
                                                             std::string_view entry_name) {
  DCHECK(!DexFileLoader::IsMultiDexLocation(base_location));
  std::string_view filename = GetLocationBasename(base_location);
  if (entry_name.empty() || entry_name == "classes.dex") {
    return std::string(filename);
  }
  return std::string(filename) + DexFileLoader::kMultiDexSeparator + std::string(entry_name);
}

std::string ProfileCompilationInfo::GetProfileDexFileBaseKey(const DexFile* dex_file) {
  DCHECK(dex_file != nullptr);
  auto [base_location, index] = DexFileLoader::SplitMultiDexLocation(dex_file->GetLocation());
  if (dex_file->HasDexContainer()) {
    // Use the new "!1" syntax for container dex files, since just the zip entry name isn't unique.
    return GetProfileDexFileBaseKey(base_location, std::to_string(index));
  } else {
    // Use the old "!classes2.dex" syntax for backwards compatibility.
    return GetProfileDexFileBaseKey(base_location, DexFileLoader::GetMultiDexZipEntryName(index));
  }
}

std::string_view ProfileCompilationInfo::GetBaseKeyViewFromAugmentedKey(
    std::string_view profile_key) {
  size_t pos = profile_key.rfind(kSampleMetadataSeparator);
  return (pos == std::string::npos) ? profile_key : profile_key.substr(0, pos);
}

std::string ProfileCompilationInfo::GetBaseKeyFromAugmentedKey(
    const std::string& profile_key) {
  // Note: Conversions between std::string and std::string_view.
  return std::string(GetBaseKeyViewFromAugmentedKey(profile_key));
}

std::string ProfileCompilationInfo::MigrateAnnotationInfo(
    const std::string& base_key,
    const std::string& augmented_key) {
  size_t pos = augmented_key.rfind(kSampleMetadataSeparator);
  return (pos == std::string::npos)
      ? base_key
      : base_key + augmented_key.substr(pos);
}

ProfileCompilationInfo::ProfileSampleAnnotation ProfileCompilationInfo::GetAnnotationFromKey(
     const std::string& augmented_key) {
  size_t pos = augmented_key.rfind(kSampleMetadataSeparator);
  return (pos == std::string::npos)
      ? ProfileSampleAnnotation::kNone
      : ProfileSampleAnnotation(augmented_key.substr(pos + 1));
}

bool ProfileCompilationInfo::AddMethods(const std::vector<ProfileMethodInfo>& methods,
                                        MethodHotness::Flag flags,
                                        const ProfileSampleAnnotation& annotation,
                                        bool is_test) {
  for (const ProfileMethodInfo& method : methods) {
    if (!AddMethod(method, flags, annotation, is_test)) {
      return false;
    }
  }
  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.
  std::string_view descriptor = class_ref.dex_file->GetTypeDescriptorView(class_ref.TypeIndex());
  return FindOrCreateTypeIndex(dex_file, descriptor);
}

dex::TypeIndex ProfileCompilationInfo::FindOrCreateTypeIndex(const DexFile& dex_file,
                                                             std::string_view 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;
  // Check descriptor length for "extra descriptor". We are using `uint16_t` as prefix.
  if (UNLIKELY(descriptor.size() > kMaxExtraDescriptorLength)) {
    return dex::TypeIndex();  // Invalid.
  }
  auto it = extra_descriptors_indexes_.find(descriptor);
  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);
  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,
                                      std::string_view 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
  int flags = O_RDONLY;
#else
  int flags = O_RDONLY | O_NOFOLLOW | O_CLOEXEC;
#endif
  ScopedFlock profile_file =
      LockedFile::Open(filename.c_str(), flags, /*block=*/false, &error);

  if (profile_file.get() == nullptr) {
    LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
    return false;
  }

  int fd = profile_file->Fd();

  ProfileLoadStatus status = LoadInternal(fd, &error);
  if (status == ProfileLoadStatus::kSuccess) {
    return true;
  }

  LOG(WARNING) << "Could not load profile data from file " << filename << ": " << error;
  return false;
}

bool ProfileCompilationInfo::Load(const std::string& filename, bool clear_if_invalid) {
  ScopedTrace trace(__PRETTY_FUNCTION__);
  std::string error;

  if (!IsEmpty()) {
    return false;
  }

#ifdef _WIN32
  int flags = O_RDWR;
#else
  int flags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
#endif
  // There's no need to fsync profile data right away. We get many chances
  // to write it again in case something goes wrong. We can rely on a simple
  // close(), no sync, and let to the kernel decide when to write to disk.
  ScopedFlock profile_file =
      LockedFile::Open(filename.c_str(), flags, /*block=*/false, &error);

  if (profile_file.get() == nullptr) {
    if (clear_if_invalid && errno == ENOENT) {
      return true;
    }
    LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
    return false;
  }

  int fd = profile_file->Fd();

  ProfileLoadStatus status = LoadInternal(fd, &error);
  if (status == ProfileLoadStatus::kSuccess) {
    return true;
  }

  if (clear_if_invalid &&
      ((status == ProfileLoadStatus::kBadMagic) ||
       (status == ProfileLoadStatus::kVersionMismatch) ||
       (status == ProfileLoadStatus::kBadData))) {
    LOG(WARNING) << "Clearing bad or obsolete profile data from file "
                 << filename << ": " << error;
    // When ART Service is enabled, this is the only place where we mutate a profile in place.
    // TODO(jiakaiz): Get rid of this.
    if (profile_file->ClearContent()) {
      return true;
    } else {
      PLOG(WARNING) << "Could not clear profile file: " << filename;
      return false;
    }
  }

  LOG(WARNING) << "Could not load profile data from file " << filename << ": " << error;
  return false;
}

bool ProfileCompilationInfo::Save(const std::string& filename,
                                  uint64_t* bytes_written,
                                  bool flush) {
  ScopedTrace trace(__PRETTY_FUNCTION__);

#ifndef ART_TARGET_ANDROID
  return SaveFallback(filename, bytes_written, flush);
#else
  // Prior to U, SELinux policy doesn't allow apps to create profile files.
  // Additionally, when installd is being used for dexopt, it acquires a flock when working on a
  // profile. It's unclear to us whether the flock means that the file at the fd shouldn't change or
  // that the file at the path shouldn't change, especially when the installd code is modified by
  // partners. Therefore, we fall back to using a flock as well just to be safe.
  if (!android::modules::sdklevel::IsAtLeastU() ||
      !android::base::GetBoolProperty("dalvik.vm.useartservice"/*default_value=*/false)) {
    return SaveFallback(filename, bytes_written, flush);
  }

  std::string tmp_filename = filename + ".XXXXXX.tmp";
  // mkstemps creates the file with permissions 0600, which is the desired permissions, so there's
  // no need to chmod.
  android::base::unique_fd fd(mkostemps(tmp_filename.data(), /*suffixlen=*/4, O_CLOEXEC));
  if (fd.get() < 0) {
    PLOG(WARNING) << "Failed to create temp profile file for " << filename;
    return false;
  }

  // In case anything goes wrong.
  auto remove_tmp_file = android::base::make_scope_guard([&]() {
    if (unlink(tmp_filename.c_str()) != 0) {
      PLOG(WARNING) << "Failed to remove temp profile file " << tmp_filename;
    }
  });

  bool result = Save(fd.get(), flush);
  if (!result) {
    VLOG(profiler) << "Failed to save profile info to temp profile file " << tmp_filename;
    return false;
  }

  fd.reset();

  // Move the temp profile file to the final location.
  if (rename(tmp_filename.c_str(), filename.c_str()) != 0) {
    PLOG(WARNING) << "Failed to commit profile file " << filename;
    return false;
  }

  remove_tmp_file.Disable();

  if (flush) {
    std::string dirname = android::base::Dirname(filename);
    std::unique_ptr<File> dir(OS::OpenFileForReading(dirname.c_str()));
    if (dir == nullptr || dir->Flush(/*flush_metadata=*/true) != 0) {
      PLOG(WARNING) << "Failed to flush directory " << dirname;
    }
  }

  int64_t size = OS::GetFileSizeBytes(filename.c_str());
  if (size != -1) {
    VLOG(profiler) << "Successfully saved profile info to " << filename << " Size: " << size;
    if (bytes_written != nullptr) {
      *bytes_written = static_cast<uint64_t>(size);
    }
  } else {
    VLOG(profiler) << "Saved profile info to " << filename
                   << " but failed to get size: " << strerror(errno);
  }

  return true;
#endif
}

bool ProfileCompilationInfo::SaveFallback(const std::string& filename,
                                          uint64_t* bytes_written,
                                          bool flush) {
  std::string error;
#ifdef _WIN32
  int flags = O_WRONLY | O_CREAT;
#else
  int flags = O_WRONLY | O_NOFOLLOW | O_CLOEXEC | O_CREAT;
#endif
  // There's no need to fsync profile data right away. We get many chances
  // to write it again in case something goes wrong. We can rely on a simple
  // close(), no sync, and let to the kernel decide when to write to disk.
  ScopedFlock profile_file =
      LockedFile::Open(filename.c_str(), flags, /*block=*/false, &error);
  if (profile_file.get() == nullptr) {
    LOG(WARNING) << "Couldn't lock the profile file " << filename << ": " << error;
    return false;
  }

  int fd = profile_file->Fd();

  // We need to clear the data because we don't support appending to the profiles yet.
  if (!profile_file->ClearContent()) {
    PLOG(WARNING) << "Could not clear profile file: " << filename;
    return false;
  }

  // This doesn't need locking because we are trying to lock the file for exclusive
  // access and fail immediately if we can't.
  bool result = Save(fd, flush);

  if (flush) {
    std::string dirname = android::base::Dirname(filename);
    std::unique_ptr<File> dir(OS::OpenFileForReading(dirname.c_str()));
    if (dir == nullptr || dir->Flush(/*flush_metadata=*/true) != 0) {
      PLOG(WARNING) << "Failed to flush directory " << dirname;
    }
  }

  if (result) {
    int64_t size = OS::GetFileSizeBytes(filename.c_str());
    if (size != -1) {
      VLOG(profiler)
        << "Successfully saved profile info to " << filename << " Size: "
        << size;
      if (bytes_written != nullptr) {
        *bytes_written = static_cast<uint64_t>(size);
      }
    } else {
      VLOG(profiler) << "Saved profile info to " << filename
                     << " but failed to get size: " << strerror(errno);
    }
  } else {
    VLOG(profiler) << "Failed to save profile info to " << filename;
  }
  return result;
}

// Returns true if all the bytes were successfully written to the file descriptor.
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.
    reinterpret_cast<const uint8_t*&>(buffer) += bytes_written;  // Move the buffer forward.
  }
  return true;
}

/**
 * Serialization format:
 *
 * 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
 *   ClassesNoPreload - optional, zipped
 *   Methods - optional, zipped
 *   AggregationCounts - optional, zipped, server-side
 *
 * DexFiles:
 *    number_of_dex_files
 *    (checksum,num_type_ids,num_method_ids,profile_key)[number_of_dex_files]
 * where `profile_key` is a length-prefixed string, the length is `uint16_t`.
 *
 * ExtraDescriptors:
 *    number_of_extra_descriptors
 *    (extra_descriptor)[number_of_extra_descriptors]
 * where `extra_descriptor` is a length-prefixed string, the length is `uint16_t`.
 *
 * Classes contains records for any number of dex files, each consisting of:
 *    profile_index  // Index of the dex file in DexFiles section.
 *    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.
 *
 * ClassesNoPreload: the same format as 'Classes' section
 *
 * Methods contains records for any number of dex files, each consisting of:
 *    profile_index  // Index of the dex file in DexFiles section.
 *    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 `method_flags` is a union of flags recorded for methods in the referenced dex file,
 * `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, bool flush) {
  uint64_t start = NanoTime();
  ScopedTrace trace(__PRETTY_FUNCTION__);
  DCHECK_GE(fd, 0);

  // 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_) {
      // Length-prefixed string, the length is `uint16_t`.
      extra_descriptors_section_size += sizeof(uint16_t) + descriptor.size();
    }
  }
  uint64_t dex_files_section_size = sizeof(ProfileIndexType);  // Number of dex files.
  uint64_t classes_section_size = 0u;
  uint64_t classes_no_preload_section_size = 0u;
  uint64_t methods_section_size = 0u;
  DCHECK_LE(info_.size(), MaxProfileIndex());
  for (const std::unique_ptr<DexFileData>& dex_data : info_) {
    if (dex_data->profile_key.size() > kMaxDexFileKeyLength) {
      LOG(WARNING) << "DexFileKey exceeds allocated limit";
      return false;
    }
    dex_files_section_size +=
        3 * sizeof(uint32_t) +  // Checksum, num_type_ids, num_method_ids.
        // Length-prefixed string, the length is `uint16_t`.
        sizeof(uint16_t) + dex_data->profile_key.size();
    classes_section_size += dex_data->ClassesDataSize();
    classes_no_preload_section_size += dex_data->ClassesNoPreloadDataSize();
    methods_section_size += dex_data->MethodsDataSize();
  }

  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) +
      /* classes-no-preload */ (classes_no_preload_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);

  // 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 +
                                     classes_no_preload_section_size + methods_section_size;
  VLOG(profiler) << "Required capacity: " << total_uncompressed_size << " bytes.";
  if (total_uncompressed_size > GetSizeErrorThresholdBytes()) {
    LOG(WARNING) << "Profile data size exceeds "
                 << GetSizeErrorThresholdBytes()
                 << " bytes. Profile will not be written to disk."
                 << " It requires " << total_uncompressed_size << " bytes.";
    return false;
  }

  // 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;
  }

  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.WriteUintAndAdvance(dchecked_integral_cast<uint16_t>(dex_data->profile_key.size()));
      buffer.WriteAndAdvance(dex_data->profile_key.c_str(), dex_data->profile_key.size());
    }
    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.WriteUintAndAdvance(dchecked_integral_cast<uint16_t>(descriptor.size()));
      buffer.WriteAndAdvance(descriptor.c_str(), descriptor.size());
    }
    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, /*no_preload=*/false);
    }
    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 classes-no-preload section.
  if (classes_no_preload_section_size != 0u) {
    SafeBuffer buffer(classes_no_preload_section_size);
    for (const std::unique_ptr<DexFileData>& dex_data : info_) {
      dex_data->WriteClasses(buffer, /*no_preload=*/true);
    }
    if (!buffer.Deflate()) {
      return false;
    }
    if (!WriteBuffer(fd, buffer.Get(), buffer.Size())) {
      return false;
    }
    add_section_info(
        FileSectionType::kClassesNoPreload, buffer.Size(), classes_no_preload_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 " << file_offset << " bytes";
  }

  // Write section infos.
  if (lseek64(fd, sizeof(FileHeader), SEEK_SET) != sizeof(FileHeader)) {
    return false;
  }
  SafeBuffer section_infos_buffer(section_index * sizeof(FileSectionInfo));
  for (size_t i = 0; i != section_index; ++i) {
    const FileSectionInfo& info = section_infos[i];
    section_infos_buffer.WriteUintAndAdvance(enum_cast<uint32_t>(info.GetType()));
    section_infos_buffer.WriteUintAndAdvance(info.GetFileOffset());
    section_infos_buffer.WriteUintAndAdvance(info.GetFileSize());
    section_infos_buffer.WriteUintAndAdvance(info.GetInflatedSize());
  }
  DCHECK_EQ(section_infos_buffer.GetAvailableBytes(), 0u);
  if (!WriteBuffer(fd, section_infos_buffer.Get(), section_infos_buffer.Size())) {
    return false;
  }

  // Write header.
  FileHeader header(version_, section_index);
  if (lseek(fd, 0, SEEK_SET) != 0) {
    return false;
  }
  if (!WriteBuffer(fd, &header, sizeof(FileHeader))) {
    return false;
  }

  if (flush) {
    // We do not flush for non-Linux because `flush` is only used by the runtime and the runtime
    // only supports Linux.
#ifdef __linux__
    if (fsync(fd) != 0) {
      PLOG(WARNING) << "Failed to flush profile data";
    }
#endif
  }

  uint64_t total_time = NanoTime() - start;
  VLOG(profiler) << "Compressed from "
                 << std::to_string(total_uncompressed_size)
                 << " to "
                 << std::to_string(file_offset);
  VLOG(profiler) << "Time to save profile: " << std::to_string(total_time);
  return true;
}

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);
  if (profile_index_it == profile_key_map_.end() || profile_index_it->first != profile_key) {
    // We did not find the key. Create a new DexFileData if we did not reach the limit.
    DCHECK_LE(profile_key_map_.size(), MaxProfileIndex());
    if (profile_key_map_.size() == MaxProfileIndex()) {
      // 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).
      LOG(ERROR) << "Exceeded the maximum number of dex file. Something went wrong";
      return nullptr;
    }
    ProfileIndexType new_profile_index = dchecked_integral_cast<ProfileIndexType>(info_.size());
    std::unique_ptr<DexFileData> dex_file_data(new (&allocator_) DexFileData(
        &allocator_,
        profile_key,
        checksum,
        new_profile_index,
        num_type_ids,
        num_method_ids,
        IsForBootImage()));
    // Record the new data in `profile_key_map_` and `info_`.
    std::string_view new_key(dex_file_data->profile_key);
    profile_index_it = profile_key_map_.PutBefore(profile_index_it, new_key, new_profile_index);
    info_.push_back(std::move(dex_file_data));
    DCHECK_EQ(profile_key_map_.size(), info_.size());
  }

  ProfileIndexType profile_index = profile_index_it->second;
  DexFileData* result = info_[profile_index].get();

  // Check that the checksum matches.
  // This may different if for example the dex file was updated and we had a record of the old one.
  if (result->checksum != checksum) {
    LOG(WARNING) << "Checksum mismatch for dex " << profile_key;
    return nullptr;
  }

  // DCHECK that profile info map key is consistent with the one stored in the dex file data.
  // This should always be the case since since the cache map is managed by ProfileCompilationInfo.
  DCHECK_EQ(profile_key, result->profile_key);
  DCHECK_EQ(profile_index, result->profile_index);

  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_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;
  }

  return result;
}

const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexData(
      const std::string& profile_key,
      uint32_t checksum,
      bool verify_checksum) const {
  const auto profile_index_it = profile_key_map_.find(profile_key);
  if (profile_index_it == profile_key_map_.end()) {
    return nullptr;
  }

  ProfileIndexType profile_index = profile_index_it->second;
  const DexFileData* result = info_[profile_index].get();
  if (verify_checksum && !ChecksumMatch(result->checksum, checksum)) {
    return nullptr;
  }
  DCHECK_EQ(profile_key, result->profile_key);
  DCHECK_EQ(profile_index, result->profile_index);
  return result;
}

const ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::FindDexDataUsingAnnotations(
      const DexFile* dex_file,
      const ProfileSampleAnnotation& annotation) const {
  if (annotation == ProfileSampleAnnotation::kNone) {
    std::string profile_key = GetProfileDexFileBaseKey(dex_file);
    for (const std::unique_ptr<DexFileData>& dex_data : info_) {
      if (profile_key == GetBaseKeyViewFromAugmentedKey(dex_data->profile_key)) {
        if (!ChecksumMatch(dex_data->checksum, dex_file->GetLocationChecksum())) {
          return nullptr;
        }
        return dex_data.get();
      }
    }
  } else {
    std::string profile_key = GetProfileDexFileAugmentedKey(dex_file, annotation);
    return FindDexData(profile_key, dex_file->GetLocationChecksum());
  }

  return nullptr;
}

void ProfileCompilationInfo::FindAllDexData(
    const DexFile* dex_file,
    /*out*/ std::vector<const ProfileCompilationInfo::DexFileData*>* result) const {
  std::string profile_key = GetProfileDexFileBaseKey(dex_file);
  for (const std::unique_ptr<DexFileData>& dex_data : info_) {
    if (profile_key == GetBaseKeyViewFromAugmentedKey(dex_data->profile_key)) {
      if (ChecksumMatch(dex_data->checksum, dex_file->GetLocationChecksum())) {
        result->push_back(dex_data.get());
      }
    }
  }
}

ProfileCompilationInfo::ExtraDescriptorIndex ProfileCompilationInfo::AddExtraDescriptor(
    std::string_view extra_descriptor) {
  DCHECK_LE(extra_descriptor.size(), kMaxExtraDescriptorLength);
  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,
                                       bool is_test) {
  DexFileData* const data = GetOrAddDexFileData(pmi.ref.dex_file, annotation);
  if (data == nullptr) {  // checksum mismatch
    return false;
  }
  if (!data->AddMethod(flags, pmi.ref.index)) {
    return false;
  }
  if ((flags & MethodHotness::kFlagHot) == 0) {
    // The method is not hot, do not add inline caches.
    return true;
  }

  // Add inline caches.
  InlineCacheMap* inline_cache = data->FindOrAddHotMethod(pmi.ref.index);
  DCHECK(inline_cache != nullptr);

  const dex::MethodId& mid = pmi.ref.GetMethodId();
  const DexFile& dex_file = *pmi.ref.dex_file;
  const dex::ClassDef* class_def = dex_file.FindClassDef(mid.class_idx_);
  // If `is_test` is true, we don't try to look at whether dex_pc fit in the
  // code item of that method.
  uint32_t dex_pc_max = 0u;
  if (is_test) {
    dex_pc_max = std::numeric_limits<uint32_t>::max();
  } else {
    if (class_def == nullptr || dex_file.GetClassData(*class_def) == nullptr) {
      return true;
    }
    std::optional<uint32_t> offset = dex_file.GetCodeItemOffset(*class_def, pmi.ref.index);
    if (!offset.has_value()) {
      return true;
    }
    CodeItemInstructionAccessor accessor(dex_file, dex_file.GetCodeItem(offset.value()));
    dex_pc_max = accessor.InsnsSizeInCodeUnits();
  }

  for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) {
    if (cache.dex_pc >= std::numeric_limits<uint16_t>::max()) {
      // Discard entries that don't fit the encoding. This should only apply to
      // inlined inline caches. See also `HInliner::GetInlineCacheAOT`.
      continue;
    }
    if (cache.dex_pc >= dex_pc_max) {
      // Discard entries for inlined inline caches. We don't support them in
      // profiles yet.
      continue;
    }
    if (cache.is_missing_types) {
      FindOrAddDexPc(inline_cache, cache.dex_pc)->SetIsMissingTypes();
      continue;
    }
    if  (cache.is_megamorphic) {
      FindOrAddDexPc(inline_cache, cache.dex_pc)->SetIsMegamorphic();
      continue;
    }
    for (const TypeReference& class_ref : cache.classes) {
      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::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;
}

// TODO(calin): Fix this API. ProfileCompilationInfo::Load should be static and
// return a unique pointer to a ProfileCompilationInfo upon success.
bool ProfileCompilationInfo::Load(
    int fd, bool merge_classes, const ProfileLoadFilterFn& filter_fn) {
  std::string error;

  ProfileLoadStatus status = LoadInternal(fd, &error, merge_classes, filter_fn);

  if (status == ProfileLoadStatus::kSuccess) {
    return true;
  } else {
    LOG(WARNING) << "Error when reading profile: " << error;
    return false;
  }
}

bool ProfileCompilationInfo::VerifyProfileData(const std::vector<const DexFile*>& dex_files) {
  std::unordered_map<std::string, const DexFile*> key_to_dex_file;
  for (const DexFile* dex_file : dex_files) {
    key_to_dex_file.emplace(GetProfileDexFileBaseKey(dex_file), dex_file);
  }
  for (const std::unique_ptr<DexFileData>& dex_data : info_) {
    // We need to remove any annotation from the key during verification.
    std::string base_key(GetBaseKeyViewFromAugmentedKey(dex_data->profile_key));
    const auto it = key_to_dex_file.find(base_key);
    if (it == key_to_dex_file.end()) {
      // It is okay if profile contains data for additional dex files.
      continue;
    }
    const DexFile* dex_file = it->second;
    const std::string& dex_location = dex_file->GetLocation();
    if (!ChecksumMatch(dex_data->checksum, dex_file->GetLocationChecksum())) {
      LOG(ERROR) << "Dex checksum mismatch while verifying profile "
                 << "dex location " << dex_location << " (checksum="
                 << dex_file->GetLocationChecksum() << ", profile checksum="
                 << dex_data->checksum;
      return false;
    }

    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;
    }

    // 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.
            CHECK(dex_pc_data.classes.empty());
            continue;
          }

          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 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());
        }
      }
    }
  }
  return true;
}

ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::OpenSource(
    int32_t fd,
    /*out*/ std::unique_ptr<ProfileSource>* source,
    /*out*/ std::string* error) {
  if (IsProfileFile(fd)) {
    source->reset(ProfileSource::Create(fd));
    return ProfileLoadStatus::kSuccess;
  } else {
    std::unique_ptr<ZipArchive> zip_archive(
        ZipArchive::OpenFromFd(DupCloexec(fd), "profile", error));
    if (zip_archive.get() == nullptr) {
      *error = "Could not open the profile zip archive";
      return ProfileLoadStatus::kBadData;
    }
    std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(kDexMetadataProfileEntry, error));
    if (zip_entry == nullptr) {
      // Allow archives without the profile entry. In this case, create an empty profile.
      // This gives more flexible when ure-using archives that may miss the entry.
      // (e.g. dex metadata files)
      LOG(WARNING) << "Could not find entry " << kDexMetadataProfileEntry
          << " in the zip archive. Creating an empty profile.";
      source->reset(ProfileSource::Create(MemMap::Invalid()));
      return ProfileLoadStatus::kSuccess;
    }
    if (zip_entry->GetUncompressedLength() == 0) {
      *error = "Empty profile entry in the zip archive.";
      return ProfileLoadStatus::kBadData;
    }

    // TODO(calin) pass along file names to assist with debugging.
    MemMap map = zip_entry->MapDirectlyOrExtract(
        kDexMetadataProfileEntry, "profile file", error, alignof(ProfileSource));

    if (map.IsValid()) {
      source->reset(ProfileSource::Create(std::move(map)));
      return ProfileLoadStatus::kSuccess;
    } else {
      return ProfileLoadStatus::kBadData;
    }
  }
}

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(
    void* buffer,
    size_t byte_count,
    const std::string& debug_stage,
    std::string* error) {
  if (IsMemMap()) {
    DCHECK_LE(mem_map_cur_, mem_map_.Size());
    if (byte_count > mem_map_.Size() - mem_map_cur_) {
      *error += "Profile EOF reached prematurely for " + debug_stage;
      return ProfileLoadStatus::kBadData;
    }
    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));;
      if (bytes_read == 0) {
        *error += "Profile EOF reached prematurely for " + debug_stage;
        return ProfileLoadStatus::kBadData;
      } else if (bytes_read < 0) {
        *error += "Profile IO error for " + debug_stage + strerror(errno);
        return ProfileLoadStatus::kIOError;
      }
      byte_count -= bytes_read;
      reinterpret_cast<uint8_t*&>(buffer) += bytes_read;
    }
  }
  return ProfileLoadStatus::kSuccess;
}


bool ProfileCompilationInfo::ProfileSource::HasEmptyContent() const {
  if (IsMemMap()) {
    return !mem_map_.IsValid() || mem_map_.Size() == 0;
  } else {
    struct stat stat_buffer;
    if (fstat(fd_, &stat_buffer) != 0) {
      return false;
    }
    return stat_buffer.st_size == 0;
  }
}

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 += "Error reading profile key string.";
      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) {
    std::string_view extra_descriptor;
    if (!buffer.ReadStringAndAdvance(&extra_descriptor)) {
      *error += "Error reading extra descriptor string.";
      return ProfileLoadStatus::kBadData;
    }
    if (!IsValidDescriptor(std::string(extra_descriptor).c_str())) {
      *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) {
  bool is_no_preload_section = (section_info.GetType() == FileSectionType::kClassesNoPreload);
  DCHECK(section_info.GetType() == FileSectionType::kClasses || is_no_preload_section);
  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 = is_no_preload_section ? "Error profile index in classes-no-preload section."
                                     : "Error profile index in classes section.";
      return ProfileLoadStatus::kBadData;
    }
    if (profile_index >= dex_profile_index_remap.size()) {
      *error = is_no_preload_section ? "Invalid profile index in classes-no-preload section."
                                     : "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, is_no_preload_section);
    }
    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) {
  ScopedTrace trace(__PRETTY_FUNCTION__);
  DCHECK_GE(fd, 0);

  std::unique_ptr<ProfileSource> source;
  ProfileLoadStatus status = OpenSource(fd, &source, error);
  if (status != ProfileLoadStatus::kSuccess) {
    return status;
  }

  // We allow empty profile files.
  // Profiles may be created by ActivityManager or installd before we manage to
  // process them in the runtime or profman.
  if (source->HasEmptyContent()) {
    return ProfileLoadStatus::kSuccess;
  }

  // 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::kVersionMismatch;
  }

  // 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(WARNING) << "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()) {
    LOG(WARNING) << "Profile data size exceeds "
               << GetSizeErrorThresholdBytes()
               << " bytes. It has " << uncompressed_data_size << " bytes.";
    return ProfileLoadStatus::kBadData;
  }
  if (uncompressed_data_size > GetSizeWarningThresholdBytes()) {
    LOG(WARNING) << "Profile data size exceeds "
                 << GetSizeWarningThresholdBytes()
                 << " bytes. It has " << uncompressed_data_size << " bytes.";
  }

  // 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) {
    DCHECK(!error->empty());
    return status;
  }

  // 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::kClassesNoPreload:
        has_no_preload_section = true;
        FALLTHROUGH_INTENDED;
      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;
      case FileSectionType::kAggregationCounts:
        // This section is only used on server side.
        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;
    }
  }

  return ProfileLoadStatus::kSuccess;
}

bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other,
                                       bool merge_classes) {
  if (!SameVersion(other)) {
    LOG(WARNING) << "Cannot merge different profile versions";
    return false;
  }

  // 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 std::unique_ptr<DexFileData>& other_dex_data : other.info_) {
    // verify_checksum is false because we want to differentiate between a missing dex data and
    // a mismatched checksum.
    const DexFileData* dex_data = FindDexData(other_dex_data->profile_key,
                                              /* checksum= */ 0u,
                                              /* verify_checksum= */ false);
    if ((dex_data != nullptr) && (dex_data->checksum != other_dex_data->checksum)) {
      LOG(WARNING) << "Checksum mismatch for dex " << other_dex_data->profile_key;
      return false;
    }
  }
  // All checksums match. Import the data.

  // The other profile might have a different indexing of dex files.
  // That is because each dex files gets a 'dex_profile_index' on a first come first served basis.
  // That means that the order in with the methods are added to the profile matters for the
  // actual indices.
  // The reason we cannot rely on the actual multidex index is that a single profile may store
  // data from multiple splits. This means that a profile may contain a classes2.dex from split-A
  // and one from split-B.

  // First, build a mapping from other_dex_profile_index to this_dex_profile_index.
  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) {
      // 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;
    }
    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 = 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) {
      // 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.
    for (const auto& other_method_it : other_dex_data->method_map) {
      uint16_t other_method_index = other_method_it.first;
      InlineCacheMap* inline_cache = dex_data->FindOrAddHotMethod(other_method_index);
      if (inline_cache == nullptr) {
        return false;
      }
      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 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 (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);
          }
        }
      }
    }

    // Merge the method bitmaps.
    dex_data->MergeBitmap(*other_dex_data);
  }

  return true;
}

ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::GetMethodHotness(
    const MethodReference& method_ref,
    const ProfileSampleAnnotation& annotation) const {
  const DexFileData* dex_data = FindDexDataUsingAnnotations(method_ref.dex_file, annotation);
  return dex_data != nullptr
      ? dex_data->GetHotnessInfo(method_ref.index)
      : MethodHotness();
}

bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file,
                                           dex::TypeIndex type_idx,
                                           const ProfileSampleAnnotation& annotation) const {
  const DexFileData* dex_data = FindDexDataUsingAnnotations(&dex_file, annotation);
  return (dex_data != nullptr) && dex_data->ContainsClass(type_idx);
}

uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
  uint32_t total = 0;
  for (const std::unique_ptr<DexFileData>& dex_data : info_) {
    total += dex_data->method_map.size();
  }
  return total;
}

uint32_t ProfileCompilationInfo::GetNumberOfResolvedClasses() const {
  uint32_t total = 0;
  for (const std::unique_ptr<DexFileData>& dex_data : info_) {
    total += dex_data->class_set.size();
  }
  return total;
}

std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>& dex_files,
                                             bool print_full_dex_location) const {
  std::ostringstream os;

  os << "ProfileInfo [";

  for (size_t k = 0; k <  kProfileVersionSize - 1; k++) {
    // Iterate to 'kProfileVersionSize - 1' because the version_ ends with '\0'
    // which we don't want to print.
    os << static_cast<char>(version_[k]);
  }
  os << "]\n";

  if (info_.empty()) {
    os << "-empty-";
    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_) {
    os << "\n";
    if (print_full_dex_location) {
      os << dex_data->profile_key;
    } else {
      // Replace the (empty) multidex suffix of the first key with a substitute for easier reading.
      std::string base_key = GetBaseKeyFromAugmentedKey(dex_data->profile_key);
      auto [filename, index] = DexFileLoader::SplitMultiDexLocation(base_key);
      if (filename.size() == base_key.size()) {
        os << kFirstDexFileKeySubstitute;
      } else {
        os << base_key.substr(filename.size());
      }
    }
    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) ==
              GetProfileDexFileBaseKey(current) &&
          ChecksumMatch(dex_data->checksum, current->GetLocationChecksum())) {
        dex_file = current;
        break;
      }
    }
    os << "\n\thot methods: ";
    for (const auto& method_it : dex_data->method_map) {
      if (dex_file != nullptr) {
        os << "\n\t\t" << dex_file->PrettyMethod(method_it.first, true);
      } else {
        os << method_it.first;
      }

      os << "[";
      for (const auto& inline_cache_it : method_it.second) {
        os << "{" << std::hex << inline_cache_it.first << std::dec << ":";
        if (inline_cache_it.second.is_missing_types) {
          os << "MT";
        } else if (inline_cache_it.second.is_megamorphic) {
          os << "MM";
        } else {
          const char* separator = "";
          for (dex::TypeIndex type_index : inline_cache_it.second.classes) {
            os << separator << type_index.index_;
            separator = ",";
          }
        }
        os << "}";
      }
      os << "], ";
    }
    bool startup = true;
    while (true) {
      os << "\n\t" << (startup ? "startup methods: " : "post startup methods: ");
      for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
        MethodHotness hotness_info(dex_data->GetHotnessInfo(method_idx));
        if (startup ? hotness_info.IsStartup() : hotness_info.IsPostStartup()) {
          if (dex_file != nullptr) {
            os << "\n\t\t" << dex_file->PrettyMethod(method_idx, true);
          } else {
            os << method_idx << ", ";
          }
        }
      }
      if (startup == false) {
        break;
      }
      startup = false;
    }
    os << "\n\tclasses: ";
    for (dex::TypeIndex type_index : dex_data->class_set) {
      if (dex_file != nullptr) {
        os << "\n\t\t" << PrettyDescriptor(GetTypeDescriptor(dex_file, type_index));
      } else {
        os << type_index.index_ << ",";
      }
    }
    os << "\n\tclasses-no-preload: ";
    for (dex::TypeIndex type_index : dex_data->class_set_no_preload) {
      if (dex_file != nullptr) {
        os << "\n\t\t" << PrettyDescriptor(GetTypeDescriptor(dex_file, type_index));
      } else {
        os << type_index.index_ << ",";
      }
    }
  }
  return os.str();
}

bool ProfileCompilationInfo::GetClassesAndMethods(
    const DexFile& dex_file,
    /*out*/std::set<dex::TypeIndex>* class_set,
    /*out*/std::set<uint16_t>* hot_method_set,
    /*out*/std::set<uint16_t>* startup_method_set,
    /*out*/std::set<uint16_t>* post_startup_method_method_set,
    const ProfileSampleAnnotation& annotation) const {
  std::set<std::string> ret;
  const DexFileData* dex_data = FindDexDataUsingAnnotations(&dex_file, annotation);
  if (dex_data == nullptr) {
    return false;
  }
  for (const auto& it : dex_data->method_map) {
    hot_method_set->insert(it.first);
  }
  for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
    MethodHotness hotness = dex_data->GetHotnessInfo(method_idx);
    if (hotness.IsStartup()) {
      startup_method_set->insert(method_idx);
    }
    if (hotness.IsPostStartup()) {
      post_startup_method_method_set->insert(method_idx);
    }
  }
  for (const dex::TypeIndex& type_index : dex_data->class_set) {
    class_set->insert(type_index);
  }
  return true;
}

const ArenaSet<dex::TypeIndex>* ProfileCompilationInfo::GetClasses(
    const DexFile& dex_file,
    const ProfileSampleAnnotation& annotation) const {
  const DexFileData* dex_data = FindDexDataUsingAnnotations(&dex_file, annotation);
  if (dex_data == nullptr) {
    return nullptr;
  }
  return &dex_data->class_set;
}

const ArenaSet<dex::TypeIndex>* ProfileCompilationInfo::GetClassesNoPreload(
    const DexFile& dex_file) const {
  const DexFileData* dex_data =
      FindDexDataUsingAnnotations(&dex_file, ProfileSampleAnnotation::kNone);
  if (dex_data == nullptr) {
    return nullptr;
  }
  return &dex_data->class_set_no_preload;
}

bool ProfileCompilationInfo::SameVersion(const ProfileCompilationInfo& other) const {
  return memcmp(version_, other.version_, kProfileVersionSize) == 0;
}

bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
  // No need to compare profile_key_map_. That's only a cache for fast search.
  // All the information is already in the info_ vector.
  if (!SameVersion(other)) {
    return false;
  }
  if (info_.size() != other.info_.size()) {
    return false;
  }
  for (size_t i = 0; i < info_.size(); i++) {
    const DexFileData& dex_data = *info_[i];
    const DexFileData& other_dex_data = *other.info_[i];
    if (!(dex_data == other_dex_data)) {
      return false;
    }
  }

  return true;
}

// Naive implementation to generate a random profile file suitable for testing.
bool ProfileCompilationInfo::GenerateTestProfile(int fd,
                                                 uint16_t number_of_dex_files,
                                                 uint16_t method_percentage,
                                                 uint16_t class_percentage,
                                                 uint32_t random_seed) {
  const std::string base_dex_location = "base.apk";
  ProfileCompilationInfo info;
  // The limits are defined by the dex specification.
  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_methods * method_percentage / 100;
  uint16_t number_of_classes = max_classes * class_percentage / 100;

  std::srand(random_seed);

  // Make sure we generate more samples with a low index value.
  // This makes it more likely to hit valid method/class indices in small apps.
  const uint16_t kFavorFirstN = 10000;
  const uint16_t kFavorSplit = 2;

  for (uint16_t i = 0; i < number_of_dex_files; i++) {
    std::string entry_name = DexFileLoader::GetMultiDexZipEntryName(i);
    std::string profile_key = info.GetProfileDexFileBaseKey(base_dex_location, entry_name);

    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_methods;
      if (m < (number_of_methods / kFavorSplit)) {
        method_idx %= kFavorFirstN;
      }
      // Alternate between startup and post startup.
      uint32_t flags = MethodHotness::kFlagHot;
      flags |= ((m & 1) != 0) ? MethodHotness::kFlagPostStartup : MethodHotness::kFlagStartup;
      data->AddMethod(static_cast<MethodHotness::Flag>(flags), method_idx);
    }

    for (uint16_t c = 0; c < number_of_classes; c++) {
      uint16_t type_idx = rand() % max_classes;
      if (c < (number_of_classes / kFavorSplit)) {
        type_idx %= kFavorFirstN;
      }
      data->class_set.insert(dex::TypeIndex(type_idx));
    }
  }
  return info.Save(fd);
}

// Naive implementation to generate a random profile file suitable for testing.
// Description of random selection:
// * Select a random starting point S.
// * For every index i, add (S+i) % (N - total number of methods/classes) to profile with the
//   probably of 1/(N - i - number of methods/classes needed to add in profile).
bool ProfileCompilationInfo::GenerateTestProfile(
    int fd,
    std::vector<std::unique_ptr<const DexFile>>& dex_files,
    uint16_t method_percentage,
    uint16_t class_percentage,
    uint32_t random_seed) {
  ProfileCompilationInfo info;
  std::default_random_engine rng(random_seed);
  auto create_shuffled_range = [&rng](uint32_t take, uint32_t out_of) {
    CHECK_LE(take, out_of);
    std::vector<uint32_t> vec(out_of);
    std::iota(vec.begin(), vec.end(), 0u);
    std::shuffle(vec.begin(), vec.end(), rng);
    vec.erase(vec.begin() + take, vec.end());
    std::sort(vec.begin(), vec.end());
    return vec;
  };
  for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
    std::string profile_key = info.GetProfileDexFileBaseKey(dex_file.get());
    uint32_t checksum = dex_file->GetLocationChecksum();

    uint32_t number_of_classes = dex_file->NumClassDefs();
    uint32_t classes_required_in_profile = (number_of_classes * class_percentage) / 100;

    DexFileData* const data = info.GetOrAddDexFileData(
        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_);
    }

    uint32_t number_of_methods = dex_file->NumMethodIds();
    uint32_t methods_required_in_profile = (number_of_methods * method_percentage) / 100;
    for (uint32_t method_index : create_shuffled_range(methods_required_in_profile,
                                                       number_of_methods)) {
      // Alternate between startup and post startup.
      uint32_t flags = MethodHotness::kFlagHot;
      flags |= ((method_index & 1) != 0)
                   ? MethodHotness::kFlagPostStartup
                   : MethodHotness::kFlagStartup;
      data->AddMethod(static_cast<MethodHotness::Flag>(flags), method_index);
    }
  }
  return info.Save(fd);
}

bool ProfileCompilationInfo::IsEmpty() const {
  DCHECK_EQ(info_.size(), profile_key_map_.size());
  // Note that this doesn't look at the bitmap region, so we will return true
  // when the profile contains only non-hot methods. This is generally ok
  // as for speed-profile to be useful we do need hot methods and resolved classes.
  return GetNumberOfMethods() == 0 && GetNumberOfResolvedClasses() == 0;
}

ProfileCompilationInfo::InlineCacheMap*
ProfileCompilationInfo::DexFileData::FindOrAddHotMethod(uint16_t method_index) {
  if (method_index >= num_method_ids) {
    LOG(ERROR) << "Invalid method index " << method_index << ". num_method_ids=" << num_method_ids;
    return nullptr;
  }
  return &(method_map.FindOrAdd(
      method_index,
      InlineCacheMap(std::less<uint16_t>(), allocator_->Adapter(kArenaAllocProfile)))->second);
}

// Mark a method as executed at least once.
bool ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, size_t index) {
  if (index >= num_method_ids || index > kMaxSupportedMethodIndex) {
    LOG(ERROR) << "Invalid method index " << index << ". num_method_ids=" << num_method_ids
        << ", max: " << kMaxSupportedMethodIndex;
    return false;
  }

  SetMethodHotness(index, flags);

  if ((flags & MethodHotness::kFlagHot) != 0) {
    ProfileCompilationInfo::InlineCacheMap* result = FindOrAddHotMethod(index);
    DCHECK(result != nullptr);
  }
  return true;
}

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);
    }
    return true;
  });
}

ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::DexFileData::GetHotnessInfo(
    uint32_t dex_method_index) const {
  MethodHotness ret;
  ForMethodBitmapHotnessFlags([&](MethodHotness::Flag flag) {
    if (method_bitmap.LoadBit(MethodFlagBitmapIndex(
            static_cast<MethodHotness::Flag>(flag), dex_method_index))) {
      ret.AddFlag(static_cast<MethodHotness::Flag>(flag));
    }
    return true;
  });
  auto it = method_map.find(dex_method_index);
  if (it != method_map.end()) {
    ret.SetInlineCacheMap(&it->second);
    ret.AddFlag(MethodHotness::kFlagHot);
  }
  return ret;
}

// To simplify the implementation we use the MethodHotness flag values as indexes into the internal
// bitmap representation. As such, they should never change unless the profile version is updated
// and the implementation changed accordingly.
static_assert(ProfileCompilationInfo::MethodHotness::kFlagFirst == 1 << 0);
static_assert(ProfileCompilationInfo::MethodHotness::kFlagHot == 1 << 0);
static_assert(ProfileCompilationInfo::MethodHotness::kFlagStartup == 1 << 1);
static_assert(ProfileCompilationInfo::MethodHotness::kFlagPostStartup == 1 << 2);
static_assert(ProfileCompilationInfo::MethodHotness::kFlagLastRegular == 1 << 2);
static_assert(ProfileCompilationInfo::MethodHotness::kFlag32bit == 1 << 3);
static_assert(ProfileCompilationInfo::MethodHotness::kFlag64bit == 1 << 4);
static_assert(ProfileCompilationInfo::MethodHotness::kFlagSensitiveThread == 1 << 5);
static_assert(ProfileCompilationInfo::MethodHotness::kFlagAmStartup == 1 << 6);
static_assert(ProfileCompilationInfo::MethodHotness::kFlagAmPostStartup == 1 << 7);
static_assert(ProfileCompilationInfo::MethodHotness::kFlagBoot == 1 << 8);
static_assert(ProfileCompilationInfo::MethodHotness::kFlagPostBoot == 1 << 9);
static_assert(ProfileCompilationInfo::MethodHotness::kFlagStartupBin == 1 << 10);
static_assert(ProfileCompilationInfo::MethodHotness::kFlagStartupMaxBin == 1 << 15);
static_assert(ProfileCompilationInfo::MethodHotness::kFlagLastBoot == 1 << 15);

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 true;
  });
  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(inline_cache->get_allocator()))->second);
}

bool ProfileCompilationInfo::IsProfileFile(int fd) {
  // First check if it's an empty file as we allow empty profile files.
  // Profiles may be created by ActivityManager or installd before we manage to
  // process them in the runtime or profman.
  struct stat stat_buffer;
  if (fstat(fd, &stat_buffer) != 0) {
    return false;
  }

  if (stat_buffer.st_size == 0) {
    return true;
  }

  // The files is not empty. Check if it contains the profile magic.
  size_t byte_count = sizeof(kProfileMagic);
  uint8_t buffer[sizeof(kProfileMagic)];
  if (!android::base::ReadFullyAtOffset(fd, buffer, byte_count, /*offset=*/ 0)) {
    return false;
  }

  // Reset the offset to prepare the file for reading.
  off_t rc =  TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET));
  if (rc == static_cast<off_t>(-1)) {
    PLOG(ERROR) << "Failed to reset the offset";
    return false;
  }

  return memcmp(buffer, kProfileMagic, byte_count) == 0;
}

bool ProfileCompilationInfo::UpdateProfileKeys(
    const std::vector<std::unique_ptr<const DexFile>>& dex_files, /*out*/ bool* matched) {
  // This check aligns with when dex2oat falls back from "speed-profile" to "verify".
  //
  // ART Service relies on the exit code of profman, which is determined by the value of `matched`,
  // to judge whether it should re-dexopt for "speed-profile". Therefore, a misalignment will cause
  // repeated dexopt.
  if (IsEmpty()) {
    *matched = false;
    return true;
  }
  DCHECK(!info_.empty());

  *matched = true;

  // A map from the old base key to the new base key.
  std::unordered_map<std::string, std::string> old_key_to_new_key;

  // A map from the new base key to all matching old base keys (an invert of the map above), for
  // detecting duplicate keys.
  std::unordered_map<std::string, std::unordered_set<std::string>> new_key_to_old_keys;

  for (const std::unique_ptr<DexFileData>& dex_data : info_) {
    std::string old_base_key = GetBaseKeyFromAugmentedKey(dex_data->profile_key);
    bool found = false;
    for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
      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_base_key = GetProfileDexFileBaseKey(dex_file.get());
        if (dex_file->GetHeader().HasDexContainer()) {
          // DEX v41 introduces dex containers, which store multipe dex files per zip entry.
          // This means the ZIP CRC32 alone isn't unique, so also check the location suffix.
          if (DexFileLoader::SplitMultiDexLocation(old_base_key).second !=
              DexFileLoader::SplitMultiDexLocation(new_base_key).second) {
            continue;
          }
        }
        old_key_to_new_key[old_base_key] = new_base_key;
        new_key_to_old_keys[new_base_key].insert(old_base_key);
        found = true;
        break;
      }
    }
    if (!found) {
      *matched = false;
      // Keep the old key.
      old_key_to_new_key[old_base_key] = old_base_key;
      new_key_to_old_keys[old_base_key].insert(old_base_key);
    }
  }

  for (const auto& [new_key, old_keys] : new_key_to_old_keys) {
    if (old_keys.size() > 1) {
      LOG(ERROR) << "Cannot update multiple profile keys [" << android::base::Join(old_keys, ", ")
                 << "] to the same new key '" << new_key << "'";
      return false;
    }
  }

  // Check passed. Now perform the actual mutation.
  profile_key_map_.clear();

  for (const std::unique_ptr<DexFileData>& dex_data : info_) {
    std::string old_base_key = GetBaseKeyFromAugmentedKey(dex_data->profile_key);
    const std::string& new_base_key = old_key_to_new_key[old_base_key];
    DCHECK(!new_base_key.empty());
    // Retain the annotation (if any) during the renaming by re-attaching the info from the old key.
    dex_data->profile_key = MigrateAnnotationInfo(new_base_key, dex_data->profile_key);
    profile_key_map_.Put(dex_data->profile_key, dex_data->profile_index);
  }

  return true;
}

bool ProfileCompilationInfo::ProfileFilterFnAcceptAll(
    [[maybe_unused]] const std::string& dex_location, [[maybe_unused]] uint32_t checksum) {
  return true;
}

void ProfileCompilationInfo::ClearData() {
  profile_key_map_.clear();
  info_.clear();
  extra_descriptors_indexes_.clear();
  extra_descriptors_.clear();
}

void ProfileCompilationInfo::ClearDataAndAdjustVersion(bool for_boot_image) {
  ClearData();
  memcpy(version_,
         for_boot_image ? kProfileVersionForBootImage : kProfileVersion,
         kProfileVersionSize);
}

bool ProfileCompilationInfo::IsForBootImage() const {
  return memcmp(version_, kProfileVersionForBootImage, sizeof(kProfileVersionForBootImage)) == 0;
}

const uint8_t* ProfileCompilationInfo::GetVersion() const {
  return version_;
}

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.
}

uint32_t ProfileCompilationInfo::DexFileData::ClassesNoPreloadDataSize() const {
  return class_set_no_preload.empty()
             ? 0u
             : sizeof(ProfileIndexType) +                           // Which dex file.
                   sizeof(uint16_t) +                               // Number of no-preload classes.
                   sizeof(uint16_t) * class_set_no_preload.size();  // Type index diffs.
}

void ProfileCompilationInfo::DexFileData::WriteClasses(SafeBuffer& buffer,
                                                       bool is_no_preload_classes_section) const {
  const ArenaSet<dex::TypeIndex>& classes =
      is_no_preload_classes_section ? class_set_no_preload : class_set;
  if (classes.empty()) {
    return;
  }
  buffer.WriteUintAndAdvance(profile_index);
  buffer.WriteUintAndAdvance(dchecked_integral_cast<uint16_t>(classes.size()));
  WriteClassSet(buffer, classes);
}

ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::DexFileData::ReadClasses(
    SafeBuffer& buffer,
    const dchecked_vector<ExtraDescriptorIndex>& extra_descriptors_remap,
    std::string* error,
    bool no_preload_section) {
  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 (no_preload_section && classes_size == 1 &&
        dex::TypeIndex(type_index_diff) == kNoPreloadMarker) {  // first diff is the index itself
      // It's the no-preload marker: a phony type ID to make dex2oat rely on "classes-no-preload"
      // section instead of the preloaded-classes file. Just skip it.
    } else {
      if (type_index_diff >= num_valid_type_indexes - type_index) {
        *error = "Invalid type index.";
        return ProfileLoadStatus::kBadData;
      }
      type_index += type_index_diff;
      ArenaSet<dex::TypeIndex>& classes = no_preload_section ? class_set_no_preload : class_set;
      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;
        }
        classes.insert(dex::TypeIndex(num_type_ids + new_extra_descriptor_index));
      } else {
        classes.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::CountStartupClasses() const {
  return class_set.size();
}

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.Subregion(saved_bitmap_index * num_method_ids, num_method_ids).CopyBits(src);
      ++saved_bitmap_index;
    }
    return true;
  });
  DCHECK_EQ(saved_bitmap_index * num_method_ids, saved_bitmap_bit_size);
  // Clear the padding bits.
  size_t padding_bit_size = saved_bitmap_byte_size * kBitsPerByte - saved_bitmap_bit_size;
  BitMemoryRegion padding_region(buffer.GetCurrentPtr(), saved_bitmap_bit_size, padding_bit_size);
  padding_region.StoreBits(/*bit_offset=*/ 0u, /*value=*/ 0u, /*bit_length=*/ padding_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.Subregion(index * num_method_ids, num_method_ids).OrBits(src);
      ++saved_bitmap_index;
    }
    return true;
  });
  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;
              }
              dex_pc_data->AddClass(dex::TypeIndex(num_type_ids + new_extra_descriptor_index));
            } else {
              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;
}

uint32_t ProfileCompilationInfo::DexFileData::CountStartupMethods() const {
  uint32_t num_startup_methods = 0;
  for (uint32_t method_idx = 0; method_idx < num_method_ids; ++method_idx) {
    if (GetHotnessInfo(method_idx).IsStartup()) {
      ++num_startup_methods;
    }
  }
  return num_startup_methods;
}

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;
}

size_t ProfileCompilationInfo::GetSizeErrorThresholdBytes() const {
  return IsForBootImage() ?  kSizeErrorThresholdBootBytes : kSizeErrorThresholdBytes;
}

std::ostream& operator<<(std::ostream& stream,
                         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;
}

FlattenProfileData::FlattenProfileData() :
    max_aggregation_for_methods_(0),
    max_aggregation_for_classes_(0) {
}

FlattenProfileData::ItemMetadata::ItemMetadata() :
    flags_(0) {
}

void FlattenProfileData::ItemMetadata::ExtractInlineCacheInfo(
    const ProfileCompilationInfo& profile_info,
    const DexFile* dex_file,
    uint16_t dex_method_idx) {
  ProfileCompilationInfo::MethodHotness hotness =
      profile_info.GetMethodHotness(MethodReference(dex_file, dex_method_idx));
  DCHECK(!hotness.IsHot() || hotness.GetInlineCacheMap() != nullptr);
  if (!hotness.IsHot() || hotness.GetInlineCacheMap()->empty()) {
    return;
  }
  const dex::MethodId& id = dex_file->GetMethodId(dex_method_idx);
  const ProfileCompilationInfo::InlineCacheMap* inline_caches = hotness.GetInlineCacheMap();
  const dex::ClassDef* class_def = dex_file->FindClassDef(id.class_idx_);
  if (class_def == nullptr) {
    // No class def found.
    return;
  }

  CodeItemInstructionAccessor accessor(
      *dex_file, dex_file->GetCodeItem(dex_file->FindCodeItemOffset(*class_def, dex_method_idx)));
  for (const auto& [pc, ic_data] : *inline_caches) {
    if (pc >= accessor.InsnsSizeInCodeUnits()) {
      // Inlined inline caches are not supported in AOT, so discard any pc beyond the
      // code item size. See also `HInliner::GetInlineCacheAOT`.
      continue;
    }
    const Instruction& inst = accessor.InstructionAt(pc);
    const dex::MethodId& target = dex_file->GetMethodId(inst.VRegB());
    if (ic_data.classes.empty() && !ic_data.is_megamorphic && !ic_data.is_missing_types) {
      continue;
    }
    InlineCacheInfo& val =
        inline_cache_.FindOrAdd(TypeReference(dex_file, target.class_idx_))->second;
    if (ic_data.is_megamorphic) {
      val.is_megamorphic_ = true;
    }
    if (ic_data.is_missing_types) {
      val.is_missing_types_ = true;
    }
    for (dex::TypeIndex type_index : ic_data.classes) {
      val.classes_.insert(profile_info.GetTypeDescriptor(dex_file, type_index));
    }
  }
}

std::unique_ptr<FlattenProfileData> ProfileCompilationInfo::ExtractProfileData(
    const std::vector<std::unique_ptr<const DexFile>>& dex_files) const {

  std::unique_ptr<FlattenProfileData> result(new FlattenProfileData());

  auto create_metadata_fn = []() { return FlattenProfileData::ItemMetadata(); };

  // Iterate through all the dex files, find the methods/classes associated with each of them,
  // and add them to the flatten result.
  for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
    // Find all the dex data for the given dex file.
    // We may have multiple dex data if the methods or classes were added using
    // different annotations.
    std::vector<const DexFileData*> all_dex_data;
    FindAllDexData(dex_file.get(), &all_dex_data);
    for (const DexFileData* dex_data : all_dex_data) {
      // Extract the annotation from the key as we want to store it in the flatten result.
      ProfileSampleAnnotation annotation = GetAnnotationFromKey(dex_data->profile_key);

      // Check which methods from the current dex files are in the profile.
      for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) {
        MethodHotness hotness = dex_data->GetHotnessInfo(method_idx);
        if (!hotness.IsInProfile()) {
          // Not in the profile, continue.
          continue;
        }
        // The method is in the profile, create metadata item for it and added to the result.
        MethodReference ref(dex_file.get(), method_idx);
        FlattenProfileData::ItemMetadata& metadata =
            result->method_metadata_.GetOrCreate(ref, create_metadata_fn);
        metadata.flags_ |= hotness.flags_;
        metadata.annotations_.push_back(annotation);
        metadata.ExtractInlineCacheInfo(*this, dex_file.get(), method_idx);

        // Update the max aggregation counter for methods.
        // This is essentially a cache, to avoid traversing all the methods just to find out
        // this value.
        result->max_aggregation_for_methods_ = std::max(
            result->max_aggregation_for_methods_,
            static_cast<uint32_t>(metadata.annotations_.size()));
      }

      // 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);
        metadata.annotations_.push_back(annotation);
        // Update the max aggregation counter for classes.
        result->max_aggregation_for_classes_ = std::max(
            result->max_aggregation_for_classes_,
            static_cast<uint32_t>(metadata.annotations_.size()));
      }
    }
  }

  return result;
}

void FlattenProfileData::ItemMetadata::MergeInlineCacheInfo(
    const SafeMap<TypeReference, InlineCacheInfo, TypeReferenceValueComparator>& other) {
  for (const auto& [target, inline_cache_data] : other) {
    if (!inline_cache_data.is_megamorphic_ && !inline_cache_data.is_missing_types_ &&
        inline_cache_data.classes_.empty()) {
      continue;
    }
    InlineCacheInfo& val = inline_cache_.FindOrAdd(target)->second;
    if (inline_cache_data.is_megamorphic_) {
      val.is_megamorphic_ = true;
    }
    if (inline_cache_data.is_missing_types_) {
      val.is_missing_types_ = true;
    }
    for (const std::string& cls : inline_cache_data.classes_) {
      val.classes_.insert(cls);
    }
  }
}

void FlattenProfileData::MergeData(const FlattenProfileData& other) {
  auto create_metadata_fn = []() { return FlattenProfileData::ItemMetadata(); };
  for (const auto& it : other.method_metadata_) {
    const MethodReference& otherRef = it.first;
    const FlattenProfileData::ItemMetadata otherData = it.second;
    const std::list<ProfileCompilationInfo::ProfileSampleAnnotation>& other_annotations =
        otherData.GetAnnotations();

    FlattenProfileData::ItemMetadata& metadata =
        method_metadata_.GetOrCreate(otherRef, create_metadata_fn);
    metadata.flags_ |= otherData.GetFlags();
    metadata.annotations_.insert(
        metadata.annotations_.end(), other_annotations.begin(), other_annotations.end());
    metadata.MergeInlineCacheInfo(otherData.GetInlineCache());

    max_aggregation_for_methods_ = std::max(
          max_aggregation_for_methods_,
          static_cast<uint32_t>(metadata.annotations_.size()));
  }
  for (const auto& it : other.class_metadata_) {
    const TypeReference& otherRef = it.first;
    const FlattenProfileData::ItemMetadata otherData = it.second;
    const std::list<ProfileCompilationInfo::ProfileSampleAnnotation>& other_annotations =
        otherData.GetAnnotations();

    FlattenProfileData::ItemMetadata& metadata =
        class_metadata_.GetOrCreate(otherRef, create_metadata_fn);
    metadata.flags_ |= otherData.GetFlags();
    metadata.annotations_.insert(
        metadata.annotations_.end(), other_annotations.begin(), other_annotations.end());

    max_aggregation_for_classes_ = std::max(
          max_aggregation_for_classes_,
          static_cast<uint32_t>(metadata.annotations_.size()));
  }
}

}  // namespace art

Messung V0.5 in Prozent
C=86 H=81 G=83

¤ Dauer der Verarbeitung: 0.50 Sekunden  (vorverarbeitet am  2026-06-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik