summaryrefslogtreecommitdiffstats
path: root/libbacktrace/UnwindStack.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libbacktrace/UnwindStack.cpp')
-rw-r--r--libbacktrace/UnwindStack.cpp158
1 files changed, 105 insertions, 53 deletions
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 61812abb5..b4481cce9 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -68,6 +68,32 @@ static bool IsUnwindLibrary(const std::string& map_name) {
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) {
UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
@@ -75,70 +101,96 @@ static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
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) {
- if (regs->pc() == 0) {
- break;
- }
unwindstack::MapInfo* map_info = maps->Find(regs->pc());
+ bool stepped;
+ bool in_device_map = false;
if (map_info == nullptr) {
- break;
- }
+ 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);
+
+ 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);
+ }
- unwindstack::Elf* elf = map_info->GetElf(stack_map->process_memory(), true);
- uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
+ 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);
- bool skip_frame = num_frames == 0 && IsUnwindLibrary(map_info->name);
- if (num_ignore_frames == 0 && !skip_frame) {
- uint64_t adjusted_rel_pc = rel_pc;
- if (adjust_rel_pc) {
- adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
+ 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--;
+ }
}
- frames->resize(num_frames + 1);
- backtrace_frame_data_t* frame = &frames->at(num_frames);
- frame->num = num_frames;
- // 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.load_bias = elf->GetLoadBias();
- frame->map.flags = map_info->flags;
- frame->map.name = map_info->name;
-
- 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());
+
+ 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 {
- frame->func_name = "";
+ 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;
+ }
+ }
}
- frame->func_offset = func_offset;
- 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 if (!skip_frame && num_ignore_frames > 0) {
- num_ignore_frames--;
}
adjust_rel_pc = true;
- // Do not unwind through a device map.
- if (map_info->flags & PROT_DEVICE_MAP) {
- break;
- }
- unwindstack::MapInfo* sp_info = maps->Find(regs->sp());
- if (sp_info->flags & PROT_DEVICE_MAP) {
- break;
- }
-
- if (!elf->Step(rel_pc + map_info->elf_offset, regs, stack_map->process_memory().get())) {
- break;
+ 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;
}
}