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:
parent
d65c6ba955
commit
0ca49b09ad
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
@ -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
Loading…
Reference in New Issue