diff options
author | Todd Fiala <todd.fiala@gmail.com> | 2014-08-25 20:29:09 +0000 |
---|---|---|
committer | Todd Fiala <todd.fiala@gmail.com> | 2014-08-25 20:29:09 +0000 |
commit | cb6562cef3d840f7435ca29e0dc2c94e32af9a44 (patch) | |
tree | 3610d8118445fd8a5a407376aa42868b1dc57a49 | |
parent | 360adc2488c89d67b28553fcffd33861f11487fc (diff) |
On x86 & x86_64, try to use eh_frame for frame 0.
We decided to use assmbly profiler instead of eh_frame for frame 0 because for compiler generated code, eh_frame is usually synchronous(a.k.a. only valid at call site); and we have no way to tell if it's asynchronous or not.
But for x86 & x86_64 compiler generated code:
1. clang & GCC describes all prologue instructions in eh_frame;
2. mid-function stack pointer altering instructions can be easily detected.
So we can grab eh_frame, and use assembly profiler to augment it into asynchronous unwind table.
This change also benefits hand-written assembly; eh_frame for hand-written assembly is often asynchronous,so we have a much better chance to successfully unwind through them.
Change by Tong Shen.
git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@216406 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/lldb/Symbol/FuncUnwinders.h | 2 | ||||
-rw-r--r-- | include/lldb/Symbol/UnwindPlan.h | 3 | ||||
-rw-r--r-- | include/lldb/Target/UnwindAssembly.h | 5 | ||||
-rw-r--r-- | source/Commands/CommandObjectTarget.cpp | 2 | ||||
-rw-r--r-- | source/Plugins/Process/Utility/RegisterContextLLDB.cpp | 4 | ||||
-rw-r--r-- | source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp | 8 | ||||
-rw-r--r-- | source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h | 5 | ||||
-rw-r--r-- | source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp | 270 | ||||
-rw-r--r-- | source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h | 5 | ||||
-rw-r--r-- | source/Symbol/FuncUnwinders.cpp | 15 | ||||
-rw-r--r-- | source/Symbol/UnwindPlan.cpp | 13 |
11 files changed, 315 insertions, 17 deletions
diff --git a/include/lldb/Symbol/FuncUnwinders.h b/include/lldb/Symbol/FuncUnwinders.h index b664bb736..0cf584239 100644 --- a/include/lldb/Symbol/FuncUnwinders.h +++ b/include/lldb/Symbol/FuncUnwinders.h @@ -44,7 +44,7 @@ public: GetUnwindPlanAtCallSite (int current_offset); lldb::UnwindPlanSP - GetUnwindPlanAtNonCallSite (lldb_private::Thread& thread); + GetUnwindPlanAtNonCallSite (Target& target, lldb_private::Thread& thread, int current_offset); lldb::UnwindPlanSP GetUnwindPlanFastUnwind (lldb_private::Thread& Thread); diff --git a/include/lldb/Symbol/UnwindPlan.h b/include/lldb/Symbol/UnwindPlan.h index 6fc5ce042..e1b146fe2 100644 --- a/include/lldb/Symbol/UnwindPlan.h +++ b/include/lldb/Symbol/UnwindPlan.h @@ -365,6 +365,9 @@ public: void AppendRow (const RowSP& row_sp); + void + InsertRow (const RowSP& row_sp); + // Returns a pointer to the best row for the given offset into the function's instructions. // If offset is -1 it indicates that the function start is unknown - the final row in the UnwindPlan is returned. // In practice, the UnwindPlan for a function with no known start address will be the architectural default diff --git a/include/lldb/Target/UnwindAssembly.h b/include/lldb/Target/UnwindAssembly.h index 254382ac0..963949cf0 100644 --- a/include/lldb/Target/UnwindAssembly.h +++ b/include/lldb/Target/UnwindAssembly.h @@ -33,6 +33,11 @@ public: UnwindPlan& unwind_plan) = 0; virtual bool + AugmentUnwindPlanFromCallSite (AddressRange& func, + Thread& thread, + UnwindPlan& unwind_plan) = 0; + + virtual bool GetFastUnwindPlan (AddressRange& func, Thread& thread, UnwindPlan &unwind_plan) = 0; diff --git a/source/Commands/CommandObjectTarget.cpp b/source/Commands/CommandObjectTarget.cpp index c5e8826cc..24a6b9dca 100644 --- a/source/Commands/CommandObjectTarget.cpp +++ b/source/Commands/CommandObjectTarget.cpp @@ -3724,7 +3724,7 @@ protected: result.GetOutputStream().Printf ("\n"); } - UnwindPlanSP non_callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtNonCallSite(*thread.get()); + UnwindPlanSP non_callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtNonCallSite(*target, *thread.get(), -1); if (non_callsite_unwind_plan.get()) { result.GetOutputStream().Printf("Asynchronous (not restricted to call-sites) UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); diff --git a/source/Plugins/Process/Utility/RegisterContextLLDB.cpp b/source/Plugins/Process/Utility/RegisterContextLLDB.cpp index cfbe133f1..b58e6bb60 100644 --- a/source/Plugins/Process/Utility/RegisterContextLLDB.cpp +++ b/source/Plugins/Process/Utility/RegisterContextLLDB.cpp @@ -794,7 +794,7 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame () // Typically the NonCallSite UnwindPlan is the unwind created by inspecting the assembly language instructions if (behaves_like_zeroth_frame) { - unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (m_thread); + unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (process->GetTarget(), m_thread, m_current_offset_backed_up_one); if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc)) { if (unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo) @@ -822,7 +822,7 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame () // We'd prefer to use an UnwindPlan intended for call sites when we're at a call site but if we've // struck out on that, fall back to using the non-call-site assembly inspection UnwindPlan if possible. - unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (m_thread); + unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (process->GetTarget(), m_thread, m_current_offset_backed_up_one); if (unwind_plan_sp && unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo) { // We probably have an UnwindPlan created by inspecting assembly instructions, and we probably diff --git a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp index 42453ce72..b8d56d390 100644 --- a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp +++ b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp @@ -285,6 +285,14 @@ UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly (AddressRange& } bool +UnwindAssemblyInstEmulation::AugmentUnwindPlanFromCallSite (AddressRange& func, + Thread& thread, + UnwindPlan& unwind_plan) +{ + return false; +} + +bool UnwindAssemblyInstEmulation::GetFastUnwindPlan (AddressRange& func, Thread& thread, UnwindPlan &unwind_plan) diff --git a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h index 6a02f0a55..758c3ce2e 100644 --- a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h +++ b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h @@ -31,6 +31,11 @@ public: lldb_private::UnwindPlan& unwind_plan); virtual bool + AugmentUnwindPlanFromCallSite (lldb_private::AddressRange& func, + lldb_private::Thread& thread, + lldb_private::UnwindPlan& unwind_plan); + + virtual bool GetFastUnwindPlan (lldb_private::AddressRange& func, lldb_private::Thread& thread, lldb_private::UnwindPlan &unwind_plan); diff --git a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp index 6df4bade5..d1836bf62 100644 --- a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp +++ b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp @@ -123,6 +123,8 @@ public: bool get_non_call_site_unwind_plan (UnwindPlan &unwind_plan); + bool augment_unwind_plan_from_call_site (AddressRange& func, UnwindPlan &unwind_plan); + bool get_fast_unwind_plan (AddressRange& func, UnwindPlan &unwind_plan); bool find_first_non_prologue_insn (Address &address); @@ -135,9 +137,14 @@ private: bool push_0_pattern_p (); bool mov_rsp_rbp_pattern_p (); bool sub_rsp_pattern_p (int& amount); + bool add_rsp_pattern_p (int& amount); bool push_reg_p (int& regno); + bool pop_reg_p (int& regno); + bool push_imm_pattern_p (); bool mov_reg_to_local_stack_frame_p (int& regno, int& fp_offset); bool ret_pattern_p (); + bool pop_rbp_pattern_p (); + bool call_next_insn_pattern_p(); uint32_t extract_4 (uint8_t *b); bool machine_regno_to_lldb_regno (int machine_regno, uint32_t& lldb_regno); bool instruction_length (Address addr, int &length); @@ -149,13 +156,13 @@ private: Address m_cur_insn; uint8_t m_cur_insn_bytes[kMaxInstructionByteSize]; - int m_machine_ip_regnum; - int m_machine_sp_regnum; - int m_machine_fp_regnum; + uint32_t m_machine_ip_regnum; + uint32_t m_machine_sp_regnum; + uint32_t m_machine_fp_regnum; - int m_lldb_ip_regnum; - int m_lldb_sp_regnum; - int m_lldb_fp_regnum; + uint32_t m_lldb_ip_regnum; + uint32_t m_lldb_sp_regnum; + uint32_t m_lldb_fp_regnum; int m_wordsize; int m_cpu; @@ -318,6 +325,15 @@ bool AssemblyParse_x86::push_0_pattern_p () return false; } +// pushq $0 +// pushl $0 +bool AssemblyParse_x86::push_imm_pattern_p () { + uint8_t *p = m_cur_insn_bytes; + if (*p == 0x68 || *p == 0x6a) + return true; + return false; +} + // movq %rsp, %rbp [0x48 0x8b 0xec] or [0x48 0x89 0xe5] // movl %esp, %ebp [0x8b 0xec] or [0x89 0xe5] bool AssemblyParse_x86::mov_rsp_rbp_pattern_p () { @@ -346,13 +362,29 @@ bool AssemblyParse_x86::sub_rsp_pattern_p (int& amount) { amount = (int32_t) extract_4 (p + 2); return true; } - // Not handled: [0x83 0xc4] for imm8 with neg values - // [0x81 0xc4] for imm32 with neg values + return false; +} + +// addq $0x20, %rsp +bool AssemblyParse_x86::add_rsp_pattern_p (int& amount) { + uint8_t *p = m_cur_insn_bytes; + if (m_wordsize == 8 && *p == 0x48) + p++; + // 8-bit immediate operand + if (*p == 0x83 && *(p + 1) == 0xc4) { + amount = (int8_t) *(p + 2); + return true; + } + // 32-bit immediate operand + if (*p == 0x81 && *(p + 1) == 0xc4) { + amount = (int32_t) extract_4 (p + 2); + return true; + } return false; } // pushq %rbx -// pushl $ebx +// pushl %ebx bool AssemblyParse_x86::push_reg_p (int& regno) { uint8_t *p = m_cur_insn_bytes; int regno_prefix_bit = 0; @@ -368,6 +400,37 @@ bool AssemblyParse_x86::push_reg_p (int& regno) { return false; } +// popq %rbx +// popl %ebx +bool AssemblyParse_x86::pop_reg_p (int& regno) { + uint8_t *p = m_cur_insn_bytes; + int regno_prefix_bit = 0; + // If we have a rex prefix byte, check to see if a B bit is set + if (m_wordsize == 8 && *p == 0x41) { + regno_prefix_bit = 1 << 3; + p++; + } + if (*p >= 0x58 && *p <= 0x5f) { + regno = (*p - 0x58) | regno_prefix_bit; + return true; + } + return false; +} + +// popq %rbp [0x5d] +// popl %ebp [0x5d] +bool AssemblyParse_x86::pop_rbp_pattern_p () { + uint8_t *p = m_cur_insn_bytes; + return (*p == 0x5d); +} + +// call $0 [0xe8 0x0 0x0 0x0 0x0] +bool AssemblyParse_x86::call_next_insn_pattern_p () { + uint8_t *p = m_cur_insn_bytes; + return (*p == 0xe8) && (*(p+1) == 0x0) && (*(p+2) == 0x0) + && (*(p+3) == 0x0) && (*(p+4) == 0x0); +} + // Look for an instruction sequence storing a nonvolatile register // on to the stack frame. @@ -608,7 +671,7 @@ AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan) bool need_to_push_row = false; // the PUSH instruction has moved the stack pointer - if the CFA is set in terms of the stack pointer, // we need to add a new row of instructions. - if (row->GetCFARegister() == static_cast<uint32_t>(m_lldb_sp_regnum)) + if (row->GetCFARegister() == m_lldb_sp_regnum) { need_to_push_row = true; row->SetCFAOffset (current_sp_bytes_offset_from_cfa); @@ -659,7 +722,7 @@ AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan) if (sub_rsp_pattern_p (stack_offset)) { current_sp_bytes_offset_from_cfa += stack_offset; - if (row->GetCFARegister() == static_cast<uint32_t>(m_lldb_sp_regnum)) + if (row->GetCFARegister() == m_lldb_sp_regnum) { row->SetOffset (current_func_text_offset + insn_len); row->SetCFAOffset (current_sp_bytes_offset_from_cfa); @@ -787,6 +850,183 @@ loopnext: return true; } +bool +AssemblyParse_x86::augment_unwind_plan_from_call_site (AddressRange& func, UnwindPlan &unwind_plan) +{ + // Is func address valid? + Address addr_start = func.GetBaseAddress(); + if (!addr_start.IsValid()) + return false; + + // Is original unwind_plan valid? + // unwind_plan should have at least one row which is ABI-default (CFA register is sp), + // and another row in mid-function. + if (unwind_plan.GetRowCount() < 2) + return false; + UnwindPlan::RowSP first_row = unwind_plan.GetRowAtIndex (0); + if (first_row->GetOffset() != 0) + return false; + uint32_t cfa_reg = m_exe_ctx.GetThreadPtr()->GetRegisterContext() + ->ConvertRegisterKindToRegisterNumber (unwind_plan.GetRegisterKind(), + first_row->GetCFARegister()); + if (cfa_reg != m_lldb_sp_regnum || first_row->GetCFAOffset() != m_wordsize) + return false; + + Target *target = m_exe_ctx.GetTargetPtr(); + m_cur_insn = func.GetBaseAddress(); + uint64_t offset = 0; + int row_id = 1; + UnwindPlan::RowSP row(new UnwindPlan::Row(*first_row)); + while (func.ContainsFileAddress (m_cur_insn)) + { + int insn_len; + if (!instruction_length (m_cur_insn, insn_len) + || insn_len == 0 || insn_len > kMaxInstructionByteSize) + { + // An unrecognized/junk instruction. + break; + } + const bool prefer_file_cache = true; + Error error; + if (target->ReadMemory (m_cur_insn, prefer_file_cache, m_cur_insn_bytes, + insn_len, error) == static_cast<size_t>(-1)) + { + // Error reading the instruction out of the file, stop scanning. + break; + } + + // Advance offsets. + offset += insn_len; + m_cur_insn.SetOffset(m_cur_insn.GetOffset() + insn_len); + + // If we already have one row for this instruction, we can continue. + while (row_id < unwind_plan.GetRowCount() + && unwind_plan.GetRowAtIndex (row_id)->GetOffset() <= offset) + row_id++; + UnwindPlan::RowSP original_row = unwind_plan.GetRowAtIndex (row_id - 1); + if (original_row->GetOffset() == offset) + { + *row = *original_row; + continue; + } + + if (row_id == 0) { + // If we are here, compiler didn't generate CFI for prologue. + // This won't happen to GCC or clang. + // In this case, bail out directly. + return false; + } + + // Inspect the instruction to check if we need a new row for it. + cfa_reg = m_exe_ctx.GetThreadPtr()->GetRegisterContext() + ->ConvertRegisterKindToRegisterNumber (unwind_plan.GetRegisterKind(), + row->GetCFARegister()); + if (cfa_reg == m_lldb_sp_regnum) + { + // CFA register is sp. + + // call next instruction + // call 0 + // => pop %ebx + if (call_next_insn_pattern_p ()) + { + row->SetOffset (offset); + row->SetCFAOffset (m_wordsize + row->GetCFAOffset()); + + UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); + unwind_plan.InsertRow (new_row); + continue; + } + + // push/pop register + int regno; + if (push_reg_p (regno)) { + row->SetOffset (offset); + row->SetCFAOffset (m_wordsize + row->GetCFAOffset()); + + UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); + unwind_plan.InsertRow (new_row); + continue; + } + if (pop_reg_p (regno)) { + // Technically, this might be a nonvolatile register recover in epilogue. + // We should reset RegisterInfo for the register. + // But in practice, previous rule for the register is still valid... + // So we ignore this case. + + row->SetOffset (offset); + row->SetCFAOffset (-m_wordsize + row->GetCFAOffset()); + + UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); + unwind_plan.InsertRow (new_row); + continue; + } + + // push imm + if (push_imm_pattern_p ()) { + row->SetOffset (offset); + row->SetCFAOffset (m_wordsize + row->GetCFAOffset()); + UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); + unwind_plan.InsertRow (new_row); + continue; + } + + // add/sub %rsp/%esp + int amount; + if (add_rsp_pattern_p (amount)) { + row->SetOffset (offset); + row->SetCFAOffset (-amount + row->GetCFAOffset()); + + UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); + unwind_plan.InsertRow (new_row); + continue; + } + if (sub_rsp_pattern_p (amount)) { + row->SetOffset (offset); + row->SetCFAOffset (amount + row->GetCFAOffset()); + + UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); + unwind_plan.InsertRow (new_row); + continue; + } + } + else if (cfa_reg == m_lldb_fp_regnum) + { + // CFA register is fp. + + // The only case we care about is epilogue: + // [0x5d] pop %rbp/%ebp + // => [0xc3] ret + if (pop_rbp_pattern_p ()) + { + if (target->ReadMemory (m_cur_insn, prefer_file_cache, m_cur_insn_bytes, + 1, error) != static_cast<size_t>(-1) + && ret_pattern_p ()) + { + row->SetOffset (offset); + row->SetCFARegister (first_row->GetCFARegister()); + row->SetCFAOffset (m_wordsize); + + UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); + unwind_plan.InsertRow (new_row); + continue; + } + } + } + else + { + // CFA register is not sp or fp. + + // This must be hand-written assembly. + // Just trust eh_frame and assume we have finished. + break; + } + } + + unwind_plan.SetPlanValidAddressRange (func); + return true; +} + /* The "fast unwind plan" is valid for functions that follow the usual convention of using the frame pointer register (ebp, rbp), i.e. the function prologue looks like push %rbp [0x55] @@ -946,6 +1186,14 @@ UnwindAssembly_x86::GetNonCallSiteUnwindPlanFromAssembly (AddressRange& func, Th } bool +UnwindAssembly_x86::AugmentUnwindPlanFromCallSite (AddressRange& func, Thread& thread, UnwindPlan& unwind_plan) +{ + ExecutionContext exe_ctx (thread.shared_from_this()); + AssemblyParse_x86 asm_parse(exe_ctx, m_cpu, m_arch, func); + return asm_parse.augment_unwind_plan_from_call_site (func, unwind_plan); +} + +bool UnwindAssembly_x86::GetFastUnwindPlan (AddressRange& func, Thread& thread, UnwindPlan &unwind_plan) { ExecutionContext exe_ctx (thread.shared_from_this()); diff --git a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h index eebaa7b6c..8a4fe7c09 100644 --- a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h +++ b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h @@ -27,6 +27,11 @@ public: lldb_private::UnwindPlan& unwind_plan); virtual bool + AugmentUnwindPlanFromCallSite (lldb_private::AddressRange& func, + lldb_private::Thread& thread, + lldb_private::UnwindPlan& unwind_plan); + + virtual bool GetFastUnwindPlan (lldb_private::AddressRange& func, lldb_private::Thread& thread, lldb_private::UnwindPlan &unwind_plan); diff --git a/source/Symbol/FuncUnwinders.cpp b/source/Symbol/FuncUnwinders.cpp index 4ed04a00a..d6f89bc29 100644 --- a/source/Symbol/FuncUnwinders.cpp +++ b/source/Symbol/FuncUnwinders.cpp @@ -32,7 +32,7 @@ FuncUnwinders::FuncUnwinders ) : m_unwind_table(unwind_table), m_range(range), - m_mutex (Mutex::eMutexTypeNormal), + m_mutex (Mutex::eMutexTypeRecursive), m_unwind_plan_call_site_sp (), m_unwind_plan_non_call_site_sp (), m_unwind_plan_fast_sp (), @@ -94,7 +94,7 @@ FuncUnwinders::GetUnwindPlanAtCallSite (int current_offset) } UnwindPlanSP -FuncUnwinders::GetUnwindPlanAtNonCallSite (Thread& thread) +FuncUnwinders::GetUnwindPlanAtNonCallSite (Target& target, Thread& thread, int current_offset) { // Lock the mutex to ensure we can always give out the most appropriate // information. We want to make sure if someone requests an unwind @@ -114,6 +114,17 @@ FuncUnwinders::GetUnwindPlanAtNonCallSite (Thread& thread) UnwindAssemblySP assembly_profiler_sp (GetUnwindAssemblyProfiler()); if (assembly_profiler_sp) { + if (target.GetArchitecture().GetCore() == ArchSpec::eCore_x86_32_i386 + || target.GetArchitecture().GetCore() == ArchSpec::eCore_x86_64_x86_64) + { + // For 0th frame on i386 & x86_64, we fetch eh_frame and try using assembly profiler + // to augment it into asynchronous unwind table. + GetUnwindPlanAtCallSite(current_offset); + if (m_unwind_plan_call_site_sp + && assembly_profiler_sp->AugmentUnwindPlanFromCallSite(m_range, thread, *m_unwind_plan_call_site_sp)) + return m_unwind_plan_call_site_sp; + } + m_unwind_plan_non_call_site_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); if (!assembly_profiler_sp->GetNonCallSiteUnwindPlanFromAssembly (m_range, thread, *m_unwind_plan_non_call_site_sp)) m_unwind_plan_non_call_site_sp.reset(); diff --git a/source/Symbol/UnwindPlan.cpp b/source/Symbol/UnwindPlan.cpp index f1cb1a994..ff0468e31 100644 --- a/source/Symbol/UnwindPlan.cpp +++ b/source/Symbol/UnwindPlan.cpp @@ -313,6 +313,19 @@ UnwindPlan::AppendRow (const UnwindPlan::RowSP &row_sp) m_row_list.back() = row_sp; } +void +UnwindPlan::InsertRow (const UnwindPlan::RowSP &row_sp) +{ + collection::iterator it = m_row_list.begin(); + while (it != m_row_list.end()) { + RowSP row = *it; + if (row->GetOffset() > row_sp->GetOffset()) + break; + it++; + } + m_row_list.insert(it, row_sp); +} + UnwindPlan::RowSP UnwindPlan::GetRowForFunctionOffset (int offset) const { |