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

Commit 0b06a590 authored by Christopher Ferris's avatar Christopher Ferris
Browse files

Add extra frame when dex_pc is non-zero.

Use the art dex file library to read the dex data.

Add unit tests for the UnwindDexFile code.

Bug: 72070049

Test: All unit tests continue to pass.
Test: Dumped the backtrace of the 137-cfi test while running in interpreter
Test: mode and verified that the stack trace is correct. Did this on host
Test: and for arm/arm64.

Change-Id: Ia6f343318c5dd6968a954015a7d59fdf101575b0
parent b04bbccb
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -114,6 +114,7 @@ cc_library_static {
        "libbacktrace",
        "libunwind",
        "libunwindstack",
        "libdexfile",
        "liblzma",
        "libcutils",
    ],
+24 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ libbacktrace_sources = [
    "BacktracePtrace.cpp",
    "thread_utils.c",
    "ThreadEntry.cpp",
    "UnwindDexFile.cpp",
    "UnwindStack.cpp",
    "UnwindStackMap.cpp",
]
@@ -92,13 +93,27 @@ cc_library {
                "liblog",
                "libunwind",
                "libunwindstack",
                "libdexfile",
            ],

            static_libs: ["libcutils"],

            // libdexfile will eventually properly export headers, for now
            // include these directly.
            include_dirs: [
                "art/runtime",
            ],

            header_libs: [ "jni_headers", ],
        },
        android: {
            static_libs: ["libasync_safe"],
        },
        vendor: {
            cflags: ["-DNO_LIBDEXFILE"],
            exclude_srcs: ["UnwindDexFile.cpp"],
            exclude_shared_libs: ["libdexfile"],
        },
    },
    whole_static_libs: ["libdemangle"],
}
@@ -161,6 +176,8 @@ cc_test {
        "backtrace_test.cpp",
        "GetPss.cpp",
        "thread_utils.c",

        "unwind_dex_test.cpp",
    ],

    cflags: [
@@ -172,6 +189,7 @@ cc_test {
    shared_libs: [
        "libbacktrace_test",
        "libbacktrace",
        "libdexfile",
        "libbase",
        "libcutils",
        "liblog",
@@ -212,6 +230,12 @@ cc_test {
        },
    },

    // libdexfile will eventually properly export headers, for now
    // include these directly.
    include_dirs: [
        "art/runtime",
    ],

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

#include <stdint.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <memory>

#include <android-base/unique_fd.h>

#include <dex/code_item_accessors-no_art-inl.h>
#include <dex/compact_dex_file.h>
#include <dex/dex_file-inl.h>
#include <dex/dex_file_loader.h>
#include <dex/standard_dex_file.h>

#include <unwindstack/MapInfo.h>
#include <unwindstack/Memory.h>

#include "UnwindDexFile.h"

UnwindDexFile* UnwindDexFile::Create(uint64_t dex_file_offset_in_memory,
                                     unwindstack::Memory* memory, unwindstack::MapInfo* info) {
  if (!info->name.empty()) {
    std::unique_ptr<UnwindDexFileFromFile> dex_file(new UnwindDexFileFromFile);
    if (dex_file->Open(dex_file_offset_in_memory - info->start + info->offset, info->name)) {
      return dex_file.release();
    }
  }

  std::unique_ptr<UnwindDexFileFromMemory> dex_file(new UnwindDexFileFromMemory);
  if (dex_file->Open(dex_file_offset_in_memory, memory)) {
    return dex_file.release();
  }
  return nullptr;
}

void UnwindDexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
                                         uint64_t* method_offset) {
  if (dex_file_ == nullptr) {
    return;
  }

  for (uint32_t i = 0; i < dex_file_->NumClassDefs(); ++i) {
    const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(i);
    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()) {
        continue;
      }
      const art::DexFile::CodeItem* code_item = it.GetMethodCodeItem();
      if (code_item == nullptr) {
        continue;
      }
      art::CodeItemInstructionAccessor code(*dex_file_.get(), code_item);
      if (!code.HasCodeItem()) {
        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);
        *method_offset = dex_offset - offset;
        return;
      }
    }
  }
}

UnwindDexFileFromFile::~UnwindDexFileFromFile() {
  if (size_ != 0) {
    munmap(mapped_memory_, size_);
  }
}

bool UnwindDexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) {
  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
  if (fd == -1) {
    return false;
  }
  struct stat buf;
  if (fstat(fd, &buf) == -1) {
    return false;
  }
  uint64_t length;
  if (buf.st_size < 0 ||
      __builtin_add_overflow(dex_file_offset_in_file, sizeof(art::DexFile::Header), &length) ||
      static_cast<uint64_t>(buf.st_size) < length) {
    return false;
  }

  mapped_memory_ = mmap(nullptr, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
  if (mapped_memory_ == MAP_FAILED) {
    return false;
  }
  size_ = buf.st_size;

  uint8_t* memory = reinterpret_cast<uint8_t*>(mapped_memory_);

  art::DexFile::Header* header =
      reinterpret_cast<art::DexFile::Header*>(&memory[dex_file_offset_in_file]);
  if (!art::StandardDexFile::IsMagicValid(header->magic_) &&
      !art::CompactDexFile::IsMagicValid(header->magic_)) {
    return false;
  }

  if (__builtin_add_overflow(dex_file_offset_in_file, header->file_size_, &length) ||
      static_cast<uint64_t>(buf.st_size) < length) {
    return false;
  }

  art::DexFileLoader loader;
  std::string error_msg;
  auto dex = loader.Open(&memory[dex_file_offset_in_file], header->file_size_, "", 0, nullptr,
                         false, false, &error_msg);
  dex_file_.reset(dex.release());
  return dex_file_ != nullptr;
}

bool UnwindDexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, unwindstack::Memory* memory) {
  art::DexFile::Header header;
  if (!memory->ReadFully(dex_file_offset_in_memory, &header, sizeof(header))) {
    return false;
  }

  if (!art::StandardDexFile::IsMagicValid(header.magic_) &&
      !art::CompactDexFile::IsMagicValid(header.magic_)) {
    return false;
  }

  memory_.resize(header.file_size_);
  if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), header.file_size_)) {
    return false;
  }

  art::DexFileLoader loader;
  std::string error_msg;
  auto dex =
      loader.Open(memory_.data(), header.file_size_, "", 0, nullptr, false, false, &error_msg);
  dex_file_.reset(dex.release());
  return dex_file_ != nullptr;
}
+70 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _LIBBACKTRACE_UNWIND_DEX_FILE_H
#define _LIBBACKTRACE_UNWIND_DEX_FILE_H

#include <stdint.h>

#include <memory>
#include <string>
#include <vector>

#include <dex/dex_file-inl.h>

namespace unwindstack {
class Memory;
struct MapInfo;
}  // namespace unwindstack

class UnwindDexFile {
 public:
  UnwindDexFile() = default;
  virtual ~UnwindDexFile() = default;

  void GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);

  static UnwindDexFile* Create(uint64_t dex_file_offset_in_memory, unwindstack::Memory* memory,
                               unwindstack::MapInfo* info);

 protected:
  std::unique_ptr<const art::DexFile> dex_file_;
};

class UnwindDexFileFromFile : public UnwindDexFile {
 public:
  UnwindDexFileFromFile() = default;
  virtual ~UnwindDexFileFromFile();

  bool Open(uint64_t dex_file_offset_in_file, const std::string& name);

 private:
  void* mapped_memory_ = nullptr;
  size_t size_ = 0;
};

class UnwindDexFileFromMemory : public UnwindDexFile {
 public:
  UnwindDexFileFromMemory() = default;
  virtual ~UnwindDexFileFromMemory() = default;

  bool Open(uint64_t dex_file_offset_in_memory, unwindstack::Memory* memory);

 private:
  std::vector<uint8_t> memory_;
};

#endif  // _LIBBACKTRACE_UNWIND_DEX_FILE_H
+80 −3
Original line number Diff line number Diff line
@@ -40,9 +40,67 @@
#include <unwindstack/Unwinder.h>

#include "BacktraceLog.h"
#ifndef NO_LIBDEXFILE
#include "UnwindDexFile.h"
#endif
#include "UnwindStack.h"
#include "UnwindStackMap.h"

static void FillInDexFrame(UnwindStackMap* stack_map, uint64_t dex_pc,
                           backtrace_frame_data_t* frame) {
  // The DEX PC points into the .dex section within an ELF file.
  // However, this is a BBS section manually mmaped to a .vdex file,
  // so we need to get the following map to find the ELF data.
  unwindstack::Maps* maps = stack_map->stack_maps();
  auto it = maps->begin();
  uint64_t rel_dex_pc;
  unwindstack::MapInfo* info;
  for (; it != maps->end(); ++it) {
    auto entry = *it;
    if (dex_pc >= entry->start && dex_pc < entry->end) {
      info = entry;
      rel_dex_pc = dex_pc - entry->start;
      frame->map.start = entry->start;
      frame->map.end = entry->end;
      frame->map.offset = entry->offset;
      frame->map.load_bias = entry->load_bias;
      frame->map.flags = entry->flags;
      frame->map.name = entry->name;
      frame->rel_pc = rel_dex_pc;
      break;
    }
  }
  if (it == maps->end() || ++it == maps->end()) {
    return;
  }

  auto entry = *it;
  auto process_memory = stack_map->process_memory();
  unwindstack::Elf* elf = entry->GetElf(process_memory, true);
  if (!elf->valid()) {
    return;
  }

  // Adjust the relative dex by the offset.
  rel_dex_pc += entry->elf_offset;

  uint64_t dex_offset;
  if (!elf->GetFunctionName(rel_dex_pc, &frame->func_name, &dex_offset)) {
    return;
  }
  frame->func_offset = dex_offset;
  if (frame->func_name != "$dexfile") {
    return;
  }

#ifndef NO_LIBDEXFILE
  UnwindDexFile* dex_file = stack_map->GetDexFile(dex_pc - dex_offset, info);
  if (dex_file != nullptr) {
    dex_file->GetMethodInformation(dex_offset, &frame->func_name, &frame->func_offset);
  }
#endif
}

bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
                       std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
                       std::vector<std::string>* skip_names) {
@@ -60,19 +118,25 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
    return true;
  }

  frames->resize(unwinder.NumFrames() - num_ignore_frames);
  auto unwinder_frames = unwinder.frames();
  // Get the real number of frames we'll need.
  size_t total_frames = 0;
  for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++, total_frames++) {
    if (unwinder_frames[i].dex_pc != 0) {
      total_frames++;
    }
  }
  frames->resize(total_frames);
  size_t cur_frame = 0;
  for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++, cur_frame++) {
    auto frame = &unwinder_frames[i];
    backtrace_frame_data_t* back_frame = &frames->at(cur_frame);

    back_frame->num = frame->num - num_ignore_frames;
    back_frame->num = cur_frame;

    back_frame->rel_pc = frame->rel_pc;
    back_frame->pc = frame->pc;
    back_frame->sp = frame->sp;
    back_frame->dex_pc = frame->dex_pc;

    back_frame->func_name = demangle(frame->function_name.c_str());
    back_frame->func_offset = frame->function_offset;
@@ -83,6 +147,19 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
    back_frame->map.offset = frame->map_offset;
    back_frame->map.load_bias = frame->map_load_bias;
    back_frame->map.flags = frame->map_flags;

    // Inject a frame that represents the dex pc data.
    if (frame->dex_pc != 0) {
      cur_frame++;
      backtrace_frame_data_t* dex_frame = &frames->at(cur_frame);
      dex_frame->num = cur_frame;
      dex_frame->pc = frame->dex_pc;
      dex_frame->rel_pc = frame->dex_pc;
      dex_frame->sp = back_frame->sp;
      dex_frame->stack_size = 0;
      dex_frame->func_offset = 0;
      FillInDexFrame(stack_map, frame->dex_pc, dex_frame);
    }
  }

  return true;
Loading