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

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

Merge "Update the Unwinder object and add tests."

parents 4e008547 f6f691b6
Loading
Loading
Loading
Loading
+30 −120
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <ucontext.h>

#include <memory>
#include <set>
#include <string>

#if !defined(__ANDROID__)
@@ -37,6 +38,8 @@
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.h>

#include <unwindstack/Unwinder.h>

#include "BacktraceLog.h"
#include "UnwindStack.h"
#include "UnwindStackMap.h"
@@ -63,135 +66,42 @@ static std::string GetFunctionName(BacktraceMap* back_map, uintptr_t pc, uintptr
  return name;
}

static bool IsUnwindLibrary(const std::string& map_name) {
  const std::string library(basename(map_name.c_str()));
  return library == "libunwindstack.so" || library == "libbacktrace.so";
}

static void SetFrameInfo(unwindstack::Regs* regs, unwindstack::MapInfo* map_info,
                         uint64_t adjusted_rel_pc, backtrace_frame_data_t* frame) {
  // This will point to the adjusted absolute pc. regs->pc() is
  // unaltered.
  frame->pc = map_info->start + adjusted_rel_pc;
  frame->sp = regs->sp();
  frame->rel_pc = adjusted_rel_pc;
  frame->stack_size = 0;

  frame->map.start = map_info->start;
  frame->map.end = map_info->end;
  frame->map.offset = map_info->offset;
  frame->map.flags = map_info->flags;
  frame->map.name = map_info->name;

  unwindstack::Elf* elf = map_info->elf;
  frame->map.load_bias = elf->GetLoadBias();
  uint64_t func_offset = 0;
  if (elf->GetFunctionName(adjusted_rel_pc, &frame->func_name, &func_offset)) {
    frame->func_name = demangle(frame->func_name.c_str());
  } else {
    frame->func_name = "";
  }
  frame->func_offset = func_offset;
}

static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
                   std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames) {
  static std::set<std::string> skip_names{"libunwindstack.so", "libbacktrace.so"};
  UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
  unwindstack::Maps* maps = stack_map->stack_maps();
  bool adjust_rel_pc = false;
  size_t num_frames = 0;
  frames->clear();
  bool return_address_attempted = false;
  auto process_memory = stack_map->process_memory();
  while (num_frames < MAX_BACKTRACE_FRAMES) {
    unwindstack::MapInfo* map_info = maps->Find(regs->pc());
    bool stepped;
    bool in_device_map = false;
    if (map_info == nullptr) {
      stepped = false;
      if (num_ignore_frames == 0) {
        frames->resize(num_frames + 1);
        backtrace_frame_data_t* frame = &frames->at(num_frames);
        frame->pc = regs->pc();
        frame->sp = regs->sp();
        frame->rel_pc = frame->pc;
        num_frames++;
      } else {
        num_ignore_frames--;
      }
    } else {
      unwindstack::Elf* elf = map_info->GetElf(process_memory, true);
      uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
  unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
                                 regs, stack_map->process_memory());
  unwinder.Unwind(&skip_names);

      if (frames->size() != 0 || !IsUnwindLibrary(map_info->name)) {
        if (num_ignore_frames == 0) {
          uint64_t adjusted_rel_pc = rel_pc;
          if (adjust_rel_pc) {
            adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
  if (num_ignore_frames >= unwinder.NumFrames()) {
    frames->resize(0);
    return true;
  }

          frames->resize(num_frames + 1);
          backtrace_frame_data_t* frame = &frames->at(num_frames);
          frame->num = num_frames;
          SetFrameInfo(regs, map_info, adjusted_rel_pc, frame);
  frames->resize(unwinder.NumFrames() - num_ignore_frames);
  auto unwinder_frames = unwinder.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);

          if (num_frames > 0) {
            // Set the stack size for the previous frame.
            backtrace_frame_data_t* prev = &frames->at(num_frames - 1);
            prev->stack_size = frame->sp - prev->sp;
          }
          num_frames++;
        } else {
          num_ignore_frames--;
        }
      }
    back_frame->num = frame->num;

      if (map_info->flags & PROT_DEVICE_MAP) {
        // Do not stop here, fall through in case we are
        // in the speculative unwind path and need to remove
        // some of the speculative frames.
        stepped = false;
        in_device_map = true;
      } else {
        unwindstack::MapInfo* sp_info = maps->Find(regs->sp());
        if (sp_info->flags & PROT_DEVICE_MAP) {
          // Do not stop here, fall through in case we are
          // in the speculative unwind path and need to remove
          // some of the speculative frames.
          stepped = false;
          in_device_map = true;
        } else {
          bool finished;
          stepped = elf->Step(rel_pc + map_info->elf_offset, regs, process_memory.get(), &finished);
          if (stepped && finished) {
            break;
          }
        }
      }
    }
    adjust_rel_pc = true;
    back_frame->rel_pc = frame->rel_pc;
    back_frame->pc = frame->pc;
    back_frame->sp = frame->sp;

    if (!stepped) {
      if (return_address_attempted) {
        // Remove the speculative frame.
        if (frames->size() > 0) {
          frames->pop_back();
        }
        break;
      } else if (in_device_map) {
        // Do not attempt any other unwinding, pc or sp is in a device
        // map.
        break;
      } else {
        // Stepping didn't work, try this secondary method.
        if (!regs->SetPcFromReturnAddress(process_memory.get())) {
          break;
        }
        return_address_attempted = true;
      }
    } else {
      return_address_attempted = false;
    }
    back_frame->func_name = frame->function_name;
    back_frame->func_offset = frame->function_offset;

    back_frame->map.name = frame->map_name;
    back_frame->map.start = frame->map_start;
    back_frame->map.end = frame->map_end;
    back_frame->map.offset = frame->map_offset;
    back_frame->map.load_bias = frame->map_load_bias;
    back_frame->map.flags = frame->map_flags;
  }

  return true;
+2 −0
Original line number Diff line number Diff line
@@ -107,6 +107,7 @@ cc_test {
        "tests/DwarfOpTest.cpp",
        "tests/DwarfSectionTest.cpp",
        "tests/DwarfSectionImplTest.cpp",
        "tests/ElfFake.cpp",
        "tests/ElfInterfaceArmTest.cpp",
        "tests/ElfInterfaceTest.cpp",
        "tests/ElfTest.cpp",
@@ -125,6 +126,7 @@ cc_test {
        "tests/RegsTest.cpp",
        "tests/SymbolsTest.cpp",
        "tests/UnwindTest.cpp",
        "tests/UnwinderTest.cpp",
    ],

    cflags: [
+0 −20
Original line number Diff line number Diff line
@@ -33,26 +33,6 @@

namespace unwindstack {

template <typename AddressType>
bool RegsImpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) {
  switch (return_loc_.type) {
  case LOCATION_REGISTER:
    CHECK(return_loc_.value < total_regs_);
    *value = regs_[return_loc_.value];
    return true;
  case LOCATION_SP_OFFSET:
    AddressType return_value;
    if (!memory->Read(sp_ + return_loc_.value, &return_value, sizeof(return_value))) {
      return false;
    }
    *value = return_value;
    return true;
  case LOCATION_UNKNOWN:
  default:
    return false;
  }
}

RegsArm::RegsArm()
    : RegsImpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {}

+54 −16
Original line number Diff line number Diff line
@@ -14,9 +14,11 @@
 * limitations under the License.
 */

#define _GNU_SOURCE 1
#include <elf.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

@@ -28,35 +30,33 @@

namespace unwindstack {

void Unwinder::FillInFrame(MapInfo* map_info, uint64_t* rel_pc) {
void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc) {
  size_t frame_num = frames_.size();
  frames_.resize(frame_num + 1);
  FrameData* frame = &frames_.at(frame_num);
  frame->num = frame_num;
  frame->pc = regs_->pc();
  frame->sp = regs_->sp();
  frame->rel_pc = frame->pc;
  frame->rel_pc = rel_pc;

  if (map_info == nullptr) {
    return;
  }

  Elf* elf = map_info->GetElf(process_memory_, true);
  *rel_pc = elf->GetRelPc(regs_->pc(), map_info);
  if (frame_num != 0) {
  if (adjust_pc) {
    // Don't adjust the first frame pc.
    frame->rel_pc = regs_->GetAdjustedPc(*rel_pc, elf);
    frame->rel_pc = regs_->GetAdjustedPc(rel_pc, elf);

    // Adjust the original pc.
    frame->pc -= *rel_pc - frame->rel_pc;
  } else {
    frame->rel_pc = *rel_pc;
    frame->pc -= rel_pc - frame->rel_pc;
  }

  frame->map_name = map_info->name;
  frame->map_offset = map_info->elf_offset;
  frame->map_start = map_info->start;
  frame->map_end = map_info->end;
  frame->map_flags = map_info->flags;
  frame->map_load_bias = elf->GetLoadBias();

  if (!elf->GetFunctionName(frame->rel_pc, &frame->function_name, &frame->function_offset)) {
    frame->function_name = "";
@@ -64,32 +64,70 @@ void Unwinder::FillInFrame(MapInfo* map_info, uint64_t* rel_pc) {
  }
}

void Unwinder::Unwind() {
void Unwinder::Unwind(std::set<std::string>* initial_map_names_to_skip) {
  frames_.clear();

  bool return_address_attempt = false;
  bool adjust_pc = false;
  for (; frames_.size() < max_frames_;) {
    MapInfo* map_info = maps_->Find(regs_->pc());

    uint64_t rel_pc;
    FillInFrame(map_info, &rel_pc);
    Elf* elf;
    if (map_info == nullptr) {
      rel_pc = regs_->pc();
    } else {
      elf = map_info->GetElf(process_memory_, true);
      rel_pc = elf->GetRelPc(regs_->pc(), map_info);
    }

    if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
        initial_map_names_to_skip->find(basename(map_info->name.c_str())) ==
            initial_map_names_to_skip->end()) {
      FillInFrame(map_info, elf, rel_pc, adjust_pc);
      // Once a frame is added, stop skipping frames.
      initial_map_names_to_skip = nullptr;
    }
    adjust_pc = true;

    bool stepped;
    bool in_device_map = false;
    if (map_info == nullptr) {
      stepped = false;
    } else {
      if (map_info->flags & MAPS_FLAGS_DEVICE_MAP) {
        // Do not stop here, fall through in case we are
        // in the speculative unwind path and need to remove
        // some of the speculative frames.
        stepped = false;
        in_device_map = true;
      } else {
        MapInfo* sp_info = maps_->Find(regs_->sp());
        if (sp_info != nullptr && sp_info->flags & MAPS_FLAGS_DEVICE_MAP) {
          // Do not stop here, fall through in case we are
          // in the speculative unwind path and need to remove
          // some of the speculative frames.
          stepped = false;
          in_device_map = true;
        } else {
          bool finished;
      stepped = map_info->elf->Step(rel_pc + map_info->elf_offset, regs_, process_memory_.get(),
                                    &finished);
          stepped =
              elf->Step(rel_pc + map_info->elf_offset, regs_, process_memory_.get(), &finished);
          if (stepped && finished) {
            break;
          }
        }
      }
    }
    if (!stepped) {
      if (return_address_attempt) {
        // Remove the speculative frame.
        frames_.pop_back();
        break;
      } else if (in_device_map) {
        // Do not attempt any other unwinding, pc or sp is in a device
        // map.
        break;
      } else {
        // Steping didn't work, try this secondary method.
        if (!regs_->SetPcFromReturnAddress(process_memory_.get())) {
+0 −4
Original line number Diff line number Diff line
@@ -55,8 +55,6 @@ class Regs {
  virtual uint64_t pc() = 0;
  virtual uint64_t sp() = 0;

  virtual bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) = 0;

  virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;

  virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0;
@@ -86,8 +84,6 @@ class RegsImpl : public Regs {
      : Regs(total_regs, sp_reg, return_loc), regs_(total_regs) {}
  virtual ~RegsImpl() = default;

  bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) override;

  uint64_t pc() override { return pc_; }
  uint64_t sp() override { return sp_; }

Loading