diff options
Diffstat (limited to 'libbacktrace/UnwindStack.cpp')
| -rw-r--r-- | libbacktrace/UnwindStack.cpp | 158 |
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; } } |
