Commit 4405902a authored by Siddhartha's avatar Siddhartha Committed by Commit Bot

Android: Support unwinding function prolog

If the sampler stoped a thread at prolog of a chrome function, use the
LR register to find the next return address.

BUG=859260

Change-Id: Ic20d793ee11a8b0fe3edc9438c0f12ccc19f487c
Reviewed-on: https://chromium-review.googlesource.com/c/1265560
Commit-Queue: ssid <ssid@chromium.org>
Reviewed-by: default avatarEgor Pasko <pasko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#602077}
parent 56ba112b
......@@ -197,11 +197,12 @@ size_t CFIBacktraceAndroid::Unwind(const void** out_trace, size_t max_depth) {
asm volatile("mov %0, pc" : "=r"(pc));
asm volatile("mov %0, sp" : "=r"(sp));
return Unwind(pc, sp, out_trace, max_depth);
return Unwind(pc, sp, /*lr=*/0, out_trace, max_depth);
}
size_t CFIBacktraceAndroid::Unwind(uintptr_t pc,
uintptr_t sp,
uintptr_t lr,
const void** out_trace,
size_t max_depth) {
if (!can_unwind_stack_frames())
......@@ -214,8 +215,19 @@ size_t CFIBacktraceAndroid::Unwind(uintptr_t pc,
// The offset of function from the start of the chrome.so binary:
uintptr_t func_addr = pc - executable_start_addr();
CFIRow cfi{};
if (!FindCFIRowForPC(func_addr, &cfi))
if (!FindCFIRowForPC(func_addr, &cfi)) {
if (depth == 1 && lr != 0 && pc != lr) {
// If CFI data is not found for the frame, then we stopped in prolog of
// a function. The return address is stored in LR when in function
// prolog. So, update the PC with address in LR and do not update SP
// since SP was not updated by the prolog yet.
// TODO(ssid): Write tests / add info to detect if we are actually in
// function prolog. https://crbug.com/898276
pc = lr;
continue;
}
break;
}
// The rules for unwinding using the CFI information are:
// SP_prev = SP_cur + cfa_offset and
......
......@@ -62,12 +62,13 @@ class BASE_EXPORT CFIBacktraceAndroid {
// Initialize() returns success.
size_t Unwind(const void** out_trace, size_t max_depth);
// Same as above function, but starts from a given program counter |pc| and
// stack pointer |sp|. This can be from current thread or any other thread.
// But the caller must make sure that the thread's stack segment is not racy
// to read.
// Same as above function, but starts from a given program counter |pc|,
// stack pointer |sp| and link register |lr|. This can be from current thread
// or any other thread. But the caller must make sure that the thread's stack
// segment is not racy to read.
size_t Unwind(uintptr_t pc,
uintptr_t sp,
uintptr_t lr,
const void** out_trace,
size_t max_depth);
......
......@@ -123,8 +123,10 @@ size_t TraceStackWithContext(unw_cursor_t* cursor,
if (CFIBacktraceAndroid::is_chrome_address(ip)) {
// Continue unwinding CFI unwinder if we found stack frame from chrome
// library.
return depth +
cfi_unwinder->Unwind(ip, sp, out_trace + depth, max_depth - depth);
uintptr_t lr = 0;
unw_get_reg(cursor, UNW_ARM_LR, &lr);
return depth + cfi_unwinder->Unwind(ip, sp, lr, out_trace + depth,
max_depth - depth);
} else if (depth == 0) {
RecordUnwindResult(SamplingProfilerUnwindResult::kFirstFrameUnmapped);
}
......@@ -368,7 +370,7 @@ size_t StackUnwinderAndroid::TraceStack(base::PlatformThreadId tid,
// Do not use libunwind if we stopped at chrome frame.
if (CFIBacktraceAndroid::is_chrome_address(ip))
return cfi_unwinder->Unwind(ip, sp, out_trace, max_depth);
return cfi_unwinder->Unwind(ip, sp, 0, out_trace, max_depth);
// Reset the unwind cursor to previous function and continue with libunwind.
// TODO(ssid): Dynamic allocation functions might require registers to be
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment