Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d1a1202e authored by Christopher Ferris's avatar Christopher Ferris Committed by Gerrit Code Review
Browse files

Merge "Create lookup table of DEX symbols."

parents d6f97d67 02d0f796
Loading
Loading
Loading
Loading
+47 −9
Original line number Diff line number Diff line
@@ -68,17 +68,51 @@ bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name
    return false;  // The DEX offset is not within the bytecode of this dex file.
  }

  for (uint32_t i = 0; i < dex_file_->NumClassDefs(); ++i) {
    const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(i);
  if (dex_file_->IsCompactDexFile()) {
    // The data section of compact dex files might be shared.
    // Check the subrange unique to this compact dex.
    const auto& cdex_header = dex_file_->AsCompactDexFile()->GetHeader();
    uint32_t begin = cdex_header.data_off_ + cdex_header.OwnedDataBegin();
    uint32_t end = cdex_header.data_off_ + cdex_header.OwnedDataEnd();
    if (dex_offset < begin || dex_offset >= end) {
      return false;  // The DEX offset is not within the bytecode of this dex file.
    }
  }

  // The method data is cached in a std::map indexed by method end offset and
  // contains the start offset and the method member index.
  // Only cache the method data as it is searched. Do not read the entire
  // set of method data into the cache at once.
  // This is done because many unwinds only find a single frame with dex file
  // info, so reading the entire method data is wasteful. However, still cache
  // the data so that anything doing multiple unwinds will have this data
  // cached for future use.

  // First look in the method cache.
  auto entry = method_cache_.upper_bound(dex_offset);
  if (entry != method_cache_.end() && dex_offset >= entry->second.first) {
    *method_name = dex_file_->PrettyMethod(entry->second.second, false);
    *method_offset = dex_offset - entry->second.first;
    return true;
  }

  // Check the methods we haven't cached.
  for (; class_def_index_ < dex_file_->NumClassDefs(); class_def_index_++) {
    const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(class_def_index_);
    const uint8_t* class_data = dex_file_->GetClassData(class_def);
    if (class_data == nullptr) {
      continue;
    }
    for (art::ClassDataItemIterator it(*dex_file_.get(), class_data); it.HasNext(); it.Next()) {
      if (!it.IsAtMethod()) {

    if (class_it_.get() == nullptr || !class_it_->HasNext()) {
      class_it_.reset(new art::ClassDataItemIterator(*dex_file_.get(), class_data));
    }

    for (; class_it_->HasNext(); class_it_->Next()) {
      if (!class_it_->IsAtMethod()) {
        continue;
      }
      const art::DexFile::CodeItem* code_item = it.GetMethodCodeItem();
      const art::DexFile::CodeItem* code_item = class_it_->GetMethodCodeItem();
      if (code_item == nullptr) {
        continue;
      }
@@ -87,11 +121,15 @@ bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name
        continue;
      }

      uint64_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
      size_t size = code.InsnsSizeInCodeUnits() * sizeof(uint16_t);
      if (offset <= dex_offset && dex_offset < offset + size) {
        *method_name = dex_file_->PrettyMethod(it.GetMemberIndex(), false);
      uint32_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file_->Begin();
      uint32_t offset_end = offset + code.InsnsSizeInCodeUnits() * sizeof(uint16_t);
      uint32_t member_index = class_it_->GetMemberIndex();
      method_cache_[offset_end] = std::make_pair(offset, member_index);
      if (offset <= dex_offset && dex_offset < offset_end) {
        *method_name = dex_file_->PrettyMethod(member_index, false);
        *method_offset = dex_offset - offset;
        // Move past this element.
        class_it_->Next();
        return true;
      }
    }
+8 −0
Original line number Diff line number Diff line
@@ -19,8 +19,10 @@

#include <stdint.h>

#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include <dex/dex_file-inl.h>
@@ -37,7 +39,13 @@ class DexFile {
  static DexFile* Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info);

 protected:
  void Init();

  std::unique_ptr<const art::DexFile> dex_file_;
  std::map<uint32_t, std::pair<uint64_t, uint32_t>> method_cache_;  // dex offset to method index.

  uint32_t class_def_index_ = 0;
  std::unique_ptr<art::ClassDataItemIterator> class_it_;
};

class DexFileFromFile : public DexFile {
+33 −6
Original line number Diff line number Diff line
@@ -206,15 +206,42 @@ TEST(DexFileTest, get_method) {

  std::string method;
  uint64_t method_offset;
  dex_file->GetMethodInformation(0x102, &method, &method_offset);
  ASSERT_TRUE(dex_file->GetMethodInformation(0x102, &method, &method_offset));
  EXPECT_EQ("Main.<init>", method);
  EXPECT_EQ(2U, method_offset);

  method = "not_in_a_method";
  method_offset = 0x123;
  dex_file->GetMethodInformation(0x100000, &method, &method_offset);
  EXPECT_EQ("not_in_a_method", method);
  EXPECT_EQ(0x123U, method_offset);
  ASSERT_TRUE(dex_file->GetMethodInformation(0x118, &method, &method_offset));
  EXPECT_EQ("Main.main", method);
  EXPECT_EQ(0U, method_offset);

  // Make sure that any data that is cached is still retrievable.
  ASSERT_TRUE(dex_file->GetMethodInformation(0x104, &method, &method_offset));
  EXPECT_EQ("Main.<init>", method);
  EXPECT_EQ(4U, method_offset);

  ASSERT_TRUE(dex_file->GetMethodInformation(0x119, &method, &method_offset));
  EXPECT_EQ("Main.main", method);
  EXPECT_EQ(1U, method_offset);
}

TEST(DexFileTest, get_method_empty) {
  MemoryFake memory;
  memory.SetMemory(0x4000, kDexData, sizeof(kDexData));
  MapInfo info(0x100, 0x10000, 0x200, 0x5, "");
  std::unique_ptr<DexFile> dex_file(DexFile::Create(0x4000, &memory, &info));
  ASSERT_TRUE(dex_file != nullptr);

  std::string method;
  uint64_t method_offset;
  EXPECT_FALSE(dex_file->GetMethodInformation(0x100000, &method, &method_offset));

  EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset));

  // Make sure that once the whole dex file has been cached, no problems occur.
  EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset));

  // Choose a value that is in the cached map, but not in a valid method.
  EXPECT_FALSE(dex_file->GetMethodInformation(0x110, &method, &method_offset));
}

}  // namespace unwindstack