From 5822173bc842fcd50a413267917e8386863f00ee Mon Sep 17 00:00:00 2001 From: Jim Ingham Date: Fri, 5 Nov 2010 00:18:21 +0000 Subject: [PATCH] Handle stepping through ObjC vtable trampoline code. llvm-svn: 118270 --- .../AppleObjCTrampolineHandler.cpp | 414 +++++++++++++++++- .../AppleObjCTrampolineHandler.h | 126 +++++- ...pleThreadPlanStepThroughObjCTrampoline.cpp | 7 + lldb/source/Target/ThreadPlanStepInRange.cpp | 51 ++- .../source/Target/ThreadPlanStepOverRange.cpp | 14 +- 5 files changed, 567 insertions(+), 45 deletions(-) diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp index 498937eb2643..a10ddcf92c4e 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.cpp @@ -15,7 +15,9 @@ // Project includes #include "AppleThreadPlanStepThroughObjCTrampoline.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Core/ConstString.h" +#include "lldb/Core/Debugger.h" #include "lldb/Core/FileSpec.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" @@ -33,6 +35,351 @@ using namespace lldb; using namespace lldb_private; + +AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::VTableRegion(AppleObjCVTables *owner, lldb::addr_t header_addr) : + m_valid (true), + m_owner(owner), + m_header_addr (header_addr), + m_code_start_addr(0), + m_code_end_addr (0), + m_next_region (0) +{ + SetUpRegion (); +} + +void +AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::SetUpRegion() +{ + // The header looks like: + // + // uint16_t headerSize + // uint16_t descSize + // uint32_t descCount + // void * next + // + // First read in the header: + + char memory_buffer[16]; + Process *process = m_owner->GetProcess(); + DataExtractor data(memory_buffer, sizeof(memory_buffer), + process->GetByteOrder(), + process->GetAddressByteSize()); + size_t actual_size = 8 + process->GetAddressByteSize(); + Error error; + size_t bytes_read = process->ReadMemory (m_header_addr, memory_buffer, actual_size, error); + if (bytes_read != actual_size) + { + m_valid = false; + return; + } + + uint32_t offset_ptr = 0; + uint16_t header_size = data.GetU16(&offset_ptr); + uint16_t descriptor_size = data.GetU16(&offset_ptr); + size_t num_descriptors = data.GetU32(&offset_ptr); + + m_next_region = data.GetPointer(&offset_ptr); + + // If the header size is 0, that means we've come in too early before this data is set up. + // Set ourselves as not valid, and continue. + if (header_size == 0) + { + m_valid = false; + return; + } + + // Now read in all the descriptors: + // The descriptor looks like: + // + // uint32_t offset + // uint32_t flags + // + // Where offset is either 0 - in which case it is unused, or + // it is the offset of the vtable code from the beginning of the descriptor record. + // Below, we'll convert that into an absolute code address, since I don't want to have + // to compute it over and over. + + // Ingest the whole descriptor array: + lldb::addr_t desc_ptr = m_header_addr + header_size; + size_t desc_array_size = num_descriptors * descriptor_size; + DataBufferSP data_sp(new DataBufferHeap (desc_array_size, '\0')); + uint8_t* dst = (uint8_t*)data_sp->GetBytes(); + + DataExtractor desc_extractor (dst, desc_array_size, + process->GetByteOrder(), + process->GetAddressByteSize()); + bytes_read = process->ReadMemory(desc_ptr, dst, desc_array_size, error); + if (bytes_read != desc_array_size) + { + m_valid = false; + return; + } + + // The actual code for the vtables will be laid out consecutively, so I also + // compute the start and end of the whole code block. + + offset_ptr = 0; + m_code_start_addr = 0; + m_code_end_addr = 0; + + for (int i = 0; i < num_descriptors; i++) + { + lldb::addr_t start_offset = offset_ptr; + uint32_t offset = desc_extractor.GetU32 (&offset_ptr); + uint32_t flags = desc_extractor.GetU32 (&offset_ptr); + lldb:addr_t code_addr = desc_ptr + start_offset + offset; + m_descriptors.push_back (VTableDescriptor(flags, code_addr)); + + if (m_code_start_addr == 0 || code_addr < m_code_start_addr) + m_code_start_addr = code_addr; + if (code_addr > m_code_end_addr) + m_code_end_addr = code_addr; + + offset_ptr = start_offset + descriptor_size; + } + // Finally, a little bird told me that all the vtable code blocks are the same size. + // Let's compute the blocks and if they are all the same add the size to the code end address: + lldb::addr_t code_size = 0; + bool all_the_same = true; + for (int i = 0; i < num_descriptors - 1; i++) + { + lldb::addr_t this_size = m_descriptors[i + 1].code_start - m_descriptors[i].code_start; + if (code_size == 0) + code_size = this_size; + else + { + if (this_size != code_size) + all_the_same = false; + if (this_size > code_size) + code_size = this_size; + } + } + if (all_the_same) + m_code_end_addr += code_size; +} + +bool +AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::AddressInRegion (lldb::addr_t addr, uint32_t &flags) +{ + if (!IsValid()) + return false; + + if (addr < m_code_start_addr || addr > m_code_end_addr) + return false; + + std::vector::iterator pos, end = m_descriptors.end(); + for (pos = m_descriptors.begin(); pos != end; pos++) + { + if (addr <= (*pos).code_start) + { + flags = (*pos).flags; + return true; + } + } + return false; +} + +void +AppleObjCTrampolineHandler::AppleObjCVTables::VTableRegion::Dump (Stream &s) +{ + s.Printf ("Header addr: 0x%llx Code start: 0x%llx Code End: 0x%llx Next: 0x%llx\n", + m_header_addr, m_code_start_addr, m_code_end_addr, m_next_region); + size_t num_elements = m_descriptors.size(); + for (size_t i = 0; i < num_elements; i++) + { + s.Indent(); + s.Printf ("Code start: 0x%llx Flags: %d\n", m_descriptors[i].code_start, m_descriptors[i].flags); + } +} + +AppleObjCTrampolineHandler::AppleObjCVTables::AppleObjCVTables (ProcessSP &process_sp, ModuleSP &objc_module_sp) : + m_process_sp(process_sp), + m_trampoline_header(LLDB_INVALID_ADDRESS), + m_trampolines_changed_bp_id(LLDB_INVALID_BREAK_ID), + m_objc_module_sp(objc_module_sp) +{ + +} + +AppleObjCTrampolineHandler::AppleObjCVTables::~AppleObjCVTables() +{ + if (m_trampolines_changed_bp_id != LLDB_INVALID_BREAK_ID) + m_process_sp->GetTarget().RemoveBreakpointByID (m_trampolines_changed_bp_id); +} + +bool +AppleObjCTrampolineHandler::AppleObjCVTables::InitializeVTableSymbols () +{ + if (m_trampoline_header != LLDB_INVALID_ADDRESS) + return true; + Target &target = m_process_sp->GetTarget(); + + ModuleList &modules = target.GetImages(); + size_t num_modules = modules.GetSize(); + if (!m_objc_module_sp) + { + for (size_t i = 0; i < num_modules; i++) + { + if (m_process_sp->GetObjCLanguageRuntime()->IsModuleObjCLibrary (modules.GetModuleAtIndex(i))) + { + m_objc_module_sp = modules.GetModuleAtIndex(i); + break; + } + } + } + + if (m_objc_module_sp) + { + ConstString trampoline_name ("gdb_objc_trampolines"); + const Symbol *trampoline_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType(trampoline_name, + eSymbolTypeData); + if (trampoline_symbol != NULL) + { + const Address &temp_address = trampoline_symbol->GetValue(); + if (!temp_address.IsValid()) + return false; + + m_trampoline_header = temp_address.GetLoadAddress(&target); + if (m_trampoline_header == LLDB_INVALID_ADDRESS) + return false; + + // Next look up the "changed" symbol and set a breakpoint on that... + ConstString changed_name ("gdb_objc_trampolines_changed"); + const Symbol *changed_symbol = m_objc_module_sp->FindFirstSymbolWithNameAndType(changed_name, + eSymbolTypeCode); + if (changed_symbol != NULL) + { + const Address &temp_address = changed_symbol->GetValue(); + if (!temp_address.IsValid()) + return false; + + lldb::addr_t changed_addr = temp_address.GetLoadAddress(&target); + if (changed_addr != LLDB_INVALID_ADDRESS) + { + BreakpointSP trampolines_changed_bp_sp = target.CreateBreakpoint (changed_addr, + true); + if (trampolines_changed_bp_sp != NULL) + { + m_trampolines_changed_bp_id = trampolines_changed_bp_sp->GetID(); + trampolines_changed_bp_sp->SetCallback (RefreshTrampolines, this, true); + return true; + } + } + } + } + } + + return false; +} + +bool +AppleObjCTrampolineHandler::AppleObjCVTables::RefreshTrampolines (void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id) +{ + AppleObjCVTables *vtable_handler = (AppleObjCVTables *) baton; + if (vtable_handler->InitializeVTableSymbols()) + { + // The Update function is called with the address of an added region. So we grab that address, and + // feed it into ReadRegions. Of course, our friend the ABI will get the values for us. + Process *process = context->exe_ctx.process; + const ABI *abi = process->GetABI(); + + ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext(); + ValueList argument_values; + Value input_value; + void *clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false); + input_value.SetValueType (Value::eValueTypeScalar); + input_value.SetContext (Value::eContextTypeOpaqueClangQualType, clang_void_ptr_type); + argument_values.PushValue(input_value); + + bool success = abi->GetArgumentValues (*(context->exe_ctx.thread), argument_values); + if (!success) + return false; + + // Now get a pointer value from the zeroth argument. + Error error; + DataExtractor data; + error = argument_values.GetValueAtIndex(0)->GetValueAsData(&(context->exe_ctx), clang_ast_context->getASTContext(), data, 0); + uint32_t offset_ptr = 0; + lldb::addr_t region_addr = data.GetPointer(&offset_ptr); + + if (region_addr != 0) + vtable_handler->ReadRegions(region_addr); + } + return false; +} + +bool +AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions () +{ + // The no argument version reads the start region from the value of the gdb_regions_header, and + // gets started from there. + + m_regions.clear(); + if (!InitializeVTableSymbols()) + return false; + char memory_buffer[8]; + DataExtractor data(memory_buffer, sizeof(memory_buffer), + m_process_sp->GetByteOrder(), + m_process_sp->GetAddressByteSize()); + Error error; + size_t bytes_read = m_process_sp->ReadMemory (m_trampoline_header, memory_buffer, m_process_sp->GetAddressByteSize(), error); + if (bytes_read != m_process_sp->GetAddressByteSize()) + return false; + + uint32_t offset_ptr = 0; + lldb::addr_t region_addr = data.GetPointer(&offset_ptr); + return ReadRegions (region_addr); +} + +bool +AppleObjCTrampolineHandler::AppleObjCVTables::ReadRegions (lldb::addr_t region_addr) +{ + if (!m_process_sp) + return false; + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + + // We aren't starting at the trampoline symbol. + InitializeVTableSymbols (); + lldb::addr_t next_region = region_addr; + + // Read in the sizes of the headers. + while (next_region != 0) + { + m_regions.push_back (VTableRegion(this, next_region)); + if (!m_regions.back().IsValid()) + { + m_regions.clear(); + return false; + } + if (log) + { + StreamString s; + m_regions.back().Dump(s); + log->Printf("Read vtable region: \n%s", s.GetData()); + } + + next_region = m_regions.back().GetNextRegionAddr(); + } + + return true; +} + +bool +AppleObjCTrampolineHandler::AppleObjCVTables::IsAddressInVTables (lldb::addr_t addr, uint32_t &flags) +{ + region_collection::iterator pos, end = m_regions.end(); + for (pos = m_regions.begin(); pos != end; pos++) + { + if ((*pos).AddressInRegion (addr, flags)) + return true; + } + return false; +} + const AppleObjCTrampolineHandler::DispatchFunction AppleObjCTrampolineHandler::g_dispatch_functions[] = { @@ -60,24 +407,9 @@ AppleObjCTrampolineHandler::g_dispatch_functions[] = {NULL} }; -bool -AppleObjCTrampolineHandler::ModuleIsObjCLibrary (const ModuleSP &module_sp) -{ - const FileSpec &module_file_spec = module_sp->GetFileSpec(); - static ConstString ObjCName ("libobjc.A.dylib"); - - if (module_file_spec) - { - if (module_file_spec.GetFilename() == ObjCName) - return true; - } - - return false; -} - -AppleObjCTrampolineHandler::AppleObjCTrampolineHandler (ProcessSP process_sp, ModuleSP objc_module) : +AppleObjCTrampolineHandler::AppleObjCTrampolineHandler (ProcessSP process_sp, ModuleSP objc_module_sp) : m_process_sp (process_sp), - m_objc_module_sp (objc_module), + m_objc_module_sp (objc_module_sp), m_impl_fn_addr (LLDB_INVALID_ADDRESS), m_impl_stret_fn_addr (LLDB_INVALID_ADDRESS) { @@ -119,6 +451,11 @@ AppleObjCTrampolineHandler::AppleObjCTrampolineHandler (ProcessSP process_sp, Mo m_msgSend_map.insert(std::pair(sym_addr, i)); } } + + // Build our vtable dispatch handler here: + m_vtables_ap.reset(new AppleObjCVTables(process_sp, m_objc_module_sp)); + if (m_vtables_ap.get()) + m_vtables_ap->ReadRegions(); } ThreadPlanSP @@ -127,13 +464,38 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool sto ThreadPlanSP ret_plan_sp; lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC(); + DispatchFunction this_dispatch; + bool found_it = false; + MsgsendMap::iterator pos; pos = m_msgSend_map.find (curr_pc); if (pos != m_msgSend_map.end()) + { + this_dispatch = g_dispatch_functions[(*pos).second]; + found_it = true; + } + + if (!found_it) + { + uint32_t flags; + if (m_vtables_ap.get()) + { + found_it = m_vtables_ap->IsAddressInVTables (curr_pc, flags); + if (found_it) + { + this_dispatch.name = "vtable"; + this_dispatch.stret_return + = (flags & AppleObjCVTables::eOBJC_TRAMPOLINE_STRET) == AppleObjCVTables::eOBJC_TRAMPOLINE_STRET; + this_dispatch.is_super = false; + this_dispatch.fixedup = DispatchFunction::eFixUpFixed; + } + } + } + + if (found_it) { Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); - const DispatchFunction *this_dispatch = &g_dispatch_functions[(*pos).second]; lldb::StackFrameSP thread_cur_frame = thread.GetStackFrameAtIndex(0); @@ -161,7 +523,7 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool sto // If this is a struct return dispatch, then the first argument is the // return struct pointer, and the object is the second, and the selector is the third. // Otherwise the object is the first and the selector the second. - if (this_dispatch->stret_return) + if (this_dispatch.stret_return) { obj_index = 1; sel_index = 2; @@ -197,7 +559,7 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool sto isa_value.SetValueType(Value::eValueTypeLoadAddress); isa_value.ResolveValue(&exec_ctx, clang_ast_context->getASTContext()); - if (this_dispatch->fixedup == DispatchFunction::eFixUpFixed) + if (this_dispatch.fixedup == DispatchFunction::eFixUpFixed) { // For the FixedUp method the Selector is actually a pointer to a // structure, the second field of which is the selector number. @@ -206,7 +568,7 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool sto sel_value->SetValueType(Value::eValueTypeLoadAddress); sel_value->ResolveValue(&exec_ctx, clang_ast_context->getASTContext()); } - else if (this_dispatch->fixedup == DispatchFunction::eFixUpToFix) + else if (this_dispatch.fixedup == DispatchFunction::eFixUpToFix) { // FIXME: If the method dispatch is not "fixed up" then the selector is actually a // pointer to the string name of the selector. We need to look that up... @@ -219,7 +581,7 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool sto // FIXME: If this is a dispatch to the super-class, we need to get the super-class from // the class, and disaptch to that instead. // But for now I just punt and return no plan. - if (this_dispatch->is_super) + if (this_dispatch.is_super) { if (log) log->Printf ("Punting on stepping into super method dispatch."); @@ -244,7 +606,7 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool sto if (impl_addr == LLDB_INVALID_ADDRESS) { - Address resolve_address(NULL, this_dispatch->stret_return ? m_impl_stret_fn_addr : m_impl_fn_addr); + Address resolve_address(NULL, this_dispatch.stret_return ? m_impl_stret_fn_addr : m_impl_fn_addr); StreamString errors; { @@ -289,6 +651,12 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool sto dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(), dispatch_values.GetValueAtIndex(1)->GetScalar().ULongLong(), stop_others)); + if (log) + { + StreamString s; + ret_plan_sp->GetDescription(&s, eDescriptionLevelFull); + log->Printf("Using ObjC step plan: %s.\n", s.GetData()); + } } else { diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h index 5e80bac414ca..8c8bea8f0a32 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCTrampolineHandler.h @@ -31,9 +31,7 @@ public: AppleObjCTrampolineHandler (ProcessSP process_sp, ModuleSP objc_module_sp); ~AppleObjCTrampolineHandler() {} - - static bool ModuleIsObjCLibrary (const ModuleSP &module_sp); - + ThreadPlanSP GetStepThroughDispatchPlan (Thread &thread, bool stop_others); @@ -57,6 +55,127 @@ public: }; private: + + class AppleObjCVTables + { + public: + // These come from objc-gdb.h. + enum VTableFlags + { + eOBJC_TRAMPOLINE_MESSAGE = (1<<0), // trampoline acts like objc_msgSend + eOBJC_TRAMPOLINE_STRET = (1<<1), // trampoline is struct-returning + eOBJC_TRAMPOLINE_VTABLE = (1<<2), // trampoline is vtable dispatcher + }; + + private: + struct VTableDescriptor + { + VTableDescriptor(uint32_t in_flags, addr_t in_code_start) : + flags(in_flags), + code_start(in_code_start) {} + + uint32_t flags; + lldb::addr_t code_start; + }; + + + class VTableRegion + { + public: + VTableRegion() : + m_valid (false), + m_owner (NULL), + m_header_addr (LLDB_INVALID_ADDRESS), + m_code_start_addr(0), + m_code_end_addr (0), + m_next_region (0) + {} + + VTableRegion(AppleObjCVTables *owner, lldb::addr_t header_addr); + + void SetUpRegion(); + + lldb::addr_t GetNextRegionAddr () + { + return m_next_region; + } + + lldb::addr_t + GetCodeStart () + { + return m_code_start_addr; + } + + lldb::addr_t + GetCodeEnd () + { + return m_code_end_addr; + } + + uint32_t + GetFlagsForVTableAtAddress (lldb::addr_t address) + { + return 0; + } + + bool + IsValid () + { + return m_valid; + } + + bool + AddressInRegion (lldb::addr_t addr, uint32_t &flags); + + void + Dump (Stream &s); + + public: + bool m_valid; + AppleObjCVTables *m_owner; + lldb::addr_t m_header_addr; + lldb::addr_t m_code_start_addr; + lldb::addr_t m_code_end_addr; + std::vector m_descriptors; + lldb::addr_t m_next_region; + }; + + public: + AppleObjCVTables(ProcessSP &process_sp, ModuleSP &objc_module_sp); + + ~AppleObjCVTables(); + + bool + InitializeVTableSymbols (); + + static bool RefreshTrampolines (void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + bool + ReadRegions (); + + bool + ReadRegions (lldb::addr_t region_addr); + + bool + IsAddressInVTables (lldb::addr_t addr, uint32_t &flags); + + Process *GetProcess () + { + return m_process_sp.get(); + } + + private: + ProcessSP m_process_sp; + typedef std::vector region_collection; + lldb::addr_t m_trampoline_header; + lldb::break_id_t m_trampolines_changed_bp_id; + region_collection m_regions; + lldb::ModuleSP m_objc_module_sp; + + }; + static const DispatchFunction g_dispatch_functions[]; typedef std::map MsgsendMap; // This table maps an dispatch fn address to the index in g_dispatch_functions @@ -68,6 +187,7 @@ private: Mutex m_impl_function_mutex; lldb::addr_t m_impl_fn_addr; lldb::addr_t m_impl_stret_fn_addr; + std::auto_ptr m_vtables_ap; }; diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp index aec37ec00d1b..41a4e55fc052 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleThreadPlanStepThroughObjCTrampoline.cpp @@ -115,6 +115,13 @@ AppleThreadPlanStepThroughObjCTrampoline::ShouldStop (Event *event_ptr) lldb::addr_t target_addr = target_addr_value.GetScalar().ULongLong(); Address target_address(NULL, target_addr); Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP); + if (target_addr == 0) + { + if (log) + log->Printf("Got target implementation of 0x0, stopping."); + SetPlanComplete(); + return true; + } if (log) log->Printf("Running to ObjC method implementation: 0x%llx", target_addr); diff --git a/lldb/source/Target/ThreadPlanStepInRange.cpp b/lldb/source/Target/ThreadPlanStepInRange.cpp index 2be93808f2a0..c19477c605eb 100644 --- a/lldb/source/Target/ThreadPlanStepInRange.cpp +++ b/lldb/source/Target/ThreadPlanStepInRange.cpp @@ -83,23 +83,6 @@ ThreadPlanStepInRange::ShouldStop (Event *event_ptr) if (InRange()) return false; - // If we're in an older frame then we should stop. - if (FrameIsOlder()) - return true; - - // See if we are in a place we should step through (i.e. a trampoline of some sort): - // One tricky bit here is that some stubs don't push a frame, so we have to check - // both the case of a frame that is younger, or the same as this frame. - // However, if the frame is the same, and we are still in the symbol we started - // in, the we don't need to do this. This first check isn't strictly necessary, - // but it is more efficient. - - if (!FrameIsYounger() && InSymbol()) - { - SetPlanComplete(); - return true; - } - ThreadPlan* new_plan = NULL; // Stepping through should be done stopping other threads in general, since we're setting a breakpoint and @@ -111,7 +94,39 @@ ThreadPlanStepInRange::ShouldStop (Event *event_ptr) else stop_others = false; - new_plan = m_thread.QueueThreadPlanForStepThrough (false, stop_others); + if (FrameIsOlder()) + { + // If we're in an older frame then we should stop. + // + // A caveat to this is if we think the frame is older but we're actually in a trampoline. + // I'm going to make the assumption that you wouldn't RETURN to a trampoline. So if we are + // in a trampoline we think the frame is older because the trampoline confused the backtracer. + new_plan = m_thread.QueueThreadPlanForStepThrough (false, stop_others); + if (new_plan == NULL) + return true; + else if (log) + { + log->Printf("Thought I stepped out, but in fact arrived at a trampoline."); + } + + } + else if (!FrameIsYounger() && InSymbol()) + { + // If we are not in a place we should step through, we're done. + // One tricky bit here is that some stubs don't push a frame, so we have to check + // both the case of a frame that is younger, or the same as this frame. + // However, if the frame is the same, and we are still in the symbol we started + // in, the we don't need to do this. This first check isn't strictly necessary, + // but it is more efficient. + + SetPlanComplete(); + return true; + } + + // We may have set the plan up above in the FrameIsOlder section: + + if (new_plan == NULL) + new_plan = m_thread.QueueThreadPlanForStepThrough (false, stop_others); if (log) { diff --git a/lldb/source/Target/ThreadPlanStepOverRange.cpp b/lldb/source/Target/ThreadPlanStepOverRange.cpp index 83e9c23a122f..731232c33a37 100644 --- a/lldb/source/Target/ThreadPlanStepOverRange.cpp +++ b/lldb/source/Target/ThreadPlanStepOverRange.cpp @@ -88,7 +88,19 @@ ThreadPlanStepOverRange::ShouldStop (Event *event_ptr) ThreadPlan* new_plan = NULL; if (FrameIsOlder()) - return true; + { + // If we're in an older frame then we should stop. + // + // A caveat to this is if we think the frame is older but we're actually in a trampoline. + // I'm going to make the assumption that you wouldn't RETURN to a trampoline. So if we are + // in a trampoline we think the frame is older because the trampoline confused the backtracer. + // As below, we step through first, and then try to figure out how to get back out again. + + new_plan = m_thread.QueueThreadPlanForStepThrough (false, stop_others); + + if (new_plan != NULL && log) + log->Printf("Thought I stepped out, but in fact arrived at a trampoline."); + } else if (FrameIsYounger()) { new_plan = m_thread.QueueThreadPlanForStepOut (false, NULL, true, stop_others, lldb::eVoteNo, lldb::eVoteNoOpinion);