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

Commit 0ca49b09 authored by Yabin Cui's avatar Yabin Cui
Browse files

libBacktraceOffline: try both .debug_frame and .ARM.exidx on arm.

Currently, libBacktraceOffline doesn't try .ARM.exidx if a function
appears in .debug_frame. This make it can't unwind through functions
appear in both .debug_frame and .ARM.exidx, but having dwarf instructions
not accepted by libunwind.
This patch fixes this by trying first .debug_frame then .ARM.exidx on arm.

Bug: http://b/69383534
Test: run libbacktrace_test on arm.

Change-Id: Ib95dd56d5cc123a20948e880b51b28ddc04b4a6e
parent d65c6ba9
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -222,6 +222,7 @@ bool BacktraceOffline::Unwind(size_t num_ignore_frames, ucontext_t* context) {
    } else {
      num_ignore_frames--;
    }
    is_debug_frame_used_ = false;
    ret = unw_step(&cursor);
  } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);

@@ -318,7 +319,8 @@ bool BacktraceOffline::FindProcInfo(unw_addr_space_t addr_space, uint64_t ip,
      }
    }
  }
  if (debug_frame->has_debug_frame || debug_frame->has_gnu_debugdata) {
  if (!is_debug_frame_used_ && (debug_frame->has_debug_frame || debug_frame->has_gnu_debugdata)) {
    is_debug_frame_used_ = true;
    unw_dyn_info_t di;
    unw_word_t segbase = map.start - debug_frame->min_vaddr;
    // TODO: http://b/32916571
+10 −1
Original line number Diff line number Diff line
@@ -48,7 +48,8 @@ class BacktraceOffline : public Backtrace {
                   bool cache_file)
      : Backtrace(pid, tid, map),
        cache_file_(cache_file),
        context_(nullptr) {
        context_(nullptr),
        is_debug_frame_used_(false) {
    stack_space_.start = stack.start;
    stack_space_.end = stack.end;
    stack_space_.data = stack.data;
@@ -78,6 +79,14 @@ class BacktraceOffline : public Backtrace {
  Space arm_extab_space_;
  Space arm_exidx_space_;
  Space stack_space_;

  // is_debug_frame_used_ is to make sure we can try both .debug_frame and .ARM.exidx in
  // FindProcInfo() on ARM. One example is EsxContext::Clear() in
  // vendor/lib/egl/libGLESv2_adreno.so. EsxContext::Clear() appears in both .debug_frame and
  // .ARM.exidx. However, libunwind fails to execute debug_frame instruction
  // "DW_CFA_offset_extended: r265 at cfa-48". So we need to try .ARM.exidx to unwind that
  // function.
  bool is_debug_frame_used_;
};

#endif  // _LIBBACKTRACE_BACKTRACE_OFFLINE_H
+19 −49
Original line number Diff line number Diff line
@@ -357,26 +357,24 @@ TEST(libbacktrace, offline_arm_exidx) {
  BacktraceOfflineTest("arm", "libbacktrace_test_arm_exidx.so");
}

// This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx
// overlap with each other, which appears in /system/lib/libart.so.
TEST(libbacktrace, offline_unwind_mix_eh_frame_and_arm_exidx) {
  // TODO: For now, only run on the given arch.
  if (std::string(ABI_STRING) != "arm") {
static void LibUnwindingTest(const std::string& arch, const std::string& testdata_name,
                             const std::string& testlib_name) {
  if (std::string(ABI_STRING) != arch) {
    GTEST_LOG_(INFO) << "Skipping test since offline for arm on " << ABI_STRING
                     << " isn't supported.";
    return;
  }
  const std::string testlib_path(GetTestPath("libart.so"));
  const std::string testlib_path(GetTestPath(testlib_name));
  struct stat st;
  ASSERT_EQ(0, stat(testlib_path.c_str(), &st)) << "can't find testlib " << testlib_path;

  const std::string offline_testdata_path(GetTestPath("offline_testdata_for_libart"));
  const std::string offline_testdata_path(GetTestPath(testdata_name));
  OfflineTestData testdata;
  ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));

  // Fix path of /system/lib/libart.so.
  // Fix path of the testlib.
  for (auto& map : testdata.maps) {
    if (map.name.find("libart.so") != std::string::npos) {
    if (map.name.find(testlib_name) != std::string::npos) {
      map.name = testlib_path;
    }
  }
@@ -392,9 +390,8 @@ TEST(libbacktrace, offline_unwind_mix_eh_frame_and_arm_exidx) {
  ucontext_t ucontext = GetUContextFromUnwContext(testdata.unw_context);
  ASSERT_TRUE(backtrace->Unwind(0, &ucontext));

  // The last frame is outside of libart.so
  ASSERT_EQ(testdata.symbols.size() + 1, backtrace->NumFrames());
  for (size_t i = 0; i + 1 < backtrace->NumFrames(); ++i) {
  ASSERT_EQ(testdata.symbols.size(), backtrace->NumFrames());
  for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
    uintptr_t vaddr_in_file =
        backtrace->GetFrame(i)->pc - testdata.maps[0].start + testdata.maps[0].load_bias;
    std::string name = FunctionNameForAddress(vaddr_in_file, testdata.symbols);
@@ -402,43 +399,16 @@ TEST(libbacktrace, offline_unwind_mix_eh_frame_and_arm_exidx) {
  }
}

TEST(libbacktrace, offline_debug_frame_with_load_bias) {
  if (std::string(ABI_STRING) != "arm") {
    GTEST_LOG_(INFO) << "Skipping test since offline for arm on " << ABI_STRING
                     << " isn't supported.";
    return;
// This test tests the situation that ranges of functions covered by .eh_frame and .ARM.exidx
// overlap with each other, which appears in /system/lib/libart.so.
TEST(libbacktrace, offline_unwind_mix_eh_frame_and_arm_exidx) {
  LibUnwindingTest("arm", "offline_testdata_for_libart", "libart.so");
}
  const std::string testlib_path(GetTestPath("libandroid_runtime.so"));
  struct stat st;
  ASSERT_EQ(0, stat(testlib_path.c_str(), &st)) << "can't find testlib " << testlib_path;

  const std::string offline_testdata_path(GetTestPath("offline_testdata_for_libandroid_runtime"));
  OfflineTestData testdata;
  ASSERT_TRUE(ReadOfflineTestData(offline_testdata_path, &testdata));

  // Fix path of /system/lib/libandroid_runtime.so.
  for (auto& map : testdata.maps) {
    if (map.name.find("libandroid_runtime.so") != std::string::npos) {
      map.name = testlib_path;
    }
TEST(libbacktrace, offline_debug_frame_with_load_bias) {
  LibUnwindingTest("arm", "offline_testdata_for_libandroid_runtime", "libandroid_runtime.so");
}

  // Do offline backtrace.
  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(testdata.pid, testdata.maps));
  ASSERT_TRUE(map != nullptr);

  std::unique_ptr<Backtrace> backtrace(
      Backtrace::CreateOffline(testdata.pid, testdata.tid, map.get(), testdata.stack_info));
  ASSERT_TRUE(backtrace != nullptr);

  ucontext_t ucontext = GetUContextFromUnwContext(testdata.unw_context);
  ASSERT_TRUE(backtrace->Unwind(0, &ucontext));

  ASSERT_EQ(testdata.symbols.size(), backtrace->NumFrames());
  for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
    uintptr_t vaddr_in_file =
        backtrace->GetFrame(i)->pc - testdata.maps[0].start + testdata.maps[0].load_bias;
    std::string name = FunctionNameForAddress(vaddr_in_file, testdata.symbols);
    ASSERT_EQ(name, testdata.symbols[i].name);
  }
TEST(libbacktrace, offline_try_armexidx_after_debug_frame) {
  LibUnwindingTest("arm", "offline_testdata_for_libGLESv2_adreno", "libGLESv2_adreno.so");
}
+4.04 MiB

File added.

No diff preview for this file type.

+6 −0
Original line number Diff line number Diff line
pid: 7288 tid: 31656
regs: pc: cc416235 sp: cc17f000
map: start: cc361000 end: cc758000 offset: 0 load_bias: 9000 flags: 5 name: /vendor/lib/egl/libGLESv2_adreno.so
stack: start: cc17f254 end: cc17f258 size: 4 b36141cc
function: start: be1f0 end: be304 name: EsxContext::Clear(unsigned int, unsigned int, unsigned int, EsxClearValues*)
function: start: be058 end: be1f0 name: EsxContext::ClearBuffersForDebug()
Loading