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
This commit is contained in:
Yabin Cui 2017-12-10 17:55:12 -08:00
parent d65c6ba955
commit 0ca49b09ad
6 changed files with 41 additions and 54 deletions

View File

@ -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

View File

@ -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

View File

@ -357,68 +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) {
map.name = testlib_path;
}
}
// 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));
// 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) {
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_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;
}
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) {
if (map.name.find(testlib_name) != std::string::npos) {
map.name = testlib_path;
}
}
@ -442,3 +398,17 @@ TEST(libbacktrace, offline_debug_frame_with_load_bias) {
ASSERT_EQ(name, testdata.symbols[i].name);
}
}
// 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");
}
TEST(libbacktrace, offline_debug_frame_with_load_bias) {
LibUnwindingTest("arm", "offline_testdata_for_libandroid_runtime", "libandroid_runtime.so");
}
TEST(libbacktrace, offline_try_armexidx_after_debug_frame) {
LibUnwindingTest("arm", "offline_testdata_for_libGLESv2_adreno", "libGLESv2_adreno.so");
}

Binary file not shown.

View File

@ -0,0 +1,6 @@
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()

File diff suppressed because one or more lines are too long