diff options
Diffstat (limited to 'source')
332 files changed, 25749 insertions, 7724 deletions
diff --git a/source/API/SBAddress.cpp b/source/API/SBAddress.cpp index 6aec0722169f..d6e32b60059b 100644 --- a/source/API/SBAddress.cpp +++ b/source/API/SBAddress.cpp @@ -23,19 +23,19 @@ using namespace lldb_private; SBAddress::SBAddress () : - m_opaque_ap () + m_opaque_ap (new Address()) { } SBAddress::SBAddress (const Address *lldb_object_ptr) : - m_opaque_ap () + m_opaque_ap (new Address()) { if (lldb_object_ptr) ref() = *lldb_object_ptr; } SBAddress::SBAddress (const SBAddress &rhs) : - m_opaque_ap () + m_opaque_ap (new Address()) { if (rhs.IsValid()) ref() = rhs.ref(); @@ -49,7 +49,7 @@ SBAddress::SBAddress (lldb::SBSection section, lldb::addr_t offset) : // Create an address by resolving a load address using the supplied target SBAddress::SBAddress (lldb::addr_t load_addr, lldb::SBTarget &target) : - m_opaque_ap() + m_opaque_ap(new Address()) { SetLoadAddress (load_addr, target); } @@ -68,7 +68,7 @@ SBAddress::operator = (const SBAddress &rhs) if (rhs.IsValid()) ref() = rhs.ref(); else - m_opaque_ap.reset(); + m_opaque_ap.reset (new Address()); } return *this; } @@ -82,7 +82,7 @@ SBAddress::IsValid () const void SBAddress::Clear () { - m_opaque_ap.reset(); + m_opaque_ap.reset (new Address()); } void @@ -100,13 +100,13 @@ SBAddress::SetAddress (const Address *lldb_object_ptr) if (lldb_object_ptr) ref() = *lldb_object_ptr; else - m_opaque_ap.reset(); + m_opaque_ap.reset (new Address()); } lldb::addr_t SBAddress::GetFileAddress () const { - if (m_opaque_ap.get()) + if (m_opaque_ap->IsValid()) return m_opaque_ap->GetFileAddress(); else return LLDB_INVALID_ADDRESS; @@ -121,7 +121,7 @@ SBAddress::GetLoadAddress (const SBTarget &target) const TargetSP target_sp (target.GetSP()); if (target_sp) { - if (m_opaque_ap.get()) + if (m_opaque_ap->IsValid()) { Mutex::Locker api_locker (target_sp->GetAPIMutex()); addr = m_opaque_ap->GetLoadAddress (target_sp.get()); @@ -162,7 +162,7 @@ SBAddress::SetLoadAddress (lldb::addr_t load_addr, lldb::SBTarget &target) bool SBAddress::OffsetAddress (addr_t offset) { - if (m_opaque_ap.get()) + if (m_opaque_ap->IsValid()) { addr_t addr_offset = m_opaque_ap->GetOffset(); if (addr_offset != LLDB_INVALID_ADDRESS) @@ -178,7 +178,7 @@ lldb::SBSection SBAddress::GetSection () { lldb::SBSection sb_section; - if (m_opaque_ap.get()) + if (m_opaque_ap->IsValid()) sb_section.SetSP (m_opaque_ap->GetSection()); return sb_section; } @@ -186,7 +186,7 @@ SBAddress::GetSection () lldb::addr_t SBAddress::GetOffset () { - if (m_opaque_ap.get()) + if (m_opaque_ap->IsValid()) return m_opaque_ap->GetOffset(); return 0; } @@ -233,7 +233,7 @@ SBAddress::GetDescription (SBStream &description) // Call "ref()" on the stream to make sure it creates a backing stream in // case there isn't one already... Stream &strm = description.ref(); - if (m_opaque_ap.get()) + if (m_opaque_ap->IsValid()) { m_opaque_ap->Dump (&strm, NULL, @@ -255,7 +255,7 @@ SBModule SBAddress::GetModule () { SBModule sb_module; - if (m_opaque_ap.get()) + if (m_opaque_ap->IsValid()) sb_module.SetSP (m_opaque_ap->GetModule()); return sb_module; } @@ -264,7 +264,7 @@ SBSymbolContext SBAddress::GetSymbolContext (uint32_t resolve_scope) { SBSymbolContext sb_sc; - if (m_opaque_ap.get()) + if (m_opaque_ap->IsValid()) m_opaque_ap->CalculateSymbolContext (&sb_sc.ref(), resolve_scope); return sb_sc; } @@ -273,7 +273,7 @@ SBCompileUnit SBAddress::GetCompileUnit () { SBCompileUnit sb_comp_unit; - if (m_opaque_ap.get()) + if (m_opaque_ap->IsValid()) sb_comp_unit.reset(m_opaque_ap->CalculateSymbolContextCompileUnit()); return sb_comp_unit; } @@ -282,7 +282,7 @@ SBFunction SBAddress::GetFunction () { SBFunction sb_function; - if (m_opaque_ap.get()) + if (m_opaque_ap->IsValid()) sb_function.reset(m_opaque_ap->CalculateSymbolContextFunction()); return sb_function; } @@ -291,7 +291,7 @@ SBBlock SBAddress::GetBlock () { SBBlock sb_block; - if (m_opaque_ap.get()) + if (m_opaque_ap->IsValid()) sb_block.SetPtr(m_opaque_ap->CalculateSymbolContextBlock()); return sb_block; } @@ -300,7 +300,7 @@ SBSymbol SBAddress::GetSymbol () { SBSymbol sb_symbol; - if (m_opaque_ap.get()) + if (m_opaque_ap->IsValid()) sb_symbol.reset(m_opaque_ap->CalculateSymbolContextSymbol()); return sb_symbol; } @@ -309,7 +309,7 @@ SBLineEntry SBAddress::GetLineEntry () { SBLineEntry sb_line_entry; - if (m_opaque_ap.get()) + if (m_opaque_ap->IsValid()) { LineEntry line_entry; if (m_opaque_ap->CalculateSymbolContextLineEntry (line_entry)) @@ -321,7 +321,7 @@ SBAddress::GetLineEntry () AddressClass SBAddress::GetAddressClass () { - if (m_opaque_ap.get()) + if (m_opaque_ap->IsValid()) return m_opaque_ap->GetAddressClass(); return eAddressClassInvalid; } diff --git a/source/API/SBBreakpoint.cpp b/source/API/SBBreakpoint.cpp index a950ca934c68..dd4c80caf45d 100644 --- a/source/API/SBBreakpoint.cpp +++ b/source/API/SBBreakpoint.cpp @@ -13,6 +13,7 @@ #include "lldb/API/SBEvent.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" #include "lldb/API/SBThread.h" #include "lldb/Breakpoint/Breakpoint.h" @@ -653,6 +654,83 @@ SBBreakpoint::SetScriptCallbackBody (const char *callback_body_text) return sb_error; } +bool +SBBreakpoint::AddName (const char *new_name) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBreakpoint(%p)::AddName (name=%s)", + static_cast<void*>(m_opaque_sp.get()), + new_name); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + Error error; // Think I'm just going to swallow the error here, it's probably more annoying to have to provide it. + return m_opaque_sp->AddName(new_name, error); + } + + return false; +} + +void +SBBreakpoint::RemoveName (const char *name_to_remove) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBreakpoint(%p)::RemoveName (name=%s)", + static_cast<void*>(m_opaque_sp.get()), + name_to_remove); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->RemoveName(name_to_remove); + } +} + +bool +SBBreakpoint::MatchesName (const char *name) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBreakpoint(%p)::MatchesName (name=%s)", + static_cast<void*>(m_opaque_sp.get()), + name); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + return m_opaque_sp->MatchesName(name); + } + + return false; +} + +void +SBBreakpoint::GetNames (SBStringList &names) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBreakpoint(%p)::GetNames ()", + static_cast<void*>(m_opaque_sp.get())); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + std::vector<std::string> names_vec; + m_opaque_sp->GetNames(names_vec); + for (std::string name : names_vec) + { + names.AppendString (name.c_str()); + } + } +} + lldb_private::Breakpoint * SBBreakpoint::operator->() const { diff --git a/source/API/SBCommandInterpreter.cpp b/source/API/SBCommandInterpreter.cpp index e1adea795b08..193d06e4d920 100644 --- a/source/API/SBCommandInterpreter.cpp +++ b/source/API/SBCommandInterpreter.cpp @@ -20,6 +20,7 @@ #include "lldb/API/SBBroadcaster.h" #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBExecutionContext.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBTarget.h" #include "lldb/API/SBListener.h" @@ -29,6 +30,100 @@ using namespace lldb; using namespace lldb_private; +SBCommandInterpreterRunOptions::SBCommandInterpreterRunOptions() +{ + m_opaque_up.reset(new CommandInterpreterRunOptions()); +} + +SBCommandInterpreterRunOptions::~SBCommandInterpreterRunOptions() +{ + +} + +bool +SBCommandInterpreterRunOptions::GetStopOnContinue () const +{ + return m_opaque_up->GetStopOnContinue(); +} + +void +SBCommandInterpreterRunOptions::SetStopOnContinue (bool stop_on_continue) +{ + m_opaque_up->SetStopOnContinue(stop_on_continue); +} + +bool +SBCommandInterpreterRunOptions::GetStopOnError () const +{ + return m_opaque_up->GetStopOnError(); +} + +void +SBCommandInterpreterRunOptions::SetStopOnError (bool stop_on_error) +{ + m_opaque_up->SetStopOnError(stop_on_error); +} + +bool +SBCommandInterpreterRunOptions::GetStopOnCrash () const +{ + return m_opaque_up->GetStopOnCrash(); +} + +void +SBCommandInterpreterRunOptions::SetStopOnCrash (bool stop_on_crash) +{ + m_opaque_up->SetStopOnCrash(stop_on_crash); +} + +bool +SBCommandInterpreterRunOptions::GetEchoCommands () const +{ + return m_opaque_up->GetEchoCommands(); +} + +void +SBCommandInterpreterRunOptions::SetEchoCommands (bool echo_commands) +{ + m_opaque_up->SetEchoCommands(echo_commands); +} + +bool +SBCommandInterpreterRunOptions::GetPrintResults () const +{ + return m_opaque_up->GetPrintResults(); +} + +void +SBCommandInterpreterRunOptions::SetPrintResults (bool print_results) +{ + m_opaque_up->SetPrintResults(print_results); +} + +bool +SBCommandInterpreterRunOptions::GetAddToHistory () const +{ + return m_opaque_up->GetAddToHistory(); +} + +void +SBCommandInterpreterRunOptions::SetAddToHistory (bool add_to_history) +{ + m_opaque_up->SetAddToHistory(add_to_history); +} + +lldb_private::CommandInterpreterRunOptions * +SBCommandInterpreterRunOptions::get () const +{ + return m_opaque_up.get(); +} + +lldb_private::CommandInterpreterRunOptions & +SBCommandInterpreterRunOptions::ref () const +{ + return *m_opaque_up.get(); +} + class CommandPluginInterfaceImplementation : public CommandObjectParsed { public: @@ -128,6 +223,13 @@ SBCommandInterpreter::GetIOHandlerControlSequence(char ch) lldb::ReturnStatus SBCommandInterpreter::HandleCommand (const char *command_line, SBCommandReturnObject &result, bool add_to_history) { + SBExecutionContext sb_exe_ctx; + return HandleCommand (command_line, sb_exe_ctx, result, add_to_history); +} + +lldb::ReturnStatus +SBCommandInterpreter::HandleCommand (const char *command_line, SBExecutionContext &override_context, SBCommandReturnObject &result, bool add_to_history) +{ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) @@ -135,11 +237,21 @@ SBCommandInterpreter::HandleCommand (const char *command_line, SBCommandReturnOb static_cast<void*>(m_opaque_ptr), command_line, static_cast<void*>(result.get()), add_to_history); + ExecutionContext ctx, *ctx_ptr; + if (override_context.get()) + { + ctx = override_context.get()->Lock(true); + ctx_ptr = &ctx; + } + else + ctx_ptr = nullptr; + + result.Clear(); if (command_line && m_opaque_ptr) { result.ref().SetInteractive(false); - m_opaque_ptr->HandleCommand (command_line, add_to_history ? eLazyBoolYes : eLazyBoolNo, result.ref()); + m_opaque_ptr->HandleCommand (command_line, add_to_history ? eLazyBoolYes : eLazyBoolNo, result.ref(), ctx_ptr); } else { @@ -162,6 +274,54 @@ SBCommandInterpreter::HandleCommand (const char *command_line, SBCommandReturnOb return result.GetStatus(); } +void +SBCommandInterpreter::HandleCommandsFromFile (lldb::SBFileSpec &file, + lldb::SBExecutionContext &override_context, + lldb::SBCommandInterpreterRunOptions &options, + lldb::SBCommandReturnObject result) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + { + SBStream s; + file.GetDescription (s); + log->Printf ("SBCommandInterpreter(%p)::HandleCommandsFromFile (file=\"%s\", SBCommandReturnObject(%p))", + static_cast<void*>(m_opaque_ptr), s.GetData(), + static_cast<void*>(result.get())); + } + + if (!m_opaque_ptr) + { + result->AppendError ("SBCommandInterpreter is not valid."); + result->SetStatus (eReturnStatusFailed); + return; + } + + if (!file.IsValid()) + { + SBStream s; + file.GetDescription (s); + result->AppendErrorWithFormat ("File is not valid: %s.", s.GetData()); + result->SetStatus (eReturnStatusFailed); + } + + FileSpec tmp_spec = file.ref(); + ExecutionContext ctx, *ctx_ptr; + if (override_context.get()) + { + ctx = override_context.get()->Lock(true); + ctx_ptr = &ctx; + } + else + ctx_ptr = nullptr; + + + m_opaque_ptr->HandleCommandsFromFile (tmp_spec, ctx_ptr, options.ref(), result.ref()); + +} + + int SBCommandInterpreter::HandleCompletion (const char *current_line, const char *cursor, @@ -434,6 +594,7 @@ LLDBSwigPythonCallTypeScript (const char *python_function_name, void *session_dictionary, const lldb::ValueObjectSP& valobj_sp, void** pyfunct_wrapper, + const lldb::TypeSummaryOptionsSP& options_sp, std::string& retval); extern "C" void* @@ -442,6 +603,17 @@ LLDBSwigPythonCreateSyntheticProvider (const char *python_class_name, const lldb::ValueObjectSP& valobj_sp); +extern "C" void* +LLDBSwigPythonCreateScriptedThreadPlan (const char *python_class_name, + const char *session_dictionary_name, + const lldb::ThreadPlanSP& thread_plan_sp); + +extern "C" bool +LLDBSWIGPythonCallThreadPlan (void *implementor, + const char *method_name, + Event *event_sp, + bool &got_error); + extern "C" uint32_t LLDBSwigPython_CalculateNumChildren (void *implementor); @@ -463,12 +635,16 @@ LLDBSwigPython_UpdateSynthProviderInstance (void* implementor); extern "C" bool LLDBSwigPython_MightHaveChildrenSynthProviderInstance (void* implementor); +extern "C" void * +LLDBSwigPython_GetValueSynthProviderInstance (void* implementor); + extern "C" bool LLDBSwigPythonCallCommand (const char *python_function_name, const char *session_dictionary_name, lldb::DebuggerSP& debugger, const char* args, - lldb_private::CommandReturnObject &cmd_retobj); + lldb_private::CommandReturnObject &cmd_retobj, + lldb::ExecutionContextRefSP exe_ctx_ref_sp); extern "C" bool LLDBSwigPythonCallModuleInit (const char *python_module_name, @@ -504,6 +680,12 @@ LLDBSWIGPythonRunScriptKeywordFrame (const char* python_function_name, lldb::StackFrameSP& frame, std::string& output); +extern "C" bool +LLDBSWIGPythonRunScriptKeywordValue (const char* python_function_name, + const char* session_dictionary_name, + lldb::ValueObjectSP& value, + std::string& output); + extern "C" void* LLDBSWIGPython_GetDynamicSetting (void* module, const char* setting, @@ -532,6 +714,7 @@ SBCommandInterpreter::InitializeSWIG () LLDBSWIGPython_GetValueObjectSPFromSBValue, LLDBSwigPython_UpdateSynthProviderInstance, LLDBSwigPython_MightHaveChildrenSynthProviderInstance, + LLDBSwigPython_GetValueSynthProviderInstance, LLDBSwigPythonCallCommand, LLDBSwigPythonCallModuleInit, LLDBSWIGPythonCreateOSPlugin, @@ -539,7 +722,10 @@ SBCommandInterpreter::InitializeSWIG () LLDBSWIGPythonRunScriptKeywordThread, LLDBSWIGPythonRunScriptKeywordTarget, LLDBSWIGPythonRunScriptKeywordFrame, - LLDBSWIGPython_GetDynamicSetting); + LLDBSWIGPythonRunScriptKeywordValue, + LLDBSWIGPython_GetDynamicSetting, + LLDBSwigPythonCreateScriptedThreadPlan, + LLDBSWIGPythonCallThreadPlan); #endif } } diff --git a/source/API/SBCommunication.cpp b/source/API/SBCommunication.cpp index df0b864fad94..956b6cfcdd31 100644 --- a/source/API/SBCommunication.cpp +++ b/source/API/SBCommunication.cpp @@ -10,8 +10,8 @@ #include "lldb/API/SBCommunication.h" #include "lldb/API/SBBroadcaster.h" #include "lldb/Core/Communication.h" -#include "lldb/Core/ConnectionFileDescriptor.h" #include "lldb/Core/Log.h" +#include "lldb/Host/ConnectionFileDescriptor.h" using namespace lldb; using namespace lldb_private; @@ -71,7 +71,7 @@ SBCommunication::Connect (const char *url) if (m_opaque) { if (!m_opaque->HasConnection ()) - m_opaque->SetConnection (new ConnectionFileDescriptor()); + m_opaque->SetConnection(Connection::CreateDefaultConnection(url)); return m_opaque->Connect (url, NULL); } return eConnectionStatusNoConnection; diff --git a/source/API/SBCompileUnit.cpp b/source/API/SBCompileUnit.cpp index 03c25710a9e4..5d904ce56300 100644 --- a/source/API/SBCompileUnit.cpp +++ b/source/API/SBCompileUnit.cpp @@ -228,6 +228,14 @@ SBCompileUnit::FindSupportFileIndex (uint32_t start_idx, const SBFileSpec &sb_fi return 0; } +lldb::LanguageType +SBCompileUnit::GetLanguage () +{ + if (m_opaque_ptr) + return m_opaque_ptr->GetLanguage(); + return lldb::eLanguageTypeUnknown; +} + bool SBCompileUnit::IsValid () const { diff --git a/source/API/SBDebugger.cpp b/source/API/SBDebugger.cpp index dae567525a4a..a95f2ffc06d9 100644 --- a/source/API/SBDebugger.cpp +++ b/source/API/SBDebugger.cpp @@ -715,16 +715,13 @@ SBDebugger::CreateTarget (const char *filename) TargetSP target_sp; if (m_opaque_sp) { - ArchSpec arch = Target::GetDefaultArchitecture (); Error error; const bool add_dependent_modules = true; - - PlatformSP platform_sp(m_opaque_sp->GetPlatformList().GetSelectedPlatform()); - error = m_opaque_sp->GetTargetList().CreateTarget (*m_opaque_sp, + error = m_opaque_sp->GetTargetList().CreateTarget (*m_opaque_sp, filename, - arch, + NULL, add_dependent_modules, - platform_sp, + NULL, target_sp); if (error.Success()) @@ -972,7 +969,32 @@ SBDebugger::RunCommandInterpreter (bool auto_handle_events, bool spawn_thread) { if (m_opaque_sp) - m_opaque_sp->GetCommandInterpreter().RunCommandInterpreter(auto_handle_events, spawn_thread); + { + CommandInterpreterRunOptions options; + + m_opaque_sp->GetCommandInterpreter().RunCommandInterpreter(auto_handle_events, + spawn_thread, + options); + } +} + +void +SBDebugger::RunCommandInterpreter (bool auto_handle_events, + bool spawn_thread, + SBCommandInterpreterRunOptions &options, + int &num_errors, + bool &quit_requested, + bool &stopped_for_crash) + +{ + if (m_opaque_sp) + { + CommandInterpreter &interp = m_opaque_sp->GetCommandInterpreter(); + interp.RunCommandInterpreter(auto_handle_events, spawn_thread, options.ref()); + num_errors = interp.GetNumErrors(); + quit_requested = interp.GetQuitRequested(); + stopped_for_crash = interp.GetStoppedForCrash(); + } } void @@ -1186,19 +1208,42 @@ SBDebugger::GetID() SBError -SBDebugger::SetCurrentPlatform (const char *platform_name) +SBDebugger::SetCurrentPlatform (const char *platform_name_cstr) { SBError sb_error; if (m_opaque_sp) { - PlatformSP platform_sp (Platform::Create (platform_name, sb_error.ref())); - - if (platform_sp) + if (platform_name_cstr && platform_name_cstr[0]) { - bool make_selected = true; - m_opaque_sp->GetPlatformList().Append (platform_sp, make_selected); + ConstString platform_name (platform_name_cstr); + PlatformSP platform_sp (Platform::Find (platform_name)); + + if (platform_sp) + { + // Already have a platform with this name, just select it + m_opaque_sp->GetPlatformList().SetSelectedPlatform(platform_sp); + } + else + { + // We don't have a platform by this name yet, create one + platform_sp = Platform::Create (platform_name, sb_error.ref()); + if (platform_sp) + { + // We created the platform, now append and select it + bool make_selected = true; + m_opaque_sp->GetPlatformList().Append (platform_sp, make_selected); + } + } + } + else + { + sb_error.ref().SetErrorString("invalid platform name"); } } + else + { + sb_error.ref().SetErrorString("invalid debugger"); + } return sb_error; } diff --git a/source/API/SBEvent.cpp b/source/API/SBEvent.cpp index 57a699fd739d..c62c495b87c8 100644 --- a/source/API/SBEvent.cpp +++ b/source/API/SBEvent.cpp @@ -43,6 +43,12 @@ SBEvent::SBEvent (EventSP &event_sp) : { } +SBEvent::SBEvent (Event *event_ptr) : + m_event_sp (), + m_opaque_ptr (event_ptr) +{ +} + SBEvent::SBEvent (const SBEvent &rhs) : m_event_sp (rhs.m_event_sp), m_opaque_ptr (rhs.m_opaque_ptr) diff --git a/source/API/SBExecutionContext.cpp b/source/API/SBExecutionContext.cpp new file mode 100644 index 000000000000..dc20c6092132 --- /dev/null +++ b/source/API/SBExecutionContext.cpp @@ -0,0 +1,129 @@ +//===-- SBExecutionContext.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBExecutionContext.h" + +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBThread.h" +#include "lldb/API/SBFrame.h" + +#include "lldb/Target/ExecutionContext.h" + +using namespace lldb; +using namespace lldb_private; + +SBExecutionContext::SBExecutionContext() : +m_exe_ctx_sp() +{ +} + +SBExecutionContext::SBExecutionContext (const lldb::SBExecutionContext &rhs) : +m_exe_ctx_sp(rhs.m_exe_ctx_sp) +{ +} + +SBExecutionContext::SBExecutionContext (lldb::ExecutionContextRefSP exe_ctx_ref_sp) : +m_exe_ctx_sp(exe_ctx_ref_sp) +{ +} + +SBExecutionContext::SBExecutionContext (const lldb::SBTarget &target) : +m_exe_ctx_sp(new ExecutionContextRef()) +{ + m_exe_ctx_sp->SetTargetSP(target.GetSP()); +} + +SBExecutionContext::SBExecutionContext (const lldb::SBProcess &process) : +m_exe_ctx_sp(new ExecutionContextRef()) +{ + m_exe_ctx_sp->SetProcessSP(process.GetSP()); +} + +SBExecutionContext::SBExecutionContext (lldb::SBThread thread) : +m_exe_ctx_sp(new ExecutionContextRef()) +{ + m_exe_ctx_sp->SetThreadPtr(thread.get()); +} + +SBExecutionContext::SBExecutionContext (const lldb::SBFrame &frame) : +m_exe_ctx_sp(new ExecutionContextRef()) +{ + m_exe_ctx_sp->SetFrameSP(frame.GetFrameSP()); +} + +SBExecutionContext::~SBExecutionContext() +{ +} + +const SBExecutionContext & +SBExecutionContext::operator = (const lldb::SBExecutionContext &rhs) +{ + m_exe_ctx_sp = rhs.m_exe_ctx_sp; + return *this; +} + +ExecutionContextRef * +SBExecutionContext::get () const +{ + return m_exe_ctx_sp.get(); +} + +SBTarget +SBExecutionContext::GetTarget () const +{ + SBTarget sb_target; + if (m_exe_ctx_sp) + { + TargetSP target_sp(m_exe_ctx_sp->GetTargetSP()); + if (target_sp) + sb_target.SetSP(target_sp); + } + return sb_target; +} + +SBProcess +SBExecutionContext::GetProcess () const +{ + SBProcess sb_process; + if (m_exe_ctx_sp) + { + ProcessSP process_sp(m_exe_ctx_sp->GetProcessSP()); + if (process_sp) + sb_process.SetSP(process_sp); + } + return sb_process; +} + +SBThread +SBExecutionContext::GetThread () const +{ + SBThread sb_thread; + if (m_exe_ctx_sp) + { + ThreadSP thread_sp(m_exe_ctx_sp->GetThreadSP()); + if (thread_sp) + sb_thread.SetThread(thread_sp); + } + return sb_thread; +} + +SBFrame +SBExecutionContext::GetFrame () const +{ + SBFrame sb_frame; + if (m_exe_ctx_sp) + { + StackFrameSP frame_sp(m_exe_ctx_sp->GetFrameSP()); + if (frame_sp) + sb_frame.SetFrameSP(frame_sp); + } + return sb_frame; +} + diff --git a/source/API/SBFunction.cpp b/source/API/SBFunction.cpp index 3d185da17f26..bf5e9180a437 100644 --- a/source/API/SBFunction.cpp +++ b/source/API/SBFunction.cpp @@ -227,5 +227,15 @@ SBFunction::GetBlock () return sb_block; } +lldb::LanguageType +SBFunction::GetLanguage () +{ + if (m_opaque_ptr) + { + if (m_opaque_ptr->GetCompileUnit()) + return m_opaque_ptr->GetCompileUnit()->GetLanguage(); + } + return lldb::eLanguageTypeUnknown; +} diff --git a/source/API/SBHostOS.cpp b/source/API/SBHostOS.cpp index ec1e2f2e9cba..008ca4d9672e 100644 --- a/source/API/SBHostOS.cpp +++ b/source/API/SBHostOS.cpp @@ -13,6 +13,9 @@ #include "lldb/Core/Log.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostNativeThread.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/ThreadLauncher.h" using namespace lldb; using namespace lldb_private; @@ -69,31 +72,54 @@ SBHostOS::ThreadCreate // FIXME: You should log the return value? - return Host::ThreadCreate (name, thread_function, thread_arg, error_ptr ? error_ptr->get() : NULL); + HostThread thread(ThreadLauncher::LaunchThread(name, thread_function, thread_arg, error_ptr ? error_ptr->get() : NULL)); + return thread.Release(); } void SBHostOS::ThreadCreated (const char *name) { - Host::ThreadCreated (name); } bool SBHostOS::ThreadCancel (lldb::thread_t thread, SBError *error_ptr) { - return Host::ThreadCancel (thread, error_ptr ? error_ptr->get() : NULL); + Error error; + HostThread host_thread(thread); + error = host_thread.Cancel(); + if (error_ptr) + error_ptr->SetError(error); + host_thread.Release(); + return error.Success(); } bool SBHostOS::ThreadDetach (lldb::thread_t thread, SBError *error_ptr) { - return Host::ThreadDetach (thread, error_ptr ? error_ptr->get() : NULL); + Error error; +#if defined(_WIN32) + if (error_ptr) + error_ptr->SetErrorString("ThreadDetach is not supported on this platform"); +#else + HostThread host_thread(thread); + error = host_thread.GetNativeThread().Detach(); + if (error_ptr) + error_ptr->SetError(error); + host_thread.Release(); +#endif + return error.Success(); } bool SBHostOS::ThreadJoin (lldb::thread_t thread, lldb::thread_result_t *result, SBError *error_ptr) { - return Host::ThreadJoin (thread, result, error_ptr ? error_ptr->get() : NULL); + Error error; + HostThread host_thread(thread); + error = host_thread.Join(result); + if (error_ptr) + error_ptr->SetError(error); + host_thread.Release(); + return error.Success(); } diff --git a/source/API/SBInstruction.cpp b/source/API/SBInstruction.cpp index 2334cc0d124a..eccc4e29aadf 100644 --- a/source/API/SBInstruction.cpp +++ b/source/API/SBInstruction.cpp @@ -20,6 +20,7 @@ #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/EmulateInstruction.h" +#include "lldb/Core/Module.h" #include "lldb/Core/StreamFile.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/StackFrame.h" @@ -170,9 +171,15 @@ SBInstruction::GetDescription (lldb::SBStream &s) { if (m_opaque_sp) { + SymbolContext sc; + const Address &addr = m_opaque_sp->GetAddress(); + ModuleSP module_sp (addr.GetModule()); + if (module_sp) + module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, sc); // Use the "ref()" instead of the "get()" accessor in case the SBStream // didn't have a stream already created, one will get created... - m_opaque_sp->Dump (&s.ref(), 0, true, false, NULL); + const char *disassemble_format = "${addr-file-or-load}: "; + m_opaque_sp->Dump (&s.ref(), 0, true, false, NULL, &sc, NULL, disassemble_format); return true; } return false; @@ -186,8 +193,14 @@ SBInstruction::Print (FILE *out) if (m_opaque_sp) { + SymbolContext sc; + const Address &addr = m_opaque_sp->GetAddress(); + ModuleSP module_sp (addr.GetModule()); + if (module_sp) + module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, sc); StreamFile out_stream (out, false); - m_opaque_sp->Dump (&out_stream, 0, true, false, NULL); + const char *disassemble_format = "${addr-file-or-load}: "; + m_opaque_sp->Dump (&out_stream, 0, true, false, NULL, &sc, NULL, disassemble_format); } } diff --git a/source/API/SBInstructionList.cpp b/source/API/SBInstructionList.cpp index fe22d9c29e4a..31585b3e6868 100644 --- a/source/API/SBInstructionList.cpp +++ b/source/API/SBInstructionList.cpp @@ -11,7 +11,9 @@ #include "lldb/API/SBInstruction.h" #include "lldb/API/SBStream.h" #include "lldb/Core/Disassembler.h" +#include "lldb/Core/Module.h" #include "lldb/Core/Stream.h" +#include "lldb/Symbol/SymbolContext.h" using namespace lldb; using namespace lldb_private; @@ -100,12 +102,24 @@ SBInstructionList::GetDescription (lldb::SBStream &description) // exist already inside description... Stream &sref = description.ref(); const uint32_t max_opcode_byte_size = m_opaque_sp->GetInstructionList().GetMaxOpcocdeByteSize(); + const char *disassemble_format = "${addr-file-or-load}: "; + SymbolContext sc; + SymbolContext prev_sc; for (size_t i=0; i<num_instructions; ++i) { Instruction *inst = m_opaque_sp->GetInstructionList().GetInstructionAtIndex (i).get(); if (inst == NULL) break; - inst->Dump (&sref, max_opcode_byte_size, true, false, NULL); + + const Address &addr = inst->GetAddress(); + prev_sc = sc; + ModuleSP module_sp (addr.GetModule()); + if (module_sp) + { + module_sp->ResolveSymbolContextForAddress(addr, eSymbolContextEverything, sc); + } + + inst->Dump (&sref, max_opcode_byte_size, true, false, NULL, &sc, &prev_sc, disassemble_format); sref.EOL(); } return true; diff --git a/source/API/SBListener.cpp b/source/API/SBListener.cpp index bad9ba82bc91..87318739a3a4 100644 --- a/source/API/SBListener.cpp +++ b/source/API/SBListener.cpp @@ -69,6 +69,12 @@ SBListener::SBListener (Listener &listener) : { } +SBListener::SBListener (const lldb::ListenerSP &listener_sp) : + m_opaque_sp (listener_sp), + m_opaque_ptr (listener_sp.get()) +{ +} + SBListener::~SBListener () { } diff --git a/source/API/SBPlatform.cpp b/source/API/SBPlatform.cpp index 9914852cead7..d3e769ae675b 100644 --- a/source/API/SBPlatform.cpp +++ b/source/API/SBPlatform.cpp @@ -269,7 +269,8 @@ SBPlatform::SBPlatform (const char *platform_name) : m_opaque_sp () { Error error; - m_opaque_sp = Platform::Create (platform_name, error); + if (platform_name && platform_name[0]) + m_opaque_sp = Platform::Create (ConstString(platform_name), error); } SBPlatform::~SBPlatform() diff --git a/source/API/SBProcess.cpp b/source/API/SBProcess.cpp index 41efd86177d6..9a0b23bc93d2 100644 --- a/source/API/SBProcess.cpp +++ b/source/API/SBProcess.cpp @@ -38,6 +38,7 @@ #include "lldb/API/SBEvent.h" #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBThread.h" +#include "lldb/API/SBThreadCollection.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" #include "lldb/API/SBUnixSignals.h" @@ -738,18 +739,10 @@ SBProcess::Continue () { Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); - Error error (process_sp->Resume()); - if (error.Success()) - { - if (process_sp->GetTarget().GetDebugger().GetAsyncExecution () == false) - { - if (log) - log->Printf ("SBProcess(%p)::Continue () waiting for process to stop...", - static_cast<void*>(process_sp.get())); - process_sp->WaitForProcessToStop (NULL); - } - } - sb_error.SetError(error); + if (process_sp->GetTarget().GetDebugger().GetAsyncExecution ()) + sb_error.ref() = process_sp->Resume (); + else + sb_error.ref() = process_sp->ResumeSynchronous (NULL); } else sb_error.SetErrorString ("SBProcess is invalid"); @@ -1381,3 +1374,30 @@ SBProcess::GetExtendedBacktraceTypeAtIndex (uint32_t idx) } return NULL; } + +SBThreadCollection +SBProcess::GetHistoryThreads (addr_t addr) +{ + ProcessSP process_sp(GetSP()); + SBThreadCollection threads; + if (process_sp) + { + threads = SBThreadCollection(process_sp->GetHistoryThreads(addr)); + } + return threads; +} + +bool +SBProcess::IsInstrumentationRuntimePresent(InstrumentationRuntimeType type) +{ + ProcessSP process_sp(GetSP()); + if (! process_sp) + return false; + + InstrumentationRuntimeSP runtime_sp = process_sp->GetInstrumentationRuntime(type); + + if (! runtime_sp.get()) + return false; + + return runtime_sp->IsActive(); +} diff --git a/source/API/SBSection.cpp b/source/API/SBSection.cpp index 3fb84e81465c..809eca60c683 100644 --- a/source/API/SBSection.cpp +++ b/source/API/SBSection.cpp @@ -250,6 +250,14 @@ SBSection::GetSectionType () return eSectionTypeInvalid; } +uint32_t +SBSection::GetTargetByteSize () +{ + SectionSP section_sp (GetSP()); + if (section_sp.get()) + return section_sp->GetTargetByteSize(); + return 0; +} bool SBSection::operator == (const SBSection &rhs) diff --git a/source/API/SBTarget.cpp b/source/API/SBTarget.cpp index 3d5828c5fe00..b87b1acf45df 100644 --- a/source/API/SBTarget.cpp +++ b/source/API/SBTarget.cpp @@ -58,6 +58,7 @@ #include "lldb/Interpreter/CommandReturnObject.h" #include "../source/Commands/CommandObjectBreakpoint.h" +#include "llvm/Support/Regex.h" using namespace lldb; @@ -132,6 +133,18 @@ SBLaunchInfo::SetExecutableFile (SBFileSpec exe_file, bool add_as_first_arg) m_opaque_sp->SetExecutableFile(exe_file.ref(), add_as_first_arg); } +SBListener +SBLaunchInfo::GetListener () +{ + return SBListener(m_opaque_sp->GetListener()); +} + +void +SBLaunchInfo::SetListener (SBListener &listener) +{ + m_opaque_sp->SetListener(listener.GetSP()); +} + uint32_t SBLaunchInfo::GetNumArguments () { @@ -235,13 +248,16 @@ SBLaunchInfo::SetProcessPluginName (const char *plugin_name) const char * SBLaunchInfo::GetShell () { - return m_opaque_sp->GetShell(); + // Constify this string so that it is saved in the string pool. Otherwise + // it would be freed when this function goes out of scope. + ConstString shell(m_opaque_sp->GetShell().GetPath().c_str()); + return shell.AsCString(); } void SBLaunchInfo::SetShell (const char * path) { - m_opaque_sp->SetShell (path); + m_opaque_sp->SetShell (FileSpec(path, false)); } uint32_t @@ -516,6 +532,17 @@ SBAttachInfo::ParentProcessIDIsValid() return m_opaque_sp->ParentProcessIDIsValid(); } +SBListener +SBAttachInfo::GetListener () +{ + return SBListener(m_opaque_sp->GetListener()); +} + +void +SBAttachInfo::SetListener (SBListener &listener) +{ + m_opaque_sp->SetListener(listener.GetSP()); +} //---------------------------------------------------------------------- // SBTarget constructor @@ -583,6 +610,19 @@ SBTarget::GetProcess () return sb_process; } +SBPlatform +SBTarget::GetPlatform () +{ + TargetSP target_sp(GetSP()); + if (!target_sp) + return SBPlatform(); + + SBPlatform platform; + platform.m_opaque_sp = target_sp->GetPlatform(); + + return platform; +} + SBDebugger SBTarget::GetDebugger () const { @@ -734,9 +774,9 @@ SBTarget::Launch launch_info.GetEnvironmentEntries ().SetArguments (envp); if (listener.IsValid()) - error.SetError (target_sp->Launch(listener.ref(), launch_info)); - else - error.SetError (target_sp->Launch(target_sp->GetDebugger().GetListener(), launch_info)); + launch_info.SetListener(listener.GetSP()); + + error.SetError (target_sp->Launch(launch_info, NULL)); sb_process.SetSP(target_sp->GetProcessSP()); } @@ -800,7 +840,7 @@ SBTarget::Launch (SBLaunchInfo &sb_launch_info, SBError& error) if (arch_spec.IsValid()) launch_info.GetArchitecture () = arch_spec; - error.SetError (target_sp->Launch (target_sp->GetDebugger().GetListener(), launch_info)); + error.SetError (target_sp->Launch (launch_info, NULL)); sb_process.SetSP(target_sp->GetProcessSP()); } else @@ -1000,7 +1040,7 @@ SBTarget::AttachToProcessWithID // If we are doing synchronous mode, then wait for the // process to stop! if (target_sp->GetDebugger().GetAsyncExecution () == false) - process_sp->WaitForProcessToStop (NULL); + process_sp->WaitForProcessToStop (NULL); } } else @@ -1227,6 +1267,22 @@ SBTarget::ResolveLoadAddress (lldb::addr_t vm_addr) return sb_addr; } +lldb::SBAddress +SBTarget::ResolveFileAddress (lldb::addr_t file_addr) +{ + lldb::SBAddress sb_addr; + Address &addr = sb_addr.ref(); + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + if (target_sp->ResolveFileAddress (file_addr, addr)) + return sb_addr; + } + + addr.SetRawAddress(file_addr); + return sb_addr; +} lldb::SBAddress SBTarget::ResolvePastLoadAddress (uint32_t stop_id, lldb::addr_t vm_addr) @@ -1261,6 +1317,27 @@ SBTarget::ResolveSymbolContextForAddress (const SBAddress& addr, return sc; } +size_t +SBTarget::ReadMemory (const SBAddress addr, + void *buf, + size_t size, + lldb::SBError &error) +{ + SBError sb_error; + size_t bytes_read = 0; + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + bytes_read = target_sp->ReadMemory(addr.ref(), false, buf, size, sb_error.ref()); + } + else + { + sb_error.SetErrorString("invalid target"); + } + + return bytes_read; +} SBBreakpoint SBTarget::BreakpointCreateByLocation (const char *file, @@ -1868,30 +1945,10 @@ SBTarget::CreateValueFromAddress (const char *name, SBAddress addr, SBType type) lldb::ValueObjectSP new_value_sp; if (IsValid() && name && *name && addr.IsValid() && type.IsValid()) { - lldb::addr_t address(addr.GetLoadAddress(*this)); - lldb::TypeImplSP type_impl_sp (type.GetSP()); - ClangASTType pointer_ast_type(type_impl_sp->GetClangASTType(true).GetPointerType ()); - if (pointer_ast_type) - { - lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(&address,sizeof(lldb::addr_t))); - - ExecutionContext exe_ctx (ExecutionContextRef(ExecutionContext(m_opaque_sp.get(),false))); - ValueObjectSP ptr_result_valobj_sp(ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), - pointer_ast_type, - ConstString(name), - buffer, - exe_ctx.GetByteOrder(), - exe_ctx.GetAddressByteSize())); - - if (ptr_result_valobj_sp) - { - ptr_result_valobj_sp->GetValue().SetValueType(Value::eValueTypeLoadAddress); - Error err; - new_value_sp = ptr_result_valobj_sp->Dereference(err); - if (new_value_sp) - new_value_sp->SetName(ConstString(name)); - } - } + lldb::addr_t load_addr(addr.GetLoadAddress(*this)); + ExecutionContext exe_ctx (ExecutionContextRef(ExecutionContext(m_opaque_sp.get(),false))); + ClangASTType ast_type(type.GetSP()->GetClangASTType(true)); + new_value_sp = ValueObject::CreateValueObjectFromAddress(name, load_addr, exe_ctx, ast_type); } sb_value.SetSP(new_value_sp); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); @@ -1908,6 +1965,58 @@ SBTarget::CreateValueFromAddress (const char *name, SBAddress addr, SBType type) return sb_value; } +lldb::SBValue +SBTarget::CreateValueFromData (const char *name, lldb::SBData data, lldb::SBType type) +{ + SBValue sb_value; + lldb::ValueObjectSP new_value_sp; + if (IsValid() && name && *name && data.IsValid() && type.IsValid()) + { + DataExtractorSP extractor(*data); + ExecutionContext exe_ctx (ExecutionContextRef(ExecutionContext(m_opaque_sp.get(),false))); + ClangASTType ast_type(type.GetSP()->GetClangASTType(true)); + new_value_sp = ValueObject::CreateValueObjectFromData(name, *extractor, exe_ctx, ast_type); + } + sb_value.SetSP(new_value_sp); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (new_value_sp) + log->Printf ("SBTarget(%p)::CreateValueFromData => \"%s\"", + static_cast<void*>(m_opaque_sp.get()), + new_value_sp->GetName().AsCString()); + else + log->Printf ("SBTarget(%p)::CreateValueFromData => NULL", + static_cast<void*>(m_opaque_sp.get())); + } + return sb_value; +} + +lldb::SBValue +SBTarget::CreateValueFromExpression (const char *name, const char* expr) +{ + SBValue sb_value; + lldb::ValueObjectSP new_value_sp; + if (IsValid() && name && *name && expr && *expr) + { + ExecutionContext exe_ctx (ExecutionContextRef(ExecutionContext(m_opaque_sp.get(),false))); + new_value_sp = ValueObject::CreateValueObjectFromExpression(name, expr, exe_ctx); + } + sb_value.SetSP(new_value_sp); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (new_value_sp) + log->Printf ("SBTarget(%p)::CreateValueFromExpression => \"%s\"", + static_cast<void*>(m_opaque_sp.get()), + new_value_sp->GetName().AsCString()); + else + log->Printf ("SBTarget(%p)::CreateValueFromExpression => NULL", + static_cast<void*>(m_opaque_sp.get())); + } + return sb_value; +} + bool SBTarget::DeleteAllWatchpoints () { @@ -2057,6 +2166,28 @@ SBTarget::GetTriple () } uint32_t +SBTarget::GetDataByteSize () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + return target_sp->GetArchitecture().GetDataByteSize() ; + } + return 0; +} + +uint32_t +SBTarget::GetCodeByteSize () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + return target_sp->GetArchitecture().GetCodeByteSize() ; + } + return 0; +} + +uint32_t SBTarget::GetAddressByteSize() { TargetSP target_sp(GetSP()); @@ -2154,6 +2285,34 @@ SBTarget::FindFunctions (const char *name, uint32_t name_type_mask) return sb_sc_list; } +lldb::SBSymbolContextList +SBTarget::FindGlobalFunctions(const char *name, uint32_t max_matches, MatchType matchtype) +{ + lldb::SBSymbolContextList sb_sc_list; + if (name && name[0]) + { + TargetSP target_sp(GetSP()); + if (target_sp) + { + std::string regexstr; + switch (matchtype) + { + case eMatchTypeRegex: + target_sp->GetImages().FindFunctions(RegularExpression(name), true, true, true, *sb_sc_list); + break; + case eMatchTypeStartsWith: + regexstr = llvm::Regex::escape(name) + ".*"; + target_sp->GetImages().FindFunctions(RegularExpression(regexstr.c_str()), true, true, true, *sb_sc_list); + break; + default: + target_sp->GetImages().FindFunctions(ConstString(name), eFunctionNameTypeAny, true, true, true, *sb_sc_list); + break; + } + } + } + return sb_sc_list; +} + lldb::SBType SBTarget::FindFirstType (const char* typename_cstr) { @@ -2188,14 +2347,19 @@ SBTarget::FindFirstType (const char* typename_cstr) if (objc_language_runtime) { - TypeVendor *objc_type_vendor = objc_language_runtime->GetTypeVendor(); + DeclVendor *objc_decl_vendor = objc_language_runtime->GetDeclVendor(); - if (objc_type_vendor) + if (objc_decl_vendor) { - std::vector <ClangASTType> types; + std::vector <clang::NamedDecl *> decls; - if (objc_type_vendor->FindTypes(const_typename, true, 1, types) > 0) - return SBType(types[0]); + if (objc_decl_vendor->FindDecls(const_typename, true, 1, decls) > 0) + { + if (ClangASTType type = ClangASTContext::GetTypeForDecl(decls[0])) + { + return SBType(type); + } + } } } } @@ -2261,17 +2425,20 @@ SBTarget::FindTypes (const char* typename_cstr) if (objc_language_runtime) { - TypeVendor *objc_type_vendor = objc_language_runtime->GetTypeVendor(); + DeclVendor *objc_decl_vendor = objc_language_runtime->GetDeclVendor(); - if (objc_type_vendor) + if (objc_decl_vendor) { - std::vector <ClangASTType> types; + std::vector <clang::NamedDecl *> decls; - if (objc_type_vendor->FindTypes(const_typename, true, UINT32_MAX, types)) + if (objc_decl_vendor->FindDecls(const_typename, true, 1, decls) > 0) { - for (ClangASTType &type : types) + for (clang::NamedDecl *decl : decls) { - sb_type_list.Append(SBType(type)); + if (ClangASTType type = ClangASTContext::GetTypeForDecl(decl)) + { + sb_type_list.Append(SBType(type)); + } } } } @@ -2321,6 +2488,61 @@ SBTarget::FindGlobalVariables (const char *name, uint32_t max_matches) return sb_value_list; } +SBValueList +SBTarget::FindGlobalVariables(const char *name, uint32_t max_matches, MatchType matchtype) +{ + SBValueList sb_value_list; + + TargetSP target_sp(GetSP()); + if (name && target_sp) + { + VariableList variable_list; + const bool append = true; + + std::string regexstr; + uint32_t match_count; + switch (matchtype) + { + case eMatchTypeNormal: + match_count = target_sp->GetImages().FindGlobalVariables(ConstString(name), + append, + max_matches, + variable_list); + break; + case eMatchTypeRegex: + match_count = target_sp->GetImages().FindGlobalVariables(RegularExpression(name), + append, + max_matches, + variable_list); + break; + case eMatchTypeStartsWith: + regexstr = llvm::Regex::escape(name) + ".*"; + match_count = target_sp->GetImages().FindGlobalVariables(RegularExpression(regexstr.c_str()), + append, + max_matches, + variable_list); + break; + } + + + if (match_count > 0) + { + ExecutionContextScope *exe_scope = target_sp->GetProcessSP().get(); + if (exe_scope == NULL) + exe_scope = target_sp.get(); + for (uint32_t i = 0; i<match_count; ++i) + { + lldb::ValueObjectSP valobj_sp(ValueObjectVariable::Create(exe_scope, variable_list.GetVariableAtIndex(i))); + if (valobj_sp) + sb_value_list.Append(SBValue(valobj_sp)); + } + } + } + + return sb_value_list; +} + + lldb::SBValue SBTarget::FindFirstGlobalVariable (const char* name) { diff --git a/source/API/SBThread.cpp b/source/API/SBThread.cpp index a0bfa4313535..6524d10fb705 100644 --- a/source/API/SBThread.cpp +++ b/source/API/SBThread.cpp @@ -41,6 +41,7 @@ #include "lldb/API/SBEvent.h" #include "lldb/API/SBFrame.h" #include "lldb/API/SBProcess.h" +#include "lldb/API/SBThreadPlan.h" #include "lldb/API/SBValue.h" using namespace lldb; @@ -194,6 +195,7 @@ SBThread::GetStopReasonDataCount () case eStopReasonExec: case eStopReasonPlanComplete: case eStopReasonThreadExiting: + case eStopReasonInstrumentation: // There is no data for these stop reasons. return 0; @@ -254,6 +256,7 @@ SBThread::GetStopReasonDataAtIndex (uint32_t idx) case eStopReasonExec: case eStopReasonPlanComplete: case eStopReasonThreadExiting: + case eStopReasonInstrumentation: // There is no data for these stop reasons. return 0; @@ -305,6 +308,26 @@ SBThread::GetStopReasonDataAtIndex (uint32_t idx) return 0; } +bool +SBThread::GetStopReasonExtendedInfoAsJSON (lldb::SBStream &stream) +{ + Stream &strm = stream.ref(); + + ExecutionContext exe_ctx (m_opaque_sp.get()); + if (! exe_ctx.HasThreadScope()) + return false; + + + StopInfoSP stop_info = exe_ctx.GetThreadPtr()->GetStopInfo(); + StructuredData::ObjectSP info = stop_info->GetExtendedInfo(); + if (! info) + return false; + + info->Dump(strm); + + return true; +} + size_t SBThread::GetStopDescription (char *dst, size_t dst_len) { @@ -687,15 +710,11 @@ SBThread::ResumeNewPlan (ExecutionContext &exe_ctx, ThreadPlan *new_plan) // Why do we need to set the current thread by ID here??? process->GetThreadList().SetSelectedThreadByID (thread->GetID()); - sb_error.ref() = process->Resume(); - - if (sb_error.Success()) - { - // If we are doing synchronous mode, then wait for the - // process to stop yet again! - if (process->GetTarget().GetDebugger().GetAsyncExecution () == false) - process->WaitForProcessToStop (NULL); - } + + if (process->GetTarget().GetDebugger().GetAsyncExecution ()) + sb_error.ref() = process->Resume (); + else + sb_error.ref() = process->ResumeSynchronous (NULL); return sb_error; } @@ -918,7 +937,9 @@ SBThread::RunToAddress (lldb::addr_t addr) Thread *thread = exe_ctx.GetThreadPtr(); - ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForRunToAddress (abort_other_plans, target_addr, stop_other_threads)); + ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForRunToAddress (abort_other_plans, + target_addr, + stop_other_threads)); // This returns an error, we should use it! ResumeNewPlan (exe_ctx, new_plan_sp.get()); @@ -1073,6 +1094,46 @@ SBThread::StepOverUntil (lldb::SBFrame &sb_frame, } SBError +SBThread::StepUsingScriptedThreadPlan (const char *script_class_name) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBError sb_error; + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (log) + { + log->Printf ("SBThread(%p)::StepUsingScriptedThreadPlan: class name: %s", + static_cast<void*>(exe_ctx.GetThreadPtr()), + script_class_name); + } + + + if (!exe_ctx.HasThreadScope()) + { + sb_error.SetErrorString("this SBThread object is invalid"); + return sb_error; + } + + Thread *thread = exe_ctx.GetThreadPtr(); + ThreadPlanSP thread_plan_sp = thread->QueueThreadPlanForStepScripted(false, script_class_name, false); + + if (thread_plan_sp) + sb_error = ResumeNewPlan(exe_ctx, thread_plan_sp.get()); + else + { + sb_error.SetErrorStringWithFormat("Error queuing thread plan for class: %s.", script_class_name); + if (log) + log->Printf ("SBThread(%p)::StepUsingScriptedThreadPlan: Error queuing thread plan for class: %s", + static_cast<void*>(exe_ctx.GetThreadPtr()), + script_class_name); + } + + return sb_error; +} + +SBError SBThread::JumpToLine (lldb::SBFileSpec &file_spec, uint32_t line) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); @@ -1473,7 +1534,8 @@ SBThread::GetExtendedBacktraceThread (const char *type) const char *queue_name = new_thread_sp->GetQueueName(); if (queue_name == NULL) queue_name = ""; - log->Printf ("SBThread(%p)::GetExtendedBacktraceThread() => new extended Thread created (%p) with queue_id 0x%" PRIx64 " queue name '%s'", + log->Printf ("SBThread(%p)::GetExtendedBacktraceThread() => new extended Thread " + "created (%p) with queue_id 0x%" PRIx64 " queue name '%s'", static_cast<void*>(exe_ctx.GetThreadPtr()), static_cast<void*>(new_thread_sp.get()), new_thread_sp->GetQueueID(), @@ -1515,3 +1577,24 @@ SBThread::SafeToCallFunctions () return thread_sp->SafeToCallFunctions(); return true; } + +lldb_private::Thread * +SBThread::operator->() +{ + ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); + if (thread_sp) + return thread_sp.get(); + else + return NULL; +} + +lldb_private::Thread * +SBThread::get() +{ + ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); + if (thread_sp) + return thread_sp.get(); + else + return NULL; +} + diff --git a/source/API/SBThreadCollection.cpp b/source/API/SBThreadCollection.cpp new file mode 100644 index 000000000000..841f93253a53 --- /dev/null +++ b/source/API/SBThreadCollection.cpp @@ -0,0 +1,97 @@ +//===-- SBThreadCollection.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBThreadCollection.h" +#include "lldb/API/SBThread.h" +#include "lldb/Target/ThreadList.h" + +using namespace lldb; +using namespace lldb_private; + + +SBThreadCollection::SBThreadCollection () : + m_opaque_sp() +{ +} + +SBThreadCollection::SBThreadCollection(const SBThreadCollection &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +const SBThreadCollection & +SBThreadCollection::operator = (const SBThreadCollection &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +SBThreadCollection::SBThreadCollection (const ThreadCollectionSP &threads) : + m_opaque_sp(threads) +{ +} + +SBThreadCollection::~SBThreadCollection () +{ +} + +void +SBThreadCollection::SetOpaque (const lldb::ThreadCollectionSP &threads) +{ + m_opaque_sp = threads; +} + +lldb_private::ThreadCollection * +SBThreadCollection::get() const +{ + return m_opaque_sp.get(); +} + +lldb_private::ThreadCollection * +SBThreadCollection::operator->() const +{ + return m_opaque_sp.operator->(); +} + +lldb::ThreadCollectionSP & +SBThreadCollection::operator*() +{ + return m_opaque_sp; +} + +const lldb::ThreadCollectionSP & +SBThreadCollection::operator*() const +{ + return m_opaque_sp; +} + + +bool +SBThreadCollection::IsValid () const +{ + return m_opaque_sp.get() != NULL; +} + +size_t +SBThreadCollection::GetSize () +{ + if (m_opaque_sp) + return m_opaque_sp->GetSize(); + return 0; +} + +SBThread +SBThreadCollection::GetThreadAtIndex(size_t idx) +{ + SBThread thread; + if (m_opaque_sp && idx < m_opaque_sp->GetSize()) + thread = m_opaque_sp->GetThreadAtIndex(idx); + return thread; +} diff --git a/source/API/SBThreadPlan.cpp b/source/API/SBThreadPlan.cpp new file mode 100644 index 000000000000..02b1a8d893b6 --- /dev/null +++ b/source/API/SBThreadPlan.cpp @@ -0,0 +1,285 @@ +//===-- SBThread.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBThread.h" + +#include "lldb/API/SBSymbolContext.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBStream.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StructuredData.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/SystemRuntime.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Queue.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanPython.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepRange.h" +#include "lldb/Target/ThreadPlanStepInRange.h" + + +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBThreadPlan.h" +#include "lldb/API/SBValue.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Constructors +//---------------------------------------------------------------------- +SBThreadPlan::SBThreadPlan () +{ +} + +SBThreadPlan::SBThreadPlan (const ThreadPlanSP& lldb_object_sp) : + m_opaque_sp (lldb_object_sp) +{ +} + +SBThreadPlan::SBThreadPlan (const SBThreadPlan &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ + +} + +SBThreadPlan::SBThreadPlan (lldb::SBThread &sb_thread, const char *class_name) +{ + Thread *thread = sb_thread.get(); + if (thread) + m_opaque_sp.reset(new ThreadPlanPython(*thread, class_name)); +} + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- + +const lldb::SBThreadPlan & +SBThreadPlan::operator = (const SBThreadPlan &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SBThreadPlan::~SBThreadPlan() +{ +} + +lldb_private::ThreadPlan * +SBThreadPlan::get() +{ + return m_opaque_sp.get(); +} + +bool +SBThreadPlan::IsValid() const +{ + return m_opaque_sp.get() != NULL; +} + +void +SBThreadPlan::Clear () +{ + m_opaque_sp.reset(); +} + +lldb::StopReason +SBThreadPlan::GetStopReason() +{ + return eStopReasonNone; +} + +size_t +SBThreadPlan::GetStopReasonDataCount() +{ + return 0; +} + +uint64_t +SBThreadPlan::GetStopReasonDataAtIndex(uint32_t idx) +{ + return 0; +} + +SBThread +SBThreadPlan::GetThread () const +{ + if (m_opaque_sp) + { + return SBThread(m_opaque_sp->GetThread().shared_from_this()); + } + else + return SBThread(); +} + +bool +SBThreadPlan::GetDescription (lldb::SBStream &description) const +{ + if (m_opaque_sp) + { + m_opaque_sp->GetDescription(description.get(), eDescriptionLevelFull); + } + else + { + description.Printf("Empty SBThreadPlan"); + } + return true; +} + +void +SBThreadPlan::SetThreadPlan (const ThreadPlanSP& lldb_object_sp) +{ + m_opaque_sp = lldb_object_sp; +} + +void +SBThreadPlan::SetPlanComplete (bool success) +{ + if (m_opaque_sp) + m_opaque_sp->SetPlanComplete (success); +} + +bool +SBThreadPlan::IsPlanComplete() +{ + if (m_opaque_sp) + return m_opaque_sp->IsPlanComplete(); + else + return true; +} + +bool +SBThreadPlan::IsValid() +{ + if (m_opaque_sp) + return m_opaque_sp->ValidatePlan(nullptr); + else + return false; +} + + // This section allows an SBThreadPlan to push another of the common types of plans... + // + // FIXME, you should only be able to queue thread plans from inside the methods of a + // Scripted Thread Plan. Need a way to enforce that. + +SBThreadPlan +SBThreadPlan::QueueThreadPlanForStepOverRange (SBAddress &sb_start_address, + lldb::addr_t size) +{ + if (m_opaque_sp) + { + Address *start_address = sb_start_address.get(); + if (!start_address) + { + return SBThreadPlan(); + } + + AddressRange range (*start_address, size); + SymbolContext sc; + start_address->CalculateSymbolContext(&sc); + return SBThreadPlan (m_opaque_sp->GetThread().QueueThreadPlanForStepOverRange (false, + range, + sc, + eAllThreads)); + } + else + { + return SBThreadPlan(); + } +} + +SBThreadPlan +SBThreadPlan::QueueThreadPlanForStepInRange (SBAddress &sb_start_address, + lldb::addr_t size) +{ + if (m_opaque_sp) + { + Address *start_address = sb_start_address.get(); + if (!start_address) + { + return SBThreadPlan(); + } + + AddressRange range (*start_address, size); + SymbolContext sc; + start_address->CalculateSymbolContext(&sc); + return SBThreadPlan (m_opaque_sp->GetThread().QueueThreadPlanForStepInRange (false, + range, + sc, + NULL, + eAllThreads)); + } + else + { + return SBThreadPlan(); + } +} + +SBThreadPlan +SBThreadPlan::QueueThreadPlanForStepOut (uint32_t frame_idx_to_step_to, bool first_insn) +{ + if (m_opaque_sp) + { + SymbolContext sc; + sc = m_opaque_sp->GetThread().GetStackFrameAtIndex(0)->GetSymbolContext(lldb::eSymbolContextEverything); + return SBThreadPlan (m_opaque_sp->GetThread().QueueThreadPlanForStepOut (false, + &sc, + first_insn, + false, + eVoteYes, + eVoteNoOpinion, + frame_idx_to_step_to)); + } + else + { + return SBThreadPlan(); + } +} + +SBThreadPlan +SBThreadPlan::QueueThreadPlanForRunToAddress (SBAddress sb_address) +{ + if (m_opaque_sp) + { + Address *address = sb_address.get(); + if (!address) + return SBThreadPlan(); + + return SBThreadPlan (m_opaque_sp->GetThread().QueueThreadPlanForRunToAddress (false, + *address, + false)); + } + else + { + return SBThreadPlan(); + } +} + + diff --git a/source/API/SBType.cpp b/source/API/SBType.cpp index 064fb32c953f..8a0f5d848a3d 100644 --- a/source/API/SBType.cpp +++ b/source/API/SBType.cpp @@ -156,6 +156,14 @@ SBType::IsPointerType() } bool +SBType::IsArrayType() +{ + if (!IsValid()) + return false; + return m_opaque_sp->GetClangASTType(true).IsArrayType(nullptr, nullptr, nullptr); +} + +bool SBType::IsReferenceType() { if (!IsValid()) @@ -204,6 +212,14 @@ SBType::GetDereferencedType() return SBType(TypeImplSP(new TypeImpl(m_opaque_sp->GetDereferencedType()))); } +SBType +SBType::GetArrayElementType() +{ + if (!IsValid()) + return SBType(); + return SBType(TypeImplSP(new TypeImpl(m_opaque_sp->GetClangASTType(true).GetArrayElementType()))); +} + bool SBType::IsFunctionType () { @@ -220,7 +236,13 @@ SBType::IsPolymorphicClass () return m_opaque_sp->GetClangASTType(true).IsPolymorphicClass(); } - +bool +SBType::IsTypedefType () +{ + if (!IsValid()) + return false; + return m_opaque_sp->GetClangASTType(true).IsTypedefType(); +} lldb::SBType SBType::GetFunctionReturnType () @@ -252,6 +274,25 @@ SBType::GetFunctionArgumentTypes () return sb_type_list; } +uint32_t +SBType::GetNumberOfMemberFunctions () +{ + if (IsValid()) + { + return m_opaque_sp->GetClangASTType(true).GetNumMemberFunctions(); + } + return 0; +} + +lldb::SBTypeMemberFunction +SBType::GetMemberFunctionAtIndex (uint32_t idx) +{ + SBTypeMemberFunction sb_func_type; + if (IsValid()) + sb_func_type.reset(new TypeMemberFunctionImpl(m_opaque_sp->GetClangASTType(true).GetMemberFunctionAtIndex(idx))); + return sb_func_type; +} + lldb::SBType SBType::GetUnqualifiedType() { @@ -305,7 +346,7 @@ uint32_t SBType::GetNumberOfFields () { if (IsValid()) - return m_opaque_sp->GetClangASTType(false).GetNumFields(); + return m_opaque_sp->GetClangASTType(true).GetNumFields(); return 0; } @@ -430,6 +471,14 @@ SBType::IsTypeComplete() return m_opaque_sp->GetClangASTType(false).IsCompleteType(); } +uint32_t +SBType::GetTypeFlags () +{ + if (!IsValid()) + return 0; + return m_opaque_sp->GetClangASTType(true).GetTypeInfo(); +} + const char* SBType::GetName() { @@ -450,7 +499,7 @@ lldb::TypeClass SBType::GetTypeClass () { if (IsValid()) - return m_opaque_sp->GetClangASTType(false).GetTypeClass(); + return m_opaque_sp->GetClangASTType(true).GetTypeClass(); return lldb::eTypeClassInvalid; } @@ -684,3 +733,121 @@ SBTypeMember::ref () const { return *m_opaque_ap.get(); } + +SBTypeMemberFunction::SBTypeMemberFunction() : +m_opaque_sp() +{ +} + +SBTypeMemberFunction::~SBTypeMemberFunction() +{ +} + +SBTypeMemberFunction::SBTypeMemberFunction (const SBTypeMemberFunction& rhs) : + m_opaque_sp(rhs.m_opaque_sp) +{ +} + +lldb::SBTypeMemberFunction& +SBTypeMemberFunction::operator = (const lldb::SBTypeMemberFunction& rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +bool +SBTypeMemberFunction::IsValid() const +{ + return m_opaque_sp.get(); +} + +const char * +SBTypeMemberFunction::GetName () +{ + if (m_opaque_sp) + return m_opaque_sp->GetName().GetCString(); + return NULL; +} + +SBType +SBTypeMemberFunction::GetType () +{ + SBType sb_type; + if (m_opaque_sp) + { + sb_type.SetSP(lldb::TypeImplSP(new TypeImpl(m_opaque_sp->GetType()))); + } + return sb_type; +} + +lldb::SBType +SBTypeMemberFunction::GetReturnType () +{ + SBType sb_type; + if (m_opaque_sp) + { + sb_type.SetSP(lldb::TypeImplSP(new TypeImpl(m_opaque_sp->GetReturnType()))); + } + return sb_type; +} + +uint32_t +SBTypeMemberFunction::GetNumberOfArguments () +{ + if (m_opaque_sp) + return m_opaque_sp->GetNumArguments(); + return 0; +} + +lldb::SBType +SBTypeMemberFunction::GetArgumentTypeAtIndex (uint32_t i) +{ + SBType sb_type; + if (m_opaque_sp) + { + sb_type.SetSP(lldb::TypeImplSP(new TypeImpl(m_opaque_sp->GetArgumentAtIndex(i)))); + } + return sb_type; +} + +lldb::MemberFunctionKind +SBTypeMemberFunction::GetKind () +{ + if (m_opaque_sp) + return m_opaque_sp->GetKind(); + return lldb::eMemberFunctionKindUnknown; + +} + +bool +SBTypeMemberFunction::GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level) +{ + Stream &strm = description.ref(); + + if (m_opaque_sp) + return m_opaque_sp->GetDescription(strm); + + return false; +} + +void +SBTypeMemberFunction::reset(TypeMemberFunctionImpl *type_member_impl) +{ + m_opaque_sp.reset(type_member_impl); +} + +TypeMemberFunctionImpl & +SBTypeMemberFunction::ref () +{ + if (!m_opaque_sp) + m_opaque_sp.reset (new TypeMemberFunctionImpl()); + return *m_opaque_sp.get(); +} + +const TypeMemberFunctionImpl & +SBTypeMemberFunction::ref () const +{ + return *m_opaque_sp.get(); +} diff --git a/source/API/SBTypeSummary.cpp b/source/API/SBTypeSummary.cpp index aaa09c289cb1..8a235bf50080 100644 --- a/source/API/SBTypeSummary.cpp +++ b/source/API/SBTypeSummary.cpp @@ -20,6 +20,103 @@ using namespace lldb_private; #ifndef LLDB_DISABLE_PYTHON +SBTypeSummaryOptions::SBTypeSummaryOptions() +{ + m_opaque_ap.reset(new TypeSummaryOptions()); +} + +SBTypeSummaryOptions::SBTypeSummaryOptions (const lldb::SBTypeSummaryOptions &rhs) +{ + if (rhs.m_opaque_ap) + m_opaque_ap.reset(new TypeSummaryOptions(*rhs.m_opaque_ap.get())); + else + m_opaque_ap.reset(new TypeSummaryOptions()); +} + +SBTypeSummaryOptions::~SBTypeSummaryOptions () +{ +} + +bool +SBTypeSummaryOptions::IsValid() +{ + return m_opaque_ap.get(); +} + +lldb::LanguageType +SBTypeSummaryOptions::GetLanguage () +{ + if (IsValid()) + return m_opaque_ap->GetLanguage(); + return lldb::eLanguageTypeUnknown; +} + +lldb::TypeSummaryCapping +SBTypeSummaryOptions::GetCapping () +{ + if (IsValid()) + return m_opaque_ap->GetCapping(); + return eTypeSummaryCapped; +} + +void +SBTypeSummaryOptions::SetLanguage (lldb::LanguageType l) +{ + if (IsValid()) + m_opaque_ap->SetLanguage(l); +} + +void +SBTypeSummaryOptions::SetCapping (lldb::TypeSummaryCapping c) +{ + if (IsValid()) + m_opaque_ap->SetCapping(c); +} + +lldb_private::TypeSummaryOptions * +SBTypeSummaryOptions::operator->() +{ + return m_opaque_ap.get(); +} + +const lldb_private::TypeSummaryOptions * +SBTypeSummaryOptions::operator->() const +{ + return m_opaque_ap.get(); +} + +lldb_private::TypeSummaryOptions * +SBTypeSummaryOptions::get () +{ + return m_opaque_ap.get(); +} + +lldb_private::TypeSummaryOptions & +SBTypeSummaryOptions::ref() +{ + return *m_opaque_ap.get(); +} + +const lldb_private::TypeSummaryOptions & +SBTypeSummaryOptions::ref() const +{ + return *m_opaque_ap.get(); +} + +SBTypeSummaryOptions::SBTypeSummaryOptions (const lldb_private::TypeSummaryOptions *lldb_object_ptr) +{ + SetOptions(lldb_object_ptr); +} + +void +SBTypeSummaryOptions::SetOptions (const lldb_private::TypeSummaryOptions *lldb_object_ptr) +{ + if (lldb_object_ptr) + m_opaque_ap.reset(new TypeSummaryOptions(*lldb_object_ptr)); + else + m_opaque_ap.reset(new TypeSummaryOptions()); +} + SBTypeSummary::SBTypeSummary() : m_opaque_sp() { diff --git a/source/API/SBValue.cpp b/source/API/SBValue.cpp index 3a9621b1e3bc..0d3d7ad956ee 100644 --- a/source/API/SBValue.cpp +++ b/source/API/SBValue.cpp @@ -543,6 +543,36 @@ SBValue::GetObjectDescription () return cstr; } +const char * +SBValue::GetTypeValidatorResult () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char *cstr = NULL; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + const auto& validation(value_sp->GetValidationStatus()); + if (TypeValidatorResult::Failure == validation.first) + { + if (validation.second.empty()) + cstr = "unknown error"; + else + cstr = validation.second.c_str(); + } + } + if (log) + { + if (cstr) + log->Printf ("SBValue(%p)::GetTypeValidatorResult() => \"%s\"", + static_cast<void*>(value_sp.get()), cstr); + else + log->Printf ("SBValue(%p)::GetTypeValidatorResult() => NULL", + static_cast<void*>(value_sp.get())); + } + return cstr; +} + SBType SBValue::GetType() { @@ -610,6 +640,32 @@ SBValue::GetSummary () } return cstr; } + +const char * +SBValue::GetSummary (lldb::SBStream& stream, + lldb::SBTypeSummaryOptions& options) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + std::string buffer; + if (value_sp->GetSummaryAsCString(buffer,options.ref()) && !buffer.empty()) + stream.Printf("%s",buffer.c_str()); + } + const char* cstr = stream.GetData(); + if (log) + { + if (cstr) + log->Printf ("SBValue(%p)::GetSummary() => \"%s\"", + static_cast<void*>(value_sp.get()), cstr); + else + log->Printf ("SBValue(%p)::GetSummary() => NULL", + static_cast<void*>(value_sp.get())); + } + return cstr; +} #endif // LLDB_DISABLE_PYTHON const char * @@ -808,21 +864,11 @@ SBValue::CreateValueFromExpression (const char *name, const char *expression, SB if (value_sp) { ExecutionContext exe_ctx (value_sp->GetExecutionContextRef()); - Target* target = exe_ctx.GetTargetPtr(); - if (target) - { - options.ref().SetKeepInMemory(true); - target->EvaluateExpression (expression, - exe_ctx.GetFramePtr(), - new_value_sp, - options.ref()); - if (new_value_sp) - { - new_value_sp->SetName(ConstString(name)); - sb_value.SetSP(new_value_sp); - } - } + new_value_sp = ValueObject::CreateValueObjectFromExpression(name, expression, exe_ctx, options.ref()); + if (new_value_sp) + new_value_sp->SetName(ConstString(name)); } + sb_value.SetSP(new_value_sp); if (log) { if (new_value_sp) @@ -846,30 +892,11 @@ SBValue::CreateValueFromAddress(const char* name, lldb::addr_t address, SBType s lldb::TypeImplSP type_impl_sp (sb_type.GetSP()); if (value_sp && type_impl_sp) { - ClangASTType pointer_ast_type(type_impl_sp->GetClangASTType(false).GetPointerType ()); - if (pointer_ast_type) - { - lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(&address,sizeof(lldb::addr_t))); - - ExecutionContext exe_ctx (value_sp->GetExecutionContextRef()); - ValueObjectSP ptr_result_valobj_sp(ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), - pointer_ast_type, - ConstString(name), - buffer, - exe_ctx.GetByteOrder(), - exe_ctx.GetAddressByteSize())); - - if (ptr_result_valobj_sp) - { - ptr_result_valobj_sp->GetValue().SetValueType(Value::eValueTypeLoadAddress); - Error err; - new_value_sp = ptr_result_valobj_sp->Dereference(err); - if (new_value_sp) - new_value_sp->SetName(ConstString(name)); - } - sb_value.SetSP(new_value_sp); - } + ClangASTType ast_type(type_impl_sp->GetClangASTType(true)); + ExecutionContext exe_ctx (value_sp->GetExecutionContextRef()); + new_value_sp = ValueObject::CreateValueObjectFromAddress(name, address, exe_ctx, ast_type); } + sb_value.SetSP(new_value_sp); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) { @@ -894,15 +921,10 @@ SBValue::CreateValueFromData (const char* name, SBData data, SBType type) if (value_sp) { ExecutionContext exe_ctx (value_sp->GetExecutionContextRef()); - - new_value_sp = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), - type.m_opaque_sp->GetClangASTType(false), - ConstString(name), - *data.m_opaque_sp, - LLDB_INVALID_ADDRESS); + new_value_sp = ValueObject::CreateValueObjectFromData(name, **data, exe_ctx, type.GetSP()->GetClangASTType(true)); new_value_sp->SetAddressTypeOfChildren(eAddressTypeLoad); - sb_value.SetSP(new_value_sp); } + sb_value.SetSP(new_value_sp); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); if (log) { @@ -1836,3 +1858,16 @@ SBValue::WatchPointee (bool resolve_location, bool read, bool write, SBError &er sb_watchpoint = Dereference().Watch (resolve_location, read, write, error); return sb_watchpoint; } + +lldb::SBValue +SBValue::Persist () +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + SBValue persisted_sb; + if (value_sp) + { + persisted_sb.SetSP(value_sp->Persist()); + } + return persisted_sb; +} diff --git a/source/API/SBValueList.cpp b/source/API/SBValueList.cpp index 5069ed3f5624..71fabe0dfc0a 100644 --- a/source/API/SBValueList.cpp +++ b/source/API/SBValueList.cpp @@ -78,6 +78,21 @@ public: } return lldb::SBValue(); } + + lldb::SBValue + GetFirstValueByName (const char* name) const + { + if (name) + { + for (auto val : m_values) + { + if (val.IsValid() && val.GetName() && + strcmp(name,val.GetName()) == 0) + return val; + } + } + return lldb::SBValue(); + } private: std::vector<lldb::SBValue> m_values; @@ -261,6 +276,15 @@ SBValueList::FindValueObjectByUID (lldb::user_id_t uid) return sb_value; } +SBValue +SBValueList::GetFirstValueByName (const char* name) const +{ + SBValue sb_value; + if (m_opaque_ap.get()) + sb_value = m_opaque_ap->GetFirstValueByName(name); + return sb_value; +} + void * SBValueList::opaque_ptr () { diff --git a/source/Breakpoint/Breakpoint.cpp b/source/Breakpoint/Breakpoint.cpp index 7d08170e4aed..bc269cdb95ac 100644 --- a/source/Breakpoint/Breakpoint.cpp +++ b/source/Breakpoint/Breakpoint.cpp @@ -20,11 +20,14 @@ #include "lldb/Breakpoint/BreakpointResolver.h" #include "lldb/Breakpoint/BreakpointResolverFileLine.h" #include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/SearchFilter.h" #include "lldb/Core/Section.h" #include "lldb/Core/Stream.h" #include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/Target.h" #include "lldb/Target/ThreadSpec.h" @@ -62,6 +65,20 @@ Breakpoint::Breakpoint(Target &target, m_being_created = false; } +Breakpoint::Breakpoint (Target &new_target, Breakpoint &source_bp) : + m_being_created(true), + m_hardware(source_bp.m_hardware), + m_target(new_target), + m_name_list (source_bp.m_name_list), + m_options (source_bp.m_options), + m_locations(*this), + m_resolve_indirect_symbols(source_bp.m_resolve_indirect_symbols) +{ + // Now go through and copy the filter & resolver: + m_resolver_sp = source_bp.m_resolver_sp->CopyForBreakpoint(*this); + m_filter_sp = source_bp.m_filter_sp->CopyForBreakpoint(*this); +} + //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- @@ -69,24 +86,16 @@ Breakpoint::~Breakpoint() { } -bool -Breakpoint::IsInternal () const -{ - return LLDB_BREAK_ID_IS_INTERNAL(m_bid); -} - - - -Target& -Breakpoint::GetTarget () +const lldb::TargetSP +Breakpoint::GetTargetSP () { - return m_target; + return m_target.shared_from_this(); } -const Target& -Breakpoint::GetTarget () const +bool +Breakpoint::IsInternal () const { - return m_target; + return LLDB_BREAK_ID_IS_INTERNAL(m_bid); } BreakpointLocationSP @@ -349,10 +358,41 @@ Breakpoint::ResolveBreakpoint () } void -Breakpoint::ResolveBreakpointInModules (ModuleList &module_list) +Breakpoint::ResolveBreakpointInModules (ModuleList &module_list, BreakpointLocationCollection &new_locations) +{ + m_locations.StartRecordingNewLocations(new_locations); + + m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list); + + m_locations.StopRecordingNewLocations(); +} + +void +Breakpoint::ResolveBreakpointInModules (ModuleList &module_list, bool send_event) { if (m_resolver_sp) - m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list); + { + // If this is not an internal breakpoint, set up to record the new locations, then dispatch + // an event with the new locations. + if (!IsInternal() && send_event) + { + BreakpointEventData *new_locations_event = new BreakpointEventData (eBreakpointEventTypeLocationsAdded, + shared_from_this()); + + ResolveBreakpointInModules (module_list, new_locations_event->GetBreakpointLocationCollection()); + + if (new_locations_event->GetBreakpointLocationCollection().GetSize() != 0) + { + SendBreakpointChangedEvent (new_locations_event); + } + else + delete new_locations_event; + } + else + { + m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list); + } + } } void @@ -368,6 +408,11 @@ Breakpoint::ClearAllBreakpointSites () void Breakpoint::ModulesChanged (ModuleList &module_list, bool load, bool delete_locations) { + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("Breakpoint::ModulesChanged: num_modules: %zu load: %i delete_locations: %i\n", + module_list.GetSize(), load, delete_locations); + Mutex::Locker modules_mutex(module_list.GetMutex()); if (load) { @@ -383,32 +428,27 @@ Breakpoint::ModulesChanged (ModuleList &module_list, bool load, bool delete_loca // them after the locations pass. Have to do it this way because // resolving breakpoints will add new locations potentially. - const size_t num_locs = m_locations.GetSize(); - size_t num_modules = module_list.GetSize(); - for (size_t i = 0; i < num_modules; i++) + for (ModuleSP module_sp : module_list.ModulesNoLocking()) { bool seen = false; - ModuleSP module_sp (module_list.GetModuleAtIndexUnlocked (i)); if (!m_filter_sp->ModulePasses (module_sp)) continue; - for (size_t loc_idx = 0; loc_idx < num_locs; loc_idx++) + for (BreakpointLocationSP break_loc_sp : m_locations.BreakpointLocations()) { - BreakpointLocationSP break_loc = m_locations.GetByIndex(loc_idx); - if (!break_loc->IsEnabled()) + if (!break_loc_sp->IsEnabled()) continue; - SectionSP section_sp (break_loc->GetAddress().GetSection()); + SectionSP section_sp (break_loc_sp->GetAddress().GetSection()); if (!section_sp || section_sp->GetModule() == module_sp) { if (!seen) seen = true; - if (!break_loc->ResolveBreakpointSite()) + if (!break_loc_sp->ResolveBreakpointSite()) { - Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); if (log) log->Printf ("Warning: could not set breakpoint site for breakpoint location %d of breakpoint %d.\n", - break_loc->GetID(), GetID()); + break_loc_sp->GetID(), GetID()); } } } @@ -420,28 +460,7 @@ Breakpoint::ModulesChanged (ModuleList &module_list, bool load, bool delete_loca if (new_modules.GetSize() > 0) { - // If this is not an internal breakpoint, set up to record the new locations, then dispatch - // an event with the new locations. - if (!IsInternal()) - { - BreakpointEventData *new_locations_event = new BreakpointEventData (eBreakpointEventTypeLocationsAdded, - shared_from_this()); - - m_locations.StartRecordingNewLocations(new_locations_event->GetBreakpointLocationCollection()); - - ResolveBreakpointInModules(new_modules); - - m_locations.StopRecordingNewLocations(); - if (new_locations_event->GetBreakpointLocationCollection().GetSize() != 0) - { - SendBreakpointChangedEvent (new_locations_event); - } - else - delete new_locations_event; - } - else - ResolveBreakpointInModules(new_modules); - + ResolveBreakpointInModules(new_modules); } } else @@ -498,21 +517,251 @@ Breakpoint::ModulesChanged (ModuleList &module_list, bool load, bool delete_loca } } +namespace +{ +static bool +SymbolContextsMightBeEquivalent(SymbolContext &old_sc, SymbolContext &new_sc) +{ + bool equivalent_scs = false; + + if (old_sc.module_sp.get() == new_sc.module_sp.get()) + { + // If these come from the same module, we can directly compare the pointers: + if (old_sc.comp_unit && new_sc.comp_unit + && (old_sc.comp_unit == new_sc.comp_unit)) + { + if (old_sc.function && new_sc.function + && (old_sc.function == new_sc.function)) + { + equivalent_scs = true; + } + } + else if (old_sc.symbol && new_sc.symbol + && (old_sc.symbol == new_sc.symbol)) + { + equivalent_scs = true; + } + } + else + { + // Otherwise we will compare by name... + if (old_sc.comp_unit && new_sc.comp_unit) + { + if (FileSpec::Equal(*old_sc.comp_unit, *new_sc.comp_unit, true)) + { + // Now check the functions: + if (old_sc.function && new_sc.function + && (old_sc.function->GetName() == new_sc.function->GetName())) + { + equivalent_scs = true; + } + } + } + else if (old_sc.symbol && new_sc.symbol) + { + if (Mangled::Compare(old_sc.symbol->GetMangled(), new_sc.symbol->GetMangled()) == 0) + { + equivalent_scs = true; + } + } + } + return equivalent_scs; +} +} + void Breakpoint::ModuleReplaced (ModuleSP old_module_sp, ModuleSP new_module_sp) { - ModuleList temp_list; - temp_list.Append (new_module_sp); - ModulesChanged (temp_list, true); + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("Breakpoint::ModulesReplaced for %s\n", + old_module_sp->GetSpecificationDescription().c_str()); + // First find all the locations that are in the old module + + BreakpointLocationCollection old_break_locs; + for (BreakpointLocationSP break_loc_sp : m_locations.BreakpointLocations()) + { + SectionSP section_sp = break_loc_sp->GetAddress().GetSection(); + if (section_sp && section_sp->GetModule() == old_module_sp) + { + old_break_locs.Add(break_loc_sp); + } + } + + size_t num_old_locations = old_break_locs.GetSize(); + + if (num_old_locations == 0) + { + // There were no locations in the old module, so we just need to check if there were any in the new module. + ModuleList temp_list; + temp_list.Append (new_module_sp); + ResolveBreakpointInModules(temp_list); + } + else + { + // First search the new module for locations. + // Then compare this with the old list, copy over locations that "look the same" + // Then delete the old locations. + // Finally remember to post the creation event. + // + // Two locations are the same if they have the same comp unit & function (by name) and there are the same number + // of locations in the old function as in the new one. + + ModuleList temp_list; + temp_list.Append (new_module_sp); + BreakpointLocationCollection new_break_locs; + ResolveBreakpointInModules(temp_list, new_break_locs); + BreakpointLocationCollection locations_to_remove; + BreakpointLocationCollection locations_to_announce; + + size_t num_new_locations = new_break_locs.GetSize(); + + if (num_new_locations > 0) + { + // Break out the case of one location -> one location since that's the most common one, and there's no need + // to build up the structures needed for the merge in that case. + if (num_new_locations == 1 && num_old_locations == 1) + { + bool equivalent_locations = false; + SymbolContext old_sc, new_sc; + // The only way the old and new location can be equivalent is if they have the same amount of information: + BreakpointLocationSP old_loc_sp = old_break_locs.GetByIndex(0); + BreakpointLocationSP new_loc_sp = new_break_locs.GetByIndex(0); + + if (old_loc_sp->GetAddress().CalculateSymbolContext(&old_sc) + == new_loc_sp->GetAddress().CalculateSymbolContext(&new_sc)) + { + equivalent_locations = SymbolContextsMightBeEquivalent(old_sc, new_sc); + } + + if (equivalent_locations) + { + m_locations.SwapLocation (old_loc_sp, new_loc_sp); + } + else + { + locations_to_remove.Add(old_loc_sp); + locations_to_announce.Add(new_loc_sp); + } + } + else + { + //We don't want to have to keep computing the SymbolContexts for these addresses over and over, + // so lets get them up front: + + typedef std::map<lldb::break_id_t, SymbolContext> IDToSCMap; + IDToSCMap old_sc_map; + for (size_t idx = 0; idx < num_old_locations; idx++) + { + SymbolContext sc; + BreakpointLocationSP bp_loc_sp = old_break_locs.GetByIndex(idx); + lldb::break_id_t loc_id = bp_loc_sp->GetID(); + bp_loc_sp->GetAddress().CalculateSymbolContext(&old_sc_map[loc_id]); + } + + std::map<lldb::break_id_t, SymbolContext> new_sc_map; + for (size_t idx = 0; idx < num_new_locations; idx++) + { + SymbolContext sc; + BreakpointLocationSP bp_loc_sp = new_break_locs.GetByIndex(idx); + lldb::break_id_t loc_id = bp_loc_sp->GetID(); + bp_loc_sp->GetAddress().CalculateSymbolContext(&new_sc_map[loc_id]); + } + // Take an element from the old Symbol Contexts + while (old_sc_map.size() > 0) + { + lldb::break_id_t old_id = old_sc_map.begin()->first; + SymbolContext &old_sc = old_sc_map.begin()->second; + + // Count the number of entries equivalent to this SC for the old list: + std::vector<lldb::break_id_t> old_id_vec; + old_id_vec.push_back(old_id); + + IDToSCMap::iterator tmp_iter; + for (tmp_iter = ++old_sc_map.begin(); tmp_iter != old_sc_map.end(); tmp_iter++) + { + if (SymbolContextsMightBeEquivalent (old_sc, tmp_iter->second)) + old_id_vec.push_back (tmp_iter->first); + } + + // Now find all the equivalent locations in the new list. + std::vector<lldb::break_id_t> new_id_vec; + for (tmp_iter = new_sc_map.begin(); tmp_iter != new_sc_map.end(); tmp_iter++) + { + if (SymbolContextsMightBeEquivalent (old_sc, tmp_iter->second)) + new_id_vec.push_back(tmp_iter->first); + } + + // Alright, if we have the same number of potentially equivalent locations in the old + // and new modules, we'll just map them one to one in ascending ID order (assuming the + // resolver's order would match the equivalent ones. + // Otherwise, we'll dump all the old ones, and just take the new ones, erasing the elements + // from both maps as we go. + + if (old_id_vec.size() == new_id_vec.size()) + { + sort(old_id_vec.begin(), old_id_vec.end()); + sort(new_id_vec.begin(), new_id_vec.end()); + size_t num_elements = old_id_vec.size(); + for (size_t idx = 0; idx < num_elements; idx++) + { + BreakpointLocationSP old_loc_sp = old_break_locs.FindByIDPair(GetID(), old_id_vec[idx]); + BreakpointLocationSP new_loc_sp = new_break_locs.FindByIDPair(GetID(), new_id_vec[idx]); + m_locations.SwapLocation(old_loc_sp, new_loc_sp); + old_sc_map.erase(old_id_vec[idx]); + new_sc_map.erase(new_id_vec[idx]); + } + } + else + { + for (lldb::break_id_t old_id : old_id_vec) + { + locations_to_remove.Add(old_break_locs.FindByIDPair(GetID(), old_id)); + old_sc_map.erase(old_id); + } + for (lldb::break_id_t new_id : new_id_vec) + { + locations_to_announce.Add(new_break_locs.FindByIDPair(GetID(), new_id)); + new_sc_map.erase(new_id); + } + } + } + } + } + + // Now remove the remaining old locations, and cons up a removed locations event. + // Note, we don't put the new locations that were swapped with an old location on the locations_to_remove + // list, so we don't need to worry about telling the world about removing a location we didn't tell them + // about adding. + + BreakpointEventData *locations_event; + if (!IsInternal()) + locations_event = new BreakpointEventData (eBreakpointEventTypeLocationsRemoved, + shared_from_this()); + else + locations_event = NULL; - // TO DO: For now I'm just adding locations for the new module and removing the - // breakpoint locations that were in the old module. - // We should really go find the ones that are in the new module & if we can determine that they are "equivalent" - // carry over the options from the old location to the new. + for (BreakpointLocationSP loc_sp : locations_to_remove.BreakpointLocations()) + { + m_locations.RemoveLocation(loc_sp); + if (locations_event) + locations_event->GetBreakpointLocationCollection().Add(loc_sp); + } + SendBreakpointChangedEvent (locations_event); + + // And announce the new ones. + + if (!IsInternal()) + { + locations_event = new BreakpointEventData (eBreakpointEventTypeLocationsAdded, + shared_from_this()); + for (BreakpointLocationSP loc_sp : locations_to_announce.BreakpointLocations()) + locations_event->GetBreakpointLocationCollection().Add(loc_sp); - temp_list.Clear(); - temp_list.Append (old_module_sp); - ModulesChanged (temp_list, false, true); + SendBreakpointChangedEvent (locations_event); + } + m_locations.Compact(); + } } void @@ -534,6 +783,23 @@ Breakpoint::GetNumLocations() const return m_locations.GetSize(); } +bool +Breakpoint::AddName (const char *new_name, Error &error) +{ + if (!new_name) + return false; + if (!BreakpointID::StringIsBreakpointName(new_name, error)) + { + error.SetErrorStringWithFormat("input name \"%s\" not a breakpoint name.", new_name); + return false; + } + if (!error.Success()) + return false; + + m_name_list.insert(new_name); + return true; +} + void Breakpoint::GetDescription (Stream *s, lldb::DescriptionLevel level, bool show_locations) { @@ -584,6 +850,20 @@ Breakpoint::GetDescription (Stream *s, lldb::DescriptionLevel level, bool show_l if (level == lldb::eDescriptionLevelFull) { + if (!m_name_list.empty()) + { + s->EOL(); + s->Indent(); + s->Printf ("Names:"); + s->EOL(); + s->IndentMore(); + for (std::string name : m_name_list) + { + s->Indent(); + s->Printf("%s\n", name.c_str()); + } + s->IndentLess(); + } s->IndentLess(); s->EOL(); } diff --git a/source/Breakpoint/BreakpointID.cpp b/source/Breakpoint/BreakpointID.cpp index 9963ed68303a..31823886dd9f 100644 --- a/source/Breakpoint/BreakpointID.cpp +++ b/source/Breakpoint/BreakpointID.cpp @@ -18,6 +18,7 @@ #include "lldb/Breakpoint/BreakpointID.h" #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Core/Stream.h" +#include "lldb/Core/Error.h" using namespace lldb; using namespace lldb_private; @@ -121,3 +122,19 @@ BreakpointID::ParseCanonicalReference (const char *input, break_id_t *break_id_p return false; } +bool +BreakpointID::StringIsBreakpointName(const char *name, Error &error) +{ + error.Clear(); + + if (name && (name[0] >= 'A' && name[0] <= 'z')) + { + if (strcspn(name, ".- ") != strlen(name)) + { + error.SetErrorStringWithFormat("invalid breakpoint name: \"%s\"", name); + } + return true; + } + else + return false; +} diff --git a/source/Breakpoint/BreakpointIDList.cpp b/source/Breakpoint/BreakpointIDList.cpp index 24101b1442fb..b8b506750b34 100644 --- a/source/Breakpoint/BreakpointIDList.cpp +++ b/source/Breakpoint/BreakpointIDList.cpp @@ -159,27 +159,52 @@ BreakpointIDList::InsertStringArray (const char **string_array, size_t array_siz // NEW_ARGS should be a copy of OLD_ARGS, with and ID range specifiers replaced by the members of the range. void -BreakpointIDList::FindAndReplaceIDRanges (Args &old_args, Target *target, CommandReturnObject &result, +BreakpointIDList::FindAndReplaceIDRanges (Args &old_args, + Target *target, + bool allow_locations, + CommandReturnObject &result, Args &new_args) { std::string range_start; const char *range_end; const char *current_arg; const size_t num_old_args = old_args.GetArgumentCount(); + std::set<std::string> names_found; for (size_t i = 0; i < num_old_args; ++i) { bool is_range = false; + current_arg = old_args.GetArgumentAtIndex (i); + if (!allow_locations && strchr(current_arg, '.') != nullptr) + { + result.AppendErrorWithFormat ("Breakpoint locations not allowed, saw location: %s.", current_arg); + new_args.Clear(); + return; + } size_t range_start_len = 0; size_t range_end_pos = 0; + Error error; + if (BreakpointIDList::StringContainsIDRangeExpression (current_arg, &range_start_len, &range_end_pos)) { is_range = true; range_start.assign (current_arg, range_start_len); range_end = current_arg + range_end_pos; } + else if (BreakpointID::StringIsBreakpointName(current_arg, error)) + { + if (!error.Success()) + { + new_args.Clear(); + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return; + } + else + names_found.insert(current_arg); + } else if ((i + 2 < num_old_args) && BreakpointID::IsRangeIdentifier (old_args.GetArgumentAtIndex (i+1)) && BreakpointID::IsValidIDExpression (current_arg) @@ -342,6 +367,23 @@ BreakpointIDList::FindAndReplaceIDRanges (Args &old_args, Target *target, Comman } } + // Okay, now see if we found any names, and if we did, add them: + if (target && names_found.size()) + { + for (BreakpointSP bkpt_sp : target->GetBreakpointList().Breakpoints()) + { + for (std::string name : names_found) + { + if (bkpt_sp->MatchesName(name.c_str())) + { + StreamString canonical_id_str; + BreakpointID::GetCanonicalReference (&canonical_id_str, bkpt_sp->GetID(), LLDB_INVALID_BREAK_ID); + new_args.AppendArgument (canonical_id_str.GetData()); + } + } + } + } + result.SetStatus (eReturnStatusSuccessFinishNoResult); return; } diff --git a/source/Breakpoint/BreakpointLocation.cpp b/source/Breakpoint/BreakpointLocation.cpp index e1ac043ae905..11ecfecc5bc7 100644 --- a/source/Breakpoint/BreakpointLocation.cpp +++ b/source/Breakpoint/BreakpointLocation.cpp @@ -449,8 +449,7 @@ BreakpointLocation::ShouldStop (StoppointCallbackContext *context) bool should_stop = true; Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); - IncrementHitCount(); - + // Do this first, if a location is disabled, it shouldn't increment its hit count. if (!IsEnabled()) return false; @@ -474,6 +473,13 @@ BreakpointLocation::ShouldStop (StoppointCallbackContext *context) return should_stop; } +void +BreakpointLocation::BumpHitCount() +{ + if (IsEnabled()) + IncrementHitCount(); +} + bool BreakpointLocation::IsResolved () const { @@ -569,7 +575,7 @@ BreakpointLocation::GetDescription (Stream *s, lldb::DescriptionLevel level) s->PutCString ("re-exported target = "); else s->PutCString("where = "); - sc.DumpStopContext (s, m_owner.GetTarget().GetProcessSP().get(), m_address, false, true, false); + sc.DumpStopContext (s, m_owner.GetTarget().GetProcessSP().get(), m_address, false, true, false, true); } else { @@ -717,4 +723,13 @@ BreakpointLocation::SendBreakpointLocationChangedEvent (lldb::BreakpointEventTyp m_owner.GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, data); } } - + +void +BreakpointLocation::SwapLocation (BreakpointLocationSP swap_from) +{ + m_address = swap_from->m_address; + m_should_resolve_indirect_functions = swap_from->m_should_resolve_indirect_functions; + m_is_reexported = swap_from->m_is_reexported; + m_is_indirect = swap_from->m_is_indirect; + m_user_expression_sp.reset(); +} diff --git a/source/Breakpoint/BreakpointLocationCollection.cpp b/source/Breakpoint/BreakpointLocationCollection.cpp index ee3f56f928d5..5756ccedfaa4 100644 --- a/source/Breakpoint/BreakpointLocationCollection.cpp +++ b/source/Breakpoint/BreakpointLocationCollection.cpp @@ -196,3 +196,4 @@ BreakpointLocationCollection::GetDescription (Stream *s, lldb::DescriptionLevel (*pos)->GetDescription(s, level); } } + diff --git a/source/Breakpoint/BreakpointLocationList.cpp b/source/Breakpoint/BreakpointLocationList.cpp index ae7f863ad090..06b270a08ce9 100644 --- a/source/Breakpoint/BreakpointLocationList.cpp +++ b/source/Breakpoint/BreakpointLocationList.cpp @@ -272,6 +272,20 @@ BreakpointLocationList::AddLocation (const Address &addr, bool resolve_indirect_ return bp_loc_sp; } +void +BreakpointLocationList::SwapLocation (BreakpointLocationSP to_location_sp, BreakpointLocationSP from_location_sp) +{ + if (!from_location_sp || !to_location_sp) + return; + + m_address_to_location.erase(to_location_sp->GetAddress()); + to_location_sp->SwapLocation(from_location_sp); + RemoveLocation(from_location_sp); + m_address_to_location[to_location_sp->GetAddress()] = to_location_sp; + to_location_sp->ResolveBreakpointSite(); +} + + bool BreakpointLocationList::RemoveLocation (const lldb::BreakpointLocationSP &bp_loc_sp) { @@ -345,3 +359,16 @@ BreakpointLocationList::StopRecordingNewLocations () m_new_location_recorder = NULL; } +void +BreakpointLocationList::Compact() +{ + lldb::break_id_t highest_id = 0; + + for (BreakpointLocationSP loc_sp : m_locations) + { + lldb::break_id_t cur_id = loc_sp->GetID(); + if (cur_id > highest_id) + highest_id = cur_id; + } + m_next_id = highest_id; +} diff --git a/source/Breakpoint/BreakpointOptions.cpp b/source/Breakpoint/BreakpointOptions.cpp index ea8556d0930b..db76ffb8685c 100644 --- a/source/Breakpoint/BreakpointOptions.cpp +++ b/source/Breakpoint/BreakpointOptions.cpp @@ -237,8 +237,7 @@ BreakpointOptions::GetDescription (Stream *s, lldb::DescriptionLevel level) cons if (m_thread_spec_ap.get()) m_thread_spec_ap->GetDescription (s, level); - else if (level == eDescriptionLevelBrief) - s->PutCString ("thread spec: no "); + if (level == lldb::eDescriptionLevelFull) { s->IndentLess(); diff --git a/source/Breakpoint/BreakpointResolverAddress.cpp b/source/Breakpoint/BreakpointResolverAddress.cpp index 1bcef93aedad..d6647130c54c 100644 --- a/source/Breakpoint/BreakpointResolverAddress.cpp +++ b/source/Breakpoint/BreakpointResolverAddress.cpp @@ -109,3 +109,11 @@ BreakpointResolverAddress::Dump (Stream *s) const { } + +lldb::BreakpointResolverSP +BreakpointResolverAddress::CopyForBreakpoint (Breakpoint &breakpoint) +{ + lldb::BreakpointResolverSP ret_sp(new BreakpointResolverAddress(&breakpoint, m_addr)); + return ret_sp; +} + diff --git a/source/Breakpoint/BreakpointResolverFileLine.cpp b/source/Breakpoint/BreakpointResolverFileLine.cpp index dcee2fd54125..950054c3d720 100644 --- a/source/Breakpoint/BreakpointResolverFileLine.cpp +++ b/source/Breakpoint/BreakpointResolverFileLine.cpp @@ -110,3 +110,14 @@ BreakpointResolverFileLine::Dump (Stream *s) const } +lldb::BreakpointResolverSP +BreakpointResolverFileLine::CopyForBreakpoint (Breakpoint &breakpoint) +{ + lldb::BreakpointResolverSP ret_sp(new BreakpointResolverFileLine(&breakpoint, + m_file_spec, + m_line_number, + m_inlines, + m_skip_prologue)); + + return ret_sp; +} diff --git a/source/Breakpoint/BreakpointResolverFileRegex.cpp b/source/Breakpoint/BreakpointResolverFileRegex.cpp index 01aecee7b9c2..c71d9bf5ba8c 100644 --- a/source/Breakpoint/BreakpointResolverFileRegex.cpp +++ b/source/Breakpoint/BreakpointResolverFileRegex.cpp @@ -95,3 +95,10 @@ BreakpointResolverFileRegex::Dump (Stream *s) const } +lldb::BreakpointResolverSP +BreakpointResolverFileRegex::CopyForBreakpoint (Breakpoint &breakpoint) +{ + lldb::BreakpointResolverSP ret_sp(new BreakpointResolverFileRegex(&breakpoint, m_regex)); + return ret_sp; +} + diff --git a/source/Breakpoint/BreakpointResolverName.cpp b/source/Breakpoint/BreakpointResolverName.cpp index 3ac3ed06fc70..581f7b016173 100644 --- a/source/Breakpoint/BreakpointResolverName.cpp +++ b/source/Breakpoint/BreakpointResolverName.cpp @@ -121,6 +121,17 @@ BreakpointResolverName::~BreakpointResolverName () { } +BreakpointResolverName::BreakpointResolverName(const BreakpointResolverName &rhs) : + BreakpointResolver(rhs.m_breakpoint, BreakpointResolver::NameResolver), + m_lookups(rhs.m_lookups), + m_class_name(rhs.m_class_name), + m_regex(rhs.m_regex), + m_match_type (rhs.m_match_type), + m_skip_prologue (rhs.m_skip_prologue) +{ + +} + void BreakpointResolverName::AddNameLookup (const ConstString &name, uint32_t name_type_mask) { @@ -371,3 +382,10 @@ BreakpointResolverName::Dump (Stream *s) const } +lldb::BreakpointResolverSP +BreakpointResolverName::CopyForBreakpoint (Breakpoint &breakpoint) +{ + lldb::BreakpointResolverSP ret_sp(new BreakpointResolverName(*this)); + ret_sp->SetBreakpoint(&breakpoint); + return ret_sp; +} diff --git a/source/Breakpoint/BreakpointSite.cpp b/source/Breakpoint/BreakpointSite.cpp index 3cf6d37af379..469514b03f8a 100644 --- a/source/Breakpoint/BreakpointSite.cpp +++ b/source/Breakpoint/BreakpointSite.cpp @@ -199,6 +199,15 @@ BreakpointSite::ValidForThisThread (Thread *thread) return m_owners.ValidForThisThread(thread); } +void +BreakpointSite::BumpHitCounts() +{ + for (BreakpointLocationSP loc_sp : m_owners.BreakpointLocations()) + { + loc_sp->BumpHitCount(); + } +} + bool BreakpointSite::IntersectsRange(lldb::addr_t addr, size_t size, lldb::addr_t *intersect_addr, size_t *intersect_size, size_t *opcode_offset) const { diff --git a/source/Commands/CommandCompletions.cpp b/source/Commands/CommandCompletions.cpp index f0ad4a896739..c65dd9d460f4 100644 --- a/source/Commands/CommandCompletions.cpp +++ b/source/Commands/CommandCompletions.cpp @@ -112,7 +112,7 @@ CommandCompletions::SourceFiles if (searcher == NULL) { lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); - SearchFilter null_searcher (target_sp); + SearchFilterForUnconstrainedSearches null_searcher (target_sp); completer.DoCompletion (&null_searcher); } else @@ -375,7 +375,7 @@ CommandCompletions::Modules if (searcher == NULL) { lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); - SearchFilter null_searcher (target_sp); + SearchFilterForUnconstrainedSearches null_searcher (target_sp); completer.DoCompletion (&null_searcher); } else @@ -406,7 +406,7 @@ CommandCompletions::Symbols if (searcher == NULL) { lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); - SearchFilter null_searcher (target_sp); + SearchFilterForUnconstrainedSearches null_searcher (target_sp); completer.DoCompletion (&null_searcher); } else diff --git a/source/Commands/CommandObjectBreakpoint.cpp b/source/Commands/CommandObjectBreakpoint.cpp index 13bf1278c78c..3d4b3aff6fff 100644 --- a/source/Commands/CommandObjectBreakpoint.cpp +++ b/source/Commands/CommandObjectBreakpoint.cpp @@ -20,6 +20,8 @@ #include "lldb/Breakpoint/BreakpointIDList.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Interpreter/OptionValueUInt64.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/StreamString.h" #include "lldb/Interpreter/CommandInterpreter.h" @@ -145,6 +147,10 @@ public: m_condition.assign(option_arg); break; + case 'D': + m_use_dummy = true; + break; + case 'E': { LanguageType language = LanguageRuntime::GetLanguageTypeFromString (option_arg); @@ -236,6 +242,11 @@ public: m_func_name_type_mask |= eFunctionNameTypeAuto; break; + case 'N': + if (BreakpointID::StringIsBreakpointName(option_arg, error)) + m_breakpoint_names.push_back (option_arg); + break; + case 'o': m_one_shot = true; break; @@ -324,6 +335,8 @@ public: m_language = eLanguageTypeUnknown; m_skip_prologue = eLazyBoolCalculate; m_one_shot = false; + m_use_dummy = false; + m_breakpoint_names.clear(); } const OptionDefinition* @@ -343,6 +356,7 @@ public: uint32_t m_line_num; uint32_t m_column; std::vector<std::string> m_func_names; + std::vector<std::string> m_breakpoint_names; uint32_t m_func_name_type_mask; std::string m_func_regexp; std::string m_source_text_regexp; @@ -359,16 +373,18 @@ public: lldb::LanguageType m_language; LazyBool m_skip_prologue; bool m_one_shot; + bool m_use_dummy; }; protected: virtual bool DoExecute (Args& command, - CommandReturnObject &result) + CommandReturnObject &result) { - Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); - if (target == NULL) + Target *target = GetSelectedOrDummyTarget(m_options.m_use_dummy); + + if (target == nullptr) { result.AppendError ("Invalid target. Must set target before setting breakpoints (see 'target create' command)."); result.SetStatus (eReturnStatusFailed); @@ -551,6 +567,13 @@ protected: if (!m_options.m_condition.empty()) bp->GetOptions()->SetCondition(m_options.m_condition.c_str()); + + if (!m_options.m_breakpoint_names.empty()) + { + Error error; // We don't need to check the error here, since the option parser checked it... + for (auto name : m_options.m_breakpoint_names) + bp->AddName(name.c_str(), error); + } bp->SetOneShot (m_options.m_one_shot); } @@ -560,10 +583,17 @@ protected: Stream &output_stream = result.GetOutputStream(); const bool show_locations = false; bp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial, show_locations); - // Don't print out this warning for exception breakpoints. They can get set before the target - // is set, but we won't know how to actually set the breakpoint till we run. - if (bp->GetNumLocations() == 0 && break_type != eSetTypeException) - output_stream.Printf ("WARNING: Unable to resolve breakpoint to any actual locations.\n"); + if (target == m_interpreter.GetDebugger().GetDummyTarget()) + output_stream.Printf ("Breakpoint set in dummy target, will get copied into future targets.\n"); + else + { + // Don't print out this warning for exception breakpoints. They can get set before the target + // is set, but we won't know how to actually set the breakpoint till we run. + if (bp->GetNumLocations() == 0 && break_type != eSetTypeException) + { + output_stream.Printf ("WARNING: Unable to resolve breakpoint to any actual locations.\n"); + } + } result.SetStatus (eReturnStatusSuccessFinishResult); } else if (!bp) @@ -709,6 +739,12 @@ CommandObjectBreakpointSet::CommandOptions::g_option_table[] = { LLDB_OPT_SKIP_PROLOGUE, false, "skip-prologue", 'K', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean, "sKip the prologue if the breakpoint is at the beginning of a function. If not set the target.skip-prologue setting is used." }, + { LLDB_OPT_SET_ALL, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, + "Sets Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets."}, + + { LLDB_OPT_SET_ALL, false, "breakpoint-name", 'N', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBreakpointName, + "Adds this to the list of names for this breakopint."}, + { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; @@ -766,7 +802,8 @@ public: m_name_passed (false), m_queue_passed (false), m_condition_passed (false), - m_one_shot_passed (false) + m_one_shot_passed (false), + m_use_dummy (false) { } @@ -792,6 +829,9 @@ public: m_enable_passed = true; m_enable_value = false; break; + case 'D': + m_use_dummy = true; + break; case 'e': m_enable_passed = true; m_enable_value = true; @@ -888,6 +928,7 @@ public: m_name_passed = false; m_condition_passed = false; m_one_shot_passed = false; + m_use_dummy = false; } const OptionDefinition* @@ -918,6 +959,7 @@ public: bool m_queue_passed; bool m_condition_passed; bool m_one_shot_passed; + bool m_use_dummy; }; @@ -925,7 +967,7 @@ protected: virtual bool DoExecute (Args& command, CommandReturnObject &result) { - Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + Target *target = GetSelectedOrDummyTarget(m_options.m_use_dummy); if (target == NULL) { result.AppendError ("Invalid target. No existing target or breakpoints."); @@ -938,7 +980,7 @@ protected: BreakpointIDList valid_bp_ids; - CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs (command, target, result, &valid_bp_ids); if (result.Succeeded()) { @@ -1024,6 +1066,8 @@ CommandObjectBreakpointModify::CommandOptions::g_option_table[] = { LLDB_OPT_SET_ALL, false, "condition", 'c', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeExpression, "The breakpoint stops only if this condition expression evaluates to true."}, { LLDB_OPT_SET_1, false, "enable", 'e', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Enable the breakpoint."}, { LLDB_OPT_SET_2, false, "disable", 'd', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Disable the breakpoint."}, +{ LLDB_OPT_SET_ALL, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Sets Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets."}, + { 0, false, NULL, 0 , 0, NULL, NULL, 0, eArgTypeNone, NULL } }; @@ -1055,7 +1099,7 @@ protected: virtual bool DoExecute (Args& command, CommandReturnObject &result) { - Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + Target *target = GetSelectedOrDummyTarget(); if (target == NULL) { result.AppendError ("Invalid target. No existing target or breakpoints."); @@ -1088,7 +1132,7 @@ protected: { // Particular breakpoint selected; enable that breakpoint. BreakpointIDList valid_bp_ids; - CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs (command, target, result, &valid_bp_ids); if (result.Succeeded()) { @@ -1175,7 +1219,7 @@ protected: virtual bool DoExecute (Args& command, CommandReturnObject &result) { - Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + Target *target = GetSelectedOrDummyTarget(); if (target == NULL) { result.AppendError ("Invalid target. No existing target or breakpoints."); @@ -1208,7 +1252,7 @@ protected: // Particular breakpoint selected; disable that breakpoint. BreakpointIDList valid_bp_ids; - CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs (command, target, result, &valid_bp_ids); if (result.Succeeded()) { @@ -1293,7 +1337,8 @@ public: CommandOptions (CommandInterpreter &interpreter) : Options (interpreter), - m_level (lldb::eDescriptionLevelBrief) // Breakpoint List defaults to brief descriptions + m_level (lldb::eDescriptionLevelBrief), + m_use_dummy(false) { } @@ -1311,6 +1356,9 @@ public: case 'b': m_level = lldb::eDescriptionLevelBrief; break; + case 'D': + m_use_dummy = true; + break; case 'f': m_level = lldb::eDescriptionLevelFull; break; @@ -1333,6 +1381,7 @@ public: { m_level = lldb::eDescriptionLevelFull; m_internal = false; + m_use_dummy = false; } const OptionDefinition * @@ -1350,13 +1399,15 @@ public: lldb::DescriptionLevel m_level; bool m_internal; + bool m_use_dummy; }; protected: virtual bool DoExecute (Args& command, CommandReturnObject &result) { - Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + Target *target = GetSelectedOrDummyTarget(m_options.m_use_dummy); + if (target == NULL) { result.AppendError ("Invalid target. No current target or breakpoints."); @@ -1394,7 +1445,7 @@ protected: { // Particular breakpoints selected; show info about that breakpoint. BreakpointIDList valid_bp_ids; - CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs (command, target, result, &valid_bp_ids); if (result.Succeeded()) { @@ -1438,6 +1489,9 @@ CommandObjectBreakpointList::CommandOptions::g_option_table[] = { LLDB_OPT_SET_3, false, "verbose", 'v', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Explain everything we know about the breakpoint (for debugging debugger bugs)." }, + { LLDB_OPT_SET_ALL, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, + "List Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets."}, + { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; @@ -1540,7 +1594,7 @@ protected: virtual bool DoExecute (Args& command, CommandReturnObject &result) { - Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + Target *target = GetSelectedOrDummyTarget(); if (target == NULL) { result.AppendError ("Invalid target. No existing target or breakpoints."); @@ -1656,7 +1710,8 @@ public: CommandObjectParsed (interpreter, "breakpoint delete", "Delete the specified breakpoint(s). If no breakpoints are specified, delete them all.", - NULL) + NULL), + m_options (interpreter) { CommandArgumentEntry arg; CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID, eArgTypeBreakpointIDRange); @@ -1667,11 +1722,78 @@ public: virtual ~CommandObjectBreakpointDelete () {} + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_use_dummy (false), + m_force (false) + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'f': + m_force = true; + break; + + case 'D': + m_use_dummy = true; + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_use_dummy = false; + m_force = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + bool m_use_dummy; + bool m_force; + }; + protected: virtual bool DoExecute (Args& command, CommandReturnObject &result) { - Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + Target *target = GetSelectedOrDummyTarget(m_options.m_use_dummy); + if (target == NULL) { result.AppendError ("Invalid target. No existing target or breakpoints."); @@ -1695,7 +1817,7 @@ protected: if (command.GetArgumentCount() == 0) { - if (!m_interpreter.Confirm ("About to delete all breakpoints, do you want to do that?", true)) + if (!m_options.m_force && !m_interpreter.Confirm ("About to delete all breakpoints, do you want to do that?", true)) { result.AppendMessage("Operation cancelled..."); } @@ -1710,7 +1832,7 @@ protected: { // Particular breakpoint selected; disable that breakpoint. BreakpointIDList valid_bp_ids; - CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs (command, target, result, &valid_bp_ids); if (result.Succeeded()) { @@ -1748,7 +1870,416 @@ protected: } return result.Succeeded(); } +private: + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectBreakpointDelete::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "force", 'f', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, + "Delete all breakpoints without querying for confirmation."}, + + { LLDB_OPT_SET_1, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, + "Delete Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets."}, + + { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointName +//------------------------------------------------------------------------- + +static OptionDefinition +g_breakpoint_name_options[] = +{ + { LLDB_OPT_SET_1, false, "name", 'N', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBreakpointName, "Specifies a breakpoint name to use."}, + { LLDB_OPT_SET_2, false, "breakpoint-id", 'B', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBreakpointID, "Specify a breakpoint id to use."}, + { LLDB_OPT_SET_ALL, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, + "Operate on Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets."}, }; +class BreakpointNameOptionGroup : public OptionGroup +{ +public: + BreakpointNameOptionGroup() : + OptionGroup(), + m_breakpoint(LLDB_INVALID_BREAK_ID), + m_use_dummy (false) + { + + } + + virtual + ~BreakpointNameOptionGroup () + { + } + + virtual uint32_t + GetNumDefinitions () + { + return sizeof (g_breakpoint_name_options) / sizeof (OptionDefinition); + } + + virtual const OptionDefinition* + GetDefinitions () + { + return g_breakpoint_name_options; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value) + { + Error error; + const int short_option = g_breakpoint_name_options[option_idx].short_option; + + switch (short_option) + { + case 'N': + if (BreakpointID::StringIsBreakpointName(option_value, error) && error.Success()) + m_name.SetValueFromCString(option_value); + break; + + case 'B': + if (m_breakpoint.SetValueFromCString(option_value).Fail()) + error.SetErrorStringWithFormat ("unrecognized value \"%s\" for breakpoint", option_value); + break; + case 'D': + if (m_use_dummy.SetValueFromCString(option_value).Fail()) + error.SetErrorStringWithFormat ("unrecognized value \"%s\" for use-dummy", option_value); + break; + + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; + } + return error; + } + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter) + { + m_name.Clear(); + m_breakpoint.Clear(); + m_use_dummy.Clear(); + m_use_dummy.SetDefaultValue(false); + } + + OptionValueString m_name; + OptionValueUInt64 m_breakpoint; + OptionValueBoolean m_use_dummy; +}; + + +class CommandObjectBreakpointNameAdd : public CommandObjectParsed +{ +public: + CommandObjectBreakpointNameAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "add", + "Add a name to the breakpoints provided.", + "breakpoint name add <command-options> <breakpoint-id-list>"), + m_name_options(), + m_option_group(interpreter) + { + // Create the first variant for the first (and only) argument for this command. + CommandArgumentEntry arg1; + CommandArgumentData id_arg; + id_arg.arg_type = eArgTypeBreakpointID; + id_arg.arg_repetition = eArgRepeatOptional; + arg1.push_back(id_arg); + m_arguments.push_back (arg1); + + m_option_group.Append (&m_name_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectBreakpointNameAdd () {} + + Options * + GetOptions () + { + return &m_option_group; + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + if (!m_name_options.m_name.OptionWasSet()) + { + result.SetError("No name option provided."); + return false; + } + + Target *target = GetSelectedOrDummyTarget(m_name_options.m_use_dummy.GetCurrentValue()); + + if (target == NULL) + { + result.AppendError ("Invalid target. No existing target or breakpoints."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Mutex::Locker locker; + target->GetBreakpointList().GetListMutex(locker); + + const BreakpointList &breakpoints = target->GetBreakpointList(); + + size_t num_breakpoints = breakpoints.GetSize(); + if (num_breakpoints == 0) + { + result.SetError("No breakpoints, cannot add names."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // Particular breakpoint selected; disable that breakpoint. + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + if (valid_bp_ids.GetSize() == 0) + { + result.SetError("No breakpoints specified, cannot add names."); + result.SetStatus (eReturnStatusFailed); + return false; + } + size_t num_valid_ids = valid_bp_ids.GetSize(); + for (size_t index = 0; index < num_valid_ids; index++) + { + lldb::break_id_t bp_id = valid_bp_ids.GetBreakpointIDAtIndex(index).GetBreakpointID(); + BreakpointSP bp_sp = breakpoints.FindBreakpointByID(bp_id); + Error error; // We don't need to check the error here, since the option parser checked it... + bp_sp->AddName(m_name_options.m_name.GetCurrentValue(), error); + } + } + + return true; + } + +private: + BreakpointNameOptionGroup m_name_options; + OptionGroupOptions m_option_group; +}; + + + +class CommandObjectBreakpointNameDelete : public CommandObjectParsed +{ +public: + CommandObjectBreakpointNameDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "delete", + "Delete a name from the breakpoints provided.", + "breakpoint name delete <command-options> <breakpoint-id-list>"), + m_name_options(), + m_option_group(interpreter) + { + // Create the first variant for the first (and only) argument for this command. + CommandArgumentEntry arg1; + CommandArgumentData id_arg; + id_arg.arg_type = eArgTypeBreakpointID; + id_arg.arg_repetition = eArgRepeatOptional; + arg1.push_back(id_arg); + m_arguments.push_back (arg1); + + m_option_group.Append (&m_name_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectBreakpointNameDelete () {} + + Options * + GetOptions () + { + return &m_option_group; + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + if (!m_name_options.m_name.OptionWasSet()) + { + result.SetError("No name option provided."); + return false; + } + + Target *target = GetSelectedOrDummyTarget(m_name_options.m_use_dummy.GetCurrentValue()); + + if (target == NULL) + { + result.AppendError ("Invalid target. No existing target or breakpoints."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Mutex::Locker locker; + target->GetBreakpointList().GetListMutex(locker); + + const BreakpointList &breakpoints = target->GetBreakpointList(); + + size_t num_breakpoints = breakpoints.GetSize(); + if (num_breakpoints == 0) + { + result.SetError("No breakpoints, cannot delete names."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // Particular breakpoint selected; disable that breakpoint. + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + if (valid_bp_ids.GetSize() == 0) + { + result.SetError("No breakpoints specified, cannot delete names."); + result.SetStatus (eReturnStatusFailed); + return false; + } + size_t num_valid_ids = valid_bp_ids.GetSize(); + for (size_t index = 0; index < num_valid_ids; index++) + { + lldb::break_id_t bp_id = valid_bp_ids.GetBreakpointIDAtIndex(index).GetBreakpointID(); + BreakpointSP bp_sp = breakpoints.FindBreakpointByID(bp_id); + bp_sp->RemoveName(m_name_options.m_name.GetCurrentValue()); + } + } + + return true; + } + +private: + BreakpointNameOptionGroup m_name_options; + OptionGroupOptions m_option_group; +}; + +class CommandObjectBreakpointNameList : public CommandObjectParsed +{ +public: + CommandObjectBreakpointNameList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "list", + "List either the names for a breakpoint or the breakpoints for a given name.", + "breakpoint name list <command-options>"), + m_name_options(), + m_option_group(interpreter) + { + m_option_group.Append (&m_name_options); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectBreakpointNameList () {} + + Options * + GetOptions () + { + return &m_option_group; + } + +protected: +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = GetSelectedOrDummyTarget(m_name_options.m_use_dummy.GetCurrentValue()); + + if (target == NULL) + { + result.AppendError ("Invalid target. No existing target or breakpoints."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (m_name_options.m_name.OptionWasSet()) + { + const char *name = m_name_options.m_name.GetCurrentValue(); + Mutex::Locker locker; + target->GetBreakpointList().GetListMutex(locker); + + BreakpointList &breakpoints = target->GetBreakpointList(); + for (BreakpointSP bp_sp : breakpoints.Breakpoints()) + { + if (bp_sp->MatchesName(name)) + { + StreamString s; + bp_sp->GetDescription(&s, eDescriptionLevelBrief); + s.EOL(); + result.AppendMessage(s.GetData()); + } + } + + } + else if (m_name_options.m_breakpoint.OptionWasSet()) + { + BreakpointSP bp_sp = target->GetBreakpointList().FindBreakpointByID(m_name_options.m_breakpoint.GetCurrentValue()); + if (bp_sp) + { + std::vector<std::string> names; + bp_sp->GetNames (names); + result.AppendMessage ("Names:"); + for (auto name : names) + result.AppendMessageWithFormat (" %s\n", name.c_str()); + } + else + { + result.AppendErrorWithFormat ("Could not find breakpoint %" PRId64 ".\n", + m_name_options.m_breakpoint.GetCurrentValue()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + result.SetError ("Must specify -N or -B option to list."); + result.SetStatus (eReturnStatusFailed); + return false; + } + return true; + } + +private: + BreakpointNameOptionGroup m_name_options; + OptionGroupOptions m_option_group; +}; + +//------------------------------------------------------------------------- +// CommandObjectMultiwordBreakpoint +//------------------------------------------------------------------------- +class CommandObjectBreakpointName : public CommandObjectMultiword +{ +public: + CommandObjectBreakpointName (CommandInterpreter &interpreter) : + CommandObjectMultiword(interpreter, + "name", + "A set of commands to manage name tags for breakpoints", + "breakpoint name <command> [<command-options>]") + { + CommandObjectSP add_command_object (new CommandObjectBreakpointNameAdd (interpreter)); + CommandObjectSP delete_command_object (new CommandObjectBreakpointNameDelete (interpreter)); + CommandObjectSP list_command_object (new CommandObjectBreakpointNameList (interpreter)); + + LoadSubCommand ("add", add_command_object); + LoadSubCommand ("delete", delete_command_object); + LoadSubCommand ("list", list_command_object); + + } + + virtual + ~CommandObjectBreakpointName () + { + } + +}; + //------------------------------------------------------------------------- // CommandObjectMultiwordBreakpoint @@ -1769,6 +2300,7 @@ CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint (CommandInter CommandObjectSP set_command_object (new CommandObjectBreakpointSet (interpreter)); CommandObjectSP command_command_object (new CommandObjectBreakpointCommand (interpreter)); CommandObjectSP modify_command_object (new CommandObjectBreakpointModify(interpreter)); + CommandObjectSP name_command_object (new CommandObjectBreakpointName(interpreter)); list_command_object->SetCommandName ("breakpoint list"); enable_command_object->SetCommandName("breakpoint enable"); @@ -1778,6 +2310,7 @@ CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint (CommandInter set_command_object->SetCommandName("breakpoint set"); command_command_object->SetCommandName ("breakpoint command"); modify_command_object->SetCommandName ("breakpoint modify"); + name_command_object->SetCommandName ("breakpoint name"); LoadSubCommand ("list", list_command_object); LoadSubCommand ("enable", enable_command_object); @@ -1787,6 +2320,7 @@ CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint (CommandInter LoadSubCommand ("set", set_command_object); LoadSubCommand ("command", command_command_object); LoadSubCommand ("modify", modify_command_object); + LoadSubCommand ("name", name_command_object); } CommandObjectMultiwordBreakpoint::~CommandObjectMultiwordBreakpoint () @@ -1794,13 +2328,17 @@ CommandObjectMultiwordBreakpoint::~CommandObjectMultiwordBreakpoint () } void -CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (Args &args, Target *target, CommandReturnObject &result, - BreakpointIDList *valid_ids) +CommandObjectMultiwordBreakpoint::VerifyIDs (Args &args, + Target *target, + bool allow_locations, + CommandReturnObject &result, + BreakpointIDList *valid_ids) { // args can be strings representing 1). integers (for breakpoint ids) // 2). the full breakpoint & location canonical representation // 3). the word "to" or a hyphen, representing a range (in which case there // had *better* be an entry both before & after of one of the first two types. + // 4). A breakpoint name // If args is empty, we will use the last created breakpoint (if there is one.) Args temp_args; @@ -1824,7 +2362,7 @@ CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (Args &args, Target *targe // the new TEMP_ARGS. Do not copy breakpoint id range strings over; instead generate a list of strings for // all the breakpoint ids in the range, and shove all of those breakpoint id strings into TEMP_ARGS. - BreakpointIDList::FindAndReplaceIDRanges (args, target, result, temp_args); + BreakpointIDList::FindAndReplaceIDRanges (args, target, allow_locations, result, temp_args); // NOW, convert the list of breakpoint id strings in TEMP_ARGS into an actual BreakpointIDList: diff --git a/source/Commands/CommandObjectBreakpoint.h b/source/Commands/CommandObjectBreakpoint.h index 2d674b22d704..3fdd2a5f56be 100644 --- a/source/Commands/CommandObjectBreakpoint.h +++ b/source/Commands/CommandObjectBreakpoint.h @@ -38,8 +38,20 @@ public: ~CommandObjectMultiwordBreakpoint (); static void - VerifyBreakpointIDs (Args &args, Target *target, CommandReturnObject &result, BreakpointIDList *valid_ids); + VerifyBreakpointOrLocationIDs (Args &args, Target *target, CommandReturnObject &result, BreakpointIDList *valid_ids) + { + VerifyIDs (args, target, true, result, valid_ids); + } + static void + VerifyBreakpointIDs (Args &args, Target *target, CommandReturnObject &result, BreakpointIDList *valid_ids) + { + VerifyIDs (args, target, false, result, valid_ids); + } + +private: + static void + VerifyIDs (Args &args, Target *target, bool allow_locations, CommandReturnObject &result, BreakpointIDList *valid_ids); }; } // namespace lldb_private diff --git a/source/Commands/CommandObjectBreakpointCommand.cpp b/source/Commands/CommandObjectBreakpointCommand.cpp index fdb87d11900b..8f8404b712a5 100644 --- a/source/Commands/CommandObjectBreakpointCommand.cpp +++ b/source/Commands/CommandObjectBreakpointCommand.cpp @@ -307,17 +307,16 @@ one command per line.\n" ); result.SetImmediateOutputStream (output_stream); result.SetImmediateErrorStream (error_stream); - bool stop_on_continue = true; - bool echo_commands = false; - bool print_results = true; - - debugger.GetCommandInterpreter().HandleCommands (commands, + CommandInterpreterRunOptions options; + options.SetStopOnContinue(true); + options.SetStopOnError (data->stop_on_error); + options.SetEchoCommands (true); + options.SetPrintResults (true); + options.SetAddToHistory (false); + + debugger.GetCommandInterpreter().HandleCommands (commands, &exe_ctx, - stop_on_continue, - data->stop_on_error, - echo_commands, - print_results, - eLazyBoolNo, + options, result); result.GetImmediateOutputStream()->Flush(); result.GetImmediateErrorStream()->Flush(); @@ -390,6 +389,10 @@ one command per line.\n" ); } break; + case 'D': + m_use_dummy = true; + break; + default: break; } @@ -406,6 +409,7 @@ one command per line.\n" ); m_stop_on_error = true; m_one_liner.clear(); m_function_name.clear(); + m_use_dummy = false; } const OptionDefinition* @@ -429,13 +433,14 @@ one command per line.\n" ); std::string m_one_liner; bool m_stop_on_error; std::string m_function_name; + bool m_use_dummy; }; protected: virtual bool DoExecute (Args& command, CommandReturnObject &result) { - Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + Target *target = GetSelectedOrDummyTarget(m_options.m_use_dummy); if (target == NULL) { @@ -462,7 +467,7 @@ protected: } BreakpointIDList valid_bp_ids; - CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs (command, target, result, &valid_bp_ids); m_bp_options_vec.clear(); @@ -581,6 +586,9 @@ CommandObjectBreakpointCommandAdd::CommandOptions::g_option_table[] = { LLDB_OPT_SET_2, false, "python-function", 'F', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypePythonFunction, "Give the name of a Python function to run as command for this breakpoint. Be sure to give a module name if appropriate."}, + { LLDB_OPT_SET_ALL, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, + "Sets Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets."}, + { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; @@ -595,7 +603,8 @@ public: CommandObjectParsed (interpreter, "delete", "Delete the set of commands from a breakpoint.", - NULL) + NULL), + m_options (interpreter) { CommandArgumentEntry arg; CommandArgumentData bp_id_arg; @@ -615,11 +624,70 @@ public: virtual ~CommandObjectBreakpointCommandDelete () {} + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_use_dummy (false) + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'D': + m_use_dummy = true; + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_use_dummy = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + bool m_use_dummy; + }; + protected: virtual bool DoExecute (Args& command, CommandReturnObject &result) { - Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + Target *target = GetSelectedOrDummyTarget(m_options.m_use_dummy); if (target == NULL) { @@ -646,7 +714,7 @@ protected: } BreakpointIDList valid_bp_ids; - CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs (command, target, result, &valid_bp_ids); if (result.Succeeded()) { @@ -680,8 +748,20 @@ protected: } return result.Succeeded(); } +private: + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectBreakpointCommandDelete::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, + "Delete commands from Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets."}, + + { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; + //------------------------------------------------------------------------- // CommandObjectBreakpointCommandList //------------------------------------------------------------------------- @@ -744,7 +824,7 @@ protected: } BreakpointIDList valid_bp_ids; - CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs (command, target, result, &valid_bp_ids); if (result.Succeeded()) { diff --git a/source/Commands/CommandObjectCommands.cpp b/source/Commands/CommandObjectCommands.cpp index 7d9bb7dad8fd..f98eac055f24 100644 --- a/source/Commands/CommandObjectCommands.cpp +++ b/source/Commands/CommandObjectCommands.cpp @@ -366,7 +366,7 @@ protected: // Instance variables to hold the values for command options. OptionValueBoolean m_stop_on_error; - OptionValueBoolean m_silent_run; + OptionValueBoolean m_silent_run; OptionValueBoolean m_stop_on_continue; }; @@ -387,14 +387,15 @@ protected: m_options.m_stop_on_continue.OptionWasSet()) { // Use user set settings - LazyBool print_command = m_options.m_silent_run.GetCurrentValue() ? eLazyBoolNo : eLazyBoolYes; + CommandInterpreterRunOptions options; + options.SetStopOnContinue(m_options.m_stop_on_continue.GetCurrentValue()); + options.SetStopOnError (m_options.m_stop_on_error.GetCurrentValue()); + options.SetEchoCommands (!m_options.m_silent_run.GetCurrentValue()); + options.SetPrintResults (!m_options.m_silent_run.GetCurrentValue()); + m_interpreter.HandleCommandsFromFile (cmd_file, exe_ctx, - m_options.m_stop_on_continue.GetCurrentValue() ? eLazyBoolYes : eLazyBoolNo, // Stop on continue - m_options.m_stop_on_error.GetCurrentValue() ? eLazyBoolYes : eLazyBoolNo, // Stop on error - print_command, // Echo command - print_command, // Print command output - eLazyBoolCalculate, // Add to history + options, result); } @@ -402,13 +403,10 @@ protected: { // No options were set, inherit any settings from nested "command source" commands, // or set to sane default settings... + CommandInterpreterRunOptions options; m_interpreter.HandleCommandsFromFile (cmd_file, exe_ctx, - eLazyBoolCalculate, // Stop on continue - eLazyBoolCalculate, // Stop on error - eLazyBoolCalculate, // Echo command - eLazyBoolCalculate, // Print command output - eLazyBoolCalculate, // Add to history + options, result); } @@ -830,8 +828,16 @@ protected: { if (m_interpreter.CommandExists (command_name)) { - result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be removed.\n", - command_name); + if (cmd_obj->IsRemovable()) + { + result.AppendErrorWithFormat ("'%s' is not an alias, it is a debugger command which can be removed using the 'command delete' command.\n", + command_name); + } + else + { + result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be removed.\n", + command_name); + } result.SetStatus (eReturnStatusFailed); } else @@ -868,6 +874,77 @@ protected: } }; +#pragma mark CommandObjectCommandsDelete +//------------------------------------------------------------------------- +// CommandObjectCommandsDelete +//------------------------------------------------------------------------- + +class CommandObjectCommandsDelete : public CommandObjectParsed +{ +public: + CommandObjectCommandsDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command delete", + "Allow the user to delete user-defined regular expression, python or multi-word commands.", + NULL) + { + CommandArgumentEntry arg; + CommandArgumentData alias_arg; + + // Define the first (and only) variant of this arg. + alias_arg.arg_type = eArgTypeCommandName; + alias_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (alias_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + ~CommandObjectCommandsDelete() + { + } + +protected: + bool + DoExecute (Args& args, CommandReturnObject &result) + { + CommandObject::CommandMap::iterator pos; + + if (args.GetArgumentCount() != 0) + { + const char *command_name = args.GetArgumentAtIndex(0); + if (m_interpreter.CommandExists (command_name)) + { + if (m_interpreter.RemoveCommand (command_name)) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be removed.\n", + command_name); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("'%s' is not a known command.\nTry 'help' to see a current list of commands.\n", + command_name); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("must call '%s' with one or more valid user defined regular expression, python or multi-word command names", GetCommandName ()); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + } +}; + //------------------------------------------------------------------------- // CommandObjectCommandsAddRegex //------------------------------------------------------------------------- @@ -875,7 +952,7 @@ protected: class CommandObjectCommandsAddRegex : public CommandObjectParsed, - public IOHandlerDelegate + public IOHandlerDelegateMultiline { public: CommandObjectCommandsAddRegex (CommandInterpreter &interpreter) : @@ -883,7 +960,7 @@ public: "command regex", "Allow the user to create a regular expression command.", "command regex <cmd-name> [s/<regex>/<subst>/ ...]"), - IOHandlerDelegate(IOHandlerDelegate::Completion::LLDBCommand), + IOHandlerDelegateMultiline ("", IOHandlerDelegate::Completion::LLDBCommand), m_options (interpreter) { SetHelpLong( @@ -920,8 +997,8 @@ public: protected: - virtual void - IOHandlerActivated (IOHandler &io_handler) + void + IOHandlerActivated (IOHandler &io_handler) override { StreamFileSP output_sp(io_handler.GetOutputStreamFile()); if (output_sp) @@ -931,8 +1008,8 @@ protected: } } - virtual void - IOHandlerInputComplete (IOHandler &io_handler, std::string &data) + void + IOHandlerInputComplete (IOHandler &io_handler, std::string &data) override { io_handler.SetIsDone(true); if (m_regex_cmd_ap.get()) @@ -944,7 +1021,6 @@ protected: bool check_only = false; for (size_t i=0; i<num_lines; ++i) { - printf ("regex[%zu] = %s\n", i, lines[i].c_str()); llvm::StringRef bytes_strref (lines[i]); Error error = AppendRegexSubstitution (bytes_strref, check_only); if (error.Fail()) @@ -964,54 +1040,9 @@ protected: } } } - - virtual LineStatus - IOHandlerLinesUpdated (IOHandler &io_handler, - StringList &lines, - uint32_t line_idx, - Error &error) - { - if (line_idx == UINT32_MAX) - { - // Return true to indicate we are done getting lines (this - // is a "fake" line - the real terminating blank line was - // removed during a previous call with the code below) - error.Clear(); - return LineStatus::Done; - } - else - { - const size_t num_lines = lines.GetSize(); - if (line_idx + 1 == num_lines) - { - // The last line was edited, if this line is empty, then we are done - // getting our multiple lines. - if (lines[line_idx].empty()) - { - // Remove the last empty line from "lines" so it doesn't appear - // in our final expression and return true to indicate we are done - // getting lines - lines.PopBack(); - return LineStatus::Done; - } - } - // Check the current line to make sure it is formatted correctly - bool check_only = true; - llvm::StringRef regex_sed(lines[line_idx]); - error = AppendRegexSubstitution (regex_sed, check_only); - if (error.Fail()) - { - return LineStatus::Error; - } - else - { - return LineStatus::Success; - } - } - } bool - DoExecute (Args& command, CommandReturnObject &result) + DoExecute (Args& command, CommandReturnObject &result) override { const size_t argc = command.GetArgumentCount(); if (argc == 0) @@ -1027,16 +1058,22 @@ protected: name, m_options.GetHelp (), m_options.GetSyntax (), - 10)); + 10, + 0, + true)); if (argc == 1) { Debugger &debugger = m_interpreter.GetDebugger(); + bool color_prompt = debugger.GetUseColor(); const bool multiple_lines = true; // Get multiple lines IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger, + IOHandler::Type::Other, "lldb-regex", // Name of input reader for history - "\033[K> ", // Prompt and clear line + "> ", // Prompt + NULL, // Continuation prompt multiple_lines, + color_prompt, 0, // Don't show line numbers *this)); @@ -1110,21 +1147,25 @@ protected: if (second_separator_char_pos == std::string::npos) { - error.SetErrorStringWithFormat("missing second '%c' separator char after '%.*s'", + error.SetErrorStringWithFormat("missing second '%c' separator char after '%.*s' in '%.*s'", separator_char, (int)(regex_sed.size() - first_separator_char_pos - 1), - regex_sed.data() + (first_separator_char_pos + 1)); - return error; + regex_sed.data() + (first_separator_char_pos + 1), + (int)regex_sed.size(), + regex_sed.data()); + return error; } const size_t third_separator_char_pos = regex_sed.find (separator_char, second_separator_char_pos + 1); if (third_separator_char_pos == std::string::npos) { - error.SetErrorStringWithFormat("missing third '%c' separator char after '%.*s'", + error.SetErrorStringWithFormat("missing third '%c' separator char after '%.*s' in '%.*s'", separator_char, (int)(regex_sed.size() - second_separator_char_pos - 1), - regex_sed.data() + (second_separator_char_pos + 1)); + regex_sed.data() + (second_separator_char_pos + 1), + (int)regex_sed.size(), + regex_sed.data()); return error; } @@ -1262,8 +1303,8 @@ private: std::string m_syntax; }; - virtual Options * - GetOptions () + Options * + GetOptions () override { return &m_options; } @@ -1292,15 +1333,24 @@ public: CommandObjectPythonFunction (CommandInterpreter &interpreter, std::string name, std::string funct, + std::string help, ScriptedCommandSynchronicity synch) : CommandObjectRaw (interpreter, name.c_str(), - (std::string("Run Python function ") + funct).c_str(), + NULL, NULL), m_function_name(funct), m_synchro(synch), m_fetched_help_long(false) { + if (!help.empty()) + SetHelp(help.c_str()); + else + { + StreamString stream; + stream.Printf("For more information run 'help %s'",name.c_str()); + SetHelp(stream.GetData()); + } } virtual @@ -1357,7 +1407,8 @@ protected: raw_command_line, m_synchro, result, - error) == false) + error, + m_exe_ctx) == false) { result.AppendError(error.AsCString()); result.SetStatus(eReturnStatusFailed); @@ -1617,7 +1668,12 @@ protected: switch (short_option) { case 'f': - m_funct_name = std::string(option_arg); + if (option_arg) + m_funct_name.assign(option_arg); + break; + case 'h': + if (option_arg) + m_short_help.assign(option_arg); break; case 's': m_synchronicity = (ScriptedCommandSynchronicity) Args::StringToOptionEnum(option_arg, g_option_table[option_idx].enum_values, 0, error); @@ -1635,7 +1691,8 @@ protected: void OptionParsingStarting () { - m_funct_name = ""; + m_funct_name.clear(); + m_short_help.clear(); m_synchronicity = eScriptedCommandSynchronicitySynchronous; } @@ -1652,6 +1709,7 @@ protected: // Instance variables to hold the values for command options. std::string m_funct_name; + std::string m_short_help; ScriptedCommandSynchronicity m_synchronicity; }; @@ -1695,6 +1753,7 @@ protected: CommandObjectSP command_obj_sp(new CommandObjectPythonFunction (m_interpreter, m_cmd_name, funct_name_str.c_str(), + m_short_help, m_synchronicity)); if (!m_interpreter.AddUserCommand(m_cmd_name, command_obj_sp, true)) @@ -1748,8 +1807,9 @@ protected: return false; } - // Store the command name and synchronicity in case we get multi-line input + // Store the options in case we get multi-line input m_cmd_name = command.GetArgumentAtIndex(0); + m_short_help.assign(m_options.m_short_help); m_synchronicity = m_options.m_synchronicity; if (m_options.m_funct_name.empty()) @@ -1764,6 +1824,7 @@ protected: CommandObjectSP new_cmd(new CommandObjectPythonFunction(m_interpreter, m_cmd_name, m_options.m_funct_name, + m_options.m_short_help, m_synchronicity)); if (m_interpreter.AddUserCommand(m_cmd_name, new_cmd, true)) { @@ -1782,6 +1843,7 @@ protected: CommandOptions m_options; std::string m_cmd_name; + std::string m_short_help; ScriptedCommandSynchronicity m_synchronicity; }; @@ -1797,6 +1859,7 @@ OptionDefinition CommandObjectCommandsScriptAdd::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_1, false, "function", 'f', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypePythonFunction, "Name of the Python function to bind to this command name."}, + { LLDB_OPT_SET_1, false, "help" , 'h', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeHelpText, "The help text to display for this command."}, { LLDB_OPT_SET_1, false, "synchronicity", 's', OptionParser::eRequiredArgument, NULL, g_script_synchro_type, 0, eArgTypeScriptedCommandSynchronicity, "Set the synchronicity of this command's executions with regard to LLDB event system."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; @@ -1949,11 +2012,11 @@ public: "A set of commands for managing or customizing script commands.", "command script <subcommand> [<subcommand-options>]") { - LoadSubCommand ("add", CommandObjectSP (new CommandObjectCommandsScriptAdd (interpreter))); - LoadSubCommand ("delete", CommandObjectSP (new CommandObjectCommandsScriptDelete (interpreter))); - LoadSubCommand ("clear", CommandObjectSP (new CommandObjectCommandsScriptClear (interpreter))); + LoadSubCommand ("add", CommandObjectSP (new CommandObjectCommandsScriptAdd (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectCommandsScriptDelete (interpreter))); + LoadSubCommand ("clear", CommandObjectSP (new CommandObjectCommandsScriptClear (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectCommandsScriptList (interpreter))); - LoadSubCommand ("import", CommandObjectSP (new CommandObjectCommandsScriptImport (interpreter))); + LoadSubCommand ("import", CommandObjectSP (new CommandObjectCommandsScriptImport (interpreter))); } ~CommandObjectMultiwordCommandsScript () @@ -1978,9 +2041,10 @@ CommandObjectMultiwordCommands::CommandObjectMultiwordCommands (CommandInterpret LoadSubCommand ("source", CommandObjectSP (new CommandObjectCommandsSource (interpreter))); LoadSubCommand ("alias", CommandObjectSP (new CommandObjectCommandsAlias (interpreter))); LoadSubCommand ("unalias", CommandObjectSP (new CommandObjectCommandsUnalias (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectCommandsDelete (interpreter))); LoadSubCommand ("regex", CommandObjectSP (new CommandObjectCommandsAddRegex (interpreter))); - LoadSubCommand ("history", CommandObjectSP (new CommandObjectCommandsHistory (interpreter))); - LoadSubCommand ("script", CommandObjectSP (new CommandObjectMultiwordCommandsScript (interpreter))); + LoadSubCommand ("history", CommandObjectSP (new CommandObjectCommandsHistory (interpreter))); + LoadSubCommand ("script", CommandObjectSP (new CommandObjectMultiwordCommandsScript (interpreter))); } CommandObjectMultiwordCommands::~CommandObjectMultiwordCommands () diff --git a/source/Commands/CommandObjectExpression.cpp b/source/Commands/CommandObjectExpression.cpp index 079c62ddfdff..b4c559c81cc5 100644 --- a/source/Commands/CommandObjectExpression.cpp +++ b/source/Commands/CommandObjectExpression.cpp @@ -281,7 +281,7 @@ CommandObjectExpression::EvaluateExpression Target *target = exe_ctx.GetTargetPtr(); if (!target) - target = Host::GetDummyTarget(m_interpreter.GetDebugger()).get(); + target = GetDummyTarget(); if (target) { @@ -425,11 +425,15 @@ CommandObjectExpression::GetMultilineExpression () m_expr_line_count = 0; Debugger &debugger = GetCommandInterpreter().GetDebugger(); + bool color_prompt = debugger.GetUseColor(); const bool multiple_lines = true; // Get multiple lines IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger, + IOHandler::Type::Expression, "lldb-expr", // Name of input reader for history NULL, // No prompt + NULL, // Continuation prompt multiple_lines, + color_prompt, 1, // Show line numbers starting at 1 *this)); diff --git a/source/Commands/CommandObjectMemory.cpp b/source/Commands/CommandObjectMemory.cpp index bfbb296158a9..6c06ec831830 100644 --- a/source/Commands/CommandObjectMemory.cpp +++ b/source/Commands/CommandObjectMemory.cpp @@ -33,8 +33,10 @@ #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" #include "lldb/Interpreter/OptionValueString.h" #include "lldb/Symbol/TypeList.h" +#include "lldb/Target/MemoryHistory.h" #include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" using namespace lldb; using namespace lldb_private; @@ -612,7 +614,16 @@ protected: } size_t item_count = m_format_options.GetCountValue().GetCurrentValue(); - size_t item_byte_size = m_format_options.GetByteSizeValue().GetCurrentValue(); + + // TODO For non-8-bit byte addressable architectures this needs to be + // revisited to fully support all lldb's range of formatting options. + // Furthermore code memory reads (for those architectures) will not + // be correctly formatted even w/o formatting options. + size_t item_byte_size = + target->GetArchitecture().GetDataByteSize() > 1 ? + target->GetArchitecture().GetDataByteSize() : + m_format_options.GetByteSizeValue().GetCurrentValue(); + const size_t num_per_line = m_memory_options.m_num_per_line.GetCurrentValue(); if (total_byte_size == 0) @@ -659,7 +670,7 @@ protected: total_byte_size = end_addr - addr; item_count = total_byte_size / item_byte_size; } - + uint32_t max_unforced_size = target->GetMaximumMemReadSize(); if (total_byte_size > max_unforced_size && !m_memory_options.m_force) @@ -856,7 +867,8 @@ protected: result.SetStatus(eReturnStatusSuccessFinishResult); DataExtractor data (data_sp, target->GetArchitecture().GetByteOrder(), - target->GetArchitecture().GetAddressByteSize()); + target->GetArchitecture().GetAddressByteSize(), + target->GetArchitecture().GetDataByteSize()); Format format = m_format_options.GetFormat(); if ( ( (format == eFormatChar) || (format == eFormatCharPrintable) ) @@ -890,7 +902,7 @@ protected: format, item_byte_size, item_count, - num_per_line, + num_per_line / target->GetArchitecture().GetDataByteSize(), addr, 0, 0, @@ -1078,7 +1090,7 @@ protected: lldb::addr_t high_addr = Args::StringToAddress(&m_exe_ctx, command.GetArgumentAtIndex(1),LLDB_INVALID_ADDRESS,&error); if (high_addr == LLDB_INVALID_ADDRESS || error.Fail()) { - result.AppendError("invalid low address"); + result.AppendError("invalid high address"); return false; } @@ -1667,6 +1679,96 @@ protected: OptionGroupWriteMemory m_memory_options; }; +//---------------------------------------------------------------------- +// Get malloc/free history of a memory address. +//---------------------------------------------------------------------- +class CommandObjectMemoryHistory : public CommandObjectParsed +{ +public: + + CommandObjectMemoryHistory (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "memory history", + "Prints out the recorded stack traces for allocation/deallocation of a memory address.", + NULL, + eFlagRequiresTarget | eFlagRequiresProcess | eFlagProcessMustBePaused | eFlagProcessMustBeLaunched) + { + CommandArgumentEntry arg1; + CommandArgumentData addr_arg; + + // Define the first (and only) variant of this arg. + addr_arg.arg_type = eArgTypeAddress; + addr_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg1.push_back (addr_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg1); + } + + virtual + ~CommandObjectMemoryHistory () + { + } + + virtual const char *GetRepeatCommand (Args ¤t_command_args, uint32_t index) + { + return m_cmd_name.c_str(); + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + if (argc == 0 || argc > 1) + { + result.AppendErrorWithFormat ("%s takes an address expression", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Error error; + lldb::addr_t addr = Args::StringToAddress (&m_exe_ctx, + command.GetArgumentAtIndex(0), + LLDB_INVALID_ADDRESS, + &error); + + if (addr == LLDB_INVALID_ADDRESS) + { + result.AppendError("invalid address expression"); + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Stream *output_stream = &result.GetOutputStream(); + + const ProcessSP &process_sp = m_exe_ctx.GetProcessSP(); + const MemoryHistorySP &memory_history = MemoryHistory::FindPlugin(process_sp); + + if (! memory_history.get()) + { + result.AppendError("no available memory history provider"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + HistoryThreads thread_list = memory_history->GetHistoryThreads(addr); + + for (auto thread : thread_list) { + thread->GetStatus(*output_stream, 0, UINT32_MAX, 0); + } + + result.SetStatus(eReturnStatusSuccessFinishResult); + + return true; + } + +}; + //------------------------------------------------------------------------- // CommandObjectMemory @@ -1681,6 +1783,7 @@ CommandObjectMemory::CommandObjectMemory (CommandInterpreter &interpreter) : LoadSubCommand ("find", CommandObjectSP (new CommandObjectMemoryFind (interpreter))); LoadSubCommand ("read", CommandObjectSP (new CommandObjectMemoryRead (interpreter))); LoadSubCommand ("write", CommandObjectSP (new CommandObjectMemoryWrite (interpreter))); + LoadSubCommand ("history", CommandObjectSP (new CommandObjectMemoryHistory (interpreter))); } CommandObjectMemory::~CommandObjectMemory () diff --git a/source/Commands/CommandObjectPlatform.cpp b/source/Commands/CommandObjectPlatform.cpp index 9998dbdccdad..d176d52cb487 100644 --- a/source/Commands/CommandObjectPlatform.cpp +++ b/source/Commands/CommandObjectPlatform.cpp @@ -302,7 +302,7 @@ protected: Stream &ostrm = result.GetOutputStream(); ostrm.Printf("Available platforms:\n"); - PlatformSP host_platform_sp (Platform::GetDefaultPlatform()); + PlatformSP host_platform_sp (Platform::GetHostPlatform()); ostrm.Printf ("%s: %s\n", host_platform_sp->GetPluginName().GetCString(), host_platform_sp->GetDescription()); @@ -1347,7 +1347,6 @@ protected: ProcessSP process_sp (platform_sp->DebugProcess (m_options.launch_info, debugger, target, - debugger.GetListener(), error)); if (process_sp && process_sp->IsAlive()) { @@ -1933,7 +1932,7 @@ public: { Error err; ProcessSP remote_process_sp = - platform_sp->Attach(m_options.attach_info, m_interpreter.GetDebugger(), NULL, m_interpreter.GetDebugger().GetListener(), err); + platform_sp->Attach(m_options.attach_info, m_interpreter.GetDebugger(), NULL, err); if (err.Fail()) { result.AppendError(err.AsCString()); diff --git a/source/Commands/CommandObjectProcess.cpp b/source/Commands/CommandObjectProcess.cpp index 6536c6ef1693..ec7b478fbecc 100644 --- a/source/Commands/CommandObjectProcess.cpp +++ b/source/Commands/CommandObjectProcess.cpp @@ -258,8 +258,9 @@ protected: // Save the arguments for subsequent runs in the current target. target->SetRunArguments (launch_args); } - - Error error = target->Launch(debugger.GetListener(), m_options.launch_info); + + StreamString stream; + Error error = target->Launch(m_options.launch_info, &stream); if (error.Success()) { @@ -267,6 +268,9 @@ protected: ProcessSP process_sp (target->GetProcessSP()); if (process_sp) { + const char *data = stream.GetData(); + if (data && strlen(data) > 0) + result.AppendMessage(stream.GetData()); result.AppendMessageWithFormat ("Process %" PRIu64 " launched: '%s' (%s)\n", process_sp->GetID(), exe_module_sp->GetFileSpec().GetPath().c_str(), archname); result.SetStatus (eReturnStatusSuccessFinishResult); result.SetDidChangeProcessState (true); @@ -564,15 +568,18 @@ protected: if (error.Success()) { result.SetStatus (eReturnStatusSuccessContinuingNoResult); - StateType state = process->WaitForProcessToStop (NULL, NULL, false, listener_sp.get()); + StreamString stream; + StateType state = process->WaitForProcessToStop (NULL, NULL, false, listener_sp.get(), &stream); process->RestoreProcessEvents(); result.SetDidChangeProcessState (true); + if (stream.GetData()) + result.AppendMessage(stream.GetData()); + if (state == eStateStopped) { - result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); result.SetStatus (eReturnStatusSuccessFinishNoResult); } else @@ -791,7 +798,12 @@ protected: } } - Error error(process->Resume()); + StreamString stream; + Error error; + if (synchronous_execution) + error = process->ResumeSynchronous (&stream); + else + error = process->Resume (); if (error.Success()) { @@ -803,10 +815,11 @@ protected: result.AppendMessageWithFormat ("Process %" PRIu64 " resuming\n", process->GetID()); if (synchronous_execution) { - state = process->WaitForProcessToStop (NULL); + // If any state changed events had anything to say, add that to the result + if (stream.GetData()) + result.AppendMessage(stream.GetData()); result.SetDidChangeProcessState (true); - result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); result.SetStatus (eReturnStatusSuccessFinishNoResult); } else diff --git a/source/Commands/CommandObjectSource.cpp b/source/Commands/CommandObjectSource.cpp index 6b1b6aacc857..8fb03e69ac42 100644 --- a/source/Commands/CommandObjectSource.cpp +++ b/source/Commands/CommandObjectSource.cpp @@ -421,7 +421,7 @@ protected: { const bool show_inlines = true; m_breakpoint_locations.Reset (start_file, 0, show_inlines); - SearchFilter target_search_filter (m_exe_ctx.GetTargetSP()); + SearchFilterForUnconstrainedSearches target_search_filter (m_exe_ctx.GetTargetSP()); target_search_filter.Search (m_breakpoint_locations); } @@ -682,19 +682,21 @@ protected: m_breakpoint_locations.Clear(); const bool show_inlines = true; m_breakpoint_locations.Reset (*sc.comp_unit, 0, show_inlines); - SearchFilter target_search_filter (target->shared_from_this()); + SearchFilterForUnconstrainedSearches target_search_filter (target->shared_from_this()); target_search_filter.Search (m_breakpoint_locations); } bool show_fullpaths = true; bool show_module = true; bool show_inlined_frames = true; + const bool show_function_arguments = true; sc.DumpStopContext(&result.GetOutputStream(), m_exe_ctx.GetBestExecutionContextScope(), sc.line_entry.range.GetBaseAddress(), show_fullpaths, show_module, - show_inlined_frames); + show_inlined_frames, + show_function_arguments); result.GetOutputStream().EOL(); if (m_options.num_lines == 0) @@ -741,7 +743,7 @@ protected: { const bool show_inlines = true; m_breakpoint_locations.Reset (last_file_sp->GetFileSpec(), 0, show_inlines); - SearchFilter target_search_filter (target->shared_from_this()); + SearchFilterForUnconstrainedSearches target_search_filter (target->shared_from_this()); target_search_filter.Search (m_breakpoint_locations); } } @@ -844,7 +846,7 @@ protected: { const bool show_inlines = true; m_breakpoint_locations.Reset (*sc.comp_unit, 0, show_inlines); - SearchFilter target_search_filter (target->shared_from_this()); + SearchFilterForUnconstrainedSearches target_search_filter (target->shared_from_this()); target_search_filter.Search (m_breakpoint_locations); } else diff --git a/source/Commands/CommandObjectSyntax.cpp b/source/Commands/CommandObjectSyntax.cpp index d2021ea3eb19..5093c3b99339 100644 --- a/source/Commands/CommandObjectSyntax.cpp +++ b/source/Commands/CommandObjectSyntax.cpp @@ -69,12 +69,18 @@ CommandObjectSyntax::DoExecute (Args& command, CommandReturnObject &result) { std::string sub_command = command.GetArgumentAtIndex (i); if (!cmd_obj->IsMultiwordObject()) + { all_okay = false; + break; + } else { cmd_obj = cmd_obj->GetSubcommandObject(sub_command.c_str()); if (!cmd_obj) + { all_okay = false; + break; + } } } diff --git a/source/Commands/CommandObjectTarget.cpp b/source/Commands/CommandObjectTarget.cpp index 024f7b5a0415..0d9ffda1e96b 100644 --- a/source/Commands/CommandObjectTarget.cpp +++ b/source/Commands/CommandObjectTarget.cpp @@ -39,6 +39,7 @@ #include "lldb/Interpreter/OptionGroupPlatform.h" #include "lldb/Interpreter/OptionGroupUInt64.h" #include "lldb/Interpreter/OptionGroupUUID.h" +#include "lldb/Interpreter/OptionGroupString.h" #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/FuncUnwinders.h" @@ -2844,7 +2845,7 @@ public: "Set the load addresses for one or more sections in a target module.", "target modules load [--file <module> --uuid <uuid>] <sect-name> <address> [<sect-name> <address> ....]"), m_option_group (interpreter), - m_file_option (LLDB_OPT_SET_1, false, "file", 'f', 0, eArgTypeFilename, "Fullpath or basename for module to load."), + m_file_option (LLDB_OPT_SET_1, false, "file", 'f', 0, eArgTypeName, "Fullpath or basename for module to load.", ""), m_slide_option(LLDB_OPT_SET_1, false, "slide", 's', 0, eArgTypeOffset, "Set the load address for all sections to be the virtual address in the file plus the offset.", 0) { m_option_group.Append (&m_uuid_option_group, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); @@ -2884,7 +2885,26 @@ protected: if (m_file_option.GetOptionValue().OptionWasSet()) { search_using_module_spec = true; - module_spec.GetFileSpec() = m_file_option.GetOptionValue().GetCurrentValue(); + const char *arg_cstr = m_file_option.GetOptionValue().GetCurrentValue(); + const bool use_global_module_list = true; + ModuleList module_list; + const size_t num_matches = FindModulesByName (target, arg_cstr, module_list, use_global_module_list); + if (num_matches == 1) + { + module_spec.GetFileSpec() = module_list.GetModuleAtIndex(0)->GetFileSpec(); + } + else if (num_matches > 1 ) + { + search_using_module_spec = false; + result.AppendErrorWithFormat ("more than 1 module matched by name '%s'\n", arg_cstr); + result.SetStatus (eReturnStatusFailed); + } + else + { + search_using_module_spec = false; + result.AppendErrorWithFormat ("no object file for module '%s'\n", arg_cstr); + result.SetStatus (eReturnStatusFailed); + } } if (m_uuid_option_group.GetOptionValue().OptionWasSet()) @@ -3070,7 +3090,7 @@ protected: OptionGroupOptions m_option_group; OptionGroupUUID m_uuid_option_group; - OptionGroupFile m_file_option; + OptionGroupString m_file_option; OptionGroupUInt64 m_slide_option; }; @@ -3724,45 +3744,85 @@ protected: if (func_unwinders_sp.get() == NULL) continue; - Address first_non_prologue_insn (func_unwinders_sp->GetFirstNonPrologueInsn(*target)); - if (first_non_prologue_insn.IsValid()) - { - result.GetOutputStream().Printf("First non-prologue instruction is at address 0x%" PRIx64 " or offset %" PRId64 " into the function.\n", first_non_prologue_insn.GetLoadAddress(target), first_non_prologue_insn.GetLoadAddress(target) - start_addr); - result.GetOutputStream().Printf ("\n"); - } + result.GetOutputStream().Printf("UNWIND PLANS for %s`%s (start addr 0x%" PRIx64 ")\n\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); 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); - non_callsite_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); - result.GetOutputStream().Printf ("\n"); + result.GetOutputStream().Printf("Asynchronous (not restricted to call-sites) UnwindPlan is '%s'\n", non_callsite_unwind_plan->GetSourceName().AsCString()); } - - UnwindPlanSP callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(-1); + UnwindPlanSP callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(*target, -1); if (callsite_unwind_plan.get()) { - result.GetOutputStream().Printf("Synchronous (restricted to call-sites) UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); - callsite_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); - result.GetOutputStream().Printf ("\n"); + result.GetOutputStream().Printf("Synchronous (restricted to call-sites) UnwindPlan is '%s'\n", callsite_unwind_plan->GetSourceName().AsCString()); + } + UnwindPlanSP fast_unwind_plan = func_unwinders_sp->GetUnwindPlanFastUnwind(*thread.get()); + if (fast_unwind_plan.get()) + { + result.GetOutputStream().Printf("Fast UnwindPlan is '%s'\n", fast_unwind_plan->GetSourceName().AsCString()); } - UnwindPlanSP arch_default_unwind_plan = func_unwinders_sp->GetUnwindPlanArchitectureDefault(*thread.get()); - if (arch_default_unwind_plan.get()) + result.GetOutputStream().Printf("\n"); + + UnwindPlanSP assembly_sp = func_unwinders_sp->GetAssemblyUnwindPlan(*target, *thread.get(), 0); + if (assembly_sp) { - result.GetOutputStream().Printf("Architecture default UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); - arch_default_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); - result.GetOutputStream().Printf ("\n"); + result.GetOutputStream().Printf("Assembly language inspection UnwindPlan:\n"); + assembly_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf("\n"); } + - UnwindPlanSP fast_unwind_plan = func_unwinders_sp->GetUnwindPlanFastUnwind(*thread.get()); - if (fast_unwind_plan.get()) + UnwindPlanSP ehframe_sp = func_unwinders_sp->GetEHFrameUnwindPlan(*target, 0); + if (ehframe_sp) { - result.GetOutputStream().Printf("Fast UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); + result.GetOutputStream().Printf("eh_frame UnwindPlan:\n"); + ehframe_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf("\n"); + } + + UnwindPlanSP ehframe_augmented_sp = func_unwinders_sp->GetEHFrameAugmentedUnwindPlan(*target, *thread.get(), 0); + if (ehframe_augmented_sp) + { + result.GetOutputStream().Printf("eh_frame augmented UnwindPlan:\n"); + ehframe_augmented_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf("\n"); + } + + UnwindPlanSP compact_unwind_sp = func_unwinders_sp->GetCompactUnwindUnwindPlan(*target, 0); + if (compact_unwind_sp) + { + result.GetOutputStream().Printf("Compact unwind UnwindPlan:\n"); + compact_unwind_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf("\n"); + } + + if (fast_unwind_plan) + { + result.GetOutputStream().Printf("Fast UnwindPlan:\n"); fast_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); - result.GetOutputStream().Printf ("\n"); + result.GetOutputStream().Printf("\n"); } + ABISP abi_sp = process->GetABI(); + if (abi_sp) + { + UnwindPlan arch_default(lldb::eRegisterKindGeneric); + if (abi_sp->CreateDefaultUnwindPlan (arch_default)) + { + result.GetOutputStream().Printf("Arch default UnwindPlan:\n"); + arch_default.Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf("\n"); + } + + UnwindPlan arch_entry(lldb::eRegisterKindGeneric); + if (abi_sp->CreateFunctionEntryUnwindPlan (arch_entry)) + { + result.GetOutputStream().Printf("Arch default at entry point UnwindPlan:\n"); + arch_entry.Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf("\n"); + } + } result.GetOutputStream().Printf ("\n"); } @@ -4999,7 +5059,7 @@ protected: { m_stop_hook_sp.reset(); - Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + Target *target = GetSelectedOrDummyTarget(); if (target) { Target::StopHookSP new_hook_sp = target->CreateStopHook(); @@ -5151,7 +5211,7 @@ protected: bool DoExecute (Args& command, CommandReturnObject &result) { - Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + Target *target = GetSelectedOrDummyTarget(); if (target) { // FIXME: see if we can use the breakpoint id style parser? @@ -5227,7 +5287,7 @@ protected: bool DoExecute (Args& command, CommandReturnObject &result) { - Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + Target *target = GetSelectedOrDummyTarget(); if (target) { // FIXME: see if we can use the breakpoint id style parser? @@ -5297,7 +5357,7 @@ protected: bool DoExecute (Args& command, CommandReturnObject &result) { - Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + Target *target = GetSelectedOrDummyTarget(); if (!target) { result.AppendError ("invalid target\n"); diff --git a/source/Commands/CommandObjectThread.cpp b/source/Commands/CommandObjectThread.cpp index e7a8652ac898..bace4e58b4ad 100644 --- a/source/Commands/CommandObjectThread.cpp +++ b/source/Commands/CommandObjectThread.cpp @@ -46,7 +46,108 @@ using namespace lldb_private; // CommandObjectThreadBacktrace //------------------------------------------------------------------------- -class CommandObjectThreadBacktrace : public CommandObjectParsed +class CommandObjectIterateOverThreads : public CommandObjectParsed +{ +public: + CommandObjectIterateOverThreads (CommandInterpreter &interpreter, + const char *name, + const char *help, + const char *syntax, + uint32_t flags) : + CommandObjectParsed (interpreter, name, help, syntax, flags) + { + } + + virtual ~CommandObjectIterateOverThreads() {} + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + result.SetStatus (m_success_return); + + if (command.GetArgumentCount() == 0) + { + Thread *thread = m_exe_ctx.GetThreadPtr(); + if (!HandleOneThread (*thread, result)) + return false; + } + else if (command.GetArgumentCount() == 1 && ::strcmp (command.GetArgumentAtIndex(0), "all") == 0) + { + Process *process = m_exe_ctx.GetProcessPtr(); + uint32_t idx = 0; + for (ThreadSP thread_sp : process->Threads()) + { + if (idx != 0 && m_add_return) + result.AppendMessage(""); + + if (!HandleOneThread(*(thread_sp.get()), result)) + return false; + ++idx; + } + } + else + { + const size_t num_args = command.GetArgumentCount(); + Process *process = m_exe_ctx.GetProcessPtr(); + Mutex::Locker locker (process->GetThreadList().GetMutex()); + std::vector<ThreadSP> thread_sps; + + for (size_t i = 0; i < num_args; i++) + { + bool success; + + uint32_t thread_idx = Args::StringToUInt32(command.GetArgumentAtIndex(i), 0, 0, &success); + if (!success) + { + result.AppendErrorWithFormat ("invalid thread specification: \"%s\"\n", command.GetArgumentAtIndex(i)); + result.SetStatus (eReturnStatusFailed); + return false; + } + + thread_sps.push_back(process->GetThreadList().FindThreadByIndexID(thread_idx)); + + if (!thread_sps[i]) + { + result.AppendErrorWithFormat ("no thread with index: \"%s\"\n", command.GetArgumentAtIndex(i)); + result.SetStatus (eReturnStatusFailed); + return false; + } + + } + + for (uint32_t i = 0; i < num_args; i++) + { + if (!HandleOneThread (*(thread_sps[i].get()), result)) + return false; + + if (i < num_args - 1 && m_add_return) + result.AppendMessage(""); + } + } + return result.Succeeded(); + } + +protected: + + // Override this to do whatever you need to do for one thread. + // + // If you return false, the iteration will stop, otherwise it will proceed. + // The result is set to m_success_return (defaults to eReturnStatusSuccessFinishResult) before the iteration, + // so you only need to set the return status in HandleOneThread if you want to indicate an error. + // If m_add_return is true, a blank line will be inserted between each of the listings (except the last one.) + + virtual bool + HandleOneThread (Thread &thread, CommandReturnObject &result) = 0; + + ReturnStatus m_success_return = eReturnStatusSuccessFinishResult; + bool m_add_return = true; + +}; + +//------------------------------------------------------------------------- +// CommandObjectThreadBacktrace +//------------------------------------------------------------------------- + +class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads { public: @@ -134,7 +235,7 @@ public: }; CommandObjectThreadBacktrace (CommandInterpreter &interpreter) : - CommandObjectParsed (interpreter, + CommandObjectIterateOverThreads (interpreter, "thread backtrace", "Show the stack for one or more threads. If no threads are specified, show the currently selected thread. Use the thread-index \"all\" to see all threads.", NULL, @@ -145,18 +246,6 @@ public: eFlagProcessMustBePaused ), m_options(interpreter) { - CommandArgumentEntry arg; - CommandArgumentData thread_idx_arg; - - // Define the first (and only) variant of this arg. - thread_idx_arg.arg_type = eArgTypeThreadIndex; - thread_idx_arg.arg_repetition = eArgRepeatStar; - - // There is only one variant this argument could be; put it into the argument entry. - arg.push_back (thread_idx_arg); - - // Push the data for the first argument into the m_arguments vector. - m_arguments.push_back (arg); } ~CommandObjectThreadBacktrace() @@ -197,106 +286,28 @@ protected: } virtual bool - DoExecute (Args& command, CommandReturnObject &result) - { - result.SetStatus (eReturnStatusSuccessFinishResult); + HandleOneThread (Thread &thread, CommandReturnObject &result) + { Stream &strm = result.GetOutputStream(); // Don't show source context when doing backtraces. const uint32_t num_frames_with_source = 0; - if (command.GetArgumentCount() == 0) - { - Thread *thread = m_exe_ctx.GetThreadPtr(); - // Thread::GetStatus() returns the number of frames shown. - if (thread->GetStatus (strm, + + if (!thread.GetStatus (strm, m_options.m_start, m_options.m_count, num_frames_with_source)) - { - result.SetStatus (eReturnStatusSuccessFinishResult); - if (m_options.m_extended_backtrace) - { - DoExtendedBacktrace (thread, result); - } - } - } - else if (command.GetArgumentCount() == 1 && ::strcmp (command.GetArgumentAtIndex(0), "all") == 0) { - Process *process = m_exe_ctx.GetProcessPtr(); - uint32_t idx = 0; - for (ThreadSP thread_sp : process->Threads()) - { - if (idx != 0) - result.AppendMessage(""); - - if (!thread_sp->GetStatus (strm, - m_options.m_start, - m_options.m_count, - num_frames_with_source)) - { - result.AppendErrorWithFormat ("error displaying backtrace for thread: \"0x%4.4x\"\n", idx); - result.SetStatus (eReturnStatusFailed); - return false; - } - if (m_options.m_extended_backtrace) - { - DoExtendedBacktrace (thread_sp.get(), result); - } - - ++idx; - } + result.AppendErrorWithFormat ("error displaying backtrace for thread: \"0x%4.4x\"\n", thread.GetIndexID()); + result.SetStatus (eReturnStatusFailed); + return false; } - else + if (m_options.m_extended_backtrace) { - const size_t num_args = command.GetArgumentCount(); - Process *process = m_exe_ctx.GetProcessPtr(); - Mutex::Locker locker (process->GetThreadList().GetMutex()); - std::vector<ThreadSP> thread_sps; - - for (size_t i = 0; i < num_args; i++) - { - bool success; - - uint32_t thread_idx = Args::StringToUInt32(command.GetArgumentAtIndex(i), 0, 0, &success); - if (!success) - { - result.AppendErrorWithFormat ("invalid thread specification: \"%s\"\n", command.GetArgumentAtIndex(i)); - result.SetStatus (eReturnStatusFailed); - return false; - } - - thread_sps.push_back(process->GetThreadList().FindThreadByIndexID(thread_idx)); - - if (!thread_sps[i]) - { - result.AppendErrorWithFormat ("no thread with index: \"%s\"\n", command.GetArgumentAtIndex(i)); - result.SetStatus (eReturnStatusFailed); - return false; - } - - } - - for (uint32_t i = 0; i < num_args; i++) - { - if (!thread_sps[i]->GetStatus (strm, - m_options.m_start, - m_options.m_count, - num_frames_with_source)) - { - result.AppendErrorWithFormat ("error displaying backtrace for thread: \"%s\"\n", command.GetArgumentAtIndex(i)); - result.SetStatus (eReturnStatusFailed); - return false; - } - if (m_options.m_extended_backtrace) - { - DoExtendedBacktrace (thread_sps[i].get(), result); - } - - if (i < num_args - 1) - result.AppendMessage(""); - } + DoExtendedBacktrace (&thread, result); } - return result.Succeeded(); + + return true; } CommandOptions m_options; @@ -379,6 +390,12 @@ public: break; } break; + case 'C': + { + m_class_name.clear(); + m_class_name.assign(option_arg); + } + break; case 'm': { OptionEnumValueElement *enum_values = g_option_table[option_idx].enum_values; @@ -416,6 +433,7 @@ public: m_run_mode = eOnlyDuringStepping; m_avoid_regexp.clear(); m_step_in_target.clear(); + m_class_name.clear(); m_step_count = 1; } @@ -435,7 +453,8 @@ public: RunMode m_run_mode; std::string m_avoid_regexp; std::string m_step_in_target; - int32_t m_step_count; + std::string m_class_name; + uint32_t m_step_count; }; CommandObjectThreadStepWithTypeAndScope (CommandInterpreter &interpreter, @@ -520,6 +539,22 @@ protected: } } + if (m_step_type == eStepTypeScripted) + { + if (m_options.m_class_name.empty()) + { + result.AppendErrorWithFormat ("empty class name for scripted step."); + result.SetStatus(eReturnStatusFailed); + return false; + } + else if (!m_interpreter.GetScriptInterpreter()->CheckObjectExists(m_options.m_class_name.c_str())) + { + result.AppendErrorWithFormat ("class for scripted step: \"%s\" does not exist.", m_options.m_class_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + const bool abort_other_plans = false; const lldb::RunMode stop_other_threads = m_options.m_run_mode; @@ -530,7 +565,7 @@ protected: bool_stop_other_threads = false; else if (m_options.m_run_mode == eOnlyDuringStepping) { - if (m_step_type == eStepTypeOut) + if (m_step_type == eStepTypeOut || m_step_type == eStepTypeScripted) bool_stop_other_threads = false; else bool_stop_other_threads = true; @@ -599,6 +634,12 @@ protected: thread->GetSelectedFrameIndex(), m_options.m_step_out_avoid_no_debug); } + else if (m_step_type == eStepTypeScripted) + { + new_plan_sp = thread->QueueThreadPlanForStepScripted (abort_other_plans, + m_options.m_class_name.c_str(), + bool_stop_other_threads); + } else { result.AppendError ("step type is not supported"); @@ -622,8 +663,15 @@ protected: } } + process->GetThreadList().SetSelectedThreadByID (thread->GetID()); - process->Resume (); + + StreamString stream; + Error error; + if (synchronous_execution) + error = process->ResumeSynchronous (&stream); + else + error = process->Resume (); // There is a race condition where this thread will return up the call stack to the main command handler // and show an (lldb) prompt before HandlePrivateEvent (from PrivateStateThread) has @@ -632,17 +680,12 @@ protected: if (synchronous_execution) { - StateType state = process->WaitForProcessToStop (NULL); - - //EventSP event_sp; - //StateType state = process->WaitForStateChangedEvents (NULL, event_sp); - //while (! StateIsStoppedState (state)) - // { - // state = process->WaitForStateChangedEvents (NULL, event_sp); - // } + // If any state changed events had anything to say, add that to the result + if (stream.GetData()) + result.AppendMessage(stream.GetData()); + process->GetThreadList().SetSelectedThreadByID (thread->GetID()); result.SetDidChangeProcessState (true); - result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); result.SetStatus (eReturnStatusSuccessFinishNoResult); } else @@ -686,10 +729,11 @@ CommandObjectThreadStepWithTypeAndScope::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_1, false, "step-in-avoids-no-debug", 'a', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean, "A boolean value that sets whether stepping into functions will step over functions with no debug information."}, { LLDB_OPT_SET_1, false, "step-out-avoids-no-debug", 'A', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean, "A boolean value, if true stepping out of functions will continue to step out till it hits a function with debug information."}, -{ LLDB_OPT_SET_1, false, "count", 'c', OptionParser::eRequiredArgument, NULL, NULL, 1, eArgTypeCount, "How many times to perform the stepping operation - currently only supported for step-inst and next-inst."}, -{ LLDB_OPT_SET_1, false, "run-mode", 'm', OptionParser::eRequiredArgument, NULL, g_tri_running_mode, 0, eArgTypeRunMode, "Determine how to run other threads while stepping the current thread."}, -{ LLDB_OPT_SET_1, false, "step-over-regexp",'r', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeRegularExpression, "A regular expression that defines function names to not to stop at when stepping in."}, -{ LLDB_OPT_SET_1, false, "step-in-target", 't', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeFunctionName, "The name of the directly called function step in should stop at when stepping into."}, +{ LLDB_OPT_SET_1, false, "count", 'c', OptionParser::eRequiredArgument, NULL, NULL, 1, eArgTypeCount, "How many times to perform the stepping operation - currently only supported for step-inst and next-inst."}, +{ LLDB_OPT_SET_1, false, "run-mode", 'm', OptionParser::eRequiredArgument, NULL, g_tri_running_mode, 0, eArgTypeRunMode, "Determine how to run other threads while stepping the current thread."}, +{ LLDB_OPT_SET_1, false, "step-over-regexp", 'r', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeRegularExpression, "A regular expression that defines function names to not to stop at when stepping in."}, +{ LLDB_OPT_SET_1, false, "step-in-target", 't', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeFunctionName, "The name of the directly called function step in should stop at when stepping into."}, +{ LLDB_OPT_SET_2, false, "python-class", 'C', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypePythonClass, "The name of the class that will manage this step - only supported for Scripted Step."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; @@ -860,17 +904,25 @@ public: } } + + StreamString stream; + Error error; + if (synchronous_execution) + error = process->ResumeSynchronous (&stream); + else + error = process->Resume (); + // We should not be holding the thread list lock when we do this. - Error error (process->Resume()); if (error.Success()) { result.AppendMessageWithFormat ("Process %" PRIu64 " resuming\n", process->GetID()); if (synchronous_execution) { - state = process->WaitForProcessToStop (NULL); + // If any state changed events had anything to say, add that to the result + if (stream.GetData()) + result.AppendMessage(stream.GetData()); result.SetDidChangeProcessState (true); - result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); result.SetStatus (eReturnStatusSuccessFinishNoResult); } else @@ -1191,17 +1243,27 @@ protected: } + + process->GetThreadList().SetSelectedThreadByID (m_options.m_thread_idx); - Error error (process->Resume ()); + + StreamString stream; + Error error; + if (synchronous_execution) + error = process->ResumeSynchronous (&stream); + else + error = process->Resume (); + if (error.Success()) { result.AppendMessageWithFormat ("Process %" PRIu64 " resuming\n", process->GetID()); if (synchronous_execution) { - StateType state = process->WaitForProcessToStop (NULL); + // If any state changed events had anything to say, add that to the result + if (stream.GetData()) + result.AppendMessage(stream.GetData()); result.SetDidChangeProcessState (true); - result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); result.SetStatus (eReturnStatusSuccessFinishNoResult); } else @@ -1358,32 +1420,22 @@ protected: // CommandObjectThreadInfo //------------------------------------------------------------------------- -class CommandObjectThreadInfo : public CommandObjectParsed +class CommandObjectThreadInfo : public CommandObjectIterateOverThreads { public: CommandObjectThreadInfo (CommandInterpreter &interpreter) : - CommandObjectParsed (interpreter, - "thread info", - "Show an extended summary of information about thread(s) in a process.", - "thread info", - eFlagRequiresProcess | - eFlagTryTargetAPILock | - eFlagProcessMustBeLaunched | - eFlagProcessMustBePaused), + CommandObjectIterateOverThreads (interpreter, + "thread info", + "Show an extended summary of information about thread(s) in a process.", + "thread info", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused), m_options (interpreter) { - CommandArgumentEntry arg; - CommandArgumentData thread_idx_arg; - - thread_idx_arg.arg_type = eArgTypeThreadIndex; - thread_idx_arg.arg_repetition = eArgRepeatStar; - - // There is only one variant this argument could be; put it into the argument entry. - arg.push_back (thread_idx_arg); - - // Push the data for the first argument into the m_arguments vector. - m_arguments.push_back (arg); + m_add_return = false; } class CommandOptions : public Options @@ -1399,7 +1451,8 @@ public: void OptionParsingStarting () { - m_json = false; + m_json_thread = false; + m_json_stopinfo = false; } virtual @@ -1416,10 +1469,14 @@ public: switch (short_option) { case 'j': - m_json = true; + m_json_thread = true; + break; + + case 's': + m_json_stopinfo = true; break; - default: + default: return Error("invalid short option character '%c'", short_option); } @@ -1432,7 +1489,8 @@ public: return g_option_table; } - bool m_json; + bool m_json_thread; + bool m_json_stopinfo; static OptionDefinition g_option_table[]; }; @@ -1451,81 +1509,16 @@ public: } virtual bool - DoExecute (Args& command, CommandReturnObject &result) + HandleOneThread (Thread &thread, CommandReturnObject &result) { - result.SetStatus (eReturnStatusSuccessFinishResult); Stream &strm = result.GetOutputStream(); - - if (command.GetArgumentCount() == 0) - { - Thread *thread = m_exe_ctx.GetThreadPtr(); - if (thread->GetDescription (strm, eDescriptionLevelFull, m_options.m_json)) - { - result.SetStatus (eReturnStatusSuccessFinishResult); - } - } - else if (command.GetArgumentCount() == 1 && ::strcmp (command.GetArgumentAtIndex(0), "all") == 0) - { - Process *process = m_exe_ctx.GetProcessPtr(); - uint32_t idx = 0; - for (ThreadSP thread_sp : process->Threads()) - { - if (idx != 0) - result.AppendMessage(""); - if (!thread_sp->GetDescription (strm, eDescriptionLevelFull, m_options.m_json)) - { - result.AppendErrorWithFormat ("error displaying info for thread: \"0x%4.4x\"\n", idx); - result.SetStatus (eReturnStatusFailed); - return false; - } - ++idx; - } - } - else + if (!thread.GetDescription (strm, eDescriptionLevelFull, m_options.m_json_thread, m_options.m_json_stopinfo)) { - const size_t num_args = command.GetArgumentCount(); - Process *process = m_exe_ctx.GetProcessPtr(); - Mutex::Locker locker (process->GetThreadList().GetMutex()); - std::vector<ThreadSP> thread_sps; - - for (size_t i = 0; i < num_args; i++) - { - bool success; - - uint32_t thread_idx = Args::StringToUInt32(command.GetArgumentAtIndex(i), 0, 0, &success); - if (!success) - { - result.AppendErrorWithFormat ("invalid thread specification: \"%s\"\n", command.GetArgumentAtIndex(i)); - result.SetStatus (eReturnStatusFailed); - return false; - } - - thread_sps.push_back(process->GetThreadList().FindThreadByIndexID(thread_idx)); - - if (!thread_sps[i]) - { - result.AppendErrorWithFormat ("no thread with index: \"%s\"\n", command.GetArgumentAtIndex(i)); - result.SetStatus (eReturnStatusFailed); - return false; - } - - } - - for (uint32_t i = 0; i < num_args; i++) - { - if (!thread_sps[i]->GetDescription (strm, eDescriptionLevelFull, m_options.m_json)) - { - result.AppendErrorWithFormat ("error displaying info for thread: \"%s\"\n", command.GetArgumentAtIndex(i)); - result.SetStatus (eReturnStatusFailed); - return false; - } - - if (i < num_args - 1) - result.AppendMessage(""); - } - + result.AppendErrorWithFormat ("error displaying info for thread: \"%d\"\n", thread.GetIndexID()); + result.SetStatus (eReturnStatusFailed); + return false; } - return result.Succeeded(); + return true; } CommandOptions m_options; @@ -1536,6 +1529,7 @@ OptionDefinition CommandObjectThreadInfo::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "json",'j', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Display the thread info in JSON format."}, + { LLDB_OPT_SET_ALL, false, "stop-info",'s', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Display the extended stop info in JSON format."}, { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; @@ -1958,6 +1952,228 @@ CommandObjectThreadJump::CommandOptions::g_option_table[] = }; //------------------------------------------------------------------------- +// Next are the subcommands of CommandObjectMultiwordThreadPlan +//------------------------------------------------------------------------- + + +//------------------------------------------------------------------------- +// CommandObjectThreadPlanList +//------------------------------------------------------------------------- +class CommandObjectThreadPlanList : public CommandObjectIterateOverThreads +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'i': + { + m_internal = true; + } + break; + case 'v': + { + m_verbose = true; + } + break; + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + + } + return error; + } + + void + OptionParsingStarting () + { + m_verbose = false; + m_internal = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + bool m_verbose; + bool m_internal; + }; + + CommandObjectThreadPlanList (CommandInterpreter &interpreter) : + CommandObjectIterateOverThreads (interpreter, + "thread plan list", + "Show thread plans for one or more threads. If no threads are specified, show the " + "currently selected thread. Use the thread-index \"all\" to see all threads.", + NULL, + eFlagRequiresProcess | + eFlagRequiresThread | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_options(interpreter) + { + } + + ~CommandObjectThreadPlanList () + { + } + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + HandleOneThread (Thread &thread, CommandReturnObject &result) + { + Stream &strm = result.GetOutputStream(); + DescriptionLevel desc_level = eDescriptionLevelFull; + if (m_options.m_verbose) + desc_level = eDescriptionLevelVerbose; + + thread.DumpThreadPlans (&strm, desc_level, m_options.m_internal, true); + return true; + } + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectThreadPlanList::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "verbose", 'v', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Display more information about the thread plans"}, +{ LLDB_OPT_SET_1, false, "internal", 'i', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Display internal as well as user thread plans"}, +{ 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } +}; + +class CommandObjectThreadPlanDiscard : public CommandObjectParsed +{ +public: + CommandObjectThreadPlanDiscard (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "thread plan discard", + "Discards thread plans up to and including the plan passed as the command argument." + "Only user visible plans can be discarded, use the index from \"thread plan list\"" + " without the \"-i\" argument.", + NULL, + eFlagRequiresProcess | + eFlagRequiresThread | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ) + { + CommandArgumentEntry arg; + CommandArgumentData plan_index_arg; + + // Define the first (and only) variant of this arg. + plan_index_arg.arg_type = eArgTypeUnsignedInteger; + plan_index_arg.arg_repetition = eArgRepeatPlain; + + // There is only one variant this argument could be; put it into the argument entry. + arg.push_back (plan_index_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back (arg); + } + + virtual ~CommandObjectThreadPlanDiscard () {} + + bool + DoExecute (Args& args, CommandReturnObject &result) + { + Thread *thread = m_exe_ctx.GetThreadPtr(); + if (args.GetArgumentCount() != 1) + { + result.AppendErrorWithFormat("Too many arguments, expected one - the thread plan index - but got %zu.", + args.GetArgumentCount()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + bool success; + uint32_t thread_plan_idx = Args::StringToUInt32(args.GetArgumentAtIndex(0), 0, 0, &success); + if (!success) + { + result.AppendErrorWithFormat("Invalid thread index: \"%s\" - should be unsigned int.", + args.GetArgumentAtIndex(0)); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (thread_plan_idx == 0) + { + result.AppendErrorWithFormat("You wouldn't really want me to discard the base thread plan."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (thread->DiscardUserThreadPlansUpToIndex(thread_plan_idx)) + { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return true; + } + else + { + result.AppendErrorWithFormat("Could not find User thread plan with index %s.", + args.GetArgumentAtIndex(0)); + result.SetStatus (eReturnStatusFailed); + return false; + } + } +}; + +//------------------------------------------------------------------------- +// CommandObjectMultiwordThreadPlan +//------------------------------------------------------------------------- + +class CommandObjectMultiwordThreadPlan : public CommandObjectMultiword +{ +public: + CommandObjectMultiwordThreadPlan(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "plan", + "A set of subcommands for accessing the thread plans controlling execution control on one or more threads.", + "thread plan <subcommand> [<subcommand objects]") + { + LoadSubCommand ("list", CommandObjectSP (new CommandObjectThreadPlanList (interpreter))); + LoadSubCommand ("discard", CommandObjectSP (new CommandObjectThreadPlanDiscard (interpreter))); + } + + virtual ~CommandObjectMultiwordThreadPlan () {} + + +}; + +//------------------------------------------------------------------------- // CommandObjectMultiwordThread //------------------------------------------------------------------------- @@ -2014,6 +2230,16 @@ CommandObjectMultiwordThread::CommandObjectMultiwordThread (CommandInterpreter & NULL, eStepTypeTraceOver, eStepScopeInstruction))); + + LoadSubCommand ("step-scripted", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( + interpreter, + "thread step-scripted", + "Step as instructed by the script class passed in the -C option.", + NULL, + eStepTypeScripted, + eStepScopeSource))); + + LoadSubCommand ("plan", CommandObjectSP (new CommandObjectMultiwordThreadPlan(interpreter))); } CommandObjectMultiwordThread::~CommandObjectMultiwordThread () diff --git a/source/Commands/CommandObjectType.cpp b/source/Commands/CommandObjectType.cpp index 640fd6dd3fa4..3a4c60c00f8b 100644 --- a/source/Commands/CommandObjectType.cpp +++ b/source/Commands/CommandObjectType.cpp @@ -16,6 +16,7 @@ #include <ctype.h> // C++ Includes +#include <functional> #include "llvm/ADT/StringRef.h" @@ -31,6 +32,11 @@ #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/Options.h" #include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadList.h" using namespace lldb; using namespace lldb_private; @@ -2465,22 +2471,7 @@ protected: if (argc == 1 && strcmp(command.GetArgumentAtIndex(0),"*") == 0) { - // we want to make sure to enable "system" last and "default" first - DataVisualization::Categories::Enable(ConstString("default"), TypeCategoryMap::First); - uint32_t num_categories = DataVisualization::Categories::GetCount(); - for (uint32_t i = 0; i < num_categories; i++) - { - lldb::TypeCategoryImplSP category_sp = DataVisualization::Categories::GetCategoryAtIndex(i); - if (category_sp) - { - if ( ::strcmp(category_sp->GetName(), "system") == 0 || - ::strcmp(category_sp->GetName(), "default") == 0 ) - continue; - else - DataVisualization::Categories::Enable(category_sp, TypeCategoryMap::Default); - } - } - DataVisualization::Categories::Enable(ConstString("system"), TypeCategoryMap::Last); + DataVisualization::Categories::EnableStar(); } else { @@ -2630,14 +2621,7 @@ protected: if (argc == 1 && strcmp(command.GetArgumentAtIndex(0),"*") == 0) { - uint32_t num_categories = DataVisualization::Categories::GetCount(); - for (uint32_t i = 0; i < num_categories; i++) - { - lldb::TypeCategoryImplSP category_sp = DataVisualization::Categories::GetCategoryAtIndex(i); - // no need to check if the category is enabled - disabling a disabled category has no effect - if (category_sp) - DataVisualization::Categories::Disable(category_sp); - } + DataVisualization::Categories::DisableStar(); } else { @@ -4253,6 +4237,84 @@ CommandObjectTypeFilterAdd::CommandOptions::g_option_table[] = { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL } }; +template <typename FormatterType> +class CommandObjectFormatterInfo : public CommandObjectRaw +{ +public: + typedef std::function<typename FormatterType::SharedPointer(ValueObject&)> DiscoveryFunction; + CommandObjectFormatterInfo (CommandInterpreter &interpreter, + const char* formatter_name, + DiscoveryFunction discovery_func) : + CommandObjectRaw(interpreter, + nullptr, + nullptr, + nullptr, + eFlagRequiresFrame), + m_formatter_name(formatter_name ? formatter_name : ""), + m_discovery_function(discovery_func) + { + StreamString name; + name.Printf("type %s info", formatter_name); + SetCommandName(name.GetData()); + StreamString help; + help.Printf("This command evaluates the provided expression and shows which %s is applied to the resulting value (if any).", formatter_name); + SetHelp(help.GetData()); + StreamString syntax; + syntax.Printf("type %s info <expr>", formatter_name); + SetSyntax(syntax.GetData()); + } + + virtual + ~CommandObjectFormatterInfo () + { + } + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result) + { + auto target_sp = m_interpreter.GetDebugger().GetSelectedTarget(); + auto frame_sp = target_sp->GetProcessSP()->GetThreadList().GetSelectedThread()->GetSelectedFrame(); + ValueObjectSP result_valobj_sp; + EvaluateExpressionOptions options; + lldb::ExpressionResults expr_result = target_sp->EvaluateExpression(command, frame_sp.get(), result_valobj_sp, options); + if (expr_result == eExpressionCompleted && result_valobj_sp) + { + result_valobj_sp = result_valobj_sp->GetQualifiedRepresentationIfAvailable(target_sp->GetPreferDynamicValue(), target_sp->GetEnableSyntheticValue()); + typename FormatterType::SharedPointer formatter_sp = m_discovery_function(*result_valobj_sp); + if (formatter_sp) + { + std::string description(formatter_sp->GetDescription()); + result.AppendMessageWithFormat("%s applied to (%s) %s is: %s\n", + m_formatter_name.c_str(), + result_valobj_sp->GetDisplayTypeName().AsCString("<unknown>"), + command, + description.c_str()); + result.SetStatus(lldb::eReturnStatusSuccessFinishResult); + } + else + { + result.AppendMessageWithFormat("no %s applies to (%s) %s\n", + m_formatter_name.c_str(), + result_valobj_sp->GetDisplayTypeName().AsCString("<unknown>"), + command); + result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); + } + return true; + } + else + { + result.AppendError("failed to evaluate expression"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + } + +private: + std::string m_formatter_name; + DiscoveryFunction m_discovery_function; +}; + class CommandObjectTypeFormat : public CommandObjectMultiword { public: @@ -4266,6 +4328,11 @@ public: LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeFormatClear (interpreter))); LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeFormatDelete (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeFormatList (interpreter))); + LoadSubCommand ("info", CommandObjectSP (new CommandObjectFormatterInfo<TypeFormatImpl>(interpreter, + "format", + [](ValueObject& valobj) -> TypeFormatImpl::SharedPointer { + return valobj.GetValueFormat(); + }))); } @@ -4289,6 +4356,11 @@ public: LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeSynthClear (interpreter))); LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeSynthDelete (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeSynthList (interpreter))); + LoadSubCommand ("info", CommandObjectSP (new CommandObjectFormatterInfo<SyntheticChildren>(interpreter, + "synthetic", + [](ValueObject& valobj) -> SyntheticChildren::SharedPointer { + return valobj.GetSyntheticChildren(); + }))); } @@ -4354,6 +4426,11 @@ public: LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeSummaryClear (interpreter))); LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeSummaryDelete (interpreter))); LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeSummaryList (interpreter))); + LoadSubCommand ("info", CommandObjectSP (new CommandObjectFormatterInfo<TypeSummaryImpl>(interpreter, + "summary", + [](ValueObject& valobj) -> TypeSummaryImpl::SharedPointer { + return valobj.GetSummaryFormat(); + }))); } diff --git a/source/Commands/CommandObjectWatchpointCommand.cpp b/source/Commands/CommandObjectWatchpointCommand.cpp index f46db7a6a82b..275ee925adcc 100644 --- a/source/Commands/CommandObjectWatchpointCommand.cpp +++ b/source/Commands/CommandObjectWatchpointCommand.cpp @@ -279,17 +279,16 @@ but do NOT enter more than one command per line. \n" ); result.SetImmediateOutputStream (output_stream); result.SetImmediateErrorStream (error_stream); - bool stop_on_continue = true; - bool echo_commands = false; - bool print_results = true; + CommandInterpreterRunOptions options; + options.SetStopOnContinue (true); + options.SetStopOnError (data->stop_on_error); + options.SetEchoCommands (false); + options.SetPrintResults (true); + options.SetAddToHistory (false); debugger.GetCommandInterpreter().HandleCommands (commands, &exe_ctx, - stop_on_continue, - data->stop_on_error, - echo_commands, - print_results, - eLazyBoolNo, + options, result); result.GetImmediateOutputStream()->Flush(); result.GetImmediateErrorStream()->Flush(); diff --git a/source/Core/Address.cpp b/source/Core/Address.cpp index fa9197d12b70..a79becbf49c4 100644 --- a/source/Core/Address.cpp +++ b/source/Core/Address.cpp @@ -433,7 +433,9 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum case DumpStyleModuleWithFileAddress: if (section_sp) - s->Printf("%s[", section_sp->GetModule()->GetFileSpec().GetFilename().AsCString()); + { + s->Printf("%s[", section_sp->GetModule()->GetFileSpec().GetFilename().AsCString("<Unknown>")); + } // Fall through case DumpStyleFileAddress: { @@ -465,6 +467,7 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum case DumpStyleResolvedDescription: case DumpStyleResolvedDescriptionNoModule: + case DumpStyleResolvedDescriptionNoFunctionArguments: if (IsSectionOffset()) { uint32_t pointer_size = 4; @@ -550,7 +553,7 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum #endif Address cstr_addr(*this); cstr_addr.SetOffset(cstr_addr.GetOffset() + pointer_size); - func_sc.DumpStopContext(s, exe_scope, so_addr, true, true, false); + func_sc.DumpStopContext(s, exe_scope, so_addr, true, true, false, true); if (ReadAddress (exe_scope, cstr_addr, pointer_size, so_addr)) { #if VERBOSE_OUTPUT @@ -633,7 +636,7 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum if (pointer_sc.function || pointer_sc.symbol) { s->PutCString(": "); - pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false, false); + pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false, false, true); } } } @@ -658,6 +661,7 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum const bool show_module = (style == DumpStyleResolvedDescription); const bool show_fullpaths = false; const bool show_inlined_frames = true; + const bool show_function_arguments = (style != DumpStyleResolvedDescriptionNoFunctionArguments); if (sc.function == NULL && sc.symbol != NULL) { // If we have just a symbol make sure it is in the right section @@ -679,7 +683,8 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum *this, show_fullpaths, show_module, - show_inlined_frames); + show_inlined_frames, + show_function_arguments); } else { diff --git a/source/Core/AddressRange.cpp b/source/Core/AddressRange.cpp index 3505d56b43e2..ac64833884ee 100644 --- a/source/Core/AddressRange.cpp +++ b/source/Core/AddressRange.cpp @@ -179,7 +179,7 @@ AddressRange::Dump(Stream *s, Target *target, Address::DumpStyle style, Address: { ModuleSP module_sp (GetBaseAddress().GetModule()); if (module_sp) - s->Printf("%s", module_sp->GetFileSpec().GetFilename().AsCString()); + s->Printf("%s", module_sp->GetFileSpec().GetFilename().AsCString("<Unknown>")); } s->AddressRange(vmaddr, vmaddr + GetByteSize(), addr_size); return true; diff --git a/source/Core/AddressResolverFileLine.cpp b/source/Core/AddressResolverFileLine.cpp index f7004c8bb089..6089abd76cbc 100644 --- a/source/Core/AddressResolverFileLine.cpp +++ b/source/Core/AddressResolverFileLine.cpp @@ -96,7 +96,7 @@ AddressResolverFileLine::GetDepth() void AddressResolverFileLine::GetDescription (Stream *s) { - s->Printf ("File and line address - file: \"%s\" line: %u", m_file_spec.GetFilename().AsCString(), m_line_number); + s->Printf ("File and line address - file: \"%s\" line: %u", m_file_spec.GetFilename().AsCString("<Unknown>"), m_line_number); } diff --git a/source/Core/ArchSpec.cpp b/source/Core/ArchSpec.cpp index 5f010f066408..e7a5e489af19 100644 --- a/source/Core/ArchSpec.cpp +++ b/source/Core/ArchSpec.cpp @@ -24,6 +24,11 @@ #include "lldb/Host/Endian.h" #include "lldb/Host/HostInfo.h" #include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "Plugins/Process/Utility/ARMDefines.h" +#include "Plugins/Process/Utility/InstructionUtils.h" using namespace lldb; using namespace lldb_private; @@ -84,7 +89,7 @@ static const CoreDefinition g_core_definitions[] = { eByteOrderBig , 8, 4, 4, llvm::Triple::mips64 , ArchSpec::eCore_mips64 , "mips64" }, - { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_generic , "ppc" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_generic , "powerpc" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc601 , "ppc601" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc602 , "ppc602" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc603 , "ppc603" }, @@ -98,7 +103,7 @@ static const CoreDefinition g_core_definitions[] = { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc7450 , "ppc7450" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc970 , "ppc970" }, - { eByteOrderBig , 8, 4, 4, llvm::Triple::ppc64 , ArchSpec::eCore_ppc64_generic , "ppc64" }, + { eByteOrderBig , 8, 4, 4, llvm::Triple::ppc64 , ArchSpec::eCore_ppc64_generic , "powerpc64" }, { eByteOrderBig , 8, 4, 4, llvm::Triple::ppc64 , ArchSpec::eCore_ppc64_ppc970_64 , "ppc970-64" }, { eByteOrderLittle, 4, 4, 4, llvm::Triple::sparc , ArchSpec::eCore_sparc_generic , "sparc" }, @@ -118,7 +123,6 @@ static const CoreDefinition g_core_definitions[] = { eByteOrderLittle, 4, 4, 4 , llvm::Triple::UnknownArch , ArchSpec::eCore_uknownMach32 , "unknown-mach-32" }, { eByteOrderLittle, 8, 4, 4 , llvm::Triple::UnknownArch , ArchSpec::eCore_uknownMach64 , "unknown-mach-64" }, - { eByteOrderLittle, 4, 1, 1 , llvm::Triple::kalimba , ArchSpec::eCore_kalimba , "kalimba" }, { eByteOrderBig , 4, 1, 1 , llvm::Triple::kalimba , ArchSpec::eCore_kalimba3 , "kalimba3" }, { eByteOrderLittle, 4, 1, 1 , llvm::Triple::kalimba , ArchSpec::eCore_kalimba4 , "kalimba4" }, { eByteOrderLittle, 4, 1, 1 , llvm::Triple::kalimba , ArchSpec::eCore_kalimba5 , "kalimba5" } @@ -195,10 +199,10 @@ static const ArchDefinitionEntry g_macho_arch_entries[] = { ArchSpec::eCore_arm_armv7k , llvm::MachO::CPU_TYPE_ARM , 12 , UINT32_MAX , SUBTYPE_MASK }, { ArchSpec::eCore_arm_armv7m , llvm::MachO::CPU_TYPE_ARM , 15 , UINT32_MAX , SUBTYPE_MASK }, { ArchSpec::eCore_arm_armv7em , llvm::MachO::CPU_TYPE_ARM , 16 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_arm64 , llvm::MachO::CPU_TYPE_ARM64 , CPU_ANY, UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_arm64 , llvm::MachO::CPU_TYPE_ARM64 , 0 , UINT32_MAX , SUBTYPE_MASK }, { ArchSpec::eCore_arm_arm64 , llvm::MachO::CPU_TYPE_ARM64 , 1 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_arm64 , llvm::MachO::CPU_TYPE_ARM64 , 0 , UINT32_MAX , SUBTYPE_MASK }, { ArchSpec::eCore_arm_arm64 , llvm::MachO::CPU_TYPE_ARM64 , 13 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_arm64 , llvm::MachO::CPU_TYPE_ARM64 , CPU_ANY, UINT32_MAX , SUBTYPE_MASK }, { ArchSpec::eCore_thumb , llvm::MachO::CPU_TYPE_ARM , 0 , UINT32_MAX , SUBTYPE_MASK }, { ArchSpec::eCore_thumbv4t , llvm::MachO::CPU_TYPE_ARM , 5 , UINT32_MAX , SUBTYPE_MASK }, { ArchSpec::eCore_thumbv5 , llvm::MachO::CPU_TYPE_ARM , 7 , UINT32_MAX , SUBTYPE_MASK }, @@ -264,11 +268,9 @@ static const ArchDefinitionEntry g_elf_arch_entries[] = { ArchSpec::eCore_x86_64_x86_64 , llvm::ELF::EM_X86_64 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // AMD64 { ArchSpec::eCore_mips64 , llvm::ELF::EM_MIPS , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // MIPS { ArchSpec::eCore_hexagon_generic , llvm::ELF::EM_HEXAGON, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // HEXAGON - { ArchSpec::eCore_kalimba , llvm::ELF::EM_CSR_KALIMBA, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // KALIMBA - { ArchSpec::eCore_kalimba3 , llvm::ELF::EM_CSR_KALIMBA, 3, 0xFFFFFFFFu, 0xFFFFFFFFu }, // KALIMBA - { ArchSpec::eCore_kalimba4 , llvm::ELF::EM_CSR_KALIMBA, 4, 0xFFFFFFFFu, 0xFFFFFFFFu }, // KALIMBA - { ArchSpec::eCore_kalimba5 , llvm::ELF::EM_CSR_KALIMBA, 5, 0xFFFFFFFFu, 0xFFFFFFFFu } // KALIMBA - + { ArchSpec::eCore_kalimba3 , llvm::ELF::EM_CSR_KALIMBA, llvm::Triple::KalimbaSubArch_v3, 0xFFFFFFFFu, 0xFFFFFFFFu }, // KALIMBA + { ArchSpec::eCore_kalimba4 , llvm::ELF::EM_CSR_KALIMBA, llvm::Triple::KalimbaSubArch_v4, 0xFFFFFFFFu, 0xFFFFFFFFu }, // KALIMBA + { ArchSpec::eCore_kalimba5 , llvm::ELF::EM_CSR_KALIMBA, llvm::Triple::KalimbaSubArch_v5, 0xFFFFFFFFu, 0xFFFFFFFFu } // KALIMBA }; static const ArchDefinition g_elf_arch_def = { @@ -503,11 +505,11 @@ ArchSpec::GetDataByteSize () const switch (m_core) { case eCore_kalimba3: - return 3; + return 4; case eCore_kalimba4: return 1; case eCore_kalimba5: - return 3; + return 4; default: return 1; } @@ -1036,16 +1038,6 @@ cores_match (const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_in } break; - case ArchSpec::eCore_kalimba: - case ArchSpec::eCore_kalimba3: - case ArchSpec::eCore_kalimba4: - case ArchSpec::eCore_kalimba5: - if (core2 >= ArchSpec::kCore_kalimba_first && core2 <= ArchSpec::kCore_kalimba_last) - { - return true; - } - break; - case ArchSpec::eCore_arm_armv8: if (!enforce_exact_match) { @@ -1094,3 +1086,108 @@ lldb_private::operator<(const ArchSpec& lhs, const ArchSpec& rhs) const ArchSpec::Core rhs_core = rhs.GetCore (); return lhs_core < rhs_core; } + +static void +StopInfoOverrideCallbackTypeARM(lldb_private::Thread &thread) +{ + // We need to check if we are stopped in Thumb mode in a IT instruction + // and detect if the condition doesn't pass. If this is the case it means + // we won't actually execute this instruction. If this happens we need to + // clear the stop reason to no thread plans think we are stopped for a + // reason and the plans should keep going. + // + // We do this because when single stepping many ARM processes, debuggers + // often use the BVR/BCR registers that says "stop when the PC is not + // equal to its current value". This method of stepping means we can end + // up stopping on instructions inside an if/then block that wouldn't get + // executed. By fixing this we can stop the debugger from seeming like + // you stepped through both the "if" _and_ the "else" clause when source + // level stepping because the debugger stops regardless due to the BVR/BCR + // triggering a stop. + // + // It also means we can set breakpoints on instructions inside an an + // if/then block and correctly skip them if we use the BKPT instruction. + // The ARM and Thumb BKPT instructions are unconditional even when executed + // in a Thumb IT block. + // + // If your debugger inserts software traps in ARM/Thumb code, it will + // need to use 16 and 32 bit instruction for 16 and 32 bit thumb + // instructions respectively. If your debugger inserts a 16 bit thumb + // trap on top of a 32 bit thumb instruction for an opcode that is inside + // an if/then, it will change the it/then to conditionally execute your + // 16 bit trap and then cause your program to crash if it executes the + // trailing 16 bits (the second half of the 32 bit thumb instruction you + // partially overwrote). + + RegisterContextSP reg_ctx_sp (thread.GetRegisterContext()); + if (reg_ctx_sp) + { + const uint32_t cpsr = reg_ctx_sp->GetFlags(0); + if (cpsr != 0) + { + // Read the J and T bits to get the ISETSTATE + const uint32_t J = Bit32(cpsr, 24); + const uint32_t T = Bit32(cpsr, 5); + const uint32_t ISETSTATE = J << 1 | T; + if (ISETSTATE == 0) + { + // NOTE: I am pretty sure we want to enable the code below + // that detects when we stop on an instruction in ARM mode + // that is conditional and the condition doesn't pass. This + // can happen if you set a breakpoint on an instruction that + // is conditional. We currently will _always_ stop on the + // instruction which is bad. You can also run into this while + // single stepping and you could appear to run code in the "if" + // and in the "else" clause because it would stop at all of the + // conditional instructions in both. + // In such cases, we really don't want to stop at this location. + // I will check with the lldb-dev list first before I enable this. +#if 0 + // ARM mode: check for condition on intsruction + const addr_t pc = reg_ctx_sp->GetPC(); + Error error; + // If we fail to read the opcode we will get UINT64_MAX as the + // result in "opcode" which we can use to detect if we read a + // valid opcode. + const uint64_t opcode = thread.GetProcess()->ReadUnsignedIntegerFromMemory(pc, 4, UINT64_MAX, error); + if (opcode <= UINT32_MAX) + { + const uint32_t condition = Bits32((uint32_t)opcode, 31, 28); + if (ARMConditionPassed(condition, cpsr) == false) + { + // We ARE stopped on an ARM instruction whose condition doesn't + // pass so this instruction won't get executed. + // Regardless of why it stopped, we need to clear the stop info + thread.SetStopInfo (StopInfoSP()); + } + } +#endif + } + else if (ISETSTATE == 1) + { + // Thumb mode + const uint32_t ITSTATE = Bits32 (cpsr, 15, 10) << 2 | Bits32 (cpsr, 26, 25); + if (ITSTATE != 0) + { + const uint32_t condition = Bits32(ITSTATE, 7, 4); + if (ARMConditionPassed(condition, cpsr) == false) + { + // We ARE stopped in a Thumb IT instruction on an instruction whose + // condition doesn't pass so this instruction won't get executed. + // Regardless of why it stopped, we need to clear the stop info + thread.SetStopInfo (StopInfoSP()); + } + } + } + } + } +} + +ArchSpec::StopInfoOverrideCallbackType +ArchSpec::GetStopInfoOverrideCallback () const +{ + const llvm::Triple::ArchType machine = GetMachine(); + if (machine == llvm::Triple::arm) + return StopInfoOverrideCallbackTypeARM; + return NULL; +} diff --git a/source/Core/Communication.cpp b/source/Core/Communication.cpp index d71c9881a6f3..ea84843fe0b3 100644 --- a/source/Core/Communication.cpp +++ b/source/Core/Communication.cpp @@ -18,6 +18,8 @@ #include "lldb/Core/Timer.h" #include "lldb/Core/Event.h" #include "lldb/Host/Host.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/ThreadLauncher.h" #include <string.h> using namespace lldb; @@ -36,7 +38,6 @@ Communication::GetStaticBroadcasterClass () Communication::Communication(const char *name) : Broadcaster (NULL, name), m_connection_sp (), - m_read_thread (LLDB_INVALID_HOST_THREAD), m_read_thread_enabled (false), m_bytes(), m_bytes_mutex (Mutex::eMutexTypeRecursive), @@ -232,7 +233,7 @@ Communication::StartReadThread (Error *error_ptr) if (error_ptr) error_ptr->Clear(); - if (IS_VALID_LLDB_HOST_THREAD(m_read_thread)) + if (m_read_thread.IsJoinable()) return true; lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, @@ -243,8 +244,8 @@ Communication::StartReadThread (Error *error_ptr) snprintf(thread_name, sizeof(thread_name), "<lldb.comm.%s>", m_broadcaster_name.AsCString()); m_read_thread_enabled = true; - m_read_thread = Host::ThreadCreate (thread_name, Communication::ReadThread, this, error_ptr); - if (!IS_VALID_LLDB_HOST_THREAD(m_read_thread)) + m_read_thread = ThreadLauncher::LaunchThread(thread_name, Communication::ReadThread, this, error_ptr); + if (!m_read_thread.IsJoinable()) m_read_thread_enabled = false; return m_read_thread_enabled; } @@ -252,7 +253,7 @@ Communication::StartReadThread (Error *error_ptr) bool Communication::StopReadThread (Error *error_ptr) { - if (!IS_VALID_LLDB_HOST_THREAD(m_read_thread)) + if (!m_read_thread.IsJoinable()) return true; lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, @@ -262,22 +263,20 @@ Communication::StopReadThread (Error *error_ptr) BroadcastEvent (eBroadcastBitReadThreadShouldExit, NULL); - //Host::ThreadCancel (m_read_thread, error_ptr); + // error = m_read_thread.Cancel(); - bool status = Host::ThreadJoin (m_read_thread, NULL, error_ptr); - m_read_thread = LLDB_INVALID_HOST_THREAD; - return status; + Error error = m_read_thread.Join(nullptr); + return error.Success(); } bool Communication::JoinReadThread (Error *error_ptr) { - if (!IS_VALID_LLDB_HOST_THREAD(m_read_thread)) + if (!m_read_thread.IsJoinable()) return true; - bool success = Host::ThreadJoin (m_read_thread, NULL, error_ptr); - m_read_thread = LLDB_INVALID_HOST_THREAD; - return success; + Error error = m_read_thread.Join(nullptr); + return error.Success(); } size_t diff --git a/source/Core/Connection.cpp b/source/Core/Connection.cpp index 3c9bb8b1b7ed..3f740a1ed82a 100644 --- a/source/Core/Connection.cpp +++ b/source/Core/Connection.cpp @@ -13,6 +13,12 @@ // Project includes #include "lldb/Core/Connection.h" +#if defined(_WIN32) +#include "lldb/Host/windows/ConnectionGenericFileWindows.h" +#endif + +#include "lldb/Host/ConnectionFileDescriptor.h" + using namespace lldb_private; Connection::Connection () @@ -22,3 +28,13 @@ Connection::Connection () Connection::~Connection () { } + +Connection * +Connection::CreateDefaultConnection(const char *url) +{ +#if defined(_WIN32) + if (strstr(url, "file://") == url) + return new ConnectionGenericFile(); +#endif + return new ConnectionFileDescriptor(); +} diff --git a/source/Core/ConnectionSharedMemory.cpp b/source/Core/ConnectionSharedMemory.cpp index 5db3d687cdb2..1cbee20cd94a 100644 --- a/source/Core/ConnectionSharedMemory.cpp +++ b/source/Core/ConnectionSharedMemory.cpp @@ -6,6 +6,7 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// +#ifndef __ANDROID_NDK__ #include "lldb/Core/ConnectionSharedMemory.h" @@ -156,3 +157,4 @@ ConnectionSharedMemory::Open (bool create, const char *name, size_t size, Error return eConnectionStatusError; } +#endif // __ANDROID_NDK__ diff --git a/source/Core/ConstString.cpp b/source/Core/ConstString.cpp index 5657b483a495..37d24e0dec00 100644 --- a/source/Core/ConstString.cpp +++ b/source/Core/ConstString.cpp @@ -11,6 +11,8 @@ #include "lldb/Host/Mutex.h" #include "llvm/ADT/StringMap.h" +#include <mutex> + using namespace lldb_private; @@ -93,7 +95,7 @@ public: { Mutex::Locker locker (m_mutex); llvm::StringRef string_ref (cstr, cstr_len); - StringPoolEntryType& entry = m_string_map.GetOrCreateValue (string_ref, (StringPoolValueType)NULL); + StringPoolEntryType& entry = *m_string_map.insert (std::make_pair (string_ref, (StringPoolValueType)NULL)).first; return entry.getKeyData(); } return NULL; @@ -105,7 +107,7 @@ public: if (string_ref.data()) { Mutex::Locker locker (m_mutex); - StringPoolEntryType& entry = m_string_map.GetOrCreateValue (string_ref, (StringPoolValueType)NULL); + StringPoolEntryType& entry = *m_string_map.insert (std::make_pair (string_ref, (StringPoolValueType)NULL)).first; return entry.getKeyData(); } return NULL; @@ -118,7 +120,7 @@ public: { Mutex::Locker locker (m_mutex); // Make string pool entry with the mangled counterpart already set - StringPoolEntryType& entry = m_string_map.GetOrCreateValue (llvm::StringRef (demangled_cstr), mangled_ccstr); + StringPoolEntryType& entry = *m_string_map.insert (std::make_pair (llvm::StringRef (demangled_cstr), mangled_ccstr)).first; // Extract the const version of the demangled_cstr const char *demangled_ccstr = entry.getKeyData(); @@ -184,25 +186,16 @@ protected: // we can't guarantee that some objects won't get destroyed after the // global destructor chain is run, and trying to make sure no destructors // touch ConstStrings is difficult. So we leak the pool instead. -// -// FIXME: If we are going to keep it this way we should come up with some -// abstraction to "pthread_once" so we don't have to check the pointer -// every time. //---------------------------------------------------------------------- static Pool & StringPool() { - static Mutex g_pool_initialization_mutex; + static std::once_flag g_pool_initialization_flag; static Pool *g_string_pool = NULL; - if (g_string_pool == NULL) - { - Mutex::Locker initialization_locker(g_pool_initialization_mutex); - if (g_string_pool == NULL) - { - g_string_pool = new Pool(); - } - } + std::call_once(g_pool_initialization_flag, [] () { + g_string_pool = new Pool(); + }); return *g_string_pool; } diff --git a/source/Core/DataExtractor.cpp b/source/Core/DataExtractor.cpp index a0958bd6b1c6..6e1d63095cf3 100644 --- a/source/Core/DataExtractor.cpp +++ b/source/Core/DataExtractor.cpp @@ -22,7 +22,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/MathExtras.h" - +#include "llvm/Support/MD5.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/DataExtractor.h" @@ -132,7 +132,8 @@ DataExtractor::DataExtractor () : m_end (NULL), m_byte_order(lldb::endian::InlHostByteOrder()), m_addr_size (4), - m_data_sp () + m_data_sp (), + m_target_byte_size(1) { } @@ -140,12 +141,13 @@ DataExtractor::DataExtractor () : // This constructor allows us to use data that is owned by someone else. // The data must stay around as long as this object is valid. //---------------------------------------------------------------------- -DataExtractor::DataExtractor (const void* data, offset_t length, ByteOrder endian, uint32_t addr_size) : +DataExtractor::DataExtractor (const void* data, offset_t length, ByteOrder endian, uint32_t addr_size, uint32_t target_byte_size/*=1*/) : m_start ((uint8_t*)data), m_end ((uint8_t*)data + length), m_byte_order(endian), m_addr_size (addr_size), - m_data_sp () + m_data_sp (), + m_target_byte_size(target_byte_size) { } @@ -156,12 +158,13 @@ DataExtractor::DataExtractor (const void* data, offset_t length, ByteOrder endia // as long as any DataExtractor objects exist that have a reference to // this data. //---------------------------------------------------------------------- -DataExtractor::DataExtractor (const DataBufferSP& data_sp, ByteOrder endian, uint32_t addr_size) : +DataExtractor::DataExtractor (const DataBufferSP& data_sp, ByteOrder endian, uint32_t addr_size, uint32_t target_byte_size/*=1*/) : m_start (NULL), m_end (NULL), m_byte_order(endian), m_addr_size (addr_size), - m_data_sp () + m_data_sp (), + m_target_byte_size(target_byte_size) { SetData (data_sp); } @@ -173,12 +176,13 @@ DataExtractor::DataExtractor (const DataBufferSP& data_sp, ByteOrder endian, uin // as any object contains a reference to that data. The endian // swap and address size settings are copied from "data". //---------------------------------------------------------------------- -DataExtractor::DataExtractor (const DataExtractor& data, offset_t offset, offset_t length) : +DataExtractor::DataExtractor (const DataExtractor& data, offset_t offset, offset_t length, uint32_t target_byte_size/*=1*/) : m_start(NULL), m_end(NULL), m_byte_order(data.m_byte_order), m_addr_size(data.m_addr_size), - m_data_sp() + m_data_sp(), + m_target_byte_size(target_byte_size) { if (data.ValidOffset(offset)) { @@ -194,7 +198,8 @@ DataExtractor::DataExtractor (const DataExtractor& rhs) : m_end (rhs.m_end), m_byte_order (rhs.m_byte_order), m_addr_size (rhs.m_addr_size), - m_data_sp (rhs.m_data_sp) + m_data_sp (rhs.m_data_sp), + m_target_byte_size(rhs.m_target_byte_size) { } @@ -1480,7 +1485,9 @@ DataExtractor::Dump (Stream *s, s->EOL(); } if (base_addr != LLDB_INVALID_ADDRESS) - s->Printf ("0x%8.8" PRIx64 ": ", (uint64_t)(base_addr + (offset - start_offset))); + s->Printf ("0x%8.8" PRIx64 ": ", + (uint64_t)(base_addr + (offset - start_offset)/m_target_byte_size )); + line_start_offset = offset; } else @@ -1535,6 +1542,7 @@ DataExtractor::Dump (Stream *s, { s->Printf ("%2.2x", GetU8(&offset)); } + // Put an extra space between the groups of bytes if more than one // is being dumped in a group (item_byte_size is more than 1). if (item_byte_size > 1) @@ -2230,3 +2238,27 @@ DataExtractor::Append(void* buf, offset_t length) return true; } + +void +DataExtractor::Checksum (llvm::SmallVectorImpl<uint8_t> &dest, + uint64_t max_data) +{ + if (max_data == 0) + max_data = GetByteSize(); + else + max_data = std::min(max_data, GetByteSize()); + + llvm::MD5 md5; + + const llvm::ArrayRef<uint8_t> data(GetDataStart(),max_data); + md5.update(data); + + llvm::MD5::MD5Result result; + md5.final(result); + + dest.resize(16); + std::copy(result, + result+16, + dest.begin()); +} + diff --git a/source/Core/Debugger.cpp b/source/Core/Debugger.cpp index 178296347677..c7342ade6cad 100644 --- a/source/Core/Debugger.cpp +++ b/source/Core/Debugger.cpp @@ -18,7 +18,6 @@ #include "llvm/ADT/StringRef.h" #include "lldb/lldb-private.h" -#include "lldb/Core/ConnectionFileDescriptor.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RegisterValue.h" @@ -34,8 +33,10 @@ #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/DataFormatters/FormatManager.h" #include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/Terminal.h" +#include "lldb/Host/ThreadLauncher.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/OptionValueSInt64.h" #include "lldb/Interpreter/OptionValueString.h" @@ -44,6 +45,8 @@ #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/VariableList.h" +#include "lldb/Target/CPPLanguageRuntime.h" +#include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/TargetList.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" @@ -61,6 +64,7 @@ using namespace lldb_private; static uint32_t g_shared_debugger_refcount = 0; static lldb::user_id_t g_unique_id = 1; +static size_t g_debugger_event_thread_stack_bytes = 8 * 1024 * 1024; #pragma mark Static Functions @@ -121,12 +125,13 @@ g_language_enumerators[] = FILE_AND_LINE\ "\\n" - +#define DEFAULT_DISASSEMBLY_FORMAT "${current-pc-arrow}${addr-file-or-load}{ <${function.name-without-args}${function.concrete-only-addr-offset-no-padding}>}: " static PropertyDefinition g_properties[] = { { "auto-confirm", OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true all confirmation prompts will receive their default reply." }, +{ "disassembly-format", OptionValue::eTypeString , true, 0 , DEFAULT_DISASSEMBLY_FORMAT, NULL, "The default disassembly format string to use when disassembling instruction sequences." }, { "frame-format", OptionValue::eTypeString , true, 0 , DEFAULT_FRAME_FORMAT, NULL, "The default frame format string to use when displaying stack frame information for threads." }, { "notify-void", OptionValue::eTypeBoolean, true, false, NULL, NULL, "Notify the user explicitly if an expression returns void (default: false)." }, { "prompt", OptionValue::eTypeString , true, OptionValueString::eOptionEncodeCharacterEscapeSequences, "(lldb) ", NULL, "The debugger command line prompt displayed for the user." }, @@ -139,7 +144,8 @@ g_properties[] = { "thread-format", OptionValue::eTypeString , true, 0 , DEFAULT_THREAD_FORMAT, NULL, "The default thread format string to use when displaying thread information." }, { "use-external-editor", OptionValue::eTypeBoolean, true, false, NULL, NULL, "Whether to use an external editor or not." }, { "use-color", OptionValue::eTypeBoolean, true, true , NULL, NULL, "Whether to use Ansi color codes or not." }, -{ "auto-one-line-summaries", OptionValue::eTypeBoolean, true, true, NULL, NULL, "If true, LLDB will automatically display small structs in one-liner format (default: true)." }, +{ "auto-one-line-summaries", OptionValue::eTypeBoolean, true, true, NULL, NULL, "If true, LLDB will automatically display small structs in one-liner format (default: true)." }, +{ "escape-non-printables", OptionValue::eTypeBoolean, true, true, NULL, NULL, "If true, LLDB will automatically escape non-printable and escape characters when formatting strings." }, { NULL, OptionValue::eTypeInvalid, true, 0 , NULL, NULL, NULL } }; @@ -147,6 +153,7 @@ g_properties[] = enum { ePropertyAutoConfirm = 0, + ePropertyDisassemblyFormat, ePropertyFrameFormat, ePropertyNotiftVoid, ePropertyPrompt, @@ -159,7 +166,8 @@ enum ePropertyThreadFormat, ePropertyUseExternalEditor, ePropertyUseColor, - ePropertyAutoOneLineSummaries + ePropertyAutoOneLineSummaries, + ePropertyEscapeNonPrintables }; Debugger::LoadPluginCallbackType Debugger::g_load_plugin_callback = NULL; @@ -171,6 +179,7 @@ Debugger::SetPropertyValue (const ExecutionContext *exe_ctx, const char *value) { bool is_load_script = strcmp(property_path,"target.load-script-from-symbol-file") == 0; + bool is_escape_non_printables = strcmp(property_path, "escape-non-printables") == 0; TargetSP target_sp; LoadScriptFromSymFile load_script_old_value; if (is_load_script && exe_ctx->GetTargetSP()) @@ -218,6 +227,10 @@ Debugger::SetPropertyValue (const ExecutionContext *exe_ctx, } } } + else if (is_escape_non_printables) + { + DataVisualization::ForceUpdate(); + } } return error; } @@ -230,6 +243,13 @@ Debugger::GetAutoConfirm () const } const char * +Debugger::GetDisassemblyFormat() const +{ + const uint32_t idx = ePropertyDisassemblyFormat; + return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, g_properties[idx].default_cstr_value); +} + +const char * Debugger::GetFrameFormat() const { const uint32_t idx = ePropertyFrameFormat; @@ -353,7 +373,13 @@ Debugger::GetAutoOneLineSummaries () const { const uint32_t idx = ePropertyAutoOneLineSummaries; return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, true); +} +bool +Debugger::GetEscapeNonPrintables () const +{ + const uint32_t idx = ePropertyEscapeNonPrintables; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, true); } #pragma mark Debugger @@ -620,24 +646,25 @@ Debugger::FindTargetWithProcess (Process *process) return target_sp; } -Debugger::Debugger (lldb::LogOutputCallback log_callback, void *baton) : - UserID (g_unique_id++), - Properties(OptionValuePropertiesSP(new OptionValueProperties())), - m_input_file_sp (new StreamFile (stdin, false)), - m_output_file_sp (new StreamFile (stdout, false)), - m_error_file_sp (new StreamFile (stderr, false)), - m_terminal_state (), - m_target_list (*this), - m_platform_list (), - m_listener ("lldb.Debugger"), +Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton) : + UserID(g_unique_id++), + Properties(OptionValuePropertiesSP(new OptionValueProperties())), + m_input_file_sp(new StreamFile(stdin, false)), + m_output_file_sp(new StreamFile(stdout, false)), + m_error_file_sp(new StreamFile(stderr, false)), + m_terminal_state(), + m_target_list(*this), + m_platform_list(), + m_listener("lldb.Debugger"), m_source_manager_ap(), m_source_file_cache(), - m_command_interpreter_ap (new CommandInterpreter (*this, eScriptLanguageDefault, false)), - m_input_reader_stack (), - m_instance_name (), - m_loaded_plugins (), - m_event_handler_thread (LLDB_INVALID_HOST_THREAD), - m_io_handler_thread (LLDB_INVALID_HOST_THREAD) + m_command_interpreter_ap(new CommandInterpreter(*this, eScriptLanguageDefault, false)), + m_input_reader_stack(), + m_instance_name(), + m_loaded_plugins(), + m_event_handler_thread (), + m_io_handler_thread (), + m_sync_broadcaster (NULL, "lldb.debugger.sync") { char instance_cstr[256]; snprintf(instance_cstr, sizeof(instance_cstr), "debugger_%d", (int)GetID()); @@ -646,7 +673,7 @@ Debugger::Debugger (lldb::LogOutputCallback log_callback, void *baton) : m_log_callback_stream_sp.reset (new StreamCallback (log_callback, baton)); m_command_interpreter_ap->Initialize (); // Always add our default platform to the platform list - PlatformSP default_platform_sp (Platform::GetDefaultPlatform()); + PlatformSP default_platform_sp (Platform::GetHostPlatform()); assert (default_platform_sp.get()); m_platform_list.Append (default_platform_sp, true); @@ -903,7 +930,6 @@ Debugger::GetTopIOHandlerControlSequence(char ch) void Debugger::RunIOHandler (const IOHandlerSP& reader_sp) { - Mutex::Locker locker (m_input_reader_stack.GetMutex()); PushIOHandler (reader_sp); IOHandlerSP top_reader_sp = reader_sp; @@ -1129,6 +1155,8 @@ TestPromptFormats (StackFrame *frame) StreamString s; const char *prompt_format = "{addr = '${addr}'\n}" + "{addr-file-or-load = '${addr-file-or-load}'\n}" + "{current-pc-arrow = '${current-pc-arrow}'\n}" "{process.id = '${process.id}'\n}" "{process.name = '${process.name}'\n}" "{process.file.basename = '${process.file.basename}'\n}" @@ -1156,9 +1184,13 @@ TestPromptFormats (StackFrame *frame) "{frame.reg.xmm0 = '${frame.reg.xmm0}'\n}" "{frame.reg.carp = '${frame.reg.carp}'\n}" "{function.id = '${function.id}'\n}" + "{function.changed = '${function.changed}'\n}" + "{function.initial-function = '${function.initial-function}'\n}" "{function.name = '${function.name}'\n}" + "{function.name-without-args = '${function.name-without-args}'\n}" "{function.name-with-args = '${function.name-with-args}'\n}" "{function.addr-offset = '${function.addr-offset}'\n}" + "{function.concrete-only-addr-offset-no-padding = '${function.concrete-only-addr-offset-no-padding}'\n}" "{function.line-offset = '${function.line-offset}'\n}" "{function.pc-offset = '${function.pc-offset}'\n}" "{line.file.basename = '${line.file.basename}'\n}" @@ -1556,7 +1588,9 @@ FormatPromptRecurse const Address *addr, Stream &s, const char **end, - ValueObject* valobj + ValueObject* valobj, + bool function_changed, + bool initial_function ) { ValueObject* realvalobj = NULL; // makes it super-easy to parse pointers @@ -1598,7 +1632,7 @@ FormatPromptRecurse ++p; // Skip the '{' - if (FormatPromptRecurse (p, sc, exe_ctx, addr, sub_strm, &p, valobj)) + if (FormatPromptRecurse (p, sc, exe_ctx, addr, sub_strm, &p, valobj, function_changed, initial_function)) { // The stream had all it needed s.Write(sub_strm.GetData(), sub_strm.GetSize()); @@ -1632,6 +1666,12 @@ FormatPromptRecurse const char *cstr = NULL; std::string token_format; Address format_addr; + + // normally "addr" means print a raw address but + // "file-addr-or-load-addr" means print a module + file addr if there's no load addr + bool print_file_addr_or_load_addr = false; + bool addr_offset_concrete_func_only = false; + bool addr_offset_print_with_no_padding = false; bool calculate_format_addr_function_offset = false; // Set reg_kind and reg_num to invalid values RegisterKind reg_kind = kNumRegisterKinds; @@ -1710,6 +1750,15 @@ FormatPromptRecurse target = valobj; val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; } + else if (IsToken (var_name_begin, "var.script:")) + { + var_name_begin += ::strlen("var.script:"); + std::string script_name(var_name_begin,var_name_end); + ScriptInterpreter* script_interpreter = valobj->GetTargetSP()->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (RunScriptFormatKeyword (s, script_interpreter, valobj, script_name)) + var_success = true; + break; + } else if (IsToken (var_name_begin,"var%")) { was_var_format = true; @@ -1778,6 +1827,7 @@ FormatPromptRecurse log->Printf("[Debugger::FormatPrompt] ALL RIGHT: unparsed portion = %s, why stopping = %d," " final_value_type %d", first_unparsed, reason_to_stop, final_value_type); + target = target->GetQualifiedRepresentationIfAvailable(target->GetDynamicValueType(), true).get(); } } else @@ -1826,8 +1876,8 @@ FormatPromptRecurse // TODO use flags for these const uint32_t type_info_flags = target->GetClangType().GetTypeInfo(NULL); - bool is_array = (type_info_flags & ClangASTType::eTypeIsArray) != 0; - bool is_pointer = (type_info_flags & ClangASTType::eTypeIsPointer) != 0; + bool is_array = (type_info_flags & eTypeIsArray) != 0; + bool is_pointer = (type_info_flags & eTypeIsPointer) != 0; bool is_aggregate = target->GetClangType().IsAggregateType(); if ((is_array || is_pointer) && (!is_array_range) && val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) // this should be wrong, but there are some exceptions @@ -1942,7 +1992,7 @@ FormatPromptRecurse if (!special_directions) var_success &= item->DumpPrintableRepresentation(s,val_obj_display, custom_format); else - var_success &= FormatPromptRecurse(special_directions, sc, exe_ctx, addr, s, NULL, item); + var_success &= FormatPromptRecurse(special_directions, sc, exe_ctx, addr, s, NULL, item, function_changed, initial_function); if (--max_num_children == 0) { @@ -1958,7 +2008,12 @@ FormatPromptRecurse } break; case 'a': - if (IsToken (var_name_begin, "addr}")) + if (IsToken (var_name_begin, "addr-file-or-load}")) + { + print_file_addr_or_load_addr = true; + } + if (IsToken (var_name_begin, "addr}") + || IsToken (var_name_begin, "addr-file-or-load}")) { if (addr && addr->IsValid()) { @@ -2160,8 +2215,7 @@ FormatPromptRecurse } } break; - - + case 'm': if (IsToken (var_name_begin, "module.")) { @@ -2289,6 +2343,14 @@ FormatPromptRecurse var_success = true; } + if (IsToken (var_name_begin, "changed}") && function_changed) + { + var_success = true; + } + if (IsToken (var_name_begin, "initial-function}") && initial_function) + { + var_success = true; + } else if (IsToken (var_name_begin, "name}")) { if (sc->function) @@ -2315,6 +2377,19 @@ FormatPromptRecurse var_success = true; } } + else if (IsToken (var_name_begin, "name-without-args}")) + { + ConstString name; + if (sc->function) + name = sc->function->GetMangled().GetName (Mangled::ePreferDemangledWithoutArguments); + else if (sc->symbol) + name = sc->symbol->GetMangled().GetName (Mangled::ePreferDemangledWithoutArguments); + if (name) + { + s.PutCString(name.GetCString()); + var_success = true; + } + } else if (IsToken (var_name_begin, "name-with-args}")) { // Print the function name with arguments in it @@ -2418,7 +2493,7 @@ FormatPromptRecurse .SetHideItemNames(false) .SetShowMembersOneLiner(true), ""); - format.FormatObject(var_value_sp.get(), buffer); + format.FormatObject(var_value_sp.get(), buffer, TypeSummaryOptions()); var_representation = buffer.c_str(); } else @@ -2458,8 +2533,14 @@ FormatPromptRecurse } } } - else if (IsToken (var_name_begin, "addr-offset}")) + else if (IsToken (var_name_begin, "addr-offset}") + || IsToken (var_name_begin, "concrete-only-addr-offset-no-padding}")) { + if (IsToken (var_name_begin, "concrete-only-addr-offset-no-padding}")) + { + addr_offset_print_with_no_padding = true; + addr_offset_concrete_func_only = true; + } var_success = addr != NULL; if (var_success) { @@ -2530,6 +2611,35 @@ FormatPromptRecurse } } break; + case 'c': + if (IsToken (var_name_begin, "current-pc-arrow")) + { + if (addr && exe_ctx && exe_ctx->GetFramePtr()) + { + RegisterContextSP reg_ctx = exe_ctx->GetFramePtr()->GetRegisterContextSP(); + if (reg_ctx.get()) + { + addr_t pc_loadaddr = reg_ctx->GetPC(); + if (pc_loadaddr != LLDB_INVALID_ADDRESS) + { + Address pc; + pc.SetLoadAddress (pc_loadaddr, exe_ctx->GetTargetPtr()); + if (pc == *addr) + { + s.Printf ("-> "); + var_success = true; + } + } + } + if (var_success == false) + { + s.Printf(" "); + var_success = true; + } + } + var_success = true; + } + break; } if (var_success) @@ -2587,7 +2697,7 @@ FormatPromptRecurse if (sc->function) { func_addr = sc->function->GetAddressRange().GetBaseAddress(); - if (sc->block) + if (sc->block && addr_offset_concrete_func_only == false) { // Check to make sure we aren't in an inline // function. If we are, use the inline block @@ -2605,14 +2715,19 @@ FormatPromptRecurse if (func_addr.IsValid()) { + const char *addr_offset_padding = " "; + if (addr_offset_print_with_no_padding) + { + addr_offset_padding = ""; + } if (func_addr.GetSection() == format_addr.GetSection()) { addr_t func_file_addr = func_addr.GetFileAddress(); addr_t addr_file_addr = format_addr.GetFileAddress(); if (addr_file_addr > func_file_addr) - s.Printf(" + %" PRIu64, addr_file_addr - func_file_addr); + s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding, addr_file_addr - func_file_addr); else if (addr_file_addr < func_file_addr) - s.Printf(" - %" PRIu64, func_file_addr - addr_file_addr); + s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding, func_file_addr - addr_file_addr); var_success = true; } else @@ -2623,9 +2738,9 @@ FormatPromptRecurse addr_t func_load_addr = func_addr.GetLoadAddress (target); addr_t addr_load_addr = format_addr.GetLoadAddress (target); if (addr_load_addr > func_load_addr) - s.Printf(" + %" PRIu64, addr_load_addr - func_load_addr); + s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding, addr_load_addr - func_load_addr); else if (addr_load_addr < func_load_addr) - s.Printf(" - %" PRIu64, func_load_addr - addr_load_addr); + s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding, func_load_addr - addr_load_addr); var_success = true; } } @@ -2642,10 +2757,21 @@ FormatPromptRecurse if (vaddr != LLDB_INVALID_ADDRESS) { - int addr_width = target->GetArchitecture().GetAddressByteSize() * 2; + int addr_width = 0; + if (exe_ctx && target) + { + addr_width = target->GetArchitecture().GetAddressByteSize() * 2; + } if (addr_width == 0) addr_width = 16; - s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr); + if (print_file_addr_or_load_addr) + { + format_addr.Dump (&s, exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress, 0); + } + else + { + s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr); + } var_success = true; } } @@ -2759,9 +2885,55 @@ Debugger::FormatPrompt std::string format_str = lldb_utility::ansi::FormatAnsiTerminalCodes (format, use_color); if (format_str.length()) format = format_str.c_str(); - return FormatPromptRecurse (format, sc, exe_ctx, addr, s, NULL, valobj); + return FormatPromptRecurse (format, sc, exe_ctx, addr, s, NULL, valobj, false, false); +} + +bool +Debugger::FormatDisassemblerAddress (const char *format, + const SymbolContext *sc, + const SymbolContext *prev_sc, + const ExecutionContext *exe_ctx, + const Address *addr, + Stream &s) +{ + if (format == NULL && exe_ctx != NULL && exe_ctx->HasTargetScope()) + { + format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat(); + } + bool function_changed = false; + bool initial_function = false; + if (prev_sc && (prev_sc->function || prev_sc->symbol)) + { + if (sc && (sc->function || sc->symbol)) + { + if (prev_sc->symbol && sc->symbol) + { + if (!sc->symbol->Compare (prev_sc->symbol->GetName(), prev_sc->symbol->GetType())) + { + function_changed = true; + } + } + else if (prev_sc->function && sc->function) + { + if (prev_sc->function->GetMangled() != sc->function->GetMangled()) + { + function_changed = true; + } + } + } + } + // The first context on a list of instructions will have a prev_sc that + // has no Function or Symbol -- if SymbolContext had an IsValid() method, it + // would return false. But we do get a prev_sc pointer. + if ((sc && (sc->function || sc->symbol)) + && prev_sc && (prev_sc->function == NULL && prev_sc->symbol == NULL)) + { + initial_function = true; + } + return FormatPromptRecurse (format, sc, exe_ctx, addr, s, NULL, NULL, function_changed, initial_function); } + void Debugger::SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton) { @@ -2952,6 +3124,7 @@ Debugger::GetProcessSTDERR (Process *process, Stream *stream) return total_bytes; } + // This function handles events that were broadcast by the process. void Debugger::HandleProcessEvent (const EventSP &event_sp) @@ -2959,7 +3132,7 @@ Debugger::HandleProcessEvent (const EventSP &event_sp) using namespace lldb; const uint32_t event_type = event_sp->GetType(); ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event_sp.get()); - + StreamString output_stream; StreamString error_stream; const bool gui_enabled = IsForwardingEvents(); @@ -2968,191 +3141,27 @@ Debugger::HandleProcessEvent (const EventSP &event_sp) { bool pop_process_io_handler = false; assert (process_sp); - + if (event_type & Process::eBroadcastBitSTDOUT || event_type & Process::eBroadcastBitStateChanged) { GetProcessSTDOUT (process_sp.get(), &output_stream); } - + if (event_type & Process::eBroadcastBitSTDERR || event_type & Process::eBroadcastBitStateChanged) { GetProcessSTDERR (process_sp.get(), &error_stream); } - + if (event_type & Process::eBroadcastBitStateChanged) { - - // Drain all stout and stderr so we don't see any output come after - // we print our prompts - // Something changed in the process; get the event and report the process's current status and location to - // the user. - StateType event_state = Process::ProcessEventData::GetStateFromEvent (event_sp.get()); - if (event_state == eStateInvalid) - return; - - switch (event_state) - { - case eStateInvalid: - case eStateUnloaded: - case eStateConnected: - case eStateAttaching: - case eStateLaunching: - case eStateStepping: - case eStateDetached: - { - output_stream.Printf("Process %" PRIu64 " %s\n", - process_sp->GetID(), - StateAsCString (event_state)); - - if (event_state == eStateDetached) - pop_process_io_handler = true; - } - break; - - case eStateRunning: - // Don't be chatty when we run... - break; - - case eStateExited: - process_sp->GetStatus(output_stream); - pop_process_io_handler = true; - break; - - case eStateStopped: - case eStateCrashed: - case eStateSuspended: - // Make sure the program hasn't been auto-restarted: - if (Process::ProcessEventData::GetRestartedFromEvent (event_sp.get())) - { - size_t num_reasons = Process::ProcessEventData::GetNumRestartedReasons(event_sp.get()); - if (num_reasons > 0) - { - // FIXME: Do we want to report this, or would that just be annoyingly chatty? - if (num_reasons == 1) - { - const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex (event_sp.get(), 0); - output_stream.Printf("Process %" PRIu64 " stopped and restarted: %s\n", - process_sp->GetID(), - reason ? reason : "<UNKNOWN REASON>"); - } - else - { - output_stream.Printf("Process %" PRIu64 " stopped and restarted, reasons:\n", - process_sp->GetID()); - - - for (size_t i = 0; i < num_reasons; i++) - { - const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex (event_sp.get(), i); - output_stream.Printf("\t%s\n", reason ? reason : "<UNKNOWN REASON>"); - } - } - } - } - else - { - // Lock the thread list so it doesn't change on us, this is the scope for the locker: - { - ThreadList &thread_list = process_sp->GetThreadList(); - Mutex::Locker locker (thread_list.GetMutex()); - - ThreadSP curr_thread (thread_list.GetSelectedThread()); - ThreadSP thread; - StopReason curr_thread_stop_reason = eStopReasonInvalid; - if (curr_thread) - curr_thread_stop_reason = curr_thread->GetStopReason(); - if (!curr_thread || - !curr_thread->IsValid() || - curr_thread_stop_reason == eStopReasonInvalid || - curr_thread_stop_reason == eStopReasonNone) - { - // Prefer a thread that has just completed its plan over another thread as current thread. - ThreadSP plan_thread; - ThreadSP other_thread; - const size_t num_threads = thread_list.GetSize(); - size_t i; - for (i = 0; i < num_threads; ++i) - { - thread = thread_list.GetThreadAtIndex(i); - StopReason thread_stop_reason = thread->GetStopReason(); - switch (thread_stop_reason) - { - case eStopReasonInvalid: - case eStopReasonNone: - break; - - case eStopReasonTrace: - case eStopReasonBreakpoint: - case eStopReasonWatchpoint: - case eStopReasonSignal: - case eStopReasonException: - case eStopReasonExec: - case eStopReasonThreadExiting: - if (!other_thread) - other_thread = thread; - break; - case eStopReasonPlanComplete: - if (!plan_thread) - plan_thread = thread; - break; - } - } - if (plan_thread) - thread_list.SetSelectedThreadByID (plan_thread->GetID()); - else if (other_thread) - thread_list.SetSelectedThreadByID (other_thread->GetID()); - else - { - if (curr_thread && curr_thread->IsValid()) - thread = curr_thread; - else - thread = thread_list.GetThreadAtIndex(0); - - if (thread) - thread_list.SetSelectedThreadByID (thread->GetID()); - } - } - } - // Drop the ThreadList mutex by here, since GetThreadStatus below might have to run code, - // e.g. for Data formatters, and if we hold the ThreadList mutex, then the process is going to - // have a hard time restarting the process. - - if (GetTargetList().GetSelectedTarget().get() == &process_sp->GetTarget()) - { - const bool only_threads_with_stop_reason = true; - const uint32_t start_frame = 0; - const uint32_t num_frames = 1; - const uint32_t num_frames_with_source = 1; - process_sp->GetStatus(output_stream); - process_sp->GetThreadStatus (output_stream, - only_threads_with_stop_reason, - start_frame, - num_frames, - num_frames_with_source); - } - else - { - uint32_t target_idx = GetTargetList().GetIndexOfTarget(process_sp->GetTarget().shared_from_this()); - if (target_idx != UINT32_MAX) - output_stream.Printf ("Target %d: (", target_idx); - else - output_stream.Printf ("Target <unknown index>: ("); - process_sp->GetTarget().Dump (&output_stream, eDescriptionLevelBrief); - output_stream.Printf (") stopped.\n"); - } - - // Pop the process IO handler - pop_process_io_handler = true; - } - break; - } + Process::HandleProcessStateChangedEvent (event_sp, &output_stream, pop_process_io_handler); } - + if (output_stream.GetSize() || error_stream.GetSize()) { StreamFileSP error_stream_sp (GetOutputFile()); bool top_io_handler_hid = false; - + if (process_sp->ProcessIOHandlerIsActive() == false) top_io_handler_hid = HideTopIOHandler(); @@ -3245,17 +3254,14 @@ Debugger::DefaultEventHandler() CommandInterpreter::eBroadcastBitQuitCommandReceived | CommandInterpreter::eBroadcastBitAsynchronousOutputData | CommandInterpreter::eBroadcastBitAsynchronousErrorData ); - + + // Let the thread that spawned us know that we have started up and + // that we are now listening to all required events so no events get missed + m_sync_broadcaster.BroadcastEvent(eBroadcastBitEventThreadIsListening); + bool done = false; while (!done) { -// Mutex::Locker locker; -// if (locker.TryLock(m_input_reader_stack.GetMutex())) -// { -// if (m_input_reader_stack.IsEmpty()) -// break; -// } -// EventSP event_sp; if (listener.WaitForEvent(NULL, event_sp)) { @@ -3337,19 +3343,38 @@ Debugger::EventHandlerThread (lldb::thread_arg_t arg) bool Debugger::StartEventHandlerThread() { - if (!IS_VALID_LLDB_HOST_THREAD(m_event_handler_thread)) - m_event_handler_thread = Host::ThreadCreate("lldb.debugger.event-handler", EventHandlerThread, this, NULL); - return IS_VALID_LLDB_HOST_THREAD(m_event_handler_thread); + if (!m_event_handler_thread.IsJoinable()) + { + // We must synchronize with the DefaultEventHandler() thread to ensure + // it is up and running and listening to events before we return from + // this function. We do this by listening to events for the + // eBroadcastBitEventThreadIsListening from the m_sync_broadcaster + Listener listener("lldb.debugger.event-handler"); + listener.StartListeningForEvents(&m_sync_broadcaster, eBroadcastBitEventThreadIsListening); + + // Use larger 8MB stack for this thread + m_event_handler_thread = ThreadLauncher::LaunchThread("lldb.debugger.event-handler", EventHandlerThread, + this, + NULL, + g_debugger_event_thread_stack_bytes); + + // Make sure DefaultEventHandler() is running and listening to events before we return + // from this function. We are only listening for events of type + // eBroadcastBitEventThreadIsListening so we don't need to check the event, we just need + // to wait an infinite amount of time for it (NULL timeout as the first parameter) + lldb::EventSP event_sp; + listener.WaitForEvent(NULL, event_sp); + } + return m_event_handler_thread.IsJoinable(); } void Debugger::StopEventHandlerThread() { - if (IS_VALID_LLDB_HOST_THREAD(m_event_handler_thread)) + if (m_event_handler_thread.IsJoinable()) { GetCommandInterpreter().BroadcastEvent(CommandInterpreter::eBroadcastBitQuitCommandReceived); - Host::ThreadJoin(m_event_handler_thread, NULL, NULL); - m_event_handler_thread = LLDB_INVALID_HOST_THREAD; + m_event_handler_thread.Join(nullptr); } } @@ -3366,21 +3391,43 @@ Debugger::IOHandlerThread (lldb::thread_arg_t arg) bool Debugger::StartIOHandlerThread() { - if (!IS_VALID_LLDB_HOST_THREAD(m_io_handler_thread)) - m_io_handler_thread = Host::ThreadCreate("lldb.debugger.io-handler", IOHandlerThread, this, NULL); - return IS_VALID_LLDB_HOST_THREAD(m_io_handler_thread); + if (!m_io_handler_thread.IsJoinable()) + m_io_handler_thread = ThreadLauncher::LaunchThread ("lldb.debugger.io-handler", + IOHandlerThread, + this, + NULL, + 8*1024*1024); // Use larger 8MB stack for this thread + return m_io_handler_thread.IsJoinable(); } void Debugger::StopIOHandlerThread() { - if (IS_VALID_LLDB_HOST_THREAD(m_io_handler_thread)) + if (m_io_handler_thread.IsJoinable()) { if (m_input_file_sp) m_input_file_sp->GetFile().Close(); - Host::ThreadJoin(m_io_handler_thread, NULL, NULL); - m_io_handler_thread = LLDB_INVALID_HOST_THREAD; + m_io_handler_thread.Join(nullptr); } } +Target * +Debugger::GetDummyTarget() +{ + return m_target_list.GetDummyTarget (*this).get(); +} + +Target * +Debugger::GetSelectedOrDummyTarget(bool prefer_dummy) +{ + Target *target = nullptr; + if (!prefer_dummy) + { + target = m_target_list.GetSelectedTarget().get(); + if (target) + return target; + } + + return GetDummyTarget(); +} diff --git a/source/Core/Disassembler.cpp b/source/Core/Disassembler.cpp index 1d2b8cf04c32..649f0c5bcb26 100644 --- a/source/Core/Disassembler.cpp +++ b/source/Core/Disassembler.cpp @@ -410,17 +410,18 @@ Disassembler::PrintInstructions SymbolContext prev_sc; AddressRange sc_range; const Address *pc_addr_ptr = NULL; - ExecutionContextScope *exe_scope = exe_ctx.GetBestExecutionContextScope(); StackFrame *frame = exe_ctx.GetFramePtr(); TargetSP target_sp (exe_ctx.GetTargetSP()); SourceManager &source_manager = target_sp ? target_sp->GetSourceManager() : debugger.GetSourceManager(); if (frame) + { pc_addr_ptr = &frame->GetFrameCodeAddress(); + } const uint32_t scope = eSymbolContextLineEntry | eSymbolContextFunction | eSymbolContextSymbol; const bool use_inline_block_range = false; - for (size_t i=0; i<num_instructions_found; ++i) + for (size_t i = 0; i < num_instructions_found; ++i) { Instruction *inst = disasm_ptr->GetInstructionList().GetInstructionAtIndex (i).get(); if (inst) @@ -447,7 +448,7 @@ Disassembler::PrintInstructions if (offset != 0) strm.EOL(); - sc.DumpStopContext(&strm, exe_ctx.GetProcessPtr(), addr, false, true, false); + sc.DumpStopContext(&strm, exe_ctx.GetProcessPtr(), addr, false, true, false, false); strm.EOL(); if (sc.comp_unit && sc.line_entry.IsValid()) @@ -462,23 +463,6 @@ Disassembler::PrintInstructions } } } - else if ((sc.function || sc.symbol) && (sc.function != prev_sc.function || sc.symbol != prev_sc.symbol)) - { - if (prev_sc.function || prev_sc.symbol) - strm.EOL(); - - bool show_fullpaths = false; - bool show_module = true; - bool show_inlined_frames = true; - sc.DumpStopContext (&strm, - exe_scope, - addr, - show_fullpaths, - show_module, - show_inlined_frames); - - strm << ":\n"; - } } else { @@ -486,12 +470,13 @@ Disassembler::PrintInstructions } } - if ((options & eOptionMarkPCAddress) && pc_addr_ptr) + const bool show_bytes = (options & eOptionShowBytes) != 0; + const char *disassembly_format = "${addr-file-or-load}: "; + if (exe_ctx.HasTargetScope()) { - strm.PutCString(inst_is_at_pc ? "-> " : " "); + disassembly_format = exe_ctx.GetTargetRef().GetDebugger().GetDisassemblyFormat (); } - const bool show_bytes = (options & eOptionShowBytes) != 0; - inst->Dump(&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx); + inst->Dump (&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx, &sc, &prev_sc, disassembly_format); strm.EOL(); } else @@ -578,7 +563,10 @@ Instruction::Dump (lldb_private::Stream *s, uint32_t max_opcode_byte_size, bool show_address, bool show_bytes, - const ExecutionContext* exe_ctx) + const ExecutionContext* exe_ctx, + const SymbolContext *sym_ctx, + const SymbolContext *prev_sym_ctx, + const char *disassembly_addr_format_spec) { size_t opcode_column_width = 7; const size_t operand_column_width = 25; @@ -589,13 +577,7 @@ Instruction::Dump (lldb_private::Stream *s, if (show_address) { - m_address.Dump(&ss, - exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL, - Address::DumpStyleLoadAddress, - Address::DumpStyleModuleWithFileAddress, - 0); - - ss.PutCString(": "); + Debugger::FormatDisassemblerAddress (disassembly_addr_format_spec, sym_ctx, prev_sym_ctx, exe_ctx, &m_address, ss); } if (show_bytes) @@ -621,7 +603,7 @@ Instruction::Dump (lldb_private::Stream *s, } } - const size_t opcode_pos = ss.GetSize(); + const size_t opcode_pos = ss.GetSizeOfLastLine(); // The default opcode size of 7 characters is plenty for most architectures // but some like arm can pull out the occasional vqrshrun.s16. We won't get @@ -1003,13 +985,18 @@ InstructionList::Dump (Stream *s, { const uint32_t max_opcode_byte_size = GetMaxOpcocdeByteSize(); collection::const_iterator pos, begin, end; + const char *disassemble_format = "${addr-file-or-load}: "; + if (exe_ctx) + { + disassemble_format = exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat (); + } for (begin = m_instructions.begin(), end = m_instructions.end(), pos = begin; pos != end; ++pos) { if (pos != begin) s->EOL(); - (*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes, exe_ctx); + (*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes, exe_ctx, NULL, NULL, disassemble_format); } } diff --git a/source/Core/FastDemangle.cpp b/source/Core/FastDemangle.cpp index 00a75425b689..53e8972e8048 100644 --- a/source/Core/FastDemangle.cpp +++ b/source/Core/FastDemangle.cpp @@ -18,7 +18,7 @@ //#define DEBUG_REORDERING 1 namespace { - + /// @brief Represents the collection of qualifiers on a type enum Qualifiers @@ -51,7 +51,7 @@ enum class OperatorKind struct Operator { - const char * name; + const char *name; OperatorKind kind; }; @@ -87,28 +87,28 @@ struct NameState class SymbolDemangler { public: - + //---------------------------------------------------- // Public API //---------------------------------------------------- - + /// @brief Create a SymbolDemangler /// /// The newly created demangler allocates and owns scratch memory sufficient /// for demangling typical symbols. Additional memory will be allocated if /// needed and managed by the demangler instance. - + SymbolDemangler() { - buffer = (char *) malloc(8192); - buffer_end = buffer + 8192; - owns_buffer = true; - - rewrite_ranges = (BufferRange *) malloc(128 * sizeof(BufferRange)); - rewrite_ranges_size = 128; - owns_rewrite_ranges = true; + m_buffer = (char *) malloc(8192); + m_buffer_end = m_buffer + 8192; + m_owns_buffer = true; + + m_rewrite_ranges = (BufferRange *) malloc(128 * sizeof (BufferRange)); + m_rewrite_ranges_size = 128; + m_owns_m_rewrite_ranges = true; } - + /// @brief Create a SymbolDemangler that uses provided scratch memory /// /// The provided memory is not owned by the demangler. It will be @@ -124,34 +124,36 @@ public: /// /// @param storage_size Number of bytes of space available scratch memory /// referenced by storage_ptr - - SymbolDemangler(void * storage_ptr, int storage_size) + + SymbolDemangler(void *storage_ptr, int storage_size) { // Use up to 1/8th of the provided space for rewrite ranges - rewrite_ranges_size = (storage_size >> 3) / sizeof(BufferRange); - rewrite_ranges = (BufferRange *) storage_ptr; - owns_rewrite_ranges = false; - + m_rewrite_ranges_size = (storage_size >> 3) / sizeof (BufferRange); + m_rewrite_ranges = (BufferRange *) storage_ptr; + m_owns_m_rewrite_ranges = false; + // Use the rest for the character buffer - buffer = (char *) storage_ptr + rewrite_ranges_size * sizeof(BufferRange); - buffer_end = (const char *)storage_ptr + storage_size; - owns_buffer = false; + m_buffer = (char *) storage_ptr + m_rewrite_ranges_size * sizeof (BufferRange); + m_buffer_end = (const char *)storage_ptr + storage_size; + m_owns_buffer = false; } - + /// @brief Destroys the SymbolDemangler and deallocates any scratch /// memory that it owns - + ~SymbolDemangler() { - if (owns_buffer) free(buffer); - if (owns_rewrite_ranges) free(rewrite_ranges); + if (m_owns_buffer) + free(m_buffer); + if (m_owns_m_rewrite_ranges) + free(m_rewrite_ranges); } - + #ifdef DEBUG_HIGHWATER int highwater_store = 0; int highwater_buffer = 0; #endif - + /// @brief Parses the provided mangled name and returns a newly allocated /// demangling /// @@ -161,293 +163,334 @@ public: /// @result Newly allocated null-terminated demangled name when demangling /// is succesful, and nullptr when demangling fails. The caller is /// responsible for freeing the allocated memory. - - char * GetDemangledCopy(const char * mangled_name, - long mangled_name_length = 0) + + char * + GetDemangledCopy(const char *mangled_name, + long mangled_name_length = 0) { - if (!ParseMangling(mangled_name, mangled_name_length)) return nullptr; - + if (!ParseMangling(mangled_name, mangled_name_length)) + return nullptr; + #ifdef DEBUG_HIGHWATER - int rewrite_count = next_substitute_index + - (rewrite_ranges_size - 1 - next_template_arg_index); - int buffer_size = (int)(write_ptr - buffer); - if (rewrite_count > highwater_store) highwater_store = rewrite_count; - if (buffer_size > highwater_buffer) highwater_buffer = buffer_size; + int rewrite_count = m_next_substitute_index + + (m_rewrite_ranges_size - 1 - m_next_template_arg_index); + int buffer_size = (int)(m_write_ptr - m_buffer); + if (rewrite_count > highwater_store) + highwater_store = rewrite_count; + if (buffer_size > highwater_buffer) + highwater_buffer = buffer_size; #endif - - int length = (int)(write_ptr - buffer); - char * copy = (char *)malloc(length + 1); - memcpy(copy, buffer, length); + + int length = (int)(m_write_ptr - m_buffer); + char *copy = (char *)malloc(length + 1); + memcpy(copy, m_buffer, length); copy[length] = '\0'; return copy; } - + private: - + //---------------------------------------------------- // Grow methods // // Manage the storage used during demangling //---------------------------------------------------- - + void GrowBuffer(long min_growth = 0) { // By default, double the size of the buffer - long growth = buffer_end - buffer; - + long growth = m_buffer_end - m_buffer; + // Avoid growing by more than 1MB at a time - if (growth > 1 << 20) growth = 1 << 20; - + if (growth > 1 << 20) + growth = 1 << 20; + // ... but never grow by less than requested, // or 1K, whichever is greater - if (min_growth < 1024) min_growth = 1024; - if (growth < min_growth) growth = min_growth; - - // Allocate the new buffer and migrate content - long new_size = (buffer_end - buffer) + growth; - char * new_buffer = (char *)malloc(new_size); - memcpy(new_buffer, buffer, write_ptr - buffer); - if (owns_buffer) free(buffer); - owns_buffer = true; - + if (min_growth < 1024) + min_growth = 1024; + if (growth < min_growth) + growth = min_growth; + + // Allocate the new m_buffer and migrate content + long new_size = (m_buffer_end - m_buffer) + growth; + char *new_buffer = (char *) malloc(new_size); + memcpy(new_buffer, m_buffer, m_write_ptr - m_buffer); + if (m_owns_buffer) + free(m_buffer); + m_owns_buffer = true; + // Update references to the new buffer - write_ptr = new_buffer + (write_ptr - buffer); - buffer = new_buffer; - buffer_end = buffer + new_size; + m_write_ptr = new_buffer + (m_write_ptr - m_buffer); + m_buffer = new_buffer; + m_buffer_end = m_buffer + new_size; } - - void GrowRewriteRanges() + + void + GrowRewriteRanges() { // By default, double the size of the array - int growth = rewrite_ranges_size; - + int growth = m_rewrite_ranges_size; + // Apply reasonable minimum and maximum sizes for growth - if (growth > 128) growth = 128; - if (growth < 16) growth = 16; - + if (growth > 128) + growth = 128; + if (growth < 16) + growth = 16; + // Allocate the new array and migrate content - int bytes = (rewrite_ranges_size + growth) * sizeof(BufferRange); - BufferRange * new_ranges = (BufferRange *) malloc(bytes); - for (int index = 0; index < next_substitute_index; index++) + int bytes = (m_rewrite_ranges_size + growth) * sizeof (BufferRange); + BufferRange *new_ranges = (BufferRange *) malloc (bytes); + for (int index = 0; index < m_next_substitute_index; index++) { - new_ranges[index] = rewrite_ranges[index]; + new_ranges[index] = m_rewrite_ranges[index]; } - for (int index = rewrite_ranges_size - 1; - index > next_template_arg_index; index--) + for (int index = m_rewrite_ranges_size - 1; + index > m_next_template_arg_index; index--) { - new_ranges[index + growth] = rewrite_ranges[index]; + new_ranges[index + growth] = m_rewrite_ranges[index]; } - if (owns_rewrite_ranges) free(rewrite_ranges); - owns_rewrite_ranges = true; - + if (m_owns_m_rewrite_ranges) + free(m_rewrite_ranges); + m_owns_m_rewrite_ranges = true; + // Update references to the new array - rewrite_ranges = new_ranges; - rewrite_ranges_size += growth; - next_template_arg_index += growth; + m_rewrite_ranges = new_ranges; + m_rewrite_ranges_size += growth; + m_next_template_arg_index += growth; } - + //---------------------------------------------------- // Range and state management //---------------------------------------------------- - - int GetStartCookie() + + int + GetStartCookie() { - return (int)(write_ptr - buffer); + return (int)(m_write_ptr - m_buffer); } - - BufferRange EndRange(int start_cookie) + + BufferRange + EndRange(int start_cookie) { - return { start_cookie, (int)(write_ptr - (buffer + start_cookie)) }; + return { start_cookie, (int)(m_write_ptr - (m_buffer + start_cookie)) }; } - - void ReorderRange(BufferRange source_range, int insertion_point_cookie) + + void + ReorderRange(BufferRange source_range, int insertion_point_cookie) { // Ensure there's room the preserve the source range - if (write_ptr + source_range.length > buffer_end) + if (m_write_ptr + source_range.length > m_buffer_end) { - GrowBuffer(write_ptr + source_range.length - buffer_end); + GrowBuffer(m_write_ptr + source_range.length - m_buffer_end); } - + // Reorder the content - memcpy(write_ptr, buffer + source_range.offset, source_range.length); - memmove(buffer + insertion_point_cookie + source_range.length, - buffer + insertion_point_cookie, + memcpy(m_write_ptr, m_buffer + source_range.offset, source_range.length); + memmove(m_buffer + insertion_point_cookie + source_range.length, + m_buffer + insertion_point_cookie, source_range.offset - insertion_point_cookie); - memcpy(buffer + insertion_point_cookie, write_ptr, source_range.length); - + memcpy(m_buffer + insertion_point_cookie, m_write_ptr, source_range.length); + // Fix up rewritable ranges, covering both substitutions and templates int index = 0; while (true) { - if (index == next_substitute_index) index = next_template_arg_index + 1; - if (index == rewrite_ranges_size) break; - + if (index == m_next_substitute_index) + index = m_next_template_arg_index + 1; + if (index == m_rewrite_ranges_size) + break; + // Affected ranges are either shuffled forward when after the // insertion but before the source, or backward when inside the // source - int candidate_offset = rewrite_ranges[index].offset; + int candidate_offset = m_rewrite_ranges[index].offset; if (candidate_offset >= insertion_point_cookie) { if (candidate_offset < source_range.offset) { - rewrite_ranges[index].offset += source_range.length; + m_rewrite_ranges[index].offset += source_range.length; } else if (candidate_offset >= source_range.offset) { - rewrite_ranges[index].offset -= - (source_range.offset - insertion_point_cookie); + m_rewrite_ranges[index].offset -= (source_range.offset - insertion_point_cookie); } } ++index; } } - - void EndSubstitution(int start_cookie) + + void + EndSubstitution(int start_cookie) { - if (next_substitute_index == next_template_arg_index) GrowRewriteRanges(); - - int index = next_substitute_index++; - rewrite_ranges[index] = EndRange(start_cookie); + if (m_next_substitute_index == m_next_template_arg_index) + GrowRewriteRanges(); + + int index = m_next_substitute_index++; + m_rewrite_ranges[index] = EndRange(start_cookie); #ifdef DEBUG_SUBSTITUTIONS printf("Saved substitution # %d = %.*s\n", index, - rewrite_ranges[index].length, buffer + start_cookie); + m_rewrite_ranges[index].length, m_buffer + start_cookie); #endif } - - void EndTemplateArg(int start_cookie) + + void + EndTemplateArg(int start_cookie) { - if (next_substitute_index == next_template_arg_index) GrowRewriteRanges(); - - int index = next_template_arg_index--; - rewrite_ranges[index] = EndRange(start_cookie); + if (m_next_substitute_index == m_next_template_arg_index) + GrowRewriteRanges(); + + int index = m_next_template_arg_index--; + m_rewrite_ranges[index] = EndRange(start_cookie); #ifdef DEBUG_TEMPLATE_ARGS printf("Saved template arg # %d = %.*s\n", - rewrite_ranges_size - index - 1, - rewrite_ranges[index].length, buffer + start_cookie); + m_rewrite_ranges_size - index - 1, + m_rewrite_ranges[index].length, m_buffer + start_cookie); #endif } - - void ResetTemplateArgs() + + void + ResetTemplateArgs() { //TODO: this works, but is it the right thing to do? // Should we push/pop somehow at the call sites? - next_template_arg_index = rewrite_ranges_size - 1; + m_next_template_arg_index = m_rewrite_ranges_size - 1; } - + //---------------------------------------------------- // Write methods // // Appends content to the existing output buffer //---------------------------------------------------- - - void Write(char character) + + void + Write(char character) { - if (write_ptr == buffer_end) GrowBuffer(); - *write_ptr++ = character; + if (m_write_ptr == m_buffer_end) + GrowBuffer(); + *m_write_ptr++ = character; } - - void Write(const char * content) + + void + Write(const char *content) { Write(content, strlen(content)); } - - void Write(const char * content, long content_length) + + void + Write(const char *content, long content_length) { - char * end_write_ptr = write_ptr + content_length; - if (end_write_ptr > buffer_end) + char *end_m_write_ptr = m_write_ptr + content_length; + if (end_m_write_ptr > m_buffer_end) { - GrowBuffer(end_write_ptr - buffer_end); - end_write_ptr = write_ptr + content_length; + GrowBuffer(end_m_write_ptr - m_buffer_end); + end_m_write_ptr = m_write_ptr + content_length; } - memcpy(write_ptr, content, content_length); - write_ptr = end_write_ptr; + memcpy(m_write_ptr, content, content_length); + m_write_ptr = end_m_write_ptr; } -#define WRITE(x) Write(x, sizeof(x) - 1) - - void WriteTemplateStart() +#define WRITE(x) Write(x, sizeof (x) - 1) + + void + WriteTemplateStart() { Write('<'); } - - void WriteTemplateEnd() + + void + WriteTemplateEnd() { // Put a space between terminal > characters when nesting templates - if (write_ptr != buffer && *(write_ptr - 1) == '>') WRITE(" >"); + if (m_write_ptr != m_buffer && *(m_write_ptr - 1) == '>') + WRITE(" >"); else Write('>'); } - - void WriteCommaSpace() + + void + WriteCommaSpace() { WRITE(", "); } - - void WriteNamespaceSeparator() + + void + WriteNamespaceSeparator() { WRITE("::"); } - - void WriteStdPrefix() + + void + WriteStdPrefix() { WRITE("std::"); } - - void WriteQualifiers(int qualifiers, bool space_before_reference = true) - { - if (qualifiers & QualifierPointer) Write('*'); - if (qualifiers & QualifierConst) WRITE(" const"); - if (qualifiers & QualifierVolatile) WRITE(" volatile"); - if (qualifiers & QualifierRestrict) WRITE(" restrict"); + + void + WriteQualifiers(int qualifiers, bool space_before_reference = true) + { + if (qualifiers & QualifierPointer) + Write('*'); + if (qualifiers & QualifierConst) + WRITE(" const"); + if (qualifiers & QualifierVolatile) + WRITE(" volatile"); + if (qualifiers & QualifierRestrict) + WRITE(" restrict"); if (qualifiers & QualifierReference) { - if (space_before_reference) WRITE(" &"); + if (space_before_reference) + WRITE(" &"); else Write('&'); } if (qualifiers & QualifierRValueReference) { - if (space_before_reference) WRITE(" &&"); + if (space_before_reference) + WRITE(" &&"); else WRITE("&&"); } } - + //---------------------------------------------------- // Rewrite methods // // Write another copy of content already present // earlier in the output buffer //---------------------------------------------------- - - void RewriteRange(BufferRange range) + + void + RewriteRange(BufferRange range) { - Write(buffer + range.offset, range.length); + Write(m_buffer + range.offset, range.length); } - - bool RewriteSubstitution(int index) + + bool + RewriteSubstitution(int index) { - if (index < 0 || index >= next_substitute_index) + if (index < 0 || index >= m_next_substitute_index) { #ifdef DEBUG_FAILURES printf("*** Invalid substitution #%d\n", index); #endif return false; } - RewriteRange(rewrite_ranges[index]); + RewriteRange(m_rewrite_ranges[index]); return true; } - - bool RewriteTemplateArg(int template_index) + + bool + RewriteTemplateArg(int template_index) { - int index = rewrite_ranges_size - 1 - template_index; - if (template_index < 0 || index <= next_template_arg_index) + int index = m_rewrite_ranges_size - 1 - template_index; + if (template_index < 0 || index <= m_next_template_arg_index) { #ifdef DEBUG_FAILURES printf("*** Invalid template arg reference #%d\n", template_index); #endif return false; } - RewriteRange(rewrite_ranges[index]); + RewriteRange(m_rewrite_ranges[index]); return true; } - + //---------------------------------------------------- // TryParse methods // @@ -455,45 +498,53 @@ private: // writing to the output buffer // // Values indicating failure guarantee that the pre- - // call read_ptr is unchanged + // call m_read_ptr is unchanged //---------------------------------------------------- - - int TryParseNumber() + + int + TryParseNumber() { - unsigned char digit = *read_ptr - '0'; - if (digit > 9) return -1; - + unsigned char digit = *m_read_ptr - '0'; + if (digit > 9) + return -1; + int count = digit; while (true) { - digit = *++read_ptr - '0'; - if (digit > 9) break; - + digit = *++m_read_ptr - '0'; + if (digit > 9) + break; + count = count * 10 + digit; } return count; } - - int TryParseBase36Number() + + int + TryParseBase36Number() { - char digit = *read_ptr; + char digit = *m_read_ptr; int count; - if (digit >= '0' && digit <= '9') count = digit -= '0'; - else if (digit >= 'A' && digit <= 'Z') count = digit -= ('A' - 10); + if (digit >= '0' && digit <= '9') + count = digit -= '0'; + else if (digit >= 'A' && digit <= 'Z') + count = digit -= ('A' - 10); else return -1; - + while (true) { - digit = *++read_ptr; - if (digit >= '0' && digit <= '9') digit -= '0'; - else if (digit >= 'A' && digit <= 'Z') digit -= ('A' - 10); + digit = *++m_read_ptr; + if (digit >= '0' && digit <= '9') + digit -= '0'; + else if (digit >= 'A' && digit <= 'Z') + digit -= ('A' - 10); else break; - + count = count * 36 + digit; } return count; } - + // <builtin-type> ::= v # void // ::= w # wchar_t // ::= b # bool @@ -524,10 +575,11 @@ private: // ::= Da # auto (in dependent new-expressions) // ::= Dn # std::nullptr_t (i.e., decltype(nullptr)) // ::= u <source-name> # vendor extended type - - const char * TryParseBuiltinType() + + const char * + TryParseBuiltinType() { - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'v': return "void"; case 'w': return "wchar_t"; @@ -552,7 +604,7 @@ private: case 'z': return "..."; case 'D': { - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'd': return "decimal64"; case 'e': return "decimal128"; @@ -564,14 +616,14 @@ private: case 'c': return "decltype(auto)"; case 'n': return "std::nullptr_t"; default: - --read_ptr; + --m_read_ptr; } } } - --read_ptr; + --m_read_ptr; return nullptr; } - + // <operator-name> // ::= aa # && // ::= ad # & (unary) @@ -622,13 +674,14 @@ private: // ::= rS # >>= // ::= cv <type> # (cast) // ::= v <digit> <source-name> # vendor extended operator - - Operator TryParseOperator() + + Operator + TryParseOperator() { - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'a': - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'a': return { "&&", OperatorKind::Binary }; case 'd': return { "&", OperatorKind::Unary }; @@ -636,20 +689,20 @@ private: case 'N': return { "&=", OperatorKind::Binary }; case 'S': return { "=", OperatorKind::Binary }; } - --read_ptr; + --m_read_ptr; break; case 'c': - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'l': return { "()", OperatorKind::Other }; case 'm': return { ",", OperatorKind::Other }; case 'o': return { "~", OperatorKind::Unary }; case 'v': return { nullptr, OperatorKind::ConversionOperator }; } - --read_ptr; + --m_read_ptr; break; case 'd': - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'a': return { " delete[]", OperatorKind::Other }; case 'e': return { "*", OperatorKind::Unary }; @@ -657,34 +710,34 @@ private: case 'v': return { "/", OperatorKind::Binary }; case 'V': return { "/=", OperatorKind::Binary }; } - --read_ptr; + --m_read_ptr; break; case 'e': - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'o': return { "^", OperatorKind::Binary }; case 'O': return { "^=", OperatorKind::Binary }; case 'q': return { "==", OperatorKind::Binary }; } - --read_ptr; + --m_read_ptr; break; case 'g': - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'e': return { ">=", OperatorKind::Binary }; case 't': return { ">", OperatorKind::Binary }; } - --read_ptr; + --m_read_ptr; break; case 'i': - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'x': return { "[]", OperatorKind::Other }; } - --read_ptr; + --m_read_ptr; break; case 'l': - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'e': return { "<=", OperatorKind::Binary }; case 's': return { "<<", OperatorKind::Binary }; @@ -692,10 +745,10 @@ private: case 't': return { "<", OperatorKind::Binary }; // case 'i': return { "?", OperatorKind::Binary }; } - --read_ptr; + --m_read_ptr; break; case 'm': - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'i': return { "-", OperatorKind::Binary }; case 'I': return { "-=", OperatorKind::Binary }; @@ -703,10 +756,10 @@ private: case 'L': return { "*=", OperatorKind::Binary }; case 'm': return { "--", OperatorKind::Postfix }; } - --read_ptr; + --m_read_ptr; break; case 'n': - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'a': return { " new[]", OperatorKind::Other }; case 'e': return { "!=", OperatorKind::Binary }; @@ -714,19 +767,19 @@ private: case 't': return { "!", OperatorKind::Unary }; case 'w': return { " new", OperatorKind::Other }; } - --read_ptr; + --m_read_ptr; break; case 'o': - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'o': return { "||", OperatorKind::Binary }; case 'r': return { "|", OperatorKind::Binary }; case 'R': return { "|=", OperatorKind::Binary }; } - --read_ptr; + --m_read_ptr; break; case 'p': - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'm': return { "->*", OperatorKind::Binary }; case 's': return { "+", OperatorKind::Unary }; @@ -735,154 +788,161 @@ private: case 'p': return { "++", OperatorKind::Postfix }; case 't': return { "->", OperatorKind::Binary }; } - --read_ptr; + --m_read_ptr; break; case 'q': - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'u': return { "?", OperatorKind::Ternary }; } - --read_ptr; + --m_read_ptr; break; case 'r': - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'm': return { "%", OperatorKind::Binary }; case 'M': return { "%=", OperatorKind::Binary }; case 's': return { ">>", OperatorKind::Binary }; case 'S': return { ">=", OperatorKind::Binary }; } - --read_ptr; + --m_read_ptr; break; case 'v': - char digit = *read_ptr; + char digit = *m_read_ptr; if (digit >= '0' && digit <= '9') { - read_ptr++; + m_read_ptr++; return { nullptr, OperatorKind::Vendor }; } - --read_ptr; + --m_read_ptr; break; } - --read_ptr; + --m_read_ptr; return { nullptr, OperatorKind::NoMatch }; } - + // <CV-qualifiers> ::= [r] [V] [K] // <ref-qualifier> ::= R # & ref-qualifier // <ref-qualifier> ::= O # && ref-qualifier - - int TryParseQualifiers(bool allow_cv, bool allow_ro) + + int + TryParseQualifiers(bool allow_cv, bool allow_ro) { int qualifiers = QualifierNone; - char next = *read_ptr; + char next = *m_read_ptr; if (allow_cv) { if (next == 'r') // restrict { qualifiers |= QualifierRestrict; - next = *++read_ptr; + next = *++m_read_ptr; } if (next == 'V') // volatile { qualifiers |= QualifierVolatile; - next = *++read_ptr; + next = *++m_read_ptr; } if (next == 'K') // const { qualifiers |= QualifierConst; - next = *++read_ptr; + next = *++m_read_ptr; } } if (allow_ro) { if (next == 'R') { - ++read_ptr; + ++m_read_ptr; qualifiers |= QualifierReference; } else if (next =='O') { - ++read_ptr; + ++m_read_ptr; qualifiers |= QualifierRValueReference; } } return qualifiers; } - + // <discriminator> := _ <non-negative number> # when number < 10 // := __ <non-negative number> _ # when number >= 10 // extension := decimal-digit+ - - int TryParseDiscriminator() + + int + TryParseDiscriminator() { - const char * discriminator_start = read_ptr; - + const char *discriminator_start = m_read_ptr; + // Test the extension first, since it's what Clang uses int discriminator_value = TryParseNumber(); - if (discriminator_value != -1) return discriminator_value; - - char next = *read_ptr; + if (discriminator_value != -1) + return discriminator_value; + + char next = *m_read_ptr; if (next == '_') { - next = *++read_ptr; + next = *++m_read_ptr; if (next == '_') { - ++read_ptr; + ++m_read_ptr; discriminator_value = TryParseNumber(); - if (discriminator_value != -1 && *read_ptr++ != '_') + if (discriminator_value != -1 && *m_read_ptr++ != '_') { return discriminator_value; } } else if (next >= '0' && next <= '9') { - ++read_ptr; + ++m_read_ptr; return next - '0'; } } - + // Not a valid discriminator - read_ptr = discriminator_start; + m_read_ptr = discriminator_start; return -1; } - + //---------------------------------------------------- // Parse methods // - // Consume input starting from read_ptr and produce - // buffered output at write_ptr + // Consume input starting from m_read_ptr and produce + // buffered output at m_write_ptr // - // Failures return false and may leave read_ptr in an + // Failures return false and may leave m_read_ptr in an // indeterminate state //---------------------------------------------------- - - bool Parse(char character) + + bool + Parse(char character) { - if (*read_ptr++ == character) return true; + if (*m_read_ptr++ == character) + return true; #ifdef DEBUG_FAILURES printf("*** Expected '%c'\n", character); #endif return false; } - + // <number> ::= [n] <non-negative decimal integer> - - bool ParseNumber(bool allow_negative = false) + + bool + ParseNumber(bool allow_negative = false) { - if (allow_negative && *read_ptr == 'n') + if (allow_negative && *m_read_ptr == 'n') { Write('-'); - ++read_ptr; + ++m_read_ptr; } - const char * before_digits = read_ptr; + const char *before_digits = m_read_ptr; while (true) { - unsigned char digit = *read_ptr - '0'; - if (digit > 9) break; - ++read_ptr; + unsigned char digit = *m_read_ptr - '0'; + if (digit > 9) + break; + ++m_read_ptr; } - if (int digit_count = (int)(read_ptr - before_digits)) + if (int digit_count = (int)(m_read_ptr - before_digits)) { Write(before_digits, digit_count); return true; @@ -892,7 +952,7 @@ private: #endif return false; } - + // <substitution> ::= S <seq-id> _ // ::= S_ // <substitution> ::= Sa # ::std::allocator @@ -903,11 +963,12 @@ private: // <substitution> ::= Si # ::std::basic_istream<char, std::char_traits<char> > // <substitution> ::= So # ::std::basic_ostream<char, std::char_traits<char> > // <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> > - - bool ParseSubstitution() + + bool + ParseSubstitution() { - const char * substitution; - switch (*read_ptr) + const char *substitution; + switch (*m_read_ptr) { case 'a': substitution = "std::allocator"; break; case 'b': substitution = "std::basic_string"; break; @@ -919,25 +980,26 @@ private: // A failed attempt to parse a number will return -1 which turns out to be // perfect here as S_ is the first substitution, S0_ the next and so forth int substitution_index = TryParseBase36Number(); - if (*read_ptr++ != '_') + if (*m_read_ptr++ != '_') { #ifdef DEBUG_FAILURES printf("*** Expected terminal _ in substitution\n"); #endif return false; } - return RewriteSubstitution(substitution_index + 1); + return RewriteSubstitution (substitution_index + 1); } Write(substitution); - ++read_ptr; + ++m_read_ptr; return true; } - + // <function-type> ::= F [Y] <bare-function-type> [<ref-qualifier>] E // // <bare-function-type> ::= <signature type>+ # types are possible return type, then parameter types - - bool ParseFunctionType(int inner_qualifiers = QualifierNone) + + bool + ParseFunctionType (int inner_qualifiers = QualifierNone) { #ifdef DEBUG_FAILURES printf("*** Function types not supported\n"); @@ -947,70 +1009,77 @@ private: // int (*)() when used as the type for "name" becomes int (*name)(). // This makes substitution et al ... interesting. return false; - - if (*read_ptr == 'Y') ++read_ptr;; - + +#if 0 // TODO + if (*m_read_ptr == 'Y') + ++m_read_ptr; + int return_type_start_cookie = GetStartCookie(); - if (!ParseType()) return false; + if (!ParseType()) + return false; Write(' '); - + int insert_cookie = GetStartCookie(); Write('('); bool first_param = true; int qualifiers = QualifierNone; while (true) { - switch (*read_ptr) + switch (*m_read_ptr) { case 'E': - ++read_ptr; + ++m_read_ptr; Write(')'); break; case 'v': - ++read_ptr; + ++m_read_ptr; continue; case 'R': case 'O': - if (*(read_ptr + 1) == 'E') + if (*(m_read_ptr + 1) == 'E') { - qualifiers = TryParseQualifiers(false, true); + qualifiers = TryParseQualifiers (false, true); Parse('E'); break; } // fallthrough default: { - if (first_param) first_param = false; + if (first_param) + first_param = false; else WriteCommaSpace(); - - if (!ParseType()) return false; + + if (!ParseType()) + return false; continue; } } break; } - + if (qualifiers) { - WriteQualifiers(qualifiers); - EndSubstitution(return_type_start_cookie); + WriteQualifiers (qualifiers); + EndSubstitution (return_type_start_cookie); } - + if (inner_qualifiers) { int qualifier_start_cookie = GetStartCookie(); - Write('('); - WriteQualifiers(inner_qualifiers); - Write(')'); - ReorderRange(EndRange(qualifier_start_cookie), insert_cookie); + Write ('('); + WriteQualifiers (inner_qualifiers); + Write (')'); + ReorderRange (EndRange (qualifier_start_cookie), insert_cookie); } return true; +#endif // TODO } - + // <array-type> ::= A <positive dimension number> _ <element type> // ::= A [<dimension expression>] _ <element type> - - bool ParseArrayType(int qualifiers = QualifierNone) + + bool + ParseArrayType(int qualifiers = QualifierNone) { #ifdef DEBUG_FAILURES printf("*** Array type unsupported\n"); @@ -1021,26 +1090,32 @@ private: // //TODO: Chances are we don't do any better with references and pointers // that should be type (&) [] instead of type & [] - + return false; - - if (*read_ptr == '_') + +#if 0 // TODO + if (*m_read_ptr == '_') { - ++read_ptr; - if (!ParseType()) return false; - if (qualifiers) WriteQualifiers(qualifiers); + ++m_read_ptr; + if (!ParseType()) + return false; + if (qualifiers) + WriteQualifiers(qualifiers); WRITE(" []"); return true; } else { - const char * before_digits = read_ptr; + const char *before_digits = m_read_ptr; if (TryParseNumber() != -1) { - const char * after_digits = read_ptr; - if (!Parse('_')) return false; - if (!ParseType()) return false; - if (qualifiers) WriteQualifiers(qualifiers); + const char *after_digits = m_read_ptr; + if (!Parse('_')) + return false; + if (!ParseType()) + return false; + if (qualifiers) + WriteQualifiers(qualifiers); Write(' '); Write('['); Write(before_digits, after_digits - before_digits); @@ -1048,51 +1123,61 @@ private: else { int type_insertion_cookie = GetStartCookie(); - if (!ParseExpression()) return false; - if (!Parse('_')) return false; - + if (!ParseExpression()) + return false; + if (!Parse('_')) + return false; + int type_start_cookie = GetStartCookie(); - if (!ParseType()) return false; - if (qualifiers) WriteQualifiers(qualifiers); + if (!ParseType()) + return false; + if (qualifiers) + WriteQualifiers(qualifiers); Write(' '); Write('['); - ReorderRange(EndRange(type_start_cookie), type_insertion_cookie); + ReorderRange (EndRange (type_start_cookie), type_insertion_cookie); } Write(']'); return true; } +#endif // TODO } - + // <pointer-to-member-type> ::= M <class type> <member type> - + //TODO: Determine how to handle pointers to function members correctly, // currently not an issue because we don't have function types at all... - bool ParsePointerToMemberType() + bool + ParsePointerToMemberType() { int insertion_cookie = GetStartCookie(); Write(' '); - if (!ParseType()) return false; + if (!ParseType()) + return false; WRITE("::*"); - + int type_cookie = GetStartCookie(); - if (!ParseType()) return false; - ReorderRange(EndRange(type_cookie), insertion_cookie); + if (!ParseType()) + return false; + ReorderRange (EndRange (type_cookie), insertion_cookie); return true; } - + // <template-param> ::= T_ # first template parameter // ::= T <parameter-2 non-negative number> _ - - bool ParseTemplateParam() + + bool + ParseTemplateParam() { int count = TryParseNumber(); - if (!Parse('_')) return false; - + if (!Parse('_')) + return false; + // When no number is present we get -1, which is convenient since // T_ is the zeroth element T0_ is element 1, and so on - return RewriteTemplateArg(count + 1); + return RewriteTemplateArg (count + 1); } - + // <type> ::= <builtin-type> // ::= <function-type> // ::= <class-enum-type> @@ -1112,27 +1197,29 @@ private: // ::= U <source-name> <type> # vendor extended type qualifier // extension := U <objc-name> <objc-type> # objc-type<identifier> // extension := <vector-type> # <vector-type> starts with Dv - + // <objc-name> ::= <k0 number> objcproto <k1 number> <identifier> # k0 = 9 + <number of digits in k1> + k1 // <objc-type> := <source-name> # PU<11+>objcproto 11objc_object<source-name> 11objc_object -> id<source-name> - - bool ParseType() + + bool + ParseType() { #ifdef DEBUG_FAILURES - const char * failed_type = read_ptr; + const char *failed_type = m_read_ptr; #endif int type_start_cookie = GetStartCookie(); bool suppress_substitution = false; - - int qualifiers = TryParseQualifiers(true, false); - switch (*read_ptr) + + int qualifiers = TryParseQualifiers (true, false); + switch (*m_read_ptr) { case 'D': - ++read_ptr; - switch (*read_ptr++) + ++m_read_ptr; + switch (*m_read_ptr++) { case 'p': - if (!ParseType()) return false; + if (!ParseType()) + return false; break; case 'T': case 't': @@ -1145,44 +1232,52 @@ private: } break; case 'T': - ++read_ptr; - if (!ParseTemplateParam()) return false; + ++m_read_ptr; + if (!ParseTemplateParam()) + return false; break; case 'M': - ++read_ptr; - if (!ParsePointerToMemberType()) return false; + ++m_read_ptr; + if (!ParsePointerToMemberType()) + return false; break; case 'A': - ++read_ptr; - if (!ParseArrayType()) return false; + ++m_read_ptr; + if (!ParseArrayType()) + return false; break; case 'F': - ++read_ptr; - if (!ParseFunctionType()) return false; + ++m_read_ptr; + if (!ParseFunctionType()) + return false; break; case 'S': - if (*++read_ptr == 't') + if (*++m_read_ptr == 't') { - ++read_ptr; + ++m_read_ptr; WriteStdPrefix(); - if (!ParseName()) return false; + if (!ParseName()) + return false; } else { suppress_substitution = true; - if (!ParseSubstitution()) return false; + if (!ParseSubstitution()) + return false; } break; case 'P': { - switch (*++read_ptr) + switch (*++m_read_ptr) { case 'F': - ++read_ptr; - if (!ParseFunctionType(QualifierPointer)) return false; + ++m_read_ptr; + if (!ParseFunctionType(QualifierPointer)) + return false; break; default: - if (!ParseType()) return false; + if (!ParseType()) + return false; Write('*'); break; } @@ -1190,15 +1285,17 @@ private: } case 'R': { - ++read_ptr; - if (!ParseType()) return false; + ++m_read_ptr; + if (!ParseType()) + return false; Write('&'); break; } case 'O': { - ++read_ptr; - if (!ParseType()) return false; + ++m_read_ptr; + if (!ParseType()) + return false; Write('&'); Write('&'); break; @@ -1214,24 +1311,27 @@ private: case 'N': case 'Z': case 'L': - if (!ParseName()) return false; + if (!ParseName()) + return false; break; default: - if (const char * builtin = TryParseBuiltinType()) + if (const char *builtin = TryParseBuiltinType()) { Write(builtin); suppress_substitution = true; } else { - if (!ParseName()) return false; + if (!ParseName()) + return false; } break; } - + // Allow base substitutions to be suppressed, but always record // substitutions for the qualified variant - if (!suppress_substitution) EndSubstitution(type_start_cookie); + if (!suppress_substitution) + EndSubstitution(type_start_cookie); if (qualifiers) { WriteQualifiers(qualifiers, false); @@ -1239,40 +1339,43 @@ private: } return true; } - + // <unnamed-type-name> ::= Ut [ <nonnegative number> ] _ // ::= <closure-type-name> // // <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _ // // <lambda-sig> ::= <parameter type>+ # Parameter types or "v" if the lambda has no parameters - - bool ParseUnnamedTypeName(NameState & name_state) + + bool + ParseUnnamedTypeName(NameState & name_state) { - switch (*read_ptr++) + switch (*m_read_ptr++) { case 't': { int cookie = GetStartCookie(); WRITE("'unnamed"); - const char * before_digits = read_ptr; - if (TryParseNumber() != -1) Write(before_digits, - read_ptr - before_digits); - if (!Parse('_')) return false; + const char *before_digits = m_read_ptr; + if (TryParseNumber() != -1) Write (before_digits, + m_read_ptr - before_digits); + if (!Parse('_')) + return false; Write('\''); - name_state.last_name_range = EndRange(cookie); + name_state.last_name_range = EndRange (cookie); return true; } case 'b': { int cookie = GetStartCookie(); WRITE("'block"); - const char * before_digits = read_ptr; - if (TryParseNumber() != -1) Write(before_digits, - read_ptr - before_digits); - if (!Parse('_')) return false; + const char *before_digits = m_read_ptr; + if (TryParseNumber() != -1) Write (before_digits, + m_read_ptr - before_digits); + if (!Parse('_')) + return false; Write('\''); - name_state.last_name_range = EndRange(cookie); + name_state.last_name_range = EndRange (cookie); return true; } case 'l': @@ -1282,23 +1385,24 @@ private: return false; } #ifdef DEBUG_FAILURES - printf("*** Unknown unnamed type %.3s\n", read_ptr - 2); + printf("*** Unknown unnamed type %.3s\n", m_read_ptr - 2); #endif return false; } - + // <ctor-dtor-name> ::= C1 # complete object constructor // ::= C2 # base object constructor // ::= C3 # complete object allocating constructor - - bool ParseCtor(NameState & name_state) + + bool + ParseCtor(NameState & name_state) { - char next = *read_ptr; + char next = *m_read_ptr; if (next == '1' || next == '2' || next == '3' || next == '5') { - RewriteRange(name_state.last_name_range); + RewriteRange (name_state.last_name_range); name_state.has_no_return_type = true; - ++read_ptr; + ++m_read_ptr; return true; } #ifdef DEBUG_FAILURES @@ -1306,20 +1410,21 @@ private: #endif return false; } - + // <ctor-dtor-name> ::= D0 # deleting destructor // ::= D1 # complete object destructor // ::= D2 # base object destructor - - bool ParseDtor(NameState & name_state) + + bool + ParseDtor(NameState & name_state) { - char next = *read_ptr; + char next = *m_read_ptr; if (next == '0' || next == '1' || next == '2' || next == '5') { Write('~'); RewriteRange(name_state.last_name_range); name_state.has_no_return_type = true; - ++read_ptr; + ++m_read_ptr; return true; } #ifdef DEBUG_FAILURES @@ -1327,13 +1432,14 @@ private: #endif return false; } - + // See TryParseOperator() - - bool ParseOperatorName(NameState & name_state) + + bool + ParseOperatorName(NameState & name_state) { #ifdef DEBUG_FAILURES - const char * operator_ptr = read_ptr; + const char *operator_ptr = m_read_ptr; #endif Operator parsed_operator = TryParseOperator(); if (parsed_operator.name) @@ -1342,7 +1448,7 @@ private: Write(parsed_operator.name); return true; } - + // Handle special operators switch (parsed_operator.kind) { @@ -1361,10 +1467,11 @@ private: return false; } } - + // <source-name> ::= <positive length number> <identifier> - - bool ParseSourceName() + + bool + ParseSourceName() { int count = TryParseNumber(); if (count == -1) @@ -1374,43 +1481,45 @@ private: #endif return false; } - - const char * next_read_ptr = read_ptr + count; - if (next_read_ptr > read_end) + + const char *next_m_read_ptr = m_read_ptr + count; + if (next_m_read_ptr > m_read_end) { #ifdef DEBUG_FAILURES printf("*** Malformed source name, premature termination\n"); #endif return false; } - - if (count >= 10 && strncmp(read_ptr, "_GLOBAL__N", 10) == 0) WRITE("(anonymous namespace)"); - else Write(read_ptr, count); - - read_ptr = next_read_ptr; + + if (count >= 10 && strncmp(m_read_ptr, "_GLOBAL__N", 10) == 0) + WRITE("(anonymous namespace)"); + else Write(m_read_ptr, count); + + m_read_ptr = next_m_read_ptr; return true; } - + // <unqualified-name> ::= <operator-name> // ::= <ctor-dtor-name> // ::= <source-name> // ::= <unnamed-type-name> - - bool ParseUnqualifiedName(NameState & name_state) + + bool + ParseUnqualifiedName(NameState & name_state) { // Note that these are detected directly in ParseNestedName for // performance rather than switching on the same options twice - char next = *read_ptr; + char next = *m_read_ptr; switch (next) { case 'C': - ++read_ptr; + ++m_read_ptr; return ParseCtor(name_state); case 'D': - ++read_ptr; + ++m_read_ptr; return ParseDtor(name_state); case 'U': - ++read_ptr; + ++m_read_ptr; return ParseUnnamedTypeName(name_state); case '0': case '1': @@ -1424,7 +1533,8 @@ private: case '9': { int name_start_cookie = GetStartCookie(); - if (!ParseSourceName()) return false; + if (!ParseSourceName()) + return false; name_state.last_name_range = EndRange(name_start_cookie); return true; } @@ -1432,33 +1542,40 @@ private: return ParseOperatorName(name_state); }; } - + // <unscoped-name> ::= <unqualified-name> // ::= St <unqualified-name> # ::std:: // extension ::= StL<unqualified-name> - - bool ParseUnscopedName(NameState & name_state) + + bool + ParseUnscopedName(NameState & name_state) { - if (*read_ptr == 'S' && *(read_ptr + 1) == 't') + if (*m_read_ptr == 'S' && *(m_read_ptr + 1) == 't') { WriteStdPrefix(); - if (*(read_ptr += 2) == 'L') ++read_ptr; + if (*(m_read_ptr += 2) == 'L') + ++m_read_ptr; } return ParseUnqualifiedName(name_state); } - - bool ParseIntegerLiteral(const char * prefix, const char * suffix, + + bool + ParseIntegerLiteral(const char *prefix, const char *suffix, bool allow_negative) { - if (prefix) Write(prefix); - if (!ParseNumber(allow_negative)) return false; - if (suffix) Write(suffix); + if (prefix) + Write(prefix); + if (!ParseNumber(allow_negative)) + return false; + if (suffix) + Write(suffix); return Parse('E'); } - - bool ParseBooleanLiteral() + + bool + ParseBooleanLiteral() { - switch (*read_ptr++) + switch (*m_read_ptr++) { case '0': WRITE("false"); break; case '1': WRITE("true"); break; @@ -1470,17 +1587,18 @@ private: } return Parse('E'); } - + // <expr-primary> ::= L <type> <value number> E # integer literal // ::= L <type> <value float> E # floating literal // ::= L <string type> E # string literal // ::= L <nullptr type> E # nullptr literal (i.e., "LDnE") // ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000) // ::= L <mangled-name> E # external name - - bool ParseExpressionPrimary() + + bool + ParseExpressionPrimary() { - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'b': return ParseBooleanLiteral(); case 'x': return ParseIntegerLiteral(nullptr, "ll", true); @@ -1493,12 +1611,13 @@ private: case 'o': return ParseIntegerLiteral("(unsigned __int128)", nullptr, false); case '_': - if (*read_ptr++ == 'Z') + if (*m_read_ptr++ == 'Z') { - if (!ParseEncoding()) return false; + if (!ParseEncoding()) + return false; return Parse('E'); } - --read_ptr; + --m_read_ptr; // fallthrough case 'w': case 'c': @@ -1510,7 +1629,7 @@ private: case 'd': case 'e': #ifdef DEBUG_FAILURES - printf("*** Unsupported primary expression %.5s\n", read_ptr - 1); + printf("*** Unsupported primary expression %.5s\n", m_read_ptr - 1); #endif return false; case 'T': @@ -1521,49 +1640,55 @@ private: #endif return false; default: - --read_ptr; + --m_read_ptr; Write('('); - if (!ParseType()) return false; + if (!ParseType()) + return false; Write(')'); - if (!ParseNumber()) return false; + if (!ParseNumber()) + return false; return Parse('E'); } } - + // <unresolved-type> ::= <template-param> // ::= <decltype> // ::= <substitution> - - bool ParseUnresolvedType() + + bool + ParseUnresolvedType() { int type_start_cookie = GetStartCookie(); - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'T': - if (!ParseTemplateParam()) return false; + if (!ParseTemplateParam()) + return false; EndSubstitution(type_start_cookie); return true; case 'S': { - if (*read_ptr != 't') return ParseSubstitution(); - - ++read_ptr; + if (*m_read_ptr != 't') + return ParseSubstitution(); + + ++m_read_ptr; WriteStdPrefix(); NameState type_name = {}; - if (!ParseUnqualifiedName(type_name)) return false; + if (!ParseUnqualifiedName(type_name)) + return false; EndSubstitution(type_start_cookie); return true; - + } case 'D': default: #ifdef DEBUG_FAILURES - printf("*** Unsupported unqualified type: %3s\n", read_ptr - 1); + printf("*** Unsupported unqualified type: %3s\n", m_read_ptr - 1); #endif return false; } } - + // <base-unresolved-name> ::= <simple-id> # unresolved name // extension ::= <operator-name> # unresolved operator-function-id // extension ::= <operator-name> <template-args> # unresolved operator template-id @@ -1571,15 +1696,16 @@ private: // ::= on <operator-name> <template-args> # unresolved operator template-id // ::= dn <destructor-name> # destructor or pseudo-destructor; // # e.g. ~X or ~X<N-1> - - bool ParseBaseUnresolvedName() + + bool + ParseBaseUnresolvedName() { #ifdef DEBUG_FAILURES printf("*** Base unresolved name unsupported\n"); #endif return false; } - + // <unresolved-name> // extension ::= srN <unresolved-type> [<template-args>] <unresolved-qualifier-level>* E <base-unresolved-name> // ::= [gs] <base-unresolved-name> # x or (with "gs") ::x @@ -1589,22 +1715,25 @@ private: // extension ::= sr <unresolved-type> <template-args> <base-unresolved-name> // # T::N::x /decltype(p)::N::x // (ignored) ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name> - - bool ParseUnresolvedName() + + bool + ParseUnresolvedName() { #ifdef DEBUG_FAILURES printf("*** Unresolved names not supported\n"); #endif //TODO: grammar for all of this seems unclear... return false; - - if (*read_ptr == 'g' && *(read_ptr + 1) == 's') + +#if 0 // TODO + if (*m_read_ptr == 'g' && *(m_read_ptr + 1) == 's') { - read_ptr += 2; + m_read_ptr += 2; WriteNamespaceSeparator(); } +#endif // TODO } - + // <expression> ::= <unary operator-name> <expression> // ::= <binary operator-name> <expression> <expression> // ::= <ternary operator-name> <expression> <expression> <expression> @@ -1644,8 +1773,9 @@ private: // # freestanding dependent name (e.g., T::x), // # objectless nonstatic member reference // ::= <expr-primary> - - bool ParseExpression() + + bool + ParseExpression() { Operator expression_operator = TryParseOperator(); switch (expression_operator.kind) @@ -1653,17 +1783,21 @@ private: case OperatorKind::Unary: Write(expression_operator.name); Write('('); - if (!ParseExpression()) return false; + if (!ParseExpression()) + return false; Write(')'); return true; case OperatorKind::Binary: - if (!ParseExpression()) return false; + if (!ParseExpression()) + return false; Write(expression_operator.name); return ParseExpression(); case OperatorKind::Ternary: - if (!ParseExpression()) return false; + if (!ParseExpression()) + return false; Write('?'); - if (!ParseExpression()) return false; + if (!ParseExpression()) + return false; Write(':'); return ParseExpression(); case OperatorKind::NoMatch: @@ -1675,67 +1809,75 @@ private: #endif return false; } - - switch (*read_ptr++) + + switch (*m_read_ptr++) { case 'T': return ParseTemplateParam(); case 'L': return ParseExpressionPrimary(); case 's': - if (*read_ptr++ == 'r') return ParseUnresolvedName(); - --read_ptr; + if (*m_read_ptr++ == 'r') + return ParseUnresolvedName(); + --m_read_ptr; // fallthrough default: return ParseExpressionPrimary(); } } - + // <template-arg> ::= <type> # type or template // ::= X <expression> E # expression // ::= <expr-primary> # simple expressions // ::= J <template-arg>* E # argument pack // ::= LZ <encoding> E # extension - - bool ParseTemplateArg() + + bool + ParseTemplateArg() { - switch (*read_ptr) { + switch (*m_read_ptr) { case 'J': #ifdef DEBUG_FAILURES printf("*** Template argument packs unsupported\n"); #endif return false; case 'X': - ++read_ptr; - if (!ParseExpression()) return false; + ++m_read_ptr; + if (!ParseExpression()) + return false; return Parse('E'); case 'L': - ++read_ptr; + ++m_read_ptr; return ParseExpressionPrimary(); default: return ParseType(); } } - + // <template-args> ::= I <template-arg>* E // extension, the abi says <template-arg>+ - - bool ParseTemplateArgs(bool record_template_args = false) + + bool + ParseTemplateArgs(bool record_template_args = false) { - if (record_template_args) ResetTemplateArgs(); - + if (record_template_args) + ResetTemplateArgs(); + bool first_arg = true; - while (*read_ptr != 'E') + while (*m_read_ptr != 'E') { - if (first_arg) first_arg = false; + if (first_arg) + first_arg = false; else WriteCommaSpace(); - + int template_start_cookie = GetStartCookie(); - if (!ParseTemplateArg()) return false; - if (record_template_args) EndTemplateArg(template_start_cookie); + if (!ParseTemplateArg()) + return false; + if (record_template_args) + EndTemplateArg(template_start_cookie); } - ++read_ptr; + ++m_read_ptr; return true; } - + // <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E // ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E // @@ -1756,8 +1898,9 @@ private: // ::= <ctor-dtor-name> // ::= <source-name> // ::= <unnamed-type-name> - - bool ParseNestedName(NameState & name_state, bool parse_discriminator = false) + + bool + ParseNestedName(NameState & name_state, bool parse_discriminator = false) { int qualifiers = TryParseQualifiers(true, true); bool first_part = true; @@ -1765,30 +1908,33 @@ private: int name_start_cookie = GetStartCookie(); while (true) { - char next = *read_ptr; + char next = *m_read_ptr; if (next == 'E') { - ++read_ptr; + ++m_read_ptr; break; } - + // Record a substitution candidate for all prefixes, but not the full name - if (suppress_substitution) suppress_substitution = false; + if (suppress_substitution) + suppress_substitution = false; else EndSubstitution(name_start_cookie); - + if (next == 'I') { - ++read_ptr; + ++m_read_ptr; name_state.is_last_generic = true; WriteTemplateStart(); - if (!ParseTemplateArgs(name_state.parse_function_params)) return false; + if (!ParseTemplateArgs(name_state.parse_function_params)) + return false; WriteTemplateEnd(); continue; } - - if (first_part) first_part = false; + + if (first_part) + first_part = false; else WriteNamespaceSeparator(); - + name_state.is_last_generic = false; switch (next) { @@ -1804,34 +1950,39 @@ private: case '9': { int name_start_cookie = GetStartCookie(); - if (!ParseSourceName()) return false; + if (!ParseSourceName()) + return false; name_state.last_name_range = EndRange(name_start_cookie); continue; } case 'S': - if (*++read_ptr == 't') + if (*++m_read_ptr == 't') { WriteStdPrefix(); - ++read_ptr; - if (!ParseUnqualifiedName(name_state)) return false; + ++m_read_ptr; + if (!ParseUnqualifiedName(name_state)) + return false; } else { - if (!ParseSubstitution()) return false; + if (!ParseSubstitution()) + return false; suppress_substitution = true; } continue; case 'T': - ++read_ptr; - if (!ParseTemplateParam()) return false; + ++m_read_ptr; + if (!ParseTemplateParam()) + return false; continue; case 'C': - ++read_ptr; - if (!ParseCtor(name_state)) return false; + ++m_read_ptr; + if (!ParseCtor(name_state)) + return false; continue; case 'D': { - switch (*(read_ptr + 1)) + switch (*(m_read_ptr + 1)) { case 't': case 'T': @@ -1840,109 +1991,135 @@ private: #endif return false; } - ++read_ptr; - if (!ParseDtor(name_state)) return false; + ++m_read_ptr; + if (!ParseDtor(name_state)) + return false; continue; } case 'U': - ++read_ptr; - if (!ParseUnnamedTypeName(name_state)) return false; + ++m_read_ptr; + if (!ParseUnnamedTypeName(name_state)) + return false; continue; case 'L': - ++read_ptr; - if (!ParseUnqualifiedName(name_state)) return false; + ++m_read_ptr; + if (!ParseUnqualifiedName(name_state)) + return false; continue; default: - if (!ParseOperatorName(name_state)) return false; + if (!ParseOperatorName(name_state)) + return false; } } - - if (parse_discriminator) TryParseDiscriminator(); - if (name_state.parse_function_params && - !ParseFunctionArgs(name_state, name_start_cookie)) return false; - if (qualifiers) WriteQualifiers(qualifiers); + + if (parse_discriminator) + TryParseDiscriminator(); + if (name_state.parse_function_params + && !ParseFunctionArgs(name_state, name_start_cookie)) + { + return false; + } + if (qualifiers) + WriteQualifiers(qualifiers); return true; } - + // <local-name> := Z <function encoding> E <entity name> [<discriminator>] // := Z <function encoding> E s [<discriminator>] // := Z <function encoding> Ed [ <parameter number> ] _ <entity name> - - bool ParseLocalName(bool parse_function_params) + + bool + ParseLocalName(bool parse_function_params) { - if (!ParseEncoding()) return false; - if (!Parse('E')) return false; - - switch (*read_ptr) + if (!ParseEncoding()) + return false; + if (!Parse('E')) + return false; + + switch (*m_read_ptr) { case 's': + ++m_read_ptr; TryParseDiscriminator(); // Optional and ignored WRITE("::string literal"); break; case 'd': + ++m_read_ptr; TryParseNumber(); // Optional and ignored + if (!Parse('_')) + return false; WriteNamespaceSeparator(); - if (!ParseName()) return false; + if (!ParseName()) + return false; break; default: WriteNamespaceSeparator(); - if (!ParseName(parse_function_params, true)) return false; + if (!ParseName(parse_function_params, true)) + return false; TryParseDiscriminator(); // Optional and ignored } return true; } - + // <name> ::= <nested-name> // ::= <local-name> // ::= <unscoped-template-name> <template-args> // ::= <unscoped-name> - + // <unscoped-template-name> ::= <unscoped-name> // ::= <substitution> - - bool ParseName(bool parse_function_params = false, + + bool + ParseName(bool parse_function_params = false, bool parse_discriminator = false) { - NameState name_state = { parse_function_params }; + NameState name_state = { parse_function_params, false, false, {0, 0}}; int name_start_cookie = GetStartCookie(); - - switch (*read_ptr) + + switch (*m_read_ptr) { case 'N': - ++read_ptr; + ++m_read_ptr; return ParseNestedName(name_state, parse_discriminator); case 'Z': { - ++read_ptr; - if (!ParseLocalName(parse_function_params)) return false; + ++m_read_ptr; + if (!ParseLocalName(parse_function_params)) + return false; break; } case 'L': - ++read_ptr; + ++m_read_ptr; // fallthrough default: { - if (!ParseUnscopedName(name_state)) return false; - - if (*read_ptr == 'I') + if (!ParseUnscopedName(name_state)) + return false; + + if (*m_read_ptr == 'I') { EndSubstitution(name_start_cookie); - - ++read_ptr; + + ++m_read_ptr; name_state.is_last_generic = true; WriteTemplateStart(); - if (!ParseTemplateArgs(parse_function_params)) return false; + if (!ParseTemplateArgs(parse_function_params)) + return false; WriteTemplateEnd(); } break; } } - if (parse_discriminator) TryParseDiscriminator(); + if (parse_discriminator) + TryParseDiscriminator(); if (parse_function_params && - !ParseFunctionArgs(name_state, name_start_cookie)) return false; + !ParseFunctionArgs(name_state, name_start_cookie)) + { + return false; + } return true; } - + // <call-offset> ::= h <nv-offset> _ // ::= v <v-offset> _ // @@ -1951,20 +2128,27 @@ private: // // <v-offset> ::= <offset number> _ <virtual offset number> // # virtual base override, with vcall offset - - bool ParseCallOffset() + + bool + ParseCallOffset() { - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'h': - if (*read_ptr == 'n') ++read_ptr; - if (TryParseNumber() == -1 || *read_ptr++ != '_') break; + if (*m_read_ptr == 'n') + ++m_read_ptr; + if (TryParseNumber() == -1 || *m_read_ptr++ != '_') + break; return true; case 'v': - if (*read_ptr == 'n') ++read_ptr; - if (TryParseNumber() == -1 || *read_ptr++ != '_') break; - if (*read_ptr == 'n') ++read_ptr; - if (TryParseNumber() == -1 || *read_ptr++ != '_') break; + if (*m_read_ptr == 'n') + ++m_read_ptr; + if (TryParseNumber() == -1 || *m_read_ptr++ != '_') + break; + if (*m_read_ptr == 'n') + ++m_read_ptr; + if (TryParseNumber() == -1 || *m_read_ptr++ != '_') + break; return true; } #ifdef DEBUG_FAILURES @@ -1972,7 +2156,7 @@ private: #endif return false; } - + // <special-name> ::= TV <type> # virtual table // ::= TT <type> # VTT structure (construction vtable index) // ::= TI <type> # typeinfo structure @@ -1984,10 +2168,11 @@ private: // ::= T <call-offset> <base encoding> // # base is the nominal target function of thunk // extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first - - bool ParseSpecialNameT() + + bool + ParseSpecialNameT() { - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'V': WRITE("vtable for "); @@ -2004,11 +2189,11 @@ private: case 'c': case 'C': #ifdef DEBUG_FAILURES - printf("*** Unsupported thunk or construction vtable name: %.3s\n", read_ptr - 1); + printf("*** Unsupported thunk or construction vtable name: %.3s\n", m_read_ptr - 1); #endif return false; default: - if (*--read_ptr == 'v') + if (*--m_read_ptr == 'v') { WRITE("virtual thunk to "); } @@ -2016,26 +2201,30 @@ private: { WRITE("non-virtual thunk to "); } - if (!ParseCallOffset()) return false; + if (!ParseCallOffset()) + return false; return ParseEncoding(); } } - + // <special-name> ::= GV <object name> # Guard variable for one-time initialization // # No <type> // extension ::= GR <object name> # reference temporary for object - - bool ParseSpecialNameG() + + bool + ParseSpecialNameG() { - switch (*read_ptr++) + switch (*m_read_ptr++) { case 'V': WRITE("guard variable for "); - if (!ParseName(true)) return false; + if (!ParseName(true)) + return false; break; case 'R': WRITE("reference temporary for "); - if (!ParseName(true)) return false; + if (!ParseName(true)) + return false; break; default: #ifdef DEBUG_FAILURES @@ -2045,58 +2234,64 @@ private: } return true; } - + // <bare-function-type> ::= <signature type>+ # types are possible return type, then parameter types - - bool ParseFunctionArgs(NameState & name_state, int return_insert_cookie) + + bool + ParseFunctionArgs(NameState & name_state, int return_insert_cookie) { - char next = *read_ptr; - if (next == 'E' || next == '\0' || next == '.') return true; - + char next = *m_read_ptr; + if (next == 'E' || next == '\0' || next == '.') + return true; + // Clang has a bad habit of making unique manglings by just sticking numbers on the end of a symbol, // which is ambiguous with malformed source name manglings - const char * before_clang_uniquing_test = read_ptr; + const char *before_clang_uniquing_test = m_read_ptr; if (TryParseNumber()) { - if (*read_ptr == '\0') return true; - read_ptr = before_clang_uniquing_test; + if (*m_read_ptr == '\0') + return true; + m_read_ptr = before_clang_uniquing_test; } - + if (name_state.is_last_generic && !name_state.has_no_return_type) { int return_type_start_cookie = GetStartCookie(); - if (!ParseType()) return false; + if (!ParseType()) + return false; Write(' '); ReorderRange(EndRange(return_type_start_cookie), return_insert_cookie); } - + Write('('); bool first_param = true; while (true) { - switch (*read_ptr) + switch (*m_read_ptr) { case '\0': case 'E': case '.': break; case 'v': - ++read_ptr; + ++m_read_ptr; continue; case '_': // Not a formal part of the mangling specification, but clang emits suffixes starting with _block_invoke - if (strncmp(read_ptr, "_block_invoke", 13) == 0) + if (strncmp(m_read_ptr, "_block_invoke", 13) == 0) { - read_ptr += strlen(read_ptr); + m_read_ptr += strlen(m_read_ptr); break; } // fallthrough default: - if (first_param) first_param = false; + if (first_param) + first_param = false; else WriteCommaSpace(); - - if (!ParseType()) return false; + + if (!ParseType()) + return false; continue; } break; @@ -2104,53 +2299,60 @@ private: Write(')'); return true; } - + // <encoding> ::= <function name> <bare-function-type> // ::= <data name> // ::= <special-name> - - bool ParseEncoding() + + bool + ParseEncoding() { - switch (*read_ptr) + switch (*m_read_ptr) { case 'T': - ++read_ptr; - if (!ParseSpecialNameT()) return false; + ++m_read_ptr; + if (!ParseSpecialNameT()) + return false; break; case 'G': - ++read_ptr; - if (!ParseSpecialNameG()) return false; + ++m_read_ptr; + if (!ParseSpecialNameG()) + return false; break; default: - if (!ParseName(true)) return false; + if (!ParseName(true)) + return false; break; } return true; } - - bool ParseMangling(const char * mangled_name, long mangled_name_length = 0) - { - if (!mangled_name_length) mangled_name_length = strlen(mangled_name); - read_end = mangled_name + mangled_name_length; - read_ptr = mangled_name; - write_ptr = buffer; - next_substitute_index = 0; - next_template_arg_index = rewrite_ranges_size - 1; - - if (*read_ptr++ != '_' || *read_ptr++ != 'Z') + + bool + ParseMangling(const char *mangled_name, long mangled_name_length = 0) + { + if (!mangled_name_length) + mangled_name_length = strlen(mangled_name); + m_read_end = mangled_name + mangled_name_length; + m_read_ptr = mangled_name; + m_write_ptr = m_buffer; + m_next_substitute_index = 0; + m_next_template_arg_index = m_rewrite_ranges_size - 1; + + if (*m_read_ptr++ != '_' || *m_read_ptr++ != 'Z') { #ifdef DEBUG_FAILURES printf("*** Missing _Z prefix\n"); #endif return false; } - if (!ParseEncoding()) return false; - switch (*read_ptr) + if (!ParseEncoding()) + return false; + switch (*m_read_ptr) { case '.': Write(' '); Write('('); - Write(read_ptr, read_end - read_ptr); + Write(m_read_ptr, m_read_end - m_read_ptr); Write(')'); case '\0': return true; @@ -2161,25 +2363,25 @@ private: return false; } } - + private: - + // External scratch storage used during demanglings - - char * buffer; - const char * buffer_end; - BufferRange * rewrite_ranges; - int rewrite_ranges_size; - bool owns_buffer; - bool owns_rewrite_ranges; - + + char *m_buffer; + const char *m_buffer_end; + BufferRange *m_rewrite_ranges; + int m_rewrite_ranges_size; + bool m_owns_buffer; + bool m_owns_m_rewrite_ranges; + // Internal state used during demangling - - const char * read_ptr; - const char * read_end; - char * write_ptr; - int next_template_arg_index; - int next_substitute_index; + + const char *m_read_ptr; + const char *m_read_end; + char *m_write_ptr; + int m_next_template_arg_index; + int m_next_substitute_index; }; } // Anonymous namespace @@ -2187,17 +2389,19 @@ private: // Public entry points referenced from Mangled.cpp namespace lldb_private { - char * FastDemangle(const char* mangled_name) + char * + FastDemangle(const char *mangled_name) { char buffer[16384]; - SymbolDemangler demangler(buffer, sizeof(buffer)); + SymbolDemangler demangler(buffer, sizeof (buffer)); return demangler.GetDemangledCopy(mangled_name); } - char * FastDemangle(const char* mangled_name, long mangled_name_length) + char * + FastDemangle(const char *mangled_name, long mangled_name_length) { char buffer[16384]; - SymbolDemangler demangler(buffer, sizeof(buffer)); + SymbolDemangler demangler(buffer, sizeof (buffer)); return demangler.GetDemangledCopy(mangled_name, mangled_name_length); } } // lldb_private namespace diff --git a/source/Core/FileSpecList.cpp b/source/Core/FileSpecList.cpp index 0cec8faa6bc4..4b1c991c6be4 100644 --- a/source/Core/FileSpecList.cpp +++ b/source/Core/FileSpecList.cpp @@ -107,7 +107,7 @@ FileSpecList::Dump(Stream *s, const char *separator_cstr) const // it is found, else UINT32_MAX is returned. //------------------------------------------------------------------ size_t -FileSpecList::FindFileIndex (size_t start_idx, const FileSpec &file_spec, bool full) const +FileSpecList::FindFileIndex (size_t start_idx, const FileSpec &file_spec, bool full, bool remove_dots) const { const size_t num_files = m_files.size(); @@ -124,7 +124,7 @@ FileSpecList::FindFileIndex (size_t start_idx, const FileSpec &file_spec, bool f } else { - if (FileSpec::Equal (m_files[idx], file_spec, full)) + if (FileSpec::Equal (m_files[idx], file_spec, full, remove_dots)) return idx; } } diff --git a/source/Core/IOHandler.cpp b/source/Core/IOHandler.cpp index add3ad8c5ba3..21ba965ba212 100644 --- a/source/Core/IOHandler.cpp +++ b/source/Core/IOHandler.cpp @@ -19,7 +19,9 @@ #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/ValueObjectRegister.h" +#ifndef LLDB_DISABLE_LIBEDIT #include "lldb/Host/Editline.h" +#endif #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Symbol/Block.h" @@ -36,8 +38,9 @@ using namespace lldb; using namespace lldb_private; -IOHandler::IOHandler (Debugger &debugger) : +IOHandler::IOHandler (Debugger &debugger, IOHandler::Type type) : IOHandler (debugger, + type, StreamFileSP(), // Adopt STDIN from top input reader StreamFileSP(), // Adopt STDOUT from top input reader StreamFileSP(), // Adopt STDERR from top input reader @@ -47,6 +50,7 @@ IOHandler::IOHandler (Debugger &debugger) : IOHandler::IOHandler (Debugger &debugger, + IOHandler::Type type, const lldb::StreamFileSP &input_sp, const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp, @@ -55,7 +59,9 @@ IOHandler::IOHandler (Debugger &debugger, m_input_sp (input_sp), m_output_sp (output_sp), m_error_sp (error_sp), + m_popped (false), m_flags (flags), + m_type (type), m_user_data (NULL), m_done (false), m_active (false) @@ -151,13 +157,28 @@ IOHandler::GetIsRealTerminal () return GetInputStreamFile()->GetFile().GetIsRealTerminal(); } +void +IOHandler::SetPopped (bool b) +{ + m_popped.SetValue(b, eBroadcastOnChange); +} + +void +IOHandler::WaitForPop () +{ + m_popped.WaitForValueEqualTo(true); +} + IOHandlerConfirm::IOHandlerConfirm (Debugger &debugger, const char *prompt, bool default_response) : IOHandlerEditline(debugger, + IOHandler::Type::Confirm, NULL, // NULL editline_name means no history loaded/saved - NULL, + NULL, // No prompt + NULL, // No continuation prompt false, // Multi-line + false, // Don't colorize the prompt (i.e. the confirm message.) 0, *this), m_default_response (default_response), @@ -310,83 +331,121 @@ IOHandlerDelegate::IOHandlerComplete (IOHandler &io_handler, IOHandlerEditline::IOHandlerEditline (Debugger &debugger, + IOHandler::Type type, const char *editline_name, // Used for saving history files const char *prompt, + const char *continuation_prompt, bool multi_line, + bool color_prompts, uint32_t line_number_start, IOHandlerDelegate &delegate) : IOHandlerEditline(debugger, + type, StreamFileSP(), // Inherit input from top input reader StreamFileSP(), // Inherit output from top input reader StreamFileSP(), // Inherit error from top input reader 0, // Flags editline_name, // Used for saving history files prompt, + continuation_prompt, multi_line, + color_prompts, line_number_start, delegate) { } IOHandlerEditline::IOHandlerEditline (Debugger &debugger, + IOHandler::Type type, const lldb::StreamFileSP &input_sp, const lldb::StreamFileSP &output_sp, const lldb::StreamFileSP &error_sp, uint32_t flags, const char *editline_name, // Used for saving history files const char *prompt, + const char *continuation_prompt, bool multi_line, + bool color_prompts, uint32_t line_number_start, IOHandlerDelegate &delegate) : - IOHandler (debugger, input_sp, output_sp, error_sp, flags), + IOHandler (debugger, type, input_sp, output_sp, error_sp, flags), +#ifndef LLDB_DISABLE_LIBEDIT m_editline_ap (), +#endif m_delegate (delegate), m_prompt (), + m_continuation_prompt(), + m_current_lines_ptr (NULL), m_base_line_number (line_number_start), - m_multi_line (multi_line) + m_curr_line_idx (UINT32_MAX), + m_multi_line (multi_line), + m_color_prompts (color_prompts), + m_interrupt_exits (true) { SetPrompt(prompt); +#ifndef LLDB_DISABLE_LIBEDIT bool use_editline = false; -#ifndef _MSC_VER use_editline = m_input_sp->GetFile().GetIsRealTerminal(); -#else - // Editline is causing issues on Windows, so use the fallback. - use_editline = false; -#endif if (use_editline) { m_editline_ap.reset(new Editline (editline_name, - prompt ? prompt : "", - multi_line, GetInputFILE (), GetOutputFILE (), - GetErrorFILE ())); - if (m_base_line_number > 0) - m_editline_ap->ShowLineNumbers(true, m_base_line_number); - m_editline_ap->SetLineCompleteCallback (LineCompletedCallback, this); + GetErrorFILE (), + m_color_prompts)); + m_editline_ap->SetIsInputCompleteCallback (IsInputCompleteCallback, this); m_editline_ap->SetAutoCompleteCallback (AutoCompleteCallback, this); + // See if the delegate supports fixing indentation + const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters(); + if (indent_chars) + { + // The delegate does support indentation, hook it up so when any indentation + // character is typed, the delegate gets a chance to fix it + m_editline_ap->SetFixIndentationCallback (FixIndentationCallback, this, indent_chars); + } } - +#endif + SetBaseLineNumber (m_base_line_number); + SetPrompt(prompt ? prompt : ""); + SetContinuationPrompt(continuation_prompt); } IOHandlerEditline::~IOHandlerEditline () { +#ifndef LLDB_DISABLE_LIBEDIT m_editline_ap.reset(); +#endif +} + +void +IOHandlerEditline::Activate () +{ + IOHandler::Activate(); + m_delegate.IOHandlerActivated(*this); +} + +void +IOHandlerEditline::Deactivate () +{ + IOHandler::Deactivate(); + m_delegate.IOHandlerDeactivated(*this); } bool IOHandlerEditline::GetLine (std::string &line, bool &interrupted) { +#ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) { - return m_editline_ap->GetLine(line, interrupted).Success(); + return m_editline_ap->GetLine (line, interrupted); } else { +#endif line.clear(); FILE *in = GetInputFILE(); @@ -394,7 +453,14 @@ IOHandlerEditline::GetLine (std::string &line, bool &interrupted) { if (GetIsInteractive()) { - const char *prompt = GetPrompt(); + const char *prompt = NULL; + + if (m_multi_line && m_curr_line_idx > 0) + prompt = GetContinuationPrompt(); + + if (prompt == NULL) + prompt = GetPrompt(); + if (prompt && prompt[0]) { FILE *out = GetOutputFILE(); @@ -452,19 +518,30 @@ IOHandlerEditline::GetLine (std::string &line, bool &interrupted) SetIsDone(true); } return false; +#ifndef LLDB_DISABLE_LIBEDIT } +#endif } -LineStatus -IOHandlerEditline::LineCompletedCallback (Editline *editline, +#ifndef LLDB_DISABLE_LIBEDIT +bool +IOHandlerEditline::IsInputCompleteCallback (Editline *editline, StringList &lines, - uint32_t line_idx, - Error &error, void *baton) { IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton; - return editline_reader->m_delegate.IOHandlerLinesUpdated(*editline_reader, lines, line_idx, error); + return editline_reader->m_delegate.IOHandlerIsInputComplete(*editline_reader, lines); +} + +int +IOHandlerEditline::FixIndentationCallback (Editline *editline, + const StringList &lines, + int cursor_position, + void *baton) +{ + IOHandlerEditline *editline_reader = (IOHandlerEditline *) baton; + return editline_reader->m_delegate.IOHandlerFixIndentation(*editline_reader, lines, cursor_position); } int @@ -487,14 +564,24 @@ IOHandlerEditline::AutoCompleteCallback (const char *current_line, matches); return 0; } +#endif const char * IOHandlerEditline::GetPrompt () { +#ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) + { return m_editline_ap->GetPrompt (); - else if (m_prompt.empty()) - return NULL; + } + else + { +#endif + if (m_prompt.empty()) + return NULL; +#ifndef LLDB_DISABLE_LIBEDIT + } +#endif return m_prompt.c_str(); } @@ -505,34 +592,71 @@ IOHandlerEditline::SetPrompt (const char *p) m_prompt = p; else m_prompt.clear(); +#ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) m_editline_ap->SetPrompt (m_prompt.empty() ? NULL : m_prompt.c_str()); +#endif return true; } +const char * +IOHandlerEditline::GetContinuationPrompt () +{ + if (m_continuation_prompt.empty()) + return NULL; + return m_continuation_prompt.c_str(); +} + + +void +IOHandlerEditline::SetContinuationPrompt (const char *p) +{ + if (p && p[0]) + m_continuation_prompt = p; + else + m_continuation_prompt.clear(); + +#ifndef LLDB_DISABLE_LIBEDIT + if (m_editline_ap) + m_editline_ap->SetContinuationPrompt (m_continuation_prompt.empty() ? NULL : m_continuation_prompt.c_str()); +#endif +} + + void IOHandlerEditline::SetBaseLineNumber (uint32_t line) { m_base_line_number = line; +} + +uint32_t +IOHandlerEditline::GetCurrentLineIndex () const +{ +#ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) - m_editline_ap->ShowLineNumbers (true, line); - + return m_editline_ap->GetCurrentLine(); +#endif + return m_curr_line_idx; } + bool IOHandlerEditline::GetLines (StringList &lines, bool &interrupted) { + m_current_lines_ptr = &lines; + bool success = false; +#ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) { - std::string end_token; - success = m_editline_ap->GetLines(end_token, lines, interrupted).Success(); + return m_editline_ap->GetLines (m_base_line_number, lines, interrupted); } else { - LineStatus lines_status = LineStatus::Success; +#endif + bool done = false; Error error; - while (lines_status == LineStatus::Success) + while (!done) { // Show line numbers if we are asked to std::string line; @@ -543,31 +667,23 @@ IOHandlerEditline::GetLines (StringList &lines, bool &interrupted) ::fprintf(out, "%u%s", m_base_line_number + (uint32_t)lines.GetSize(), GetPrompt() == NULL ? " " : ""); } + m_curr_line_idx = lines.GetSize(); + bool interrupted = false; - if (GetLine(line, interrupted)) + if (GetLine(line, interrupted) && !interrupted) { - if (interrupted) - { - lines_status = LineStatus::Done; - } - else - { - lines.AppendString(line); - lines_status = m_delegate.IOHandlerLinesUpdated(*this, lines, lines.GetSize() - 1, error); - } + lines.AppendString(line); + done = m_delegate.IOHandlerIsInputComplete(*this, lines); } else { - lines_status = LineStatus::Done; + done = true; } } - - // Call the IOHandlerLinesUpdated function with UINT32_MAX as the line - // number to indicate all lines are complete - m_delegate.IOHandlerLinesUpdated(*this, lines, UINT32_MAX, error); - success = lines.GetSize() > 0; +#ifndef LLDB_DISABLE_LIBEDIT } +#endif return success; } @@ -588,12 +704,14 @@ IOHandlerEditline::Run () { if (interrupted) { - m_done = true; + m_done = m_interrupt_exits; + m_delegate.IOHandlerInputInterrupted (*this, line); + } else { line = lines.CopyList(); - m_delegate.IOHandlerInputComplete(*this, line); + m_delegate.IOHandlerInputComplete (*this, line); } } else @@ -605,8 +723,10 @@ IOHandlerEditline::Run () { if (GetLine(line, interrupted)) { - if (!interrupted) - m_delegate.IOHandlerInputComplete(*this, line); + if (interrupted) + m_delegate.IOHandlerInputInterrupted (*this, line); + else + m_delegate.IOHandlerInputComplete (*this, line); } else { @@ -619,20 +739,24 @@ IOHandlerEditline::Run () void IOHandlerEditline::Hide () { +#ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) m_editline_ap->Hide(); +#endif } void IOHandlerEditline::Refresh () { +#ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) { m_editline_ap->Refresh(); } else { +#endif const char *prompt = GetPrompt(); if (prompt && prompt[0]) { @@ -643,14 +767,18 @@ IOHandlerEditline::Refresh () ::fflush(out); } } +#ifndef LLDB_DISABLE_LIBEDIT } +#endif } void IOHandlerEditline::Cancel () { +#ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) m_editline_ap->Interrupt (); +#endif } bool @@ -660,16 +788,20 @@ IOHandlerEditline::Interrupt () if (m_delegate.IOHandlerInterrupt(*this)) return true; +#ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) return m_editline_ap->Interrupt(); +#endif return false; } void IOHandlerEditline::GotEOF() { +#ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) m_editline_ap->Interrupt(); +#endif } // we may want curses to be disabled for some builds @@ -5333,7 +5465,7 @@ protected: DisplayOptions ValueObjectListDelegate::g_options = { true }; IOHandlerCursesGUI::IOHandlerCursesGUI (Debugger &debugger) : - IOHandler (debugger) + IOHandler (debugger, IOHandler::Type::Curses) { } diff --git a/source/Core/Log.cpp b/source/Core/Log.cpp index 3adca6e5385c..d205d363b764 100644 --- a/source/Core/Log.cpp +++ b/source/Core/Log.cpp @@ -26,9 +26,12 @@ #include "lldb/Core/StreamFile.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/Host.h" -#include "lldb/Host/TimeValue.h" #include "lldb/Host/Mutex.h" +#include "lldb/Host/ThisThread.h" +#include "lldb/Host/TimeValue.h" #include "lldb/Interpreter/Args.h" + +#include "llvm/ADT/SmallString.h" using namespace lldb; using namespace lldb_private; @@ -113,7 +116,8 @@ Log::PrintfWithFlagsVarArg (uint32_t flags, const char *format, va_list args) // Add the thread name if requested if (m_options.Test (LLDB_LOG_OPTION_PREPEND_THREAD_NAME)) { - std::string thread_name (Host::GetThreadName (getpid(), Host::GetCurrentThreadID())); + llvm::SmallString<32> thread_name; + ThisThread::GetName(thread_name); if (!thread_name.empty()) header.Printf ("%s ", thread_name.c_str()); } diff --git a/source/Core/Mangled.cpp b/source/Core/Mangled.cpp index c4fa10fedd86..c0ab66cd2880 100644 --- a/source/Core/Mangled.cpp +++ b/source/Core/Mangled.cpp @@ -4986,10 +4986,12 @@ __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status) #include "lldb/Core/RegularExpression.h" #include "lldb/Core/Stream.h" #include "lldb/Core/Timer.h" +#include "lldb/Target/CPPLanguageRuntime.h" #include <ctype.h> #include <string.h> #include <stdlib.h> + using namespace lldb_private; static inline bool @@ -5000,6 +5002,57 @@ cstring_is_mangled (const char *s) return false; } +static const ConstString & +get_demangled_name_without_arguments (const Mangled *obj) +{ + // This pair is <mangled name, demangled name without function arguments> + static std::pair<ConstString, ConstString> g_most_recent_mangled_to_name_sans_args; + + // Need to have the mangled & demangled names we're currently examining as statics + // so we can return a const ref to them at the end of the func if we don't have + // anything better. + static ConstString g_last_mangled; + static ConstString g_last_demangled; + + ConstString mangled = obj->GetMangledName (); + ConstString demangled = obj->GetDemangledName (); + + if (mangled && g_most_recent_mangled_to_name_sans_args.first == mangled) + { + return g_most_recent_mangled_to_name_sans_args.second; + } + + g_last_demangled = demangled; + g_last_mangled = mangled; + + const char *mangled_name_cstr = mangled.GetCString(); + + if (demangled && mangled_name_cstr && mangled_name_cstr[0]) + { + if (mangled_name_cstr[0] == '_' && mangled_name_cstr[1] == 'Z' && + (mangled_name_cstr[2] != 'T' && // avoid virtual table, VTT structure, typeinfo structure, and typeinfo mangled_name + mangled_name_cstr[2] != 'G' && // avoid guard variables + mangled_name_cstr[2] != 'Z')) // named local entities (if we eventually handle eSymbolTypeData, we will want this back) + { + CPPLanguageRuntime::MethodName cxx_method (demangled); + if (!cxx_method.GetBasename().empty() && !cxx_method.GetContext().empty()) + { + std::string shortname = cxx_method.GetContext().str(); + shortname += "::"; + shortname += cxx_method.GetBasename().str(); + ConstString result(shortname.c_str()); + g_most_recent_mangled_to_name_sans_args.first = mangled; + g_most_recent_mangled_to_name_sans_args.second = result; + return g_most_recent_mangled_to_name_sans_args.second; + } + } + } + + if (demangled) + return g_last_demangled; + return g_last_mangled; +} + #pragma mark Mangled //---------------------------------------------------------------------- // Default constructor @@ -5215,6 +5268,14 @@ Mangled::NameMatches (const RegularExpression& regex) const const ConstString& Mangled::GetName (Mangled::NamePreference preference) const { + if (preference == ePreferDemangledWithoutArguments) + { + // Call the accessor to make sure we get a demangled name in case + // it hasn't been demangled yet... + GetDemangledName(); + + return get_demangled_name_without_arguments (this); + } if (preference == ePreferDemangled) { // Call the accessor to make sure we get a demangled name in case diff --git a/source/Core/Module.cpp b/source/Core/Module.cpp index 6f16ada49824..900eea2e0419 100644 --- a/source/Core/Module.cpp +++ b/source/Core/Module.cpp @@ -1309,6 +1309,10 @@ Module::GetObjectFile() // unknown. m_objfile_sp->GetArchitecture (m_arch); } + else + { + ReportError ("failed to load objfile for %s", GetFileSpec().GetPath().c_str()); + } } } return m_objfile_sp.get(); @@ -1706,8 +1710,9 @@ Module::PrepareForFunctionNameLookup (const ConstString &name, const char *name_cstr = name.GetCString(); lookup_name_type_mask = eFunctionNameTypeNone; match_name_after_lookup = false; - const char *base_name_start = NULL; - const char *base_name_end = NULL; + + llvm::StringRef basename; + llvm::StringRef context; if (name_type_mask & eFunctionNameTypeAuto) { @@ -1721,16 +1726,16 @@ Module::PrepareForFunctionNameLookup (const ConstString &name, lookup_name_type_mask |= eFunctionNameTypeSelector; CPPLanguageRuntime::MethodName cpp_method (name); - llvm::StringRef basename (cpp_method.GetBasename()); + basename = cpp_method.GetBasename(); if (basename.empty()) { - if (CPPLanguageRuntime::StripNamespacesFromVariableName (name_cstr, base_name_start, base_name_end)) + if (CPPLanguageRuntime::ExtractContextAndIdentifier (name_cstr, context, basename)) lookup_name_type_mask |= (eFunctionNameTypeMethod | eFunctionNameTypeBase); + else + lookup_name_type_mask = eFunctionNameTypeFull; } else { - base_name_start = basename.data(); - base_name_end = base_name_start + basename.size(); lookup_name_type_mask |= (eFunctionNameTypeMethod | eFunctionNameTypeBase); } } @@ -1745,9 +1750,7 @@ Module::PrepareForFunctionNameLookup (const ConstString &name, CPPLanguageRuntime::MethodName cpp_method (name); if (cpp_method.IsValid()) { - llvm::StringRef basename (cpp_method.GetBasename()); - base_name_start = basename.data(); - base_name_end = base_name_start + basename.size(); + basename = cpp_method.GetBasename(); if (!cpp_method.GetQualifiers().empty()) { @@ -1760,12 +1763,9 @@ Module::PrepareForFunctionNameLookup (const ConstString &name, } else { - if (!CPPLanguageRuntime::StripNamespacesFromVariableName (name_cstr, base_name_start, base_name_end)) - { - lookup_name_type_mask &= ~(eFunctionNameTypeMethod | eFunctionNameTypeBase); - if (lookup_name_type_mask == eFunctionNameTypeNone) - return; - } + // If the CPP method parser didn't manage to chop this up, try to fill in the base name if we can. + // If a::b::c is passed in, we need to just look up "c", and then we'll filter the result later. + CPPLanguageRuntime::ExtractContextAndIdentifier (name_cstr, context, basename); } } @@ -1780,16 +1780,13 @@ Module::PrepareForFunctionNameLookup (const ConstString &name, } } - if (base_name_start && - base_name_end && - base_name_start != name_cstr && - base_name_start < base_name_end) + if (!basename.empty()) { // The name supplied was a partial C++ path like "a::count". In this case we want to do a // lookup on the basename "count" and then make sure any matching results contain "a::count" // so that it would match "b::a::count" and "a::count". This is why we set "match_name_after_lookup" // to true - lookup_name.SetCStringWithLength(base_name_start, base_name_end - base_name_start); + lookup_name.SetString(basename); match_name_after_lookup = true; } else diff --git a/source/Core/ModuleList.cpp b/source/Core/ModuleList.cpp index 156f3cf9d0aa..879eb9bd1822 100644 --- a/source/Core/ModuleList.cpp +++ b/source/Core/ModuleList.cpp @@ -484,6 +484,26 @@ ModuleList::FindFunctionSymbols (const ConstString &name, return sc_list.GetSize() - old_size; } + +size_t +ModuleList::FindFunctions(const RegularExpression &name, + bool include_symbols, + bool include_inlines, + bool append, + SymbolContextList& sc_list) +{ + const size_t old_size = sc_list.GetSize(); + + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindFunctions (name, include_symbols, include_inlines, append, sc_list); + } + + return sc_list.GetSize() - old_size; +} + size_t ModuleList::FindCompileUnits (const FileSpec &path, bool append, diff --git a/source/Core/PluginManager.cpp b/source/Core/PluginManager.cpp index cda55802fab8..95574cb2deaf 100644 --- a/source/Core/PluginManager.cpp +++ b/source/Core/PluginManager.cpp @@ -2068,6 +2068,229 @@ PluginManager::GetUnwindAssemblyCreateCallbackForPluginName (const ConstString & return NULL; } +#pragma mark MemoryHistory + +struct MemoryHistoryInstance +{ + MemoryHistoryInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + MemoryHistoryCreateInstance create_callback; +}; + +typedef std::vector<MemoryHistoryInstance> MemoryHistoryInstances; + +static Mutex & +GetMemoryHistoryMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static MemoryHistoryInstances & +GetMemoryHistoryInstances () +{ + static MemoryHistoryInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + MemoryHistoryCreateInstance create_callback + ) +{ + if (create_callback) + { + MemoryHistoryInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetMemoryHistoryMutex ()); + GetMemoryHistoryInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (MemoryHistoryCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetMemoryHistoryMutex ()); + MemoryHistoryInstances &instances = GetMemoryHistoryInstances (); + + MemoryHistoryInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +MemoryHistoryCreateInstance +PluginManager::GetMemoryHistoryCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetMemoryHistoryMutex ()); + MemoryHistoryInstances &instances = GetMemoryHistoryInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +MemoryHistoryCreateInstance +PluginManager::GetMemoryHistoryCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetMemoryHistoryMutex ()); + MemoryHistoryInstances &instances = GetMemoryHistoryInstances (); + + MemoryHistoryInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + +#pragma mark InstrumentationRuntime + +struct InstrumentationRuntimeInstance +{ + InstrumentationRuntimeInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + InstrumentationRuntimeCreateInstance create_callback; + InstrumentationRuntimeGetType get_type_callback; +}; + +typedef std::vector<InstrumentationRuntimeInstance> InstrumentationRuntimeInstances; + +static Mutex & +GetInstrumentationRuntimeMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static InstrumentationRuntimeInstances & +GetInstrumentationRuntimeInstances () +{ + static InstrumentationRuntimeInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + InstrumentationRuntimeCreateInstance create_callback, + InstrumentationRuntimeGetType get_type_callback + ) +{ + if (create_callback) + { + InstrumentationRuntimeInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + instance.get_type_callback = get_type_callback; + Mutex::Locker locker (GetInstrumentationRuntimeMutex ()); + GetInstrumentationRuntimeInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (InstrumentationRuntimeCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetInstrumentationRuntimeMutex ()); + InstrumentationRuntimeInstances &instances = GetInstrumentationRuntimeInstances (); + + InstrumentationRuntimeInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +InstrumentationRuntimeGetType +PluginManager::GetInstrumentationRuntimeGetTypeCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetInstrumentationRuntimeMutex ()); + InstrumentationRuntimeInstances &instances = GetInstrumentationRuntimeInstances (); + if (idx < instances.size()) + return instances[idx].get_type_callback; + return NULL; +} + +InstrumentationRuntimeCreateInstance +PluginManager::GetInstrumentationRuntimeCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetInstrumentationRuntimeMutex ()); + InstrumentationRuntimeInstances &instances = GetInstrumentationRuntimeInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +InstrumentationRuntimeCreateInstance +PluginManager::GetInstrumentationRuntimeCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetInstrumentationRuntimeMutex ()); + InstrumentationRuntimeInstances &instances = GetInstrumentationRuntimeInstances (); + + InstrumentationRuntimeInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + +#pragma mark PluginManager + void PluginManager::DebuggerInitialize (Debugger &debugger) { diff --git a/source/Core/RegularExpression.cpp b/source/Core/RegularExpression.cpp index 88415f616828..54924d069537 100644 --- a/source/Core/RegularExpression.cpp +++ b/source/Core/RegularExpression.cpp @@ -162,20 +162,11 @@ RegularExpression::Execute(const char* s, Match *match, int execute_flags) const bool RegularExpression::Match::GetMatchAtIndex (const char* s, uint32_t idx, std::string& match_str) const { - if (idx < m_matches.size()) + llvm::StringRef match_str_ref; + if (GetMatchAtIndex(s, idx, match_str_ref)) { - if (m_matches[idx].rm_eo == m_matches[idx].rm_so) - { - // Matched the empty string... - match_str.clear(); - return true; - } - else if (m_matches[idx].rm_eo > m_matches[idx].rm_so) - { - match_str.assign (s + m_matches[idx].rm_so, - m_matches[idx].rm_eo - m_matches[idx].rm_so); - return true; - } + match_str = std::move(match_str_ref.str()); + return true; } return false; } @@ -185,6 +176,9 @@ RegularExpression::Match::GetMatchAtIndex (const char* s, uint32_t idx, llvm::St { if (idx < m_matches.size()) { + if (m_matches[idx].rm_eo == -1 && m_matches[idx].rm_so == -1) + return false; + if (m_matches[idx].rm_eo == m_matches[idx].rm_so) { // Matched the empty string... diff --git a/source/Core/SearchFilter.cpp b/source/Core/SearchFilter.cpp index dee2f2e4bd5e..9c3412a422d0 100644 --- a/source/Core/SearchFilter.cpp +++ b/source/Core/SearchFilter.cpp @@ -119,6 +119,15 @@ SearchFilter::Dump (Stream *s) const } +lldb::SearchFilterSP +SearchFilter::CopyForBreakpoint (Breakpoint &breakpoint) +{ + SearchFilterSP ret_sp = DoCopyForBreakpoint (breakpoint); + TargetSP target_sp = breakpoint.GetTargetSP(); + ret_sp->SetTarget(target_sp); + return ret_sp; +} + //---------------------------------------------------------------------- // UTILITY Functions to help iterate down through the elements of the // SymbolContext. @@ -281,29 +290,36 @@ SearchFilter::DoFunctionIteration (Function *function, const SymbolContext &cont } //---------------------------------------------------------------------- -// SearchFilterForNonModuleSpecificSearches: +// SearchFilterForUnconstrainedSearches: // Selects a shared library matching a given file spec, consulting the targets "black list". //---------------------------------------------------------------------- - bool - SearchFilterForNonModuleSpecificSearches::ModulePasses (const FileSpec &module_spec) - { - if (m_target_sp->ModuleIsExcludedForNonModuleSpecificSearches (module_spec)) - return false; - else - return true; - } - - bool - SearchFilterForNonModuleSpecificSearches::ModulePasses (const lldb::ModuleSP &module_sp) - { - if (!module_sp) - return true; - else if (m_target_sp->ModuleIsExcludedForNonModuleSpecificSearches (module_sp)) - return false; - else - return true; - } +bool +SearchFilterForUnconstrainedSearches::ModulePasses (const FileSpec &module_spec) +{ + if (m_target_sp->ModuleIsExcludedForUnconstrainedSearches (module_spec)) + return false; + else + return true; +} + +bool +SearchFilterForUnconstrainedSearches::ModulePasses (const lldb::ModuleSP &module_sp) +{ + if (!module_sp) + return true; + else if (m_target_sp->ModuleIsExcludedForUnconstrainedSearches (module_sp)) + return false; + else + return true; +} + +lldb::SearchFilterSP +SearchFilterForUnconstrainedSearches::DoCopyForBreakpoint (Breakpoint &breakpoint) +{ + SearchFilterSP ret_sp(new SearchFilterForUnconstrainedSearches(*this)); + return ret_sp; +} //---------------------------------------------------------------------- // SearchFilterByModule: @@ -434,7 +450,7 @@ SearchFilterByModule::GetDescription (Stream *s) } else { - s->PutCString(m_module_spec.GetFilename().AsCString("<unknown>")); + s->PutCString(m_module_spec.GetFilename().AsCString("<Unknown>")); } } @@ -449,6 +465,14 @@ SearchFilterByModule::Dump (Stream *s) const { } + +lldb::SearchFilterSP +SearchFilterByModule::DoCopyForBreakpoint (Breakpoint &breakpoint) +{ + SearchFilterSP ret_sp(new SearchFilterByModule(*this)); + return ret_sp; +} + //---------------------------------------------------------------------- // SearchFilterByModuleList: // Selects a shared library matching a given file spec @@ -458,7 +482,8 @@ SearchFilterByModule::Dump (Stream *s) const // SearchFilterByModuleList constructors //---------------------------------------------------------------------- -SearchFilterByModuleList::SearchFilterByModuleList (const lldb::TargetSP &target_sp, const FileSpecList &module_list) : +SearchFilterByModuleList::SearchFilterByModuleList (const lldb::TargetSP &target_sp, + const FileSpecList &module_list) : SearchFilter (target_sp), m_module_spec_list (module_list) { @@ -587,7 +612,7 @@ SearchFilterByModuleList::GetDescription (Stream *s) } else { - s->PutCString(m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString("<unknown>")); + s->PutCString(m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString("<Unknown>")); } } else @@ -603,7 +628,7 @@ SearchFilterByModuleList::GetDescription (Stream *s) } else { - s->PutCString(m_module_spec_list.GetFileSpecAtIndex(i).GetFilename().AsCString("<unknown>")); + s->PutCString(m_module_spec_list.GetFileSpecAtIndex(i).GetFilename().AsCString("<Unknown>")); } if (i != num_modules - 1) s->PutCString (", "); @@ -623,6 +648,14 @@ SearchFilterByModuleList::Dump (Stream *s) const } +lldb::SearchFilterSP +SearchFilterByModuleList::DoCopyForBreakpoint (Breakpoint &breakpoint) +{ + SearchFilterSP ret_sp(new SearchFilterByModuleList(*this)); + return ret_sp; +} + + //---------------------------------------------------------------------- // SearchFilterByModuleListAndCU: // Selects a shared library matching a given file spec @@ -778,7 +811,7 @@ SearchFilterByModuleListAndCU::GetDescription (Stream *s) } else { - s->PutCString(m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString("<unknown>")); + s->PutCString(m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString("<Unknown>")); } } else if (num_modules > 0) @@ -794,7 +827,7 @@ SearchFilterByModuleListAndCU::GetDescription (Stream *s) } else { - s->PutCString(m_module_spec_list.GetFileSpecAtIndex(i).GetFilename().AsCString("<unknown>")); + s->PutCString(m_module_spec_list.GetFileSpecAtIndex(i).GetFilename().AsCString("<Unknown>")); } if (i != num_modules - 1) s->PutCString (", "); @@ -814,3 +847,10 @@ SearchFilterByModuleListAndCU::Dump (Stream *s) const } +lldb::SearchFilterSP +SearchFilterByModuleListAndCU::DoCopyForBreakpoint (Breakpoint &breakpoint) +{ + SearchFilterSP ret_sp(new SearchFilterByModuleListAndCU(*this)); + return ret_sp; +} + diff --git a/source/Core/Section.cpp b/source/Core/Section.cpp index 3267c1866221..ea33a62b775f 100644 --- a/source/Core/Section.cpp +++ b/source/Core/Section.cpp @@ -28,7 +28,8 @@ Section::Section (const ModuleSP &module_sp, lldb::offset_t file_offset, lldb::offset_t file_size, uint32_t log2align, - uint32_t flags) : + uint32_t flags, + uint32_t target_byte_size/*=1*/) : ModuleChild (module_sp), UserID (sect_id), Flags (flags), @@ -44,7 +45,8 @@ Section::Section (const ModuleSP &module_sp, m_children (), m_fake (false), m_encrypted (false), - m_thread_specific (false) + m_thread_specific (false), + m_target_byte_size(target_byte_size) { // printf ("Section::Section(%p): module=%p, sect_id = 0x%16.16" PRIx64 ", addr=[0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), file [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), flags = 0x%8.8x, name = %s\n", // this, module_sp.get(), sect_id, file_addr, file_addr + byte_size, file_offset, file_offset + file_size, flags, name.GetCString()); @@ -61,7 +63,8 @@ Section::Section (const lldb::SectionSP &parent_section_sp, lldb::offset_t file_offset, lldb::offset_t file_size, uint32_t log2align, - uint32_t flags) : + uint32_t flags, + uint32_t target_byte_size/*=1*/) : ModuleChild (module_sp), UserID (sect_id), Flags (flags), @@ -77,7 +80,8 @@ Section::Section (const lldb::SectionSP &parent_section_sp, m_children (), m_fake (false), m_encrypted (false), - m_thread_specific (false) + m_thread_specific (false), + m_target_byte_size(target_byte_size) { // printf ("Section::Section(%p): module=%p, sect_id = 0x%16.16" PRIx64 ", addr=[0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), file [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 "), flags = 0x%8.8x, name = %s.%s\n", // this, module_sp.get(), sect_id, file_addr, file_addr + byte_size, file_offset, file_offset + file_size, flags, parent_section_sp->GetName().GetCString(), name.GetCString()); @@ -186,7 +190,7 @@ Section::ContainsFileAddress (addr_t vm_addr) const { if (file_addr <= vm_addr) { - const addr_t offset = vm_addr - file_addr; + const addr_t offset = (vm_addr - file_addr) * m_target_byte_size; return offset < GetByteSize(); } } diff --git a/source/Core/StreamString.cpp b/source/Core/StreamString.cpp index 8d7d039fd65c..ef2b70583ebd 100644 --- a/source/Core/StreamString.cpp +++ b/source/Core/StreamString.cpp @@ -65,6 +65,22 @@ StreamString::GetSize () const return m_packet.size(); } +size_t +StreamString::GetSizeOfLastLine () const +{ + const size_t length = m_packet.size(); + size_t last_line_begin_pos = m_packet.find_last_of("\r\n"); + if (last_line_begin_pos == std::string::npos) + { + return length; + } + else + { + ++last_line_begin_pos; + return length - last_line_begin_pos; + } +} + std::string & StreamString::GetString() { diff --git a/source/Core/ValueObject.cpp b/source/Core/ValueObject.cpp index 1819e834536a..fa5fb14db05b 100644 --- a/source/Core/ValueObject.cpp +++ b/source/Core/ValueObject.cpp @@ -34,8 +34,12 @@ #include "lldb/Core/ValueObjectSyntheticFilter.h" #include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/DataFormatters/StringPrinter.h" #include "lldb/DataFormatters/ValueObjectPrinter.h" +#include "lldb/Expression/ClangExpressionVariable.h" +#include "lldb/Expression/ClangPersistentVariables.h" + #include "lldb/Host/Endian.h" #include "lldb/Interpreter/CommandInterpreter.h" @@ -43,6 +47,7 @@ #include "lldb/Symbol/ClangASTType.h" #include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Type.h" #include "lldb/Target/ExecutionContext.h" @@ -77,6 +82,7 @@ ValueObject::ValueObject (ValueObject &parent) : m_location_str (), m_summary_str (), m_object_desc_str (), + m_validation_result(), m_manager(parent.GetManager()), m_children (), m_synthetic_children (), @@ -89,8 +95,10 @@ ValueObject::ValueObject (ValueObject &parent) : m_type_summary_sp(), m_type_format_sp(), m_synthetic_children_sp(), + m_type_validator_sp(), m_user_id_of_forced_summary(), m_address_type_of_ptr_or_ref_children(eAddressTypeInvalid), + m_value_checksum(), m_value_is_valid (false), m_value_did_change (false), m_children_count_valid (false), @@ -100,7 +108,8 @@ ValueObject::ValueObject (ValueObject &parent) : m_is_bitfield_for_scalar(false), m_is_child_at_offset(false), m_is_getting_summary(false), - m_did_calculate_complete_objc_class_type(false) + m_did_calculate_complete_objc_class_type(false), + m_is_synthetic_children_generated(parent.m_is_synthetic_children_generated) { m_manager->ManageObject(this); } @@ -123,6 +132,7 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope, m_location_str (), m_summary_str (), m_object_desc_str (), + m_validation_result(), m_manager(), m_children (), m_synthetic_children (), @@ -135,8 +145,10 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope, m_type_summary_sp(), m_type_format_sp(), m_synthetic_children_sp(), + m_type_validator_sp(), m_user_id_of_forced_summary(), m_address_type_of_ptr_or_ref_children(child_ptr_or_ref_addr_type), + m_value_checksum(), m_value_is_valid (false), m_value_did_change (false), m_children_count_valid (false), @@ -146,7 +158,8 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope, m_is_bitfield_for_scalar(false), m_is_child_at_offset(false), m_is_getting_summary(false), - m_did_calculate_complete_objc_class_type(false) + m_did_calculate_complete_objc_class_type(false), + m_is_synthetic_children_generated(false) { m_manager = new ValueObjectManager(); m_manager->ManageObject (this); @@ -181,7 +194,7 @@ ValueObject::UpdateValueIfNeeded (bool update_format) return m_error.Success(); } - bool first_update = m_update_point.IsFirstEvaluation(); + bool first_update = IsChecksumEmpty(); if (m_update_point.NeedsUpdating()) { @@ -210,10 +223,35 @@ ValueObject::UpdateValueIfNeeded (bool update_format) m_error.Clear(); // Call the pure virtual function to update the value + + bool need_compare_checksums = false; + llvm::SmallVector<uint8_t, 16> old_checksum; + + if (!first_update && CanProvideValue()) + { + need_compare_checksums = true; + old_checksum.resize(m_value_checksum.size()); + std::copy(m_value_checksum.begin(), m_value_checksum.end(), old_checksum.begin()); + } + bool success = UpdateValue (); SetValueIsValid (success); + if (success) + { + const uint64_t max_checksum_size = 128; + m_data.Checksum(m_value_checksum, + max_checksum_size); + } + else + { + need_compare_checksums = false; + m_value_checksum.clear(); + } + + assert (!need_compare_checksums || (!old_checksum.empty() && !m_value_checksum.empty())); + if (first_update) SetValueDidChange (false); else if (!m_value_did_change && success == false) @@ -222,6 +260,11 @@ ValueObject::UpdateValueIfNeeded (bool update_format) // as changed if the value used to be valid and now isn't SetValueDidChange (value_was_valid); } + else if (need_compare_checksums) + { + SetValueDidChange(memcmp(&old_checksum[0], &m_value_checksum[0], m_value_checksum.size())); + } + } else { @@ -253,6 +296,7 @@ ValueObject::UpdateFormatsIfNeeded() #ifndef LLDB_DISABLE_PYTHON SetSyntheticChildren(DataVisualization::GetSyntheticChildren (*this, GetDynamicValueType())); #endif + SetValidator(DataVisualization::GetValidator(*this, GetDynamicValueType())); } return any_change; @@ -486,7 +530,6 @@ ValueObject::SetValueIsValid (bool b) bool ValueObject::GetValueDidChange () { - GetValueAsCString (); return m_value_did_change; } @@ -746,9 +789,9 @@ ValueObject::MightHaveChildren() const uint32_t type_info = GetTypeInfo(); if (type_info) { - if (type_info & (ClangASTType::eTypeHasChildren | - ClangASTType::eTypeIsPointer | - ClangASTType::eTypeIsReference)) + if (type_info & (eTypeHasChildren | + eTypeIsPointer | + eTypeIsReference)) has_children = true; } else @@ -835,6 +878,14 @@ bool ValueObject::GetSummaryAsCString (TypeSummaryImpl* summary_ptr, std::string& destination) { + return GetSummaryAsCString(summary_ptr, destination, TypeSummaryOptions()); +} + +bool +ValueObject::GetSummaryAsCString (TypeSummaryImpl* summary_ptr, + std::string& destination, + const TypeSummaryOptions& options) +{ destination.clear(); // ideally we would like to bail out if passing NULL, but if we do so @@ -851,66 +902,11 @@ ValueObject::GetSummaryAsCString (TypeSummaryImpl* summary_ptr, GetName().GetCString(), summary_ptr->GetDescription().c_str());*/ - if (UpdateValueIfNeeded (false)) + if (UpdateValueIfNeeded (false) && summary_ptr) { - if (summary_ptr) - { - if (HasSyntheticValue()) - m_synthetic_value->UpdateValueIfNeeded(); // the summary might depend on the synthetic children being up-to-date (e.g. ${svar%#}) - summary_ptr->FormatObject(this, destination); - } - else - { - ClangASTType clang_type = GetClangType(); - - // Do some default printout for function pointers - if (clang_type) - { - if (clang_type.IsFunctionPointerType ()) - { - StreamString sstr; - AddressType func_ptr_address_type = eAddressTypeInvalid; - addr_t func_ptr_address = GetPointerValue (&func_ptr_address_type); - if (func_ptr_address != 0 && func_ptr_address != LLDB_INVALID_ADDRESS) - { - switch (func_ptr_address_type) - { - case eAddressTypeInvalid: - case eAddressTypeFile: - break; - - case eAddressTypeLoad: - { - ExecutionContext exe_ctx (GetExecutionContextRef()); - - Address so_addr; - Target *target = exe_ctx.GetTargetPtr(); - if (target && target->GetSectionLoadList().IsEmpty() == false) - { - if (target->GetSectionLoadList().ResolveLoadAddress(func_ptr_address, so_addr)) - { - so_addr.Dump (&sstr, - exe_ctx.GetBestExecutionContextScope(), - Address::DumpStyleResolvedDescription, - Address::DumpStyleSectionNameOffset); - } - } - } - break; - - case eAddressTypeHost: - break; - } - } - if (sstr.GetSize() > 0) - { - destination.assign (1, '('); - destination.append (sstr.GetData(), sstr.GetSize()); - destination.append (1, ')'); - } - } - } - } + if (HasSyntheticValue()) + m_synthetic_value->UpdateValueIfNeeded(); // the summary might depend on the synthetic children being up-to-date (e.g. ${svar%#}) + summary_ptr->FormatObject(this, destination, options); } m_is_getting_summary = false; return !destination.empty(); @@ -922,7 +918,8 @@ ValueObject::GetSummaryAsCString () if (UpdateValueIfNeeded(true) && m_summary_str.empty()) { GetSummaryAsCString(GetSummaryFormat().get(), - m_summary_str); + m_summary_str, + TypeSummaryOptions()); } if (m_summary_str.empty()) return NULL; @@ -930,17 +927,26 @@ ValueObject::GetSummaryAsCString () } bool +ValueObject::GetSummaryAsCString (std::string& destination, + const TypeSummaryOptions& options) +{ + return GetSummaryAsCString(GetSummaryFormat().get(), + destination, + options); +} + +bool ValueObject::IsCStringContainer(bool check_pointer) { ClangASTType pointee_or_element_clang_type; const Flags type_flags (GetTypeInfo (&pointee_or_element_clang_type)); - bool is_char_arr_ptr (type_flags.AnySet (ClangASTType::eTypeIsArray | ClangASTType::eTypeIsPointer) && + bool is_char_arr_ptr (type_flags.AnySet (eTypeIsArray | eTypeIsPointer) && pointee_or_element_clang_type.IsCharType ()); if (!is_char_arr_ptr) return false; if (!check_pointer) return true; - if (type_flags.Test(ClangASTType::eTypeIsArray)) + if (type_flags.Test(eTypeIsArray)) return true; addr_t cstr_address = LLDB_INVALID_ADDRESS; AddressType cstr_address_type = eAddressTypeInvalid; @@ -955,8 +961,8 @@ ValueObject::GetPointeeData (DataExtractor& data, { ClangASTType pointee_or_element_clang_type; const uint32_t type_info = GetTypeInfo (&pointee_or_element_clang_type); - const bool is_pointer_type = type_info & ClangASTType::eTypeIsPointer; - const bool is_array_type = type_info & ClangASTType::eTypeIsArray; + const bool is_pointer_type = type_info & eTypeIsPointer; + const bool is_array_type = type_info & eTypeIsArray; if (!(is_pointer_type || is_array_type)) return 0; @@ -1182,20 +1188,31 @@ strlen_or_inf (const char* str, return len; } +static bool +CopyStringDataToBufferSP(const StreamString& source, + lldb::DataBufferSP& destination) +{ + destination.reset(new DataBufferHeap(source.GetSize()+1,0)); + memcpy(destination->GetBytes(), source.GetString().c_str(), source.GetSize()); + return true; +} + size_t -ValueObject::ReadPointedString (Stream& s, +ValueObject::ReadPointedString (lldb::DataBufferSP& buffer_sp, Error& error, uint32_t max_length, bool honor_array, Format item_format) { + StreamString s; ExecutionContext exe_ctx (GetExecutionContextRef()); Target* target = exe_ctx.GetTargetPtr(); - + if (!target) { s << "<no target to read from>"; error.SetErrorString("no target to read from"); + CopyStringDataToBufferSP(s, buffer_sp); return 0; } @@ -1208,7 +1225,7 @@ ValueObject::ReadPointedString (Stream& s, ClangASTType clang_type = GetClangType(); ClangASTType elem_or_pointee_clang_type; const Flags type_flags (GetTypeInfo (&elem_or_pointee_clang_type)); - if (type_flags.AnySet (ClangASTType::eTypeIsArray | ClangASTType::eTypeIsPointer) && + if (type_flags.AnySet (eTypeIsArray | eTypeIsPointer) && elem_or_pointee_clang_type.IsCharType ()) { addr_t cstr_address = LLDB_INVALID_ADDRESS; @@ -1216,7 +1233,7 @@ ValueObject::ReadPointedString (Stream& s, size_t cstr_len = 0; bool capped_data = false; - if (type_flags.Test (ClangASTType::eTypeIsArray)) + if (type_flags.Test (eTypeIsArray)) { // We have an array uint64_t array_size = 0; @@ -1241,9 +1258,10 @@ ValueObject::ReadPointedString (Stream& s, { s << "<invalid address>"; error.SetErrorString("invalid address"); + CopyStringDataToBufferSP(s, buffer_sp); return 0; } - + Address cstr_so_addr (cstr_address); DataExtractor data; if (cstr_len > 0 && honor_array) @@ -1251,30 +1269,21 @@ ValueObject::ReadPointedString (Stream& s, // I am using GetPointeeData() here to abstract the fact that some ValueObjects are actually frozen pointers in the host // but the pointed-to data lives in the debuggee, and GetPointeeData() automatically takes care of this GetPointeeData(data, 0, cstr_len); - + if ((bytes_read = data.GetByteSize()) > 0) { total_bytes_read = bytes_read; - s << '"'; - data.Dump (&s, - 0, // Start offset in "data" - item_format, - 1, // Size of item (1 byte for a char!) - bytes_read, // How many bytes to print? - UINT32_MAX, // num per line - LLDB_INVALID_ADDRESS,// base address - 0, // bitfield bit size - 0); // bitfield bit offset + for (size_t offset = 0; offset < bytes_read; offset++) + s.Printf("%c", *data.PeekData(offset, 1)); if (capped_data) s << "..."; - s << '"'; } } else { cstr_len = max_length; const size_t k_max_buf_size = 64; - + size_t offset = 0; int cstr_len_displayed = -1; @@ -1288,12 +1297,10 @@ ValueObject::ReadPointedString (Stream& s, size_t len = strlen_or_inf (cstr, k_max_buf_size, k_max_buf_size+1); if (len > k_max_buf_size) len = k_max_buf_size; - if (cstr && cstr_len_displayed < 0) - s << '"'; - + if (cstr_len_displayed < 0) cstr_len_displayed = len; - + if (len == 0) break; cstr_len_displayed += len; @@ -1302,15 +1309,8 @@ ValueObject::ReadPointedString (Stream& s, if (len > cstr_len) len = cstr_len; - data.Dump (&s, - 0, // Start offset in "data" - item_format, - 1, // Size of item (1 byte for a char!) - len, // How many bytes to print? - UINT32_MAX, // num per line - LLDB_INVALID_ADDRESS,// base address - 0, // bitfield bit size - 0); // bitfield bit offset + for (size_t offset = 0; offset < bytes_read; offset++) + s.Printf("%c", *data.PeekData(offset, 1)); if (len < k_max_buf_size) break; @@ -1320,14 +1320,13 @@ ValueObject::ReadPointedString (Stream& s, capped_cstr = true; break; } - + cstr_len -= len; offset += len; } if (cstr_len_displayed >= 0) { - s << '"'; if (capped_cstr) s << "..."; } @@ -1338,9 +1337,27 @@ ValueObject::ReadPointedString (Stream& s, error.SetErrorString("not a string object"); s << "<not a string object>"; } + CopyStringDataToBufferSP(s, buffer_sp); return total_bytes_read; } +std::pair<TypeValidatorResult, std::string> +ValueObject::GetValidationStatus () +{ + if (!UpdateValueIfNeeded(true)) + return {TypeValidatorResult::Success,""}; // not the validator's job to discuss update problems + + if (m_validation_result.hasValue()) + return m_validation_result.getValue(); + + if (!m_type_validator_sp) + return {TypeValidatorResult::Success,""}; // no validator no failure + + auto outcome = m_type_validator_sp->FormatObject(this); + + return (m_validation_result = {outcome.m_result,outcome.m_message}).getValue(); +} + const char * ValueObject::GetObjectDescription () { @@ -1428,7 +1445,7 @@ ValueObject::GetValueAsCString () } else { - my_format = GetClangType().GetFormat(); + my_format = GetValue().GetClangType().GetFormat(); } } } @@ -1460,7 +1477,7 @@ uint64_t ValueObject::GetValueAsUnsigned (uint64_t fail_value, bool *success) { // If our byte size is zero this is an aggregate type that has children - if (!GetClangType().IsAggregateType()) + if (CanProvideValue()) { Scalar scalar; if (ResolveValue (scalar)) @@ -1481,7 +1498,7 @@ int64_t ValueObject::GetValueAsSigned (int64_t fail_value, bool *success) { // If our byte size is zero this is an aggregate type that has children - if (!GetClangType().IsAggregateType()) + if (CanProvideValue()) { Scalar scalar; if (ResolveValue (scalar)) @@ -1506,7 +1523,7 @@ ValueObject::HasSpecialPrintableRepresentation(ValueObjectRepresentationStyle va Format custom_format) { Flags flags(GetTypeInfo()); - if (flags.AnySet(ClangASTType::eTypeIsArray | ClangASTType::eTypeIsPointer) + if (flags.AnySet(eTypeIsArray | eTypeIsPointer) && val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) { if (IsCStringContainer(true) && @@ -1516,7 +1533,7 @@ ValueObject::HasSpecialPrintableRepresentation(ValueObjectRepresentationStyle va custom_format == eFormatVectorOfChar)) return true; - if (flags.Test(ClangASTType::eTypeIsArray)) + if (flags.Test(eTypeIsArray)) { if ((custom_format == eFormatBytes) || (custom_format == eFormatBytesWithASCII)) @@ -1555,7 +1572,7 @@ ValueObject::DumpPrintableRepresentation(Stream& s, if (allow_special) { - if (flags.AnySet(ClangASTType::eTypeIsArray | ClangASTType::eTypeIsPointer) + if (flags.AnySet(eTypeIsArray | eTypeIsPointer) && val_obj_display == ValueObject::eValueObjectRepresentationStyleValue) { // when being asked to get a printable display an array or pointer type directly, @@ -1568,11 +1585,19 @@ ValueObject::DumpPrintableRepresentation(Stream& s, custom_format == eFormatVectorOfChar)) // print char[] & char* directly { Error error; - ReadPointedString(s, + lldb::DataBufferSP buffer_sp; + ReadPointedString(buffer_sp, error, 0, (custom_format == eFormatVectorOfChar) || (custom_format == eFormatCharArray)); + lldb_private::formatters::ReadBufferAndDumpToStreamOptions options(*this); + options.SetData(DataExtractor(buffer_sp, lldb::eByteOrderInvalid, 8)); // none of this matters for a string - pass some defaults + options.SetStream(&s); + options.SetPrefixToken(0); + options.SetQuote('"'); + options.SetSourceSize(buffer_sp->GetByteSize()); + lldb_private::formatters::ReadBufferAndDumpToStream<lldb_private::formatters::StringElementType::ASCII>(options); return !error.Fail(); } @@ -1581,7 +1606,7 @@ ValueObject::DumpPrintableRepresentation(Stream& s, // this only works for arrays, because I have no way to know when // the pointed memory ends, and no special \0 end of data marker - if (flags.Test(ClangASTType::eTypeIsArray)) + if (flags.Test(eTypeIsArray)) { if ((custom_format == eFormatBytes) || (custom_format == eFormatBytesWithASCII)) @@ -1729,7 +1754,7 @@ ValueObject::DumpPrintableRepresentation(Stream& s, cstr = GetSummaryAsCString(); else if (val_obj_display == eValueObjectRepresentationStyleSummary) { - if (GetClangType().IsAggregateType()) + if (!CanProvideValue()) { strm.Printf("%s @ %s", GetTypeName().AsCString(), GetLocationAsCString()); cstr = strm.GetString().c_str(); @@ -2037,7 +2062,7 @@ ValueObject::IsPossibleDynamicType () bool ValueObject::IsObjCNil () { - const uint32_t mask = ClangASTType::eTypeIsObjC | ClangASTType::eTypeIsPointer; + const uint32_t mask = eTypeIsObjC | eTypeIsPointer; bool isObjCpointer = (((GetClangType().GetTypeInfo(NULL)) & mask) == mask); if (!isObjCpointer) return false; @@ -2050,10 +2075,10 @@ ValueObjectSP ValueObject::GetSyntheticArrayMember (size_t index, bool can_create) { const uint32_t type_info = GetTypeInfo (); - if (type_info & ClangASTType::eTypeIsArray) + if (type_info & eTypeIsArray) return GetSyntheticArrayMemberFromArray(index, can_create); - if (type_info & ClangASTType::eTypeIsPointer) + if (type_info & eTypeIsPointer) return GetSyntheticArrayMemberFromPointer(index, can_create); return ValueObjectSP(); @@ -2460,6 +2485,46 @@ ValueObject::IsBaseClass (uint32_t& depth) void ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat epformat) { + // synthetic children do not actually "exist" as part of the hierarchy, and sometimes they are consed up in ways + // that don't make sense from an underlying language/API standpoint. So, use a special code path here to return + // something that can hopefully be used in expression + if (m_is_synthetic_children_generated) + { + UpdateValueIfNeeded(); + + if (m_value.GetValueType() == Value::eValueTypeLoadAddress) + { + if (IsPointerOrReferenceType()) + { + s.Printf("((%s)0x%" PRIx64 ")", + GetTypeName().AsCString("void"), + GetValueAsUnsigned(0)); + return; + } + else + { + uint64_t load_addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + if (load_addr != LLDB_INVALID_ADDRESS) + { + s.Printf("(*( (%s *)0x%" PRIx64 "))", + GetTypeName().AsCString("void"), + load_addr); + return; + } + } + } + + if (CanProvideValue()) + { + s.Printf("((%s)%s)", + GetTypeName().AsCString("void"), + GetValueAsCString()); + return; + } + + return; + } + const bool is_deref_of_parent = IsDereferenceOfParent (); if (is_deref_of_parent && epformat == eGetExpressionPathFormatDereferencePointers) @@ -2499,12 +2564,12 @@ ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExp { const uint32_t non_base_class_parent_type_info = non_base_class_parent_clang_type.GetTypeInfo(); - if (non_base_class_parent_type_info & ClangASTType::eTypeIsPointer) + if (non_base_class_parent_type_info & eTypeIsPointer) { s.PutCString("->"); } - else if ((non_base_class_parent_type_info & ClangASTType::eTypeHasChildren) && - !(non_base_class_parent_type_info & ClangASTType::eTypeIsArray)) + else if ((non_base_class_parent_type_info & eTypeHasChildren) && + !(non_base_class_parent_type_info & eTypeIsArray)) { s.PutChar('.'); } @@ -2728,15 +2793,15 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, case '-': { if (options.m_check_dot_vs_arrow_syntax && - root_clang_type_info.Test(ClangASTType::eTypeIsPointer) ) // if you are trying to use -> on a non-pointer and I must catch the error + root_clang_type_info.Test(eTypeIsPointer) ) // if you are trying to use -> on a non-pointer and I must catch the error { *first_unparsed = expression_cstr; *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrowInsteadOfDot; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return ValueObjectSP(); } - if (root_clang_type_info.Test(ClangASTType::eTypeIsObjC) && // if yo are trying to extract an ObjC IVar when this is forbidden - root_clang_type_info.Test(ClangASTType::eTypeIsPointer) && + if (root_clang_type_info.Test(eTypeIsObjC) && // if yo are trying to extract an ObjC IVar when this is forbidden + root_clang_type_info.Test(eTypeIsPointer) && options.m_no_fragile_ivar) { *first_unparsed = expression_cstr; @@ -2756,7 +2821,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, case '.': // or fallthrough from -> { if (options.m_check_dot_vs_arrow_syntax && *expression_cstr == '.' && - root_clang_type_info.Test(ClangASTType::eTypeIsPointer)) // if you are trying to use . on a pointer and I must catch the error + root_clang_type_info.Test(eTypeIsPointer)) // if you are trying to use . on a pointer and I must catch the error { *first_unparsed = expression_cstr; *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDotInsteadOfArrow; @@ -2783,7 +2848,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, if (root->IsSynthetic()) { *first_unparsed = expression_cstr; - *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchSyntheticChild; *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; return ValueObjectSP(); } @@ -2857,9 +2922,9 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, } case '[': { - if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray) && !root_clang_type_info.Test(ClangASTType::eTypeIsPointer) && !root_clang_type_info.Test(ClangASTType::eTypeIsVector)) // if this is not a T[] nor a T* + if (!root_clang_type_info.Test(eTypeIsArray) && !root_clang_type_info.Test(eTypeIsPointer) && !root_clang_type_info.Test(eTypeIsVector)) // if this is not a T[] nor a T* { - if (!root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // if this is not even a scalar... + if (!root_clang_type_info.Test(eTypeIsScalar)) // if this is not even a scalar... { if (options.m_no_synthetic_children) // ...only chance left is synthetic { @@ -2879,7 +2944,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, } if (*(expression_cstr+1) == ']') // if this is an unbounded range it only works for arrays { - if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + if (!root_clang_type_info.Test(eTypeIsArray)) { *first_unparsed = expression_cstr; *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; @@ -2916,7 +2981,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, } if (end - expression_cstr == 1) // if this is [], only return a valid value for arrays { - if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + if (root_clang_type_info.Test(eTypeIsArray)) { *first_unparsed = expression_cstr+2; *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; @@ -2932,7 +2997,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, } } // from here on we do have a valid index - if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + if (root_clang_type_info.Test(eTypeIsArray)) { ValueObjectSP child_valobj_sp = root->GetChildAtIndex(index, true); if (!child_valobj_sp) @@ -2955,10 +3020,10 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, return ValueObjectSP(); } } - else if (root_clang_type_info.Test(ClangASTType::eTypeIsPointer)) + else if (root_clang_type_info.Test(eTypeIsPointer)) { if (*what_next == ValueObject::eExpressionPathAftermathDereference && // if this is a ptr-to-scalar, I am accessing it by index and I would have deref'ed anyway, then do it now and use this as a bitfield - pointee_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + pointee_clang_type_info.Test(eTypeIsScalar)) { Error error; root = root->Dereference(error); @@ -2978,7 +3043,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, else { if (root->GetClangType().GetMinimumLanguage() == eLanguageTypeObjC - && pointee_clang_type_info.AllClear(ClangASTType::eTypeIsPointer) + && pointee_clang_type_info.AllClear(eTypeIsPointer) && root->HasSyntheticValue() && options.m_no_synthetic_children == false) { @@ -3001,7 +3066,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, } } } - else if (root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + else if (root_clang_type_info.Test(eTypeIsScalar)) { root = root->GetSyntheticBitFieldChild(index, index, true); if (!root.get()) @@ -3019,7 +3084,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, return root; } } - else if (root_clang_type_info.Test(ClangASTType::eTypeIsVector)) + else if (root_clang_type_info.Test(eTypeIsVector)) { root = root->GetChildAtIndex(index, true); if (!root.get()) @@ -3104,7 +3169,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, index_lower = index_higher; index_higher = temp; } - if (root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // expansion only works for scalars + if (root_clang_type_info.Test(eTypeIsScalar)) // expansion only works for scalars { root = root->GetSyntheticBitFieldChild(index_lower, index_higher, true); if (!root.get()) @@ -3122,9 +3187,9 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, return root; } } - else if (root_clang_type_info.Test(ClangASTType::eTypeIsPointer) && // if this is a ptr-to-scalar, I am accessing it by index and I would have deref'ed anyway, then do it now and use this as a bitfield + else if (root_clang_type_info.Test(eTypeIsPointer) && // if this is a ptr-to-scalar, I am accessing it by index and I would have deref'ed anyway, then do it now and use this as a bitfield *what_next == ValueObject::eExpressionPathAftermathDereference && - pointee_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + pointee_clang_type_info.Test(eTypeIsScalar)) { Error error; root = root->Dereference(error); @@ -3201,9 +3266,9 @@ ValueObject::ExpandArraySliceExpression(const char* expression_cstr, { case '[': { - if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray) && !root_clang_type_info.Test(ClangASTType::eTypeIsPointer)) // if this is not a T[] nor a T* + if (!root_clang_type_info.Test(eTypeIsArray) && !root_clang_type_info.Test(eTypeIsPointer)) // if this is not a T[] nor a T* { - if (!root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // if this is not even a scalar, this syntax is just plain wrong! + if (!root_clang_type_info.Test(eTypeIsScalar)) // if this is not even a scalar, this syntax is just plain wrong! { *first_unparsed = expression_cstr; *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorInvalid; @@ -3220,7 +3285,7 @@ ValueObject::ExpandArraySliceExpression(const char* expression_cstr, } if (*(expression_cstr+1) == ']') // if this is an unbounded range it only works for arrays { - if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + if (!root_clang_type_info.Test(eTypeIsArray)) { *first_unparsed = expression_cstr; *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; @@ -3264,7 +3329,7 @@ ValueObject::ExpandArraySliceExpression(const char* expression_cstr, } if (end - expression_cstr == 1) // if this is [], only return a valid value for arrays { - if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + if (root_clang_type_info.Test(eTypeIsArray)) { const size_t max_index = root->GetNumChildren() - 1; for (size_t index = 0; index < max_index; index++) @@ -3287,7 +3352,7 @@ ValueObject::ExpandArraySliceExpression(const char* expression_cstr, } } // from here on we do have a valid index - if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + if (root_clang_type_info.Test(eTypeIsArray)) { root = root->GetChildAtIndex(index, true); if (!root.get()) @@ -3306,10 +3371,10 @@ ValueObject::ExpandArraySliceExpression(const char* expression_cstr, return 1; } } - else if (root_clang_type_info.Test(ClangASTType::eTypeIsPointer)) + else if (root_clang_type_info.Test(eTypeIsPointer)) { if (*what_next == ValueObject::eExpressionPathAftermathDereference && // if this is a ptr-to-scalar, I am accessing it by index and I would have deref'ed anyway, then do it now and use this as a bitfield - pointee_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + pointee_clang_type_info.Test(eTypeIsScalar)) { Error error; root = root->Dereference(error); @@ -3391,7 +3456,7 @@ ValueObject::ExpandArraySliceExpression(const char* expression_cstr, index_lower = index_higher; index_higher = temp; } - if (root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // expansion only works for scalars + if (root_clang_type_info.Test(eTypeIsScalar)) // expansion only works for scalars { root = root->GetSyntheticBitFieldChild(index_lower, index_higher, true); if (!root.get()) @@ -3410,9 +3475,9 @@ ValueObject::ExpandArraySliceExpression(const char* expression_cstr, return 1; } } - else if (root_clang_type_info.Test(ClangASTType::eTypeIsPointer) && // if this is a ptr-to-scalar, I am accessing it by index and I would have deref'ed anyway, then do it now and use this as a bitfield + else if (root_clang_type_info.Test(eTypeIsPointer) && // if this is a ptr-to-scalar, I am accessing it by index and I would have deref'ed anyway, then do it now and use this as a bitfield *what_next == ValueObject::eExpressionPathAftermathDereference && - pointee_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + pointee_clang_type_info.Test(eTypeIsScalar)) { Error error; root = root->Dereference(error); @@ -3480,9 +3545,7 @@ ValueObject::LogValueObject (Log *log, const DumpValueObjectOptions& options) void ValueObject::Dump (Stream &s) { - - ValueObjectPrinter printer(this,&s,DumpValueObjectOptions::DefaultOptions()); - printer.PrintValueObject(); + Dump (s, DumpValueObjectOptions::DefaultOptions()); } void @@ -3529,6 +3592,55 @@ ValueObject::CreateConstantValue (const ConstString &name) return valobj_sp; } +ValueObjectSP +ValueObject::GetQualifiedRepresentationIfAvailable (lldb::DynamicValueType dynValue, + bool synthValue) +{ + ValueObjectSP result_sp(GetSP()); + + switch (dynValue) + { + case lldb::eDynamicCanRunTarget: + case lldb::eDynamicDontRunTarget: + { + if (!result_sp->IsDynamic()) + { + if (result_sp->GetDynamicValue(dynValue)) + result_sp = result_sp->GetDynamicValue(dynValue); + } + } + break; + case lldb::eNoDynamicValues: + { + if (result_sp->IsDynamic()) + { + if (result_sp->GetStaticValue()) + result_sp = result_sp->GetStaticValue(); + } + } + break; + } + + if (synthValue) + { + if (!result_sp->IsSynthetic()) + { + if (result_sp->GetSyntheticValue()) + result_sp = result_sp->GetSyntheticValue(); + } + } + else + { + if (result_sp->IsSynthetic()) + { + if (result_sp->GetNonSyntheticValue()) + result_sp = result_sp->GetNonSyntheticValue(); + } + } + + return result_sp; +} + lldb::addr_t ValueObject::GetCPPVTableAddress (AddressType &address_type) { @@ -3538,13 +3650,13 @@ ValueObject::GetCPPVTableAddress (AddressType &address_type) if (type_info) { bool ptr_or_ref = false; - if (type_info & (ClangASTType::eTypeIsPointer | ClangASTType::eTypeIsReference)) + if (type_info & (eTypeIsPointer | eTypeIsReference)) { ptr_or_ref = true; type_info = pointee_type.GetTypeInfo(); } - const uint32_t cpp_class = ClangASTType::eTypeIsClass | ClangASTType::eTypeIsCPlusPlus; + const uint32_t cpp_class = eTypeIsClass | eTypeIsCPlusPlus; if ((type_info & cpp_class) == cpp_class) { if (ptr_or_ref) @@ -3736,16 +3848,14 @@ ValueObject::CastPointerType (const char *name, TypeSP &type_sp) ValueObject::EvaluationPoint::EvaluationPoint () : m_mod_id(), m_exe_ctx_ref(), - m_needs_update (true), - m_first_update (true) + m_needs_update (true) { } ValueObject::EvaluationPoint::EvaluationPoint (ExecutionContextScope *exe_scope, bool use_selected): m_mod_id(), m_exe_ctx_ref(), - m_needs_update (true), - m_first_update (true) + m_needs_update (true) { ExecutionContext exe_ctx(exe_scope); TargetSP target_sp (exe_ctx.GetTargetSP()); @@ -3789,8 +3899,7 @@ ValueObject::EvaluationPoint::EvaluationPoint (ExecutionContextScope *exe_scope, ValueObject::EvaluationPoint::EvaluationPoint (const ValueObject::EvaluationPoint &rhs) : m_mod_id(), m_exe_ctx_ref(rhs.m_exe_ctx_ref), - m_needs_update (true), - m_first_update (true) + m_needs_update (true) { } @@ -3884,7 +3993,6 @@ ValueObject::EvaluationPoint::SetUpdated () ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP()); if (process_sp) m_mod_id = process_sp->GetModID(); - m_first_update = false; m_needs_update = false; } @@ -3900,9 +4008,7 @@ ValueObject::ClearUserVisibleData(uint32_t clear_mask) m_location_str.clear(); if ((clear_mask & eClearUserVisibleDataItemsSummary) == eClearUserVisibleDataItemsSummary) - { m_summary_str.clear(); - } if ((clear_mask & eClearUserVisibleDataItemsDescription) == eClearUserVisibleDataItemsDescription) m_object_desc_str.clear(); @@ -3912,6 +4018,9 @@ ValueObject::ClearUserVisibleData(uint32_t clear_mask) if (m_synthetic_value) m_synthetic_value = NULL; } + + if ((clear_mask & eClearUserVisibleDataItemsValidator) == eClearUserVisibleDataItemsValidator) + m_validation_result.reset(); } SymbolContextScope * @@ -3930,6 +4039,16 @@ ValueObject::CreateValueObjectFromExpression (const char* name, const char* expression, const ExecutionContext& exe_ctx) { + return CreateValueObjectFromExpression(name, expression, exe_ctx, EvaluateExpressionOptions()); +} + + +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromExpression (const char* name, + const char* expression, + const ExecutionContext& exe_ctx, + const EvaluateExpressionOptions& options) +{ lldb::ValueObjectSP retval_sp; lldb::TargetSP target_sp(exe_ctx.GetTargetSP()); if (!target_sp) @@ -3938,7 +4057,8 @@ ValueObject::CreateValueObjectFromExpression (const char* name, return retval_sp; target_sp->EvaluateExpression (expression, exe_ctx.GetFrameSP().get(), - retval_sp); + retval_sp, + options); if (retval_sp && name && *name) retval_sp->SetName(ConstString(name)); return retval_sp; @@ -3960,7 +4080,7 @@ ValueObject::CreateValueObjectFromAddress (const char* name, pointer_type, ConstString(name), buffer, - lldb::endian::InlHostByteOrder(), + exe_ctx.GetByteOrder(), exe_ctx.GetAddressByteSize())); if (ptr_result_valobj_sp) { @@ -4057,3 +4177,77 @@ ValueObject::GetFormat () const } return m_format; } + +lldb::LanguageType +ValueObject::GetPreferredDisplayLanguage () +{ + lldb::LanguageType type = lldb::eLanguageTypeUnknown; + if (GetRoot()) + { + if (GetRoot() == this) + { + if (StackFrameSP frame_sp = GetFrameSP()) + { + const SymbolContext& sc(frame_sp->GetSymbolContext(eSymbolContextCompUnit)); + if (CompileUnit* cu = sc.comp_unit) + type = cu->GetLanguage(); + } + } + else + { + type = GetRoot()->GetPreferredDisplayLanguage(); + } + } + return type; +} + +bool +ValueObject::CanProvideValue () +{ + // we need to support invalid types as providers of values because some bare-board + // debugging scenarios have no notion of types, but still manage to have raw numeric + // values for things like registers. sigh. + const ClangASTType &type(GetClangType()); + return (false == type.IsValid()) || (0 != (type.GetTypeInfo() & eTypeHasValue)); +} + +bool +ValueObject::IsChecksumEmpty () +{ + return m_value_checksum.empty(); +} + +ValueObjectSP +ValueObject::Persist () +{ + if (!UpdateValueIfNeeded()) + return nullptr; + + TargetSP target_sp(GetTargetSP()); + if (!target_sp) + return nullptr; + + ConstString name(target_sp->GetPersistentVariables().GetNextPersistentVariableName()); + + ClangExpressionVariableSP clang_var_sp(new ClangExpressionVariable(target_sp.get(), GetValue(), name)); + if (clang_var_sp) + { + clang_var_sp->m_live_sp = clang_var_sp->m_frozen_sp; + clang_var_sp->m_flags |= ClangExpressionVariable::EVIsProgramReference; + target_sp->GetPersistentVariables().AddVariable(clang_var_sp); + } + + return clang_var_sp->GetValueObject(); +} + +bool +ValueObject::IsSyntheticChildrenGenerated () +{ + return m_is_synthetic_children_generated; +} + +void +ValueObject::SetSyntheticChildrenGenerated (bool b) +{ + m_is_synthetic_children_generated = b; +} diff --git a/source/Core/ValueObjectCast.cpp b/source/Core/ValueObjectCast.cpp index 4f4f8cc681d0..b20371b128df 100644 --- a/source/Core/ValueObjectCast.cpp +++ b/source/Core/ValueObjectCast.cpp @@ -102,7 +102,7 @@ ValueObjectCast::UpdateValue () //m_value.SetContext (Value::eContextTypeClangType, clang_type); m_value.SetClangType (clang_type); SetAddressTypeOfChildren(m_parent->GetAddressTypeOfChildren()); - if (clang_type.IsAggregateType ()) + if (!CanProvideValue()) { // this value object represents an aggregate type whose // children have values, but this object does not. So we diff --git a/source/Core/ValueObjectChild.cpp b/source/Core/ValueObjectChild.cpp index 33b91f9e30d1..2b3c8344af8f 100644 --- a/source/Core/ValueObjectChild.cpp +++ b/source/Core/ValueObjectChild.cpp @@ -208,7 +208,7 @@ ValueObjectChild::UpdateValue () { const bool thread_and_frame_only_if_stopped = true; ExecutionContext exe_ctx (GetExecutionContextRef().Lock(thread_and_frame_only_if_stopped)); - if (GetClangType().GetTypeInfo() & ClangASTType::eTypeHasValue) + if (GetClangType().GetTypeInfo() & lldb::eTypeHasValue) m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get()); else m_error.Clear(); // No value so nothing to read... diff --git a/source/Core/ValueObjectConstResult.cpp b/source/Core/ValueObjectConstResult.cpp index 387e171e3526..fc870d726221 100644 --- a/source/Core/ValueObjectConstResult.cpp +++ b/source/Core/ValueObjectConstResult.cpp @@ -122,9 +122,10 @@ ValueObjectConstResult::Create (ExecutionContextScope *exe_scope, ValueObjectSP ValueObjectConstResult::Create (ExecutionContextScope *exe_scope, Value &value, - const ConstString &name) + const ConstString &name, + Module *module) { - return (new ValueObjectConstResult (exe_scope, value, name))->GetSP(); + return (new ValueObjectConstResult (exe_scope, value, name, module))->GetSP(); } ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, @@ -222,14 +223,18 @@ ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, const Value &value, - const ConstString &name) : + const ConstString &name, + Module *module) : ValueObject (exe_scope), m_type_name (), m_byte_size (0), m_impl(this) { m_value = value; - m_value.GetData(m_data); + m_name = name; + ExecutionContext exe_ctx; + exe_scope->CalculateExecutionContext(exe_ctx); + m_error = m_value.GetValueAsData(&exe_ctx, m_data, 0, module); } ValueObjectConstResult::~ValueObjectConstResult() @@ -358,3 +363,8 @@ ValueObjectConstResult::GetDynamicValue (lldb::DynamicValueType use_dynamic) return ValueObjectSP(); } +lldb::LanguageType +ValueObjectConstResult::GetPreferredDisplayLanguage () +{ + return lldb::eLanguageTypeUnknown; +} diff --git a/source/Core/ValueObjectConstResultImpl.cpp b/source/Core/ValueObjectConstResultImpl.cpp index d3e275883509..733d767b7ee1 100644 --- a/source/Core/ValueObjectConstResultImpl.cpp +++ b/source/Core/ValueObjectConstResultImpl.cpp @@ -30,13 +30,6 @@ using namespace lldb; using namespace lldb_private; -// this macro enables a simpler implementation for some method calls in this object that relies only upon -// ValueObject knowing how to set the address type of its children correctly. the alternative implementation -// relies on being able to create a target copy of the frozen object, which makes it less bug-prone but less -// efficient as well. once we are confident the faster implementation is bug-free, this macro (and the slower -// implementations) can go -#define TRIVIAL_IMPL 1 - ValueObjectConstResultImpl::ValueObjectConstResultImpl (ValueObject* valobj, lldb::addr_t live_address) : m_impl_backend(valobj), @@ -48,38 +41,12 @@ ValueObjectConstResultImpl::ValueObjectConstResultImpl (ValueObject* valobj, } lldb::ValueObjectSP -ValueObjectConstResultImpl::DerefOnTarget() -{ - if (m_load_addr_backend.get() == NULL) - { - lldb::addr_t tgt_address = m_impl_backend->GetPointerValue(); - ExecutionContext exe_ctx (m_impl_backend->GetExecutionContextRef()); - m_load_addr_backend = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), - m_impl_backend->GetClangType(), - m_impl_backend->GetName(), - tgt_address, - eAddressTypeLoad, - exe_ctx.GetAddressByteSize()); - } - return m_load_addr_backend; -} - -lldb::ValueObjectSP ValueObjectConstResultImpl::Dereference (Error &error) { if (m_impl_backend == NULL) return lldb::ValueObjectSP(); -#if defined (TRIVIAL_IMPL) && TRIVIAL_IMPL == 1 return m_impl_backend->ValueObject::Dereference(error); -#else - m_impl_backend->UpdateValueIfNeeded(false); - - if (NeedsDerefOnTarget()) - return DerefOnTarget()->Dereference(error); - else - return m_impl_backend->ValueObject::Dereference(error); -#endif } ValueObject * @@ -139,7 +106,8 @@ ValueObjectConstResultImpl::CreateChildAtIndex (size_t idx, bool synthetic_array child_bitfield_bit_offset, child_is_base_class, child_is_deref_of_parent); - valobj->m_impl.SetLiveAddress(m_live_address+child_byte_offset); + if (m_live_address != LLDB_INVALID_ADDRESS) + valobj->m_impl.SetLiveAddress(m_live_address+child_byte_offset); } return valobj; @@ -151,16 +119,7 @@ ValueObjectConstResultImpl::GetSyntheticChildAtOffset (uint32_t offset, const Cl if (m_impl_backend == NULL) return lldb::ValueObjectSP(); -#if defined (TRIVIAL_IMPL) && TRIVIAL_IMPL == 1 return m_impl_backend->ValueObject::GetSyntheticChildAtOffset(offset, type, can_create); -#else - m_impl_backend->UpdateValueIfNeeded(false); - - if (NeedsDerefOnTarget()) - return DerefOnTarget()->GetSyntheticChildAtOffset(offset, type, can_create); - else - return m_impl_backend->ValueObject::GetSyntheticChildAtOffset(offset, type, can_create); -#endif } lldb::ValueObjectSP @@ -193,7 +152,7 @@ ValueObjectConstResultImpl::AddressOf (Error &error) return m_address_of_backend; } else - return lldb::ValueObjectSP(); + return m_impl_backend->ValueObject::AddressOf(error); } lldb::addr_t @@ -223,14 +182,5 @@ ValueObjectConstResultImpl::GetPointeeData (DataExtractor& data, { if (m_impl_backend == NULL) return 0; -#if defined (TRIVIAL_IMPL) && TRIVIAL_IMPL == 1 return m_impl_backend->ValueObject::GetPointeeData(data, item_idx, item_count); -#else - m_impl_backend->UpdateValueIfNeeded(false); - - if (NeedsDerefOnTarget() && m_impl_backend->IsPointerType()) - return DerefOnTarget()->GetPointeeData(data, item_idx, item_count); - else - return m_impl_backend->ValueObject::GetPointeeData(data, item_idx, item_count); -#endif } diff --git a/source/Core/ValueObjectDynamicValue.cpp b/source/Core/ValueObjectDynamicValue.cpp index a6fad7a9b1fd..1b8ec8083f8f 100644 --- a/source/Core/ValueObjectDynamicValue.cpp +++ b/source/Core/ValueObjectDynamicValue.cpp @@ -241,16 +241,7 @@ ValueObjectDynamicValue::UpdateValue () { if (class_type_or_name.HasType()) { - // TypeSP are always generated from debug info - if (!class_type_or_name.HasTypeSP() && class_type_or_name.GetClangASTType().IsRuntimeGeneratedType()) - { - m_type_impl = TypeImpl(m_parent->GetClangType(),FixupTypeAndOrName(class_type_or_name, *m_parent).GetClangASTType()); - class_type_or_name.SetClangASTType(ClangASTType()); - } - else - { - m_type_impl = TypeImpl(FixupTypeAndOrName(class_type_or_name, *m_parent).GetClangASTType()); - } + m_type_impl = TypeImpl(m_parent->GetClangType(),FixupTypeAndOrName(class_type_or_name, *m_parent).GetClangASTType()); } else { @@ -329,7 +320,7 @@ ValueObjectDynamicValue::UpdateValue () m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get()); if (m_error.Success()) { - if (GetClangType().IsAggregateType ()) + if (!CanProvideValue()) { // this value object represents an aggregate type whose // children have values, but this object does not. So we diff --git a/source/Core/ValueObjectMemory.cpp b/source/Core/ValueObjectMemory.cpp index d2cbbfdda240..5fbe87b66522 100644 --- a/source/Core/ValueObjectMemory.cpp +++ b/source/Core/ValueObjectMemory.cpp @@ -233,7 +233,7 @@ ValueObjectMemory::UpdateValue () } } - if (GetClangType().IsAggregateType()) + if (!CanProvideValue()) { // this value object represents an aggregate type whose // children have values, but this object does not. So we diff --git a/source/Core/ValueObjectSyntheticFilter.cpp b/source/Core/ValueObjectSyntheticFilter.cpp index 18d36164989a..dafe73a5e57e 100644 --- a/source/Core/ValueObjectSyntheticFilter.cpp +++ b/source/Core/ValueObjectSyntheticFilter.cpp @@ -66,16 +66,17 @@ ValueObjectSynthetic::ValueObjectSynthetic (ValueObject &parent, lldb::Synthetic m_name_toindex(), m_synthetic_children_count(UINT32_MAX), m_parent_type_name(parent.GetTypeName()), - m_might_have_children(eLazyBoolCalculate) + m_might_have_children(eLazyBoolCalculate), + m_provides_value(eLazyBoolCalculate) { -#ifdef LLDB_CONFIGURATION_DEBUG +#ifdef FOOBAR std::string new_name(parent.GetName().AsCString()); new_name += "$$__synth__"; SetName (ConstString(new_name.c_str())); #else SetName(parent.GetName()); #endif - CopyParentData(); + CopyValueData(m_parent); CreateSynthFilter(); } @@ -181,8 +182,8 @@ ValueObjectSynthetic::UpdateValue () if (m_synth_filter_ap->Update() == false) { // filter said that cached values are stale - m_children_byindex.clear(); - m_name_toindex.clear(); + m_children_byindex.Clear(); + m_name_toindex.Clear(); // usually, an object's value can change but this does not alter its children count // for a synthetic VO that might indeed happen, so we need to tell the upper echelons // that they need to come back to us asking for children @@ -191,7 +192,20 @@ ValueObjectSynthetic::UpdateValue () m_might_have_children = eLazyBoolCalculate; } - CopyParentData(); + m_provides_value = eLazyBoolCalculate; + + lldb::ValueObjectSP synth_val(m_synth_filter_ap->GetSyntheticValue()); + + if (synth_val && synth_val->CanProvideValue()) + { + m_provides_value = eLazyBoolYes; + CopyValueData(synth_val.get()); + } + else + { + m_provides_value = eLazyBoolNo; + CopyValueData(m_parent); + } SetValueIsValid(true); return true; @@ -202,23 +216,22 @@ ValueObjectSynthetic::GetChildAtIndex (size_t idx, bool can_create) { UpdateValueIfNeeded(); - ByIndexIterator iter = m_children_byindex.find(idx); - - if (iter == m_children_byindex.end()) + ValueObject *valobj; + if (m_children_byindex.GetValueForKey(idx, valobj) == false) { if (can_create && m_synth_filter_ap.get() != NULL) { lldb::ValueObjectSP synth_guy = m_synth_filter_ap->GetChildAtIndex (idx); if (!synth_guy) return synth_guy; - m_children_byindex[idx]= synth_guy.get(); + m_children_byindex.SetValueForKey(idx, synth_guy.get()); return synth_guy; } else return lldb::ValueObjectSP(); } else - return iter->second->GetSP(); + return valobj->GetSP(); } lldb::ValueObjectSP @@ -239,20 +252,21 @@ ValueObjectSynthetic::GetIndexOfChildWithName (const ConstString &name) { UpdateValueIfNeeded(); - NameToIndexIterator iter = m_name_toindex.find(name.GetCString()); + uint32_t found_index = UINT32_MAX; + bool did_find = m_name_toindex.GetValueForKey(name.GetCString(), found_index); - if (iter == m_name_toindex.end() && m_synth_filter_ap.get() != NULL) + if (!did_find && m_synth_filter_ap.get() != NULL) { uint32_t index = m_synth_filter_ap->GetIndexOfChildWithName (name); if (index == UINT32_MAX) return index; - m_name_toindex[name.GetCString()] = index; + m_name_toindex.SetValueForKey(name.GetCString(), index); return index; } - else if (iter == m_name_toindex.end() && m_synth_filter_ap.get() == NULL) + else if (!did_find && m_synth_filter_ap.get() == NULL) return UINT32_MAX; else /*if (iter != m_name_toindex.end())*/ - return iter->second; + return found_index; } bool @@ -268,9 +282,19 @@ ValueObjectSynthetic::GetNonSyntheticValue () } void -ValueObjectSynthetic::CopyParentData () +ValueObjectSynthetic::CopyValueData (ValueObject *source) { - m_value = m_parent->GetValue(); + m_value = (source->UpdateValueIfNeeded(), source->GetValue()); ExecutionContext exe_ctx (GetExecutionContextRef()); m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get()); } + +bool +ValueObjectSynthetic::CanProvideValue () +{ + if (!UpdateValueIfNeeded()) + return false; + if (m_provides_value == eLazyBoolYes) + return true; + return m_parent->CanProvideValue(); +} diff --git a/source/Core/ValueObjectVariable.cpp b/source/Core/ValueObjectVariable.cpp index 225dc02c8add..ab74a50e7cd5 100644 --- a/source/Core/ValueObjectVariable.cpp +++ b/source/Core/ValueObjectVariable.cpp @@ -171,14 +171,44 @@ ValueObjectVariable::UpdateValue () m_value.SetClangType(clang_type); Value::ValueType value_type = m_value.GetValueType(); - + + Process *process = exe_ctx.GetProcessPtr(); + const bool process_is_alive = process && process->IsAlive(); + const uint32_t type_info = clang_type.GetTypeInfo(); + const bool is_pointer_or_ref = (type_info & (lldb::eTypeIsPointer | lldb::eTypeIsReference)) != 0; + switch (value_type) { case Value::eValueTypeFileAddress: - SetAddressTypeOfChildren(eAddressTypeFile); + // If this type is a pointer, then its children will be considered load addresses + // if the pointer or reference is dereferenced, but only if the process is alive. + // + // There could be global variables like in the following code: + // struct LinkedListNode { Foo* foo; LinkedListNode* next; }; + // Foo g_foo1; + // Foo g_foo2; + // LinkedListNode g_second_node = { &g_foo2, NULL }; + // LinkedListNode g_first_node = { &g_foo1, &g_second_node }; + // + // When we aren't running, we should be able to look at these variables using + // the "target variable" command. Children of the "g_first_node" always will + // be of the same address type as the parent. But children of the "next" member of + // LinkedListNode will become load addresses if we have a live process, or remain + // what a file address if it what a file address. + if (process_is_alive && is_pointer_or_ref) + SetAddressTypeOfChildren(eAddressTypeLoad); + else + SetAddressTypeOfChildren(eAddressTypeFile); break; case Value::eValueTypeHostAddress: - SetAddressTypeOfChildren(eAddressTypeHost); + // Same as above for load addresses, except children of pointer or refs are always + // load addresses. Host addresses are used to store freeze dried variables. If this + // type is a struct, the entire struct contents will be copied into the heap of the + // LLDB process, but we do not currrently follow any pointers. + if (is_pointer_or_ref) + SetAddressTypeOfChildren(eAddressTypeLoad); + else + SetAddressTypeOfChildren(eAddressTypeHost); break; case Value::eValueTypeLoadAddress: case Value::eValueTypeScalar: @@ -209,8 +239,7 @@ ValueObjectVariable::UpdateValue () // Make sure this type has a value before we try and read it // If we have a file address, convert it to a load address if we can. - Process *process = exe_ctx.GetProcessPtr(); - if (value_type == Value::eValueTypeFileAddress && process && process->IsAlive()) + if (value_type == Value::eValueTypeFileAddress && process_is_alive) { lldb::addr_t file_addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); if (file_addr != LLDB_INVALID_ADDRESS) @@ -234,7 +263,7 @@ ValueObjectVariable::UpdateValue () } } - if (GetClangType().IsAggregateType()) + if (!CanProvideValue()) { // this value object represents an aggregate type whose // children have values, but this object does not. So we @@ -248,6 +277,8 @@ ValueObjectVariable::UpdateValue () Value value(m_value); value.SetContext(Value::eContextTypeVariable, variable); m_error = value.GetValueAsData(&exe_ctx, m_data, 0, GetModule().get()); + + SetValueDidChange (value_type != old_value.GetValueType() || m_value.GetScalar() != old_value.GetScalar()); } break; } diff --git a/source/DataFormatters/CF.cpp b/source/DataFormatters/CF.cpp index e131b68096fd..483419e5ac3f 100644 --- a/source/DataFormatters/CF.cpp +++ b/source/DataFormatters/CF.cpp @@ -26,7 +26,7 @@ using namespace lldb_private; using namespace lldb_private::formatters; bool -lldb_private::formatters::CFAbsoluteTimeSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::CFAbsoluteTimeSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { time_t epoch = GetOSXEpoch(); epoch = epoch + (time_t)valobj.GetValueAsUnsigned(0); @@ -41,7 +41,7 @@ lldb_private::formatters::CFAbsoluteTimeSummaryProvider (ValueObject& valobj, St } bool -lldb_private::formatters::CFBagSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::CFBagSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -105,7 +105,7 @@ lldb_private::formatters::CFBagSummaryProvider (ValueObject& valobj, Stream& str } bool -lldb_private::formatters::CFBitVectorSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::CFBitVectorSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -235,7 +235,7 @@ lldb_private::formatters::CFBitVectorSummaryProvider (ValueObject& valobj, Strea } bool -lldb_private::formatters::CFBinaryHeapSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::CFBinaryHeapSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) diff --git a/source/DataFormatters/CXXFormatterFunctions.cpp b/source/DataFormatters/CXXFormatterFunctions.cpp index ae5b35fd2b1c..04cdadf5a98f 100644 --- a/source/DataFormatters/CXXFormatterFunctions.cpp +++ b/source/DataFormatters/CXXFormatterFunctions.cpp @@ -10,6 +10,8 @@ #include "lldb/lldb-python.h" #include "lldb/DataFormatters/CXXFormatterFunctions.h" +#include "lldb/DataFormatters/StringPrinter.h" +#include "lldb/DataFormatters/TypeSummary.h" #include "llvm/Support/ConvertUTF.h" @@ -20,10 +22,16 @@ #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Host/Endian.h" #include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" +#include "lldb/Utility/ProcessStructReader.h" + #include <algorithm> +#if __ANDROID_NDK__ +#include <sys/types.h> +#endif using namespace lldb; using namespace lldb_private; @@ -187,283 +195,53 @@ lldb_private::formatters::CallSelectorOnObject (ValueObject &valobj, return valobj_sp; } -// use this call if you already have an LLDB-side buffer for the data -template<typename SourceDataType> -static bool -DumpUTFBufferToStream (ConversionResult (*ConvertFunction) (const SourceDataType**, - const SourceDataType*, - UTF8**, - UTF8*, - ConversionFlags), - DataExtractor& data, - Stream& stream, - char prefix_token = '@', - char quote = '"', - uint32_t sourceSize = 0) +bool +lldb_private::formatters::FunctionPointerSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { - if (prefix_token != 0) - stream.Printf("%c",prefix_token); - if (quote != 0) - stream.Printf("%c",quote); - if (data.GetByteSize() && data.GetDataStart() && data.GetDataEnd()) + std::string destination; + StreamString sstr; + AddressType func_ptr_address_type = eAddressTypeInvalid; + addr_t func_ptr_address = valobj.GetPointerValue (&func_ptr_address_type); + if (func_ptr_address != 0 && func_ptr_address != LLDB_INVALID_ADDRESS) { - const int bufferSPSize = data.GetByteSize(); - if (sourceSize == 0) - { - const int origin_encoding = 8*sizeof(SourceDataType); - sourceSize = bufferSPSize/(origin_encoding / 4); - } - - SourceDataType *data_ptr = (SourceDataType*)data.GetDataStart(); - SourceDataType *data_end_ptr = data_ptr + sourceSize; - - while (data_ptr < data_end_ptr) + switch (func_ptr_address_type) { - if (!*data_ptr) - { - data_end_ptr = data_ptr; + case eAddressTypeInvalid: + case eAddressTypeFile: + case eAddressTypeHost: break; + + case eAddressTypeLoad: + { + ExecutionContext exe_ctx (valobj.GetExecutionContextRef()); + + Address so_addr; + Target *target = exe_ctx.GetTargetPtr(); + if (target && target->GetSectionLoadList().IsEmpty() == false) + { + if (target->GetSectionLoadList().ResolveLoadAddress(func_ptr_address, so_addr)) + { + so_addr.Dump (&sstr, + exe_ctx.GetBestExecutionContextScope(), + Address::DumpStyleResolvedDescription, + Address::DumpStyleSectionNameOffset); + } + } } - data_ptr++; - } - - data_ptr = (SourceDataType*)data.GetDataStart(); - - lldb::DataBufferSP utf8_data_buffer_sp; - UTF8* utf8_data_ptr = nullptr; - UTF8* utf8_data_end_ptr = nullptr; - - if (ConvertFunction) - { - utf8_data_buffer_sp.reset(new DataBufferHeap(4*bufferSPSize,0)); - utf8_data_ptr = (UTF8*)utf8_data_buffer_sp->GetBytes(); - utf8_data_end_ptr = utf8_data_ptr + utf8_data_buffer_sp->GetByteSize(); - ConvertFunction ( (const SourceDataType**)&data_ptr, data_end_ptr, &utf8_data_ptr, utf8_data_end_ptr, lenientConversion ); - utf8_data_ptr = (UTF8*)utf8_data_buffer_sp->GetBytes(); // needed because the ConvertFunction will change the value of the data_ptr - } - else - { - // just copy the pointers - the cast is necessary to make the compiler happy - // but this should only happen if we are reading UTF8 data - utf8_data_ptr = (UTF8*)data_ptr; - utf8_data_end_ptr = (UTF8*)data_end_ptr; - } - - // since we tend to accept partial data (and even partially malformed data) - // we might end up with no NULL terminator before the end_ptr - // hence we need to take a slower route and ensure we stay within boundaries - for (;utf8_data_ptr != utf8_data_end_ptr; utf8_data_ptr++) - { - if (!*utf8_data_ptr) break; - stream.Printf("%c",*utf8_data_ptr); } } - if (quote != 0) - stream.Printf("%c",quote); - return true; -} - -template<typename SourceDataType> -class ReadUTFBufferAndDumpToStreamOptions -{ -public: - typedef ConversionResult (*ConvertFunctionType) (const SourceDataType**, - const SourceDataType*, - UTF8**, - UTF8*, - ConversionFlags); - - ReadUTFBufferAndDumpToStreamOptions () : - m_conversion_function(NULL), - m_location(0), - m_process_sp(), - m_stream(NULL), - m_prefix_token('@'), - m_quote('"'), - m_source_size(0), - m_needs_zero_termination(true) - { - } - - ReadUTFBufferAndDumpToStreamOptions& - SetConversionFunction (ConvertFunctionType f) - { - m_conversion_function = f; - return *this; - } - - ConvertFunctionType - GetConversionFunction () const - { - return m_conversion_function; - } - - ReadUTFBufferAndDumpToStreamOptions& - SetLocation (uint64_t l) - { - m_location = l; - return *this; - } - - uint64_t - GetLocation () const - { - return m_location; - } - - ReadUTFBufferAndDumpToStreamOptions& - SetProcessSP (ProcessSP p) - { - m_process_sp = p; - return *this; - } - - ProcessSP - GetProcessSP () const - { - return m_process_sp; - } - - ReadUTFBufferAndDumpToStreamOptions& - SetStream (Stream* s) - { - m_stream = s; - return *this; - } - - Stream* - GetStream () const - { - return m_stream; - } - - ReadUTFBufferAndDumpToStreamOptions& - SetPrefixToken (char p) - { - m_prefix_token = p; - return *this; - } - - char - GetPrefixToken () const - { - return m_prefix_token; - } - - ReadUTFBufferAndDumpToStreamOptions& - SetQuote (char q) - { - m_quote = q; - return *this; - } - - char - GetQuote () const - { - return m_quote; - } - - ReadUTFBufferAndDumpToStreamOptions& - SetSourceSize (uint32_t s) - { - m_source_size = s; - return *this; - } - - uint32_t - GetSourceSize () const - { - return m_source_size; - } - - ReadUTFBufferAndDumpToStreamOptions& - SetNeedsZeroTermination (bool z) + if (sstr.GetSize() > 0) { - m_needs_zero_termination = z; - return *this; - } - - bool - GetNeedsZeroTermination () const - { - return m_needs_zero_termination; - } - -private: - ConvertFunctionType m_conversion_function; - uint64_t m_location; - ProcessSP m_process_sp; - Stream* m_stream; - char m_prefix_token; - char m_quote; - uint32_t m_source_size; - bool m_needs_zero_termination; -}; - -template<typename SourceDataType> -static bool -ReadUTFBufferAndDumpToStream (const ReadUTFBufferAndDumpToStreamOptions<SourceDataType>& options) -{ - if (options.GetLocation() == 0 || options.GetLocation() == LLDB_INVALID_ADDRESS) - return false; - - ProcessSP process_sp(options.GetProcessSP()); - - if (!process_sp) - return false; - - const int type_width = sizeof(SourceDataType); - const int origin_encoding = 8 * type_width ; - if (origin_encoding != 8 && origin_encoding != 16 && origin_encoding != 32) - return false; - // if not UTF8, I need a conversion function to return proper UTF8 - if (origin_encoding != 8 && !options.GetConversionFunction()) - return false; - - if (!options.GetStream()) - return false; - - uint32_t sourceSize = options.GetSourceSize(); - bool needs_zero_terminator = options.GetNeedsZeroTermination(); - - if (!sourceSize) - { - sourceSize = process_sp->GetTarget().GetMaximumSizeOfStringSummary(); - needs_zero_terminator = true; + stream.Printf("(%s)", sstr.GetData()); + return true; } else - sourceSize = std::min(sourceSize,process_sp->GetTarget().GetMaximumSizeOfStringSummary()); - - const int bufferSPSize = sourceSize * type_width; - - lldb::DataBufferSP buffer_sp(new DataBufferHeap(bufferSPSize,0)); - - if (!buffer_sp->GetBytes()) return false; - - Error error; - char *buffer = reinterpret_cast<char *>(buffer_sp->GetBytes()); - - size_t data_read = 0; - if (needs_zero_terminator) - data_read = process_sp->ReadStringFromMemory(options.GetLocation(), buffer, bufferSPSize, error, type_width); - else - data_read = process_sp->ReadMemoryFromInferior(options.GetLocation(), (char*)buffer_sp->GetBytes(), bufferSPSize, error); - - if (error.Fail() || data_read == 0) - { - options.GetStream()->Printf("unable to read data"); - return true; - } - - DataExtractor data(buffer_sp, process_sp->GetByteOrder(), process_sp->GetAddressByteSize()); - - return DumpUTFBufferToStream(options.GetConversionFunction(), data, *options.GetStream(), options.GetPrefixToken(), options.GetQuote(), sourceSize); } bool -lldb_private::formatters::Char16StringSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::Char16StringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions&) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -474,14 +252,13 @@ lldb_private::formatters::Char16StringSummaryProvider (ValueObject& valobj, Stre if (!valobj_addr) return false; - ReadUTFBufferAndDumpToStreamOptions<UTF16> options; + ReadStringAndDumpToStreamOptions options(valobj); options.SetLocation(valobj_addr); - options.SetConversionFunction(ConvertUTF16toUTF8); options.SetProcessSP(process_sp); options.SetStream(&stream); options.SetPrefixToken('u'); - if (!ReadUTFBufferAndDumpToStream(options)) + if (!ReadStringAndDumpToStream<StringElementType::UTF16>(options)) { stream.Printf("Summary Unavailable"); return true; @@ -491,7 +268,7 @@ lldb_private::formatters::Char16StringSummaryProvider (ValueObject& valobj, Stre } bool -lldb_private::formatters::Char32StringSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::Char32StringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions&) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -502,14 +279,13 @@ lldb_private::formatters::Char32StringSummaryProvider (ValueObject& valobj, Stre if (!valobj_addr) return false; - ReadUTFBufferAndDumpToStreamOptions<UTF32> options; + ReadStringAndDumpToStreamOptions options(valobj); options.SetLocation(valobj_addr); - options.SetConversionFunction(ConvertUTF32toUTF8); options.SetProcessSP(process_sp); options.SetStream(&stream); options.SetPrefixToken('U'); - if (!ReadUTFBufferAndDumpToStream(options)) + if (!ReadStringAndDumpToStream<StringElementType::UTF32>(options)) { stream.Printf("Summary Unavailable"); return true; @@ -519,7 +295,7 @@ lldb_private::formatters::Char32StringSummaryProvider (ValueObject& valobj, Stre } bool -lldb_private::formatters::WCharStringSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::WCharStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions&) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -543,45 +319,20 @@ lldb_private::formatters::WCharStringSummaryProvider (ValueObject& valobj, Strea ClangASTType wchar_clang_type = ClangASTContext::GetBasicType(ast, lldb::eBasicTypeWChar); const uint32_t wchar_size = wchar_clang_type.GetBitSize(); + ReadStringAndDumpToStreamOptions options(valobj); + options.SetLocation(data_addr); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('L'); + switch (wchar_size) { case 8: - { - // utf 8 - - ReadUTFBufferAndDumpToStreamOptions<UTF8> options; - options.SetLocation(data_addr); - options.SetConversionFunction(nullptr); - options.SetProcessSP(process_sp); - options.SetStream(&stream); - options.SetPrefixToken('L'); - - return ReadUTFBufferAndDumpToStream(options); - } + return ReadStringAndDumpToStream<StringElementType::UTF8>(options); case 16: - { - // utf 16 - ReadUTFBufferAndDumpToStreamOptions<UTF16> options; - options.SetLocation(data_addr); - options.SetConversionFunction(ConvertUTF16toUTF8); - options.SetProcessSP(process_sp); - options.SetStream(&stream); - options.SetPrefixToken('L'); - - return ReadUTFBufferAndDumpToStream(options); - } + return ReadStringAndDumpToStream<StringElementType::UTF16>(options); case 32: - { - // utf 32 - ReadUTFBufferAndDumpToStreamOptions<UTF32> options; - options.SetLocation(data_addr); - options.SetConversionFunction(ConvertUTF32toUTF8); - options.SetProcessSP(process_sp); - options.SetStream(&stream); - options.SetPrefixToken('L'); - - return ReadUTFBufferAndDumpToStream(options); - } + return ReadStringAndDumpToStream<StringElementType::UTF32>(options); default: stream.Printf("size for wchar_t is not valid"); return true; @@ -590,7 +341,7 @@ lldb_private::formatters::WCharStringSummaryProvider (ValueObject& valobj, Strea } bool -lldb_private::formatters::Char16SummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::Char16SummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions&) { DataExtractor data; Error error; @@ -604,11 +355,18 @@ lldb_private::formatters::Char16SummaryProvider (ValueObject& valobj, Stream& st if (!value.empty()) stream.Printf("%s ", value.c_str()); - return DumpUTFBufferToStream<UTF16>(ConvertUTF16toUTF8,data,stream, 'u','\'',1); + ReadBufferAndDumpToStreamOptions options(valobj); + options.SetData(data); + options.SetStream(&stream); + options.SetPrefixToken('u'); + options.SetQuote('\''); + options.SetSourceSize(1); + + return ReadBufferAndDumpToStream<StringElementType::UTF16>(options); } bool -lldb_private::formatters::Char32SummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::Char32SummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions&) { DataExtractor data; Error error; @@ -622,11 +380,18 @@ lldb_private::formatters::Char32SummaryProvider (ValueObject& valobj, Stream& st if (!value.empty()) stream.Printf("%s ", value.c_str()); - return DumpUTFBufferToStream<UTF32>(ConvertUTF32toUTF8,data,stream, 'U','\'',1); + ReadBufferAndDumpToStreamOptions options(valobj); + options.SetData(data); + options.SetStream(&stream); + options.SetPrefixToken('U'); + options.SetQuote('\''); + options.SetSourceSize(1); + + return ReadBufferAndDumpToStream<StringElementType::UTF32>(options); } bool -lldb_private::formatters::WCharSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::WCharSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions&) { DataExtractor data; Error error; @@ -635,55 +400,14 @@ lldb_private::formatters::WCharSummaryProvider (ValueObject& valobj, Stream& str if (error.Fail()) return false; - clang::ASTContext* ast = valobj.GetClangType().GetASTContext(); - - if (!ast) - return false; - - ClangASTType wchar_clang_type = ClangASTContext::GetBasicType(ast, lldb::eBasicTypeWChar); - const uint32_t wchar_size = wchar_clang_type.GetBitSize(); - std::string value; + ReadBufferAndDumpToStreamOptions options(valobj); + options.SetData(data); + options.SetStream(&stream); + options.SetPrefixToken('L'); + options.SetQuote('\''); + options.SetSourceSize(1); - switch (wchar_size) - { - case 8: - // utf 8 - valobj.GetValueAsCString(lldb::eFormatChar, value); - if (!value.empty()) - stream.Printf("%s ", value.c_str()); - return DumpUTFBufferToStream<UTF8>(nullptr, - data, - stream, - 'L', - '\'', - 1); - case 16: - // utf 16 - valobj.GetValueAsCString(lldb::eFormatUnicode16, value); - if (!value.empty()) - stream.Printf("%s ", value.c_str()); - return DumpUTFBufferToStream<UTF16>(ConvertUTF16toUTF8, - data, - stream, - 'L', - '\'', - 1); - case 32: - // utf 32 - valobj.GetValueAsCString(lldb::eFormatUnicode32, value); - if (!value.empty()) - stream.Printf("%s ", value.c_str()); - return DumpUTFBufferToStream<UTF32>(ConvertUTF32toUTF8, - data, - stream, - 'L', - '\'', - 1); - default: - stream.Printf("size for wchar_t is not valid"); - return true; - } - return true; + return ReadBufferAndDumpToStream<StringElementType::UTF16>(options); } // the field layout in a libc++ string (cap, side, data or data, size, cap) @@ -769,7 +493,7 @@ ExtractLibcxxStringInfo (ValueObject& valobj, } bool -lldb_private::formatters::LibcxxWStringSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::LibcxxWStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { uint64_t size = 0; ValueObjectSP location_sp((ValueObject*)nullptr); @@ -782,34 +506,45 @@ lldb_private::formatters::LibcxxWStringSummaryProvider (ValueObject& valobj, Str } if (!location_sp) return false; - return WCharStringSummaryProvider(*location_sp.get(), stream); + return WCharStringSummaryProvider(*location_sp.get(), stream, options); } bool -lldb_private::formatters::LibcxxStringSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::LibcxxStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& summary_options) { uint64_t size = 0; ValueObjectSP location_sp((ValueObject*)nullptr); + if (!ExtractLibcxxStringInfo(valobj, location_sp, size)) return false; + if (size == 0) { stream.Printf("\"\""); return true; } + if (!location_sp) return false; - Error error; - if (location_sp->ReadPointedString(stream, - error, - 0, // max length is decided by the settings - false) == 0) // do not honor array (terminates on first 0 byte even for a char[]) - stream.Printf("\"\""); // if nothing was read, print an empty string - return error.Success(); + + DataExtractor extractor; + if (summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryCapped) + size = std::min<decltype(size)>(size, valobj.GetTargetSP()->GetMaximumSizeOfStringSummary()); + location_sp->GetPointeeData(extractor, 0, size); + + ReadBufferAndDumpToStreamOptions options(valobj); + options.SetData(extractor); // none of this matters for a string - pass some defaults + options.SetStream(&stream); + options.SetPrefixToken(0); + options.SetQuote('"'); + options.SetSourceSize(size); + lldb_private::formatters::ReadBufferAndDumpToStream<lldb_private::formatters::StringElementType::ASCII>(options); + + return true; } bool -lldb_private::formatters::ObjCClassSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::ObjCClassSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -886,7 +621,7 @@ lldb_private::formatters::ObjCClassSyntheticFrontEndCreator (CXXSyntheticChildre template<bool needs_at> bool -lldb_private::formatters::NSDataSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::NSDataSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -1037,8 +772,28 @@ lldb_private::formatters::NSTaggedString_SummaryProvider (ObjCLanguageRuntime::C return true; } +static ClangASTType +GetNSPathStore2Type (Target &target) +{ + static ConstString g_type_name("__lldb_autogen_nspathstore2"); + + ClangASTContext *ast_ctx = target.GetScratchClangASTContext(); + + if (!ast_ctx) + return ClangASTType(); + + ClangASTType voidstar = ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType(); + ClangASTType uint32 = ast_ctx->GetIntTypeFromBitSize(32, false); + + return ast_ctx->GetOrCreateStructForIdentifier(g_type_name, { + {"isa",voidstar}, + {"lengthAndRef",uint32}, + {"buffer",voidstar} + }); +} + bool -lldb_private::formatters::NSStringSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::NSStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& summary_options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -1131,8 +886,7 @@ lldb_private::formatters::NSStringSummaryProvider (ValueObject& valobj, Stream& return false; if (has_explicit_length && is_unicode) { - ReadUTFBufferAndDumpToStreamOptions<UTF16> options; - options.SetConversionFunction(ConvertUTF16toUTF8); + ReadStringAndDumpToStreamOptions options(valobj); options.SetLocation(location); options.SetProcessSP(process_sp); options.SetStream(&stream); @@ -1140,10 +894,21 @@ lldb_private::formatters::NSStringSummaryProvider (ValueObject& valobj, Stream& options.SetQuote('"'); options.SetSourceSize(explicit_length); options.SetNeedsZeroTermination(false); - return ReadUTFBufferAndDumpToStream (options); + options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped); + return ReadStringAndDumpToStream<StringElementType::UTF16>(options); } else - return ReadAsciiBufferAndDumpToStream(location+1,process_sp,stream, explicit_length); + { + ReadStringAndDumpToStreamOptions options(valobj); + options.SetLocation(location+1); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('@'); + options.SetSourceSize(explicit_length); + options.SetNeedsZeroTermination(false); + options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped); + return ReadStringAndDumpToStream<StringElementType::ASCII>(options); + } } else if (is_inline && has_explicit_length && !is_unicode && !is_special && !is_mutable) { @@ -1169,8 +934,7 @@ lldb_private::formatters::NSStringSummaryProvider (ValueObject& valobj, Stream& if (error.Fail()) return false; } - ReadUTFBufferAndDumpToStreamOptions<UTF16> options; - options.SetConversionFunction(ConvertUTF16toUTF8); + ReadStringAndDumpToStreamOptions options(valobj); options.SetLocation(location); options.SetProcessSP(process_sp); options.SetStream(&stream); @@ -1178,13 +942,16 @@ lldb_private::formatters::NSStringSummaryProvider (ValueObject& valobj, Stream& options.SetQuote('"'); options.SetSourceSize(explicit_length); options.SetNeedsZeroTermination(has_explicit_length == false); - return ReadUTFBufferAndDumpToStream (options); + options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped); + return ReadStringAndDumpToStream<StringElementType::UTF16> (options); } else if (is_special) { - uint64_t location = valobj_addr + (ptr_size == 8 ? 12 : 8); - ReadUTFBufferAndDumpToStreamOptions<UTF16> options; - options.SetConversionFunction(ConvertUTF16toUTF8); + ProcessStructReader reader(valobj.GetProcessSP().get(), valobj.GetValueAsUnsigned(0), GetNSPathStore2Type(*valobj.GetTargetSP())); + explicit_length = reader.GetField<uint32_t>(ConstString("lengthAndRef")) >> 20; + lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4; + + ReadStringAndDumpToStreamOptions options(valobj); options.SetLocation(location); options.SetProcessSP(process_sp); options.SetStream(&stream); @@ -1192,14 +959,22 @@ lldb_private::formatters::NSStringSummaryProvider (ValueObject& valobj, Stream& options.SetQuote('"'); options.SetSourceSize(explicit_length); options.SetNeedsZeroTermination(has_explicit_length == false); - return ReadUTFBufferAndDumpToStream (options); + options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped); + return ReadStringAndDumpToStream<StringElementType::UTF16> (options); } else if (is_inline) { uint64_t location = valobj_addr + 2*ptr_size; if (!has_explicit_length) location++; - return ReadAsciiBufferAndDumpToStream(location,process_sp,stream,explicit_length); + ReadStringAndDumpToStreamOptions options(valobj); + options.SetLocation(location); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('@'); + options.SetSourceSize(explicit_length); + options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped); + return ReadStringAndDumpToStream<StringElementType::ASCII>(options); } else { @@ -1209,16 +984,19 @@ lldb_private::formatters::NSStringSummaryProvider (ValueObject& valobj, Stream& return false; if (has_explicit_length && !has_null) explicit_length++; // account for the fact that there is no NULL and we need to have one added - return ReadAsciiBufferAndDumpToStream(location,process_sp,stream,explicit_length); + ReadStringAndDumpToStreamOptions options(valobj); + options.SetLocation(location); + options.SetProcessSP(process_sp); + options.SetPrefixToken('@'); + options.SetStream(&stream); + options.SetSourceSize(explicit_length); + options.SetIgnoreMaxLength(summary_options.GetCapping() == TypeSummaryCapping::eTypeSummaryUncapped); + return ReadStringAndDumpToStream<StringElementType::ASCII>(options); } - - stream.Printf("class name = %s",class_name); - return true; - } bool -lldb_private::formatters::NSAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::NSAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { TargetSP target_sp(valobj.GetTargetSP()); if (!target_sp) @@ -1241,38 +1019,38 @@ lldb_private::formatters::NSAttributedStringSummaryProvider (ValueObject& valobj ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData("string_data", data, exe_ctx, type)); child_sp->GetValueAsUnsigned(0); if (child_sp) - return NSStringSummaryProvider(*child_sp, stream); + return NSStringSummaryProvider(*child_sp, stream, options); return false; } bool -lldb_private::formatters::NSMutableAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::NSMutableAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { - return NSAttributedStringSummaryProvider(valobj, stream); + return NSAttributedStringSummaryProvider(valobj, stream, options); } bool -lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { stream.Printf("%s",valobj.GetObjectDescription()); return true; } bool -lldb_private::formatters::ObjCBOOLSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::ObjCBOOLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { const uint32_t type_info = valobj.GetClangType().GetTypeInfo(); ValueObjectSP real_guy_sp = valobj.GetSP(); - if (type_info & ClangASTType::eTypeIsPointer) + if (type_info & eTypeIsPointer) { Error err; real_guy_sp = valobj.Dereference(err); if (err.Fail() || !real_guy_sp) return false; } - else if (type_info & ClangASTType::eTypeIsReference) + else if (type_info & eTypeIsReference) { real_guy_sp = valobj.GetChildAtIndex(0, true); if (!real_guy_sp) @@ -1290,7 +1068,7 @@ lldb_private::formatters::ObjCBOOLSummaryProvider (ValueObject& valobj, Stream& template <bool is_sel_ptr> bool -lldb_private::formatters::ObjCSELSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::ObjCSELSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { lldb::ValueObjectSP valobj_sp; @@ -1398,7 +1176,7 @@ lldb_private::formatters::VectorIteratorSyntheticFrontEnd::Update() return false; Error err; m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); - m_item_sp = ValueObject::CreateValueObjectFromAddress("item", item_ptr->GetValueAsUnsigned(0), m_exe_ctx_ref, item_ptr->GetClangType().GetPointeeType()); + m_item_sp = CreateValueObjectFromAddress("item", item_ptr->GetValueAsUnsigned(0), m_exe_ctx_ref, item_ptr->GetClangType().GetPointeeType()); if (err.Fail()) m_item_sp.reset(); return false; @@ -1437,13 +1215,13 @@ lldb_private::formatters::VectorIteratorSyntheticFrontEnd::~VectorIteratorSynthe } template bool -lldb_private::formatters::NSDataSummaryProvider<true> (ValueObject&, Stream&) ; +lldb_private::formatters::NSDataSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&) ; template bool -lldb_private::formatters::NSDataSummaryProvider<false> (ValueObject&, Stream&) ; +lldb_private::formatters::NSDataSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&) ; template bool -lldb_private::formatters::ObjCSELSummaryProvider<true> (ValueObject&, Stream&) ; +lldb_private::formatters::ObjCSELSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&) ; template bool -lldb_private::formatters::ObjCSELSummaryProvider<false> (ValueObject&, Stream&) ; +lldb_private::formatters::ObjCSELSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&) ; diff --git a/source/DataFormatters/Cocoa.cpp b/source/DataFormatters/Cocoa.cpp index 8e92de4ddaa4..137fd4f483cc 100644 --- a/source/DataFormatters/Cocoa.cpp +++ b/source/DataFormatters/Cocoa.cpp @@ -26,7 +26,7 @@ using namespace lldb_private; using namespace lldb_private::formatters; bool -lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -60,7 +60,7 @@ lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), true)); StreamString summary_stream; - bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream); + bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream, options); if (was_nsstring_ok && summary_stream.GetSize() > 0) { stream.Printf("%s",summary_stream.GetData()); @@ -73,7 +73,7 @@ lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& } bool -lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -106,7 +106,7 @@ lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream uint64_t offset = ptr_size; ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType(), true)); StreamString summary_stream; - bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream); + bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream, options); if (was_nsstring_ok && summary_stream.GetSize() > 0) { stream.Printf("%s",summary_stream.GetData()); @@ -117,7 +117,7 @@ lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream } bool -lldb_private::formatters::NSNotificationSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::NSNotificationSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -150,7 +150,7 @@ lldb_private::formatters::NSNotificationSummaryProvider (ValueObject& valobj, St uint64_t offset = ptr_size; ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType(), true)); StreamString summary_stream; - bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream); + bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream, options); if (was_nsstring_ok && summary_stream.GetSize() > 0) { stream.Printf("%s",summary_stream.GetData()); @@ -163,7 +163,7 @@ lldb_private::formatters::NSNotificationSummaryProvider (ValueObject& valobj, St } bool -lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -212,7 +212,7 @@ lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream } bool -lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -290,7 +290,7 @@ lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream } bool -lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -413,7 +413,7 @@ lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& } bool -lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -453,7 +453,7 @@ lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& str if (text->GetValueAsUnsigned(0) == 0) return false; StreamString summary; - if (!NSStringSummaryProvider(*text, summary)) + if (!NSStringSummaryProvider(*text, summary, options)) return false; if (base && base->GetValueAsUnsigned(0)) { @@ -461,7 +461,7 @@ lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& str summary.GetString().resize(summary.GetSize()-1); summary.Printf(" -- "); StreamString base_summary; - if (NSURLSummaryProvider(*base, base_summary) && base_summary.GetSize() > 0) + if (NSURLSummaryProvider(*base, base_summary, options) && base_summary.GetSize() > 0) summary.Printf("%s",base_summary.GetSize() > 2 ? base_summary.GetData() + 2 : base_summary.GetData()); } if (summary.GetSize()) @@ -478,7 +478,7 @@ lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& str } bool -lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -552,7 +552,7 @@ lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& st // vendor decides to get creative time_t epoch = GetOSXEpoch(); epoch = epoch + (time_t)date_value; - tm *tm_date = localtime(&epoch); + tm *tm_date = gmtime(&epoch); if (!tm_date) return false; std::string buffer(1024,0); diff --git a/source/DataFormatters/DataVisualization.cpp b/source/DataFormatters/DataVisualization.cpp index c2c2206f3449..7ef0be50efe0 100644 --- a/source/DataFormatters/DataVisualization.cpp +++ b/source/DataFormatters/DataVisualization.cpp @@ -101,6 +101,18 @@ DataVisualization::GetSyntheticForType (lldb::TypeNameSpecifierImplSP type_sp) } #endif +lldb::TypeValidatorImplSP +DataVisualization::GetValidator (ValueObject& valobj, lldb::DynamicValueType use_dynamic) +{ + return GetFormatManager().GetValidator(valobj, use_dynamic); +} + +lldb::TypeValidatorImplSP +DataVisualization::GetValidatorForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + return GetFormatManager().GetValidatorForType(type_sp); +} + bool DataVisualization::AnyMatches (ConstString type_name, TypeCategoryImpl::FormatCategoryItems items, @@ -184,6 +196,18 @@ DataVisualization::Categories::Disable (const lldb::TypeCategoryImplSP& category } void +DataVisualization::Categories::EnableStar () +{ + GetFormatManager().EnableAllCategories (); +} + +void +DataVisualization::Categories::DisableStar () +{ + GetFormatManager().DisableAllCategories(); +} + +void DataVisualization::Categories::LoopThrough (FormatManager::CategoryCallback callback, void* callback_baton) { GetFormatManager().LoopThroughCategories(callback, callback_baton); diff --git a/source/DataFormatters/FormatCache.cpp b/source/DataFormatters/FormatCache.cpp index 3721f182f91e..aaa4bc1f958a 100644 --- a/source/DataFormatters/FormatCache.cpp +++ b/source/DataFormatters/FormatCache.cpp @@ -25,16 +25,20 @@ FormatCache::Entry::Entry () : m_format_cached(false), m_summary_cached(false), m_synthetic_cached(false), +m_validator_cached(false), m_format_sp(), m_summary_sp(), -m_synthetic_sp() +m_synthetic_sp(), +m_validator_sp() {} FormatCache::Entry::Entry (lldb::TypeFormatImplSP format_sp) : m_summary_cached(false), m_synthetic_cached(false), +m_validator_cached(false), m_summary_sp(), -m_synthetic_sp() +m_synthetic_sp(), +m_validator_sp() { SetFormat (format_sp); } @@ -42,8 +46,10 @@ m_synthetic_sp() FormatCache::Entry::Entry (lldb::TypeSummaryImplSP summary_sp) : m_format_cached(false), m_synthetic_cached(false), +m_validator_cached(false), m_format_sp(), -m_synthetic_sp() +m_synthetic_sp(), +m_validator_sp() { SetSummary (summary_sp); } @@ -51,17 +57,31 @@ m_synthetic_sp() FormatCache::Entry::Entry (lldb::SyntheticChildrenSP synthetic_sp) : m_format_cached(false), m_summary_cached(false), +m_validator_cached(false), m_format_sp(), -m_summary_sp() +m_summary_sp(), +m_validator_sp() { SetSynthetic (synthetic_sp); } -FormatCache::Entry::Entry (lldb::TypeFormatImplSP format_sp, lldb::TypeSummaryImplSP summary_sp, lldb::SyntheticChildrenSP synthetic_sp) +FormatCache::Entry::Entry (lldb::TypeValidatorImplSP validator_sp) : +m_format_cached(false), +m_summary_cached(false), +m_synthetic_cached(false), +m_format_sp(), +m_summary_sp(), +m_synthetic_sp() +{ + SetValidator (validator_sp); +} + +FormatCache::Entry::Entry (lldb::TypeFormatImplSP format_sp, lldb::TypeSummaryImplSP summary_sp, lldb::SyntheticChildrenSP synthetic_sp, lldb::TypeValidatorImplSP validator_sp) { SetFormat (format_sp); SetSummary (summary_sp); SetSynthetic (synthetic_sp); + SetValidator (validator_sp); } bool @@ -82,6 +102,12 @@ FormatCache::Entry::IsSyntheticCached () return m_synthetic_cached; } +bool +FormatCache::Entry::IsValidatorCached () +{ + return m_validator_cached; +} + lldb::TypeFormatImplSP FormatCache::Entry::GetFormat () { @@ -100,6 +126,12 @@ FormatCache::Entry::GetSynthetic () return m_synthetic_sp; } +lldb::TypeValidatorImplSP +FormatCache::Entry::GetValidator () +{ + return m_validator_sp; +} + void FormatCache::Entry::SetFormat (lldb::TypeFormatImplSP format_sp) { @@ -121,6 +153,13 @@ FormatCache::Entry::SetSynthetic (lldb::SyntheticChildrenSP synthetic_sp) m_synthetic_sp = synthetic_sp; } +void +FormatCache::Entry::SetValidator (lldb::TypeValidatorImplSP validator_sp) +{ + m_validator_cached = true; + m_validator_sp = validator_sp; +} + FormatCache::FormatCache () : m_map(), m_mutex (Mutex::eMutexTypeRecursive) @@ -201,6 +240,26 @@ FormatCache::GetSynthetic (const ConstString& type,lldb::SyntheticChildrenSP& sy return false; } +bool +FormatCache::GetValidator (const ConstString& type,lldb::TypeValidatorImplSP& validator_sp) +{ + Mutex::Locker lock(m_mutex); + auto entry = GetEntry(type); + if (entry.IsValidatorCached()) + { +#ifdef LLDB_CONFIGURATION_DEBUG + m_cache_hits++; +#endif + validator_sp = entry.GetValidator(); + return true; + } +#ifdef LLDB_CONFIGURATION_DEBUG + m_cache_misses++; +#endif + validator_sp.reset(); + return false; +} + void FormatCache::SetFormat (const ConstString& type,lldb::TypeFormatImplSP& format_sp) { @@ -223,6 +282,13 @@ FormatCache::SetSynthetic (const ConstString& type,lldb::SyntheticChildrenSP& sy } void +FormatCache::SetValidator (const ConstString& type,lldb::TypeValidatorImplSP& validator_sp) +{ + Mutex::Locker lock(m_mutex); + GetEntry(type).SetValidator(validator_sp); +} + +void FormatCache::Clear () { Mutex::Locker lock(m_mutex); diff --git a/source/DataFormatters/FormatManager.cpp b/source/DataFormatters/FormatManager.cpp index 28d108f2410a..01799cef5e4e 100644 --- a/source/DataFormatters/FormatManager.cpp +++ b/source/DataFormatters/FormatManager.cpp @@ -447,6 +447,32 @@ FormatManager::GetSyntheticChildrenForType (lldb::TypeNameSpecifierImplSP type_s } #endif +lldb::TypeValidatorImplSP +FormatManager::GetValidatorForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + if (!type_sp) + return lldb::TypeValidatorImplSP(); + lldb::TypeValidatorImplSP validator_chosen_sp; + uint32_t num_categories = m_categories_map.GetCount(); + lldb::TypeCategoryImplSP category_sp; + uint32_t prio_category = UINT32_MAX; + for (uint32_t category_id = 0; + category_id < num_categories; + category_id++) + { + category_sp = GetCategoryAtIndex(category_id); + if (category_sp->IsEnabled() == false) + continue; + lldb::TypeValidatorImplSP validator_current_sp(category_sp->GetValidatorForType(type_sp).get()); + if (validator_current_sp && (validator_chosen_sp.get() == NULL || (prio_category > category_sp->GetEnabledPosition()))) + { + prio_category = category_sp->GetEnabledPosition(); + validator_chosen_sp = validator_current_sp; + } + } + return validator_chosen_sp; +} + lldb::TypeCategoryImplSP FormatManager::GetCategory (const ConstString& category_name, bool can_create) @@ -501,10 +527,9 @@ FormatManager::ShouldPrintAsOneLiner (ValueObject& valobj) if (valobj.GetTargetSP().get() && valobj.GetTargetSP()->GetDebugger().GetAutoOneLineSummaries() == false) return false; // then don't oneline - // if this object has a summary, don't try to do anything special to it - // if the user wants one-liner, they can ask for it in summary :) + // if this object has a summary, then ask the summary if (valobj.GetSummaryFormat().get() != nullptr) - return false; + return valobj.GetSummaryFormat()->IsOneLiner(); // no children, no party if (valobj.GetNumChildren() == 0) @@ -516,6 +541,7 @@ FormatManager::ShouldPrintAsOneLiner (ValueObject& valobj) idx < valobj.GetNumChildren(); idx++) { + bool is_synth_val = false; ValueObjectSP child_sp(valobj.GetChildAtIndex(idx, true)); // something is wrong here - bail out if (!child_sp) @@ -523,7 +549,17 @@ FormatManager::ShouldPrintAsOneLiner (ValueObject& valobj) // if we decided to define synthetic children for a type, we probably care enough // to show them, but avoid nesting children in children if (child_sp->GetSyntheticChildren().get() != nullptr) - return false; + { + ValueObjectSP synth_sp(child_sp->GetSyntheticValue()); + // wait.. wat? just get out of here.. + if (!synth_sp) + return false; + // but if we only have them to provide a value, keep going + if (synth_sp->MightHaveChildren() == false && synth_sp->DoesProvideSyntheticValue()) + is_synth_val = true; + else + return false; + } total_children_name_len += child_sp->GetName().GetLength(); @@ -547,7 +583,7 @@ FormatManager::ShouldPrintAsOneLiner (ValueObject& valobj) // ...and no summary... // (if it had a summary and the summary wanted children, we would have bailed out anyway // so this only makes us bail out if this has no summary and we would then print children) - if (!child_sp->GetSummaryFormat()) + if (!child_sp->GetSummaryFormat() && !is_synth_val) // but again only do that if not a synthetic valued child return false; // then bail out } } @@ -756,6 +792,63 @@ FormatManager::GetSyntheticChildren (ValueObject& valobj, } #endif +lldb::TypeValidatorImplSP +FormatManager::GetValidator (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + TypeValidatorImplSP retval; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + ConstString valobj_type(GetTypeForCache(valobj, use_dynamic)); + if (valobj_type) + { + if (log) + log->Printf("\n\n[FormatManager::GetValidator] Looking into cache for type %s", valobj_type.AsCString("<invalid>")); + if (m_format_cache.GetValidator(valobj_type,retval)) + { + if (log) + { + log->Printf("[FormatManager::GetValidator] Cache search success. Returning."); + if (log->GetDebug()) + log->Printf("[FormatManager::GetValidator] Cache hits: %" PRIu64 " - Cache Misses: %" PRIu64, m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses()); + } + return retval; + } + if (log) + log->Printf("[FormatManager::GetValidator] Cache search failed. Going normal route"); + } + retval = m_categories_map.GetValidator(valobj, use_dynamic); + if (!retval) + { + if (log) + log->Printf("[FormatManager::GetValidator] Search failed. Giving hardcoded a chance."); + retval = GetHardcodedValidator(valobj, use_dynamic); + } + else if (valobj_type) + { + if (log) + log->Printf("[FormatManager::GetValidator] Caching %p for type %s", + static_cast<void*>(retval.get()), + valobj_type.AsCString("<invalid>")); + m_format_cache.SetValidator(valobj_type,retval); + } + if (log && log->GetDebug()) + log->Printf("[FormatManager::GetValidator] Cache hits: %" PRIu64 " - Cache Misses: %" PRIu64, m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses()); + return retval; +} + +lldb::TypeValidatorImplSP +FormatManager::GetHardcodedValidator (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + for (const auto& candidate: m_hardcoded_validators) + { + auto result = candidate(valobj,use_dynamic,*this); + if (result) + return result; + } + return nullptr; +} + FormatManager::FormatManager() : m_format_cache(), m_named_summaries_map(this), @@ -773,7 +866,8 @@ FormatManager::FormatManager() : m_appkit_category_name(ConstString("AppKit")), m_hardcoded_formats(), m_hardcoded_summaries(), - m_hardcoded_synthetics() + m_hardcoded_synthetics(), + m_hardcoded_validators() { LoadSystemFormatters(); @@ -825,6 +919,21 @@ AddStringSummary(TypeCategoryImpl::SharedPointer category_sp, category_sp->GetTypeSummariesContainer()->Add(type_name, summary_sp); } +static void +AddOneLineSummary (TypeCategoryImpl::SharedPointer category_sp, + ConstString type_name, + TypeSummaryImpl::Flags flags, + bool regex = false) +{ + flags.SetShowMembersOneLiner(true); + lldb::TypeSummaryImplSP summary_sp(new StringSummaryFormat(flags, "")); + + if (regex) + category_sp->GetRegexTypeSummariesContainer()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),summary_sp); + else + category_sp->GetTypeSummariesContainer()->Add(type_name, summary_sp); +} + #ifndef LLDB_DISABLE_PYTHON static void AddCXXSummary (TypeCategoryImpl::SharedPointer category_sp, @@ -945,13 +1054,6 @@ FormatManager::LoadLibStdcppFormatters() AddCXXSynthetic(gnu_category_sp, lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator, "std::vector iterator synthetic children", ConstString("^__gnu_cxx::__normal_iterator<.+>$"), stl_synth_flags, true); AddCXXSynthetic(gnu_category_sp, lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator, "std::map iterator synthetic children", ConstString("^std::_Rb_tree_iterator<.+>$"), stl_synth_flags, true); - - gnu_category_sp->GetTypeSummariesContainer()->Add(ConstString("std::vector<std::allocator<bool> >"), - TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); - - gnu_category_sp->GetTypeSyntheticsContainer()->Add(ConstString("std::vector<std::allocator<bool> >"), - SyntheticChildrenSP(new CXXSyntheticChildren(stl_synth_flags,"libc++ std::vector<bool> synthetic children",lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEndCreator))); - #endif } @@ -998,6 +1100,7 @@ FormatManager::LoadLibcxxFormatters() AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, "libc++ std::multiset synthetic children", ConstString("^std::__1::multiset<.+> >(( )?&)?$"), stl_synth_flags, true); AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, "libc++ std::multimap synthetic children", ConstString("^std::__1::multimap<.+> >(( )?&)?$"), stl_synth_flags, true); AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator, "libc++ std::unordered containers synthetic children", ConstString("^(std::__1::)unordered_(multi)?(map|set)<.+> >$"), stl_synth_flags, true); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxInitializerListSyntheticFrontEndCreator, "libc++ std::initializer_list synthetic children", ConstString("^std::initializer_list<.+>(( )?&)?$"), stl_synth_flags, true); libcxx_category_sp->GetRegexTypeSyntheticsContainer()->Add(RegularExpressionSP(new RegularExpression("^(std::__1::)deque<.+>(( )?&)?$")), SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, @@ -1124,6 +1227,10 @@ FormatManager::LoadObjCFormatters() .SetHideItemNames(false); TypeCategoryImpl::SharedPointer objc_category_sp = GetCategory(m_objc_category_name); + TypeCategoryImpl::SharedPointer appkit_category_sp = GetCategory(m_appkit_category_name); + TypeCategoryImpl::SharedPointer corefoundation_category_sp = GetCategory(m_corefoundation_category_name); + TypeCategoryImpl::SharedPointer coregraphics_category_sp = GetCategory(m_coregraphics_category_name); + TypeCategoryImpl::SharedPointer coreservices_category_sp = GetCategory(m_coreservices_category_name); lldb::TypeSummaryImplSP ObjC_BOOL_summary(new CXXFunctionSummaryFormat(objc_flags, lldb_private::formatters::ObjCBOOLSummaryProvider,"")); objc_category_sp->GetTypeSummariesContainer()->Add(ConstString("BOOL"), @@ -1159,8 +1266,6 @@ FormatManager::LoadObjCFormatters() ConstString("__block_literal_generic"), objc_flags); - TypeCategoryImpl::SharedPointer corefoundation_category_sp = GetCategory(m_corefoundation_category_name); - AddStringSummary(corefoundation_category_sp, "${var.years} years, ${var.months} months, ${var.days} days, ${var.hours} hours, ${var.minutes} minutes ${var.seconds} seconds", ConstString("CFGregorianUnits"), @@ -1169,43 +1274,35 @@ FormatManager::LoadObjCFormatters() "location=${var.location} length=${var.length}", ConstString("CFRange"), objc_flags); - AddStringSummary(corefoundation_category_sp, - "(x=${var.x}, y=${var.y})", - ConstString("NSPoint"), - objc_flags); - AddStringSummary(corefoundation_category_sp, + + AddStringSummary(appkit_category_sp, "location=${var.location}, length=${var.length}", ConstString("NSRange"), objc_flags); - AddStringSummary(corefoundation_category_sp, - "${var.origin}, ${var.size}", - ConstString("NSRect"), - objc_flags); - AddStringSummary(corefoundation_category_sp, + AddStringSummary(appkit_category_sp, "(${var.origin}, ${var.size}), ...", ConstString("NSRectArray"), objc_flags); - AddStringSummary(objc_category_sp, - "(width=${var.width}, height=${var.height})", - ConstString("NSSize"), - objc_flags); - - TypeCategoryImpl::SharedPointer coregraphics_category_sp = GetCategory(m_coregraphics_category_name); - - AddStringSummary(coregraphics_category_sp, - "(width=${var.width}, height=${var.height})", - ConstString("CGSize"), - objc_flags); - AddStringSummary(coregraphics_category_sp, - "(x=${var.x}, y=${var.y})", - ConstString("CGPoint"), - objc_flags); - AddStringSummary(coregraphics_category_sp, - "origin=${var.origin} size=${var.size}", - ConstString("CGRect"), - objc_flags); - TypeCategoryImpl::SharedPointer coreservices_category_sp = GetCategory(m_coreservices_category_name); + AddOneLineSummary (appkit_category_sp, + ConstString("NSPoint"), + objc_flags); + AddOneLineSummary (appkit_category_sp, + ConstString("NSSize"), + objc_flags); + AddOneLineSummary (appkit_category_sp, + ConstString("NSRect"), + objc_flags); + + AddOneLineSummary (coregraphics_category_sp, + ConstString("CGSize"), + objc_flags); + AddOneLineSummary (coregraphics_category_sp, + ConstString("CGPoint"), + objc_flags); + AddOneLineSummary (coregraphics_category_sp, + ConstString("CGRect"), + objc_flags); AddStringSummary(coreservices_category_sp, "red=${var.red} green=${var.green} blue=${var.blue}", @@ -1236,8 +1333,6 @@ FormatManager::LoadObjCFormatters() ConstString("HIRect"), objc_flags); - TypeCategoryImpl::SharedPointer appkit_category_sp = GetCategory(m_appkit_category_name); - TypeSummaryImpl::Flags appkit_flags; appkit_flags.SetCascades(true) .SetSkipPointers(false) @@ -1307,6 +1402,8 @@ FormatManager::LoadObjCFormatters() AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "NSOrderedSet synthetic children", ConstString("NSOrderedSet"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSOrderedSetI synthetic children", ConstString("__NSOrderedSetI"), ScriptedSyntheticChildren::Flags()); AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSOrderedSetM synthetic children", ConstString("__NSOrderedSetM"), ScriptedSyntheticChildren::Flags()); + + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSIndexPathSyntheticFrontEndCreator, "NSIndexPath synthetic children", ConstString("NSIndexPath"), ScriptedSyntheticChildren::Flags()); AddCXXSummary(corefoundation_category_sp,lldb_private::formatters::CFBagSummaryProvider, "CFBag summary provider", ConstString("CFBagRef"), appkit_flags); AddCXXSummary(corefoundation_category_sp,lldb_private::formatters::CFBagSummaryProvider, "CFBag summary provider", ConstString("__CFBag"), appkit_flags); @@ -1471,8 +1568,22 @@ FormatManager::LoadHardcodedFormatters() } { // insert code to load summaries here + m_hardcoded_summaries.push_back( + [](lldb_private::ValueObject& valobj, + lldb::DynamicValueType, + FormatManager&) -> TypeSummaryImpl::SharedPointer { + static CXXFunctionSummaryFormat::SharedPointer formatter_sp(new CXXFunctionSummaryFormat(TypeSummaryImpl::Flags(), lldb_private::formatters::FunctionPointerSummaryProvider, "Function pointer summary provider")); + if (valobj.GetClangType().IsFunctionPointerType()) + { + return formatter_sp; + } + return nullptr; + }); } { // insert code to load synthetics here } + { + // insert code to load validators here + } } diff --git a/source/DataFormatters/LibCxx.cpp b/source/DataFormatters/LibCxx.cpp index 174202661f03..26bbcf91242f 100644 --- a/source/DataFormatters/LibCxx.cpp +++ b/source/DataFormatters/LibCxx.cpp @@ -27,7 +27,7 @@ using namespace lldb_private; using namespace lldb_private::formatters; bool -lldb_private::formatters::LibcxxSmartPointerSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::LibcxxSmartPointerSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); if (!valobj_sp) @@ -143,7 +143,7 @@ lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetChildAtIndex (si if (bit_set && buffer_sp && buffer_sp->GetBytes()) *(buffer_sp->GetBytes()) = 1; // regardless of endianness, anything non-zero is true StreamString name; name.Printf("[%" PRIu64 "]", (uint64_t)idx); - ValueObjectSP retval_sp(ValueObject::CreateValueObjectFromData(name.GetData(), DataExtractor(buffer_sp, process_sp->GetByteOrder(), process_sp->GetAddressByteSize()), m_exe_ctx_ref, m_bool_type)); + ValueObjectSP retval_sp(CreateValueObjectFromData(name.GetData(), DataExtractor(buffer_sp, process_sp->GetByteOrder(), process_sp->GetAddressByteSize()), m_exe_ctx_ref, m_bool_type)); if (retval_sp) m_children[idx] = retval_sp; return retval_sp; @@ -378,7 +378,7 @@ lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex (siz return lldb::ValueObjectSP(); uint64_t count = 1 + shared_owners_sp->GetValueAsUnsigned(0); DataExtractor data(&count, 8, m_byte_order, m_ptr_size); - m_count_sp = ValueObject::CreateValueObjectFromData("count", data, valobj_sp->GetExecutionContextRef(), shared_owners_sp->GetClangType()); + m_count_sp = CreateValueObjectFromData("count", data, valobj_sp->GetExecutionContextRef(), shared_owners_sp->GetClangType()); } return m_count_sp; } @@ -391,7 +391,7 @@ lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::GetChildAtIndex (siz return lldb::ValueObjectSP(); uint64_t count = 1 + shared_weak_owners_sp->GetValueAsUnsigned(0); DataExtractor data(&count, 8, m_byte_order, m_ptr_size); - m_weak_count_sp = ValueObject::CreateValueObjectFromData("count", data, valobj_sp->GetExecutionContextRef(), shared_weak_owners_sp->GetClangType()); + m_weak_count_sp = CreateValueObjectFromData("count", data, valobj_sp->GetExecutionContextRef(), shared_weak_owners_sp->GetClangType()); } return m_weak_count_sp; } @@ -450,111 +450,8 @@ lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator (CXXSyntheticC return (new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp)); } -lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::LibcxxStdVectorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : - SyntheticChildrenFrontEnd(*valobj_sp.get()), - m_start(NULL), - m_finish(NULL), - m_element_type(), - m_element_size(0), - m_children() -{ - if (valobj_sp) - Update(); -} - -size_t -lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::CalculateNumChildren () -{ - if (!m_start || !m_finish) - return 0; - uint64_t start_val = m_start->GetValueAsUnsigned(0); - uint64_t finish_val = m_finish->GetValueAsUnsigned(0); - - if (start_val == 0 || finish_val == 0) - return 0; - - if (start_val >= finish_val) - return 0; - - size_t num_children = (finish_val - start_val); - if (num_children % m_element_size) - return 0; - return num_children/m_element_size; -} - -lldb::ValueObjectSP -lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex (size_t idx) -{ - if (!m_start || !m_finish) - return lldb::ValueObjectSP(); - - auto cached = m_children.find(idx); - if (cached != m_children.end()) - return cached->second; - - uint64_t offset = idx * m_element_size; - offset = offset + m_start->GetValueAsUnsigned(0); - StreamString name; - name.Printf("[%" PRIu64 "]", (uint64_t)idx); - ValueObjectSP child_sp = ValueObject::CreateValueObjectFromAddress(name.GetData(), offset, m_backend.GetExecutionContextRef(), m_element_type); - m_children[idx] = child_sp; - return child_sp; -} - -bool -lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::Update() -{ - m_start = m_finish = NULL; - m_children.clear(); - ValueObjectSP data_type_finder_sp(m_backend.GetChildMemberWithName(ConstString("__end_cap_"),true)); - if (!data_type_finder_sp) - return false; - data_type_finder_sp = data_type_finder_sp->GetChildMemberWithName(ConstString("__first_"),true); - if (!data_type_finder_sp) - return false; - m_element_type = data_type_finder_sp->GetClangType().GetPointeeType(); - m_element_size = m_element_type.GetByteSize(); - - if (m_element_size > 0) - { - // store raw pointers or end up with a circular dependency - m_start = m_backend.GetChildMemberWithName(ConstString("__begin_"),true).get(); - m_finish = m_backend.GetChildMemberWithName(ConstString("__end_"),true).get(); - } - return false; -} - -bool -lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::MightHaveChildren () -{ - return true; -} - -size_t -lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) -{ - if (!m_start || !m_finish) - return UINT32_MAX; - return ExtractIndexFromString(name.GetCString()); -} - -lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::~LibcxxStdVectorSyntheticFrontEnd () -{ - // these need to stay around because they are child objects who will follow their parent's life cycle - // delete m_start; - // delete m_finish; -} - -SyntheticChildrenFrontEnd* -lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) -{ - if (!valobj_sp) - return NULL; - return (new LibcxxStdVectorSyntheticFrontEnd(valobj_sp)); -} - bool -lldb_private::formatters::LibcxxContainerSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::LibcxxContainerSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { if (valobj.IsPointerType()) { diff --git a/source/DataFormatters/LibCxxInitializerList.cpp b/source/DataFormatters/LibCxxInitializerList.cpp new file mode 100644 index 000000000000..e76b0bec95ce --- /dev/null +++ b/source/DataFormatters/LibCxxInitializerList.cpp @@ -0,0 +1,145 @@ +//===-- LibCxxInitializerList.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/ValueObject.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { + namespace formatters { + class LibcxxInitializerListSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibcxxInitializerListSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibcxxInitializerListSyntheticFrontEnd (); + private: + ValueObject* m_start; + ClangASTType m_element_type; + uint32_t m_element_size; + size_t m_num_elements; + std::map<size_t,lldb::ValueObjectSP> m_children; + }; + } +} + +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::LibcxxInitializerListSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_start(NULL), +m_element_type(), +m_element_size(0), +m_num_elements(0), +m_children() +{ + if (valobj_sp) + Update(); +} + +size_t +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::CalculateNumChildren () +{ + static ConstString g___size_("__size_"); + m_num_elements = 0; + ValueObjectSP size_sp(m_backend.GetChildMemberWithName(g___size_, true)); + if (size_sp) + m_num_elements = size_sp->GetValueAsUnsigned(0); + return m_num_elements; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (!m_start) + return lldb::ValueObjectSP(); + + auto cached = m_children.find(idx); + if (cached != m_children.end()) + return cached->second; + + uint64_t offset = idx * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + ValueObjectSP child_sp = CreateValueObjectFromAddress(name.GetData(), offset, m_backend.GetExecutionContextRef(), m_element_type); + m_children[idx] = child_sp; + return child_sp; +} + +bool +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::Update() +{ + static ConstString g___begin_("__begin_"); + + m_start = nullptr; + m_num_elements = 0; + m_children.clear(); + lldb::TemplateArgumentKind kind; + m_element_type = m_backend.GetClangType().GetTemplateArgument(0, kind); + if (kind != lldb::eTemplateArgumentKindType || false == m_element_type.IsValid()) + return false; + + m_element_size = m_element_type.GetByteSize(); + + if (m_element_size > 0) + m_start = m_backend.GetChildMemberWithName(g___begin_,true).get(); // store raw pointers or end up with a circular dependency + + return false; +} + +bool +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + if (!m_start) + return UINT32_MAX; + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::~LibcxxInitializerListSyntheticFrontEnd () +{ + // this needs to stay around because it's a child object who will follow its parent's life cycle + // delete m_start; +} + +lldb_private::SyntheticChildrenFrontEnd* +lldb_private::formatters::LibcxxInitializerListSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibcxxInitializerListSyntheticFrontEnd(valobj_sp)); +} + diff --git a/source/DataFormatters/LibCxxList.cpp b/source/DataFormatters/LibCxxList.cpp index 7d6db1a0ccd4..5bb6ce07480f 100644 --- a/source/DataFormatters/LibCxxList.cpp +++ b/source/DataFormatters/LibCxxList.cpp @@ -25,6 +25,47 @@ using namespace lldb; using namespace lldb_private; using namespace lldb_private::formatters; +namespace lldb_private { + namespace formatters { + class LibcxxStdListSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibcxxStdListSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibcxxStdListSyntheticFrontEnd (); + private: + bool + HasLoop(size_t); + + size_t m_list_capping_size; + static const bool g_use_loop_detect = true; + size_t m_loop_detected; + lldb::addr_t m_node_address; + ValueObject* m_head; + ValueObject* m_tail; + ClangASTType m_element_type; + size_t m_count; + std::map<size_t,lldb::ValueObjectSP> m_children; + }; + } +} + class ListEntry { public: @@ -150,6 +191,7 @@ private: lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::LibcxxStdListSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_list_capping_size(0), +m_loop_detected(0), m_node_address(), m_head(NULL), m_tail(NULL), @@ -162,14 +204,15 @@ m_children() } bool -lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::HasLoop() +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::HasLoop(size_t count) { if (g_use_loop_detect == false) return false; // don't bother checking for a loop if we won't actually need to jump nodes if (m_count < 2) return false; - auto steps_left = m_count; + auto steps_left = std::min(count,m_count); + auto steps_left_save = steps_left; ListEntry slow(m_head); ListEntry fast(m_head); while (steps_left-- > 0) @@ -185,6 +228,7 @@ lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::HasLoop() if (slow == fast) return true; } + m_loop_detected = steps_left_save; return false; } @@ -206,9 +250,7 @@ lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::CalculateNumChildren ( } if (m_count != UINT32_MAX) { - if (!HasLoop()) - return m_count; - return m_count = 0; + return m_count; } else { @@ -220,8 +262,6 @@ lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::CalculateNumChildren ( return 0; if (next_val == prev_val) return 1; - if (HasLoop()) - return 0; uint64_t size = 2; ListEntry current(m_head); while (current.next() && current.next().value() != m_node_address) @@ -248,6 +288,10 @@ lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::GetChildAtIndex (size_ if (cached != m_children.end()) return cached->second; + if (m_loop_detected <= idx) + if (HasLoop(idx)) + return lldb::ValueObjectSP(); + ListIterator current(m_head); ValueObjectSP current_sp(current.advance(idx)); if (!current_sp) @@ -264,7 +308,7 @@ lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::GetChildAtIndex (size_ StreamString name; name.Printf("[%" PRIu64 "]", (uint64_t)idx); - return (m_children[idx] = ValueObject::CreateValueObjectFromData(name.GetData(), data, m_backend.GetExecutionContextRef(), m_element_type)); + return (m_children[idx] = CreateValueObjectFromData(name.GetData(), data, m_backend.GetExecutionContextRef(), m_element_type)); } bool @@ -273,6 +317,7 @@ lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::Update() m_head = m_tail = NULL; m_node_address = 0; m_count = UINT32_MAX; + m_loop_detected = false; Error err; ValueObjectSP backend_addr(m_backend.AddressOf(err)); m_list_capping_size = 0; diff --git a/source/DataFormatters/LibCxxMap.cpp b/source/DataFormatters/LibCxxMap.cpp index e665f29622d8..82e747e2db08 100644 --- a/source/DataFormatters/LibCxxMap.cpp +++ b/source/DataFormatters/LibCxxMap.cpp @@ -25,40 +25,84 @@ using namespace lldb; using namespace lldb_private; using namespace lldb_private::formatters; +namespace lldb_private { + namespace formatters { + class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibcxxStdMapSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibcxxStdMapSyntheticFrontEnd (); + private: + bool + GetDataType(); + + void + GetValueOffset (const lldb::ValueObjectSP& node); + + ValueObject* m_tree; + ValueObject* m_root_node; + ClangASTType m_element_type; + uint32_t m_skip_size; + size_t m_count; + std::map<size_t,lldb::ValueObjectSP> m_children; + }; + } +} + class MapEntry { public: MapEntry () {} - MapEntry (ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {} + explicit MapEntry (ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {} MapEntry (const MapEntry& rhs) : m_entry_sp(rhs.m_entry_sp) {} - MapEntry (ValueObject* entry) : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} + explicit MapEntry (ValueObject* entry) : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} ValueObjectSP - left () + left () const { + static ConstString g_left("__left_"); if (!m_entry_sp) return m_entry_sp; - return m_entry_sp->GetChildMemberWithName(ConstString("__left_"), true); + return m_entry_sp->GetChildMemberWithName(g_left, true); } ValueObjectSP - right () + right () const { + static ConstString g_right("__right_"); if (!m_entry_sp) return m_entry_sp; - return m_entry_sp->GetChildMemberWithName(ConstString("__right_"), true); + return m_entry_sp->GetChildMemberWithName(g_right, true); } ValueObjectSP - parent () + parent () const { + static ConstString g_parent("__parent_"); if (!m_entry_sp) return m_entry_sp; - return m_entry_sp->GetChildMemberWithName(ConstString("__parent_"), true); + return m_entry_sp->GetChildMemberWithName(g_parent, true); } uint64_t - value () + value () const { if (!m_entry_sp) return 0; @@ -66,7 +110,7 @@ public: } bool - error () + error () const { if (!m_entry_sp) return true; @@ -74,13 +118,13 @@ public: } bool - null() + null() const { return (value() == 0); } ValueObjectSP - GetEntry () + GetEntry () const { return m_entry_sp; } @@ -119,27 +163,18 @@ public: ValueObjectSP advance (size_t count) { + ValueObjectSP fail(nullptr); if (m_error) - return lldb::ValueObjectSP(); - if (count == 0) - return m_entry.GetEntry(); - if (count == 1) - { - next (); - return m_entry.GetEntry(); - } + return fail; size_t steps = 0; while (count > 0) { - if (m_error) - return lldb::ValueObjectSP(); - next (); - count--; - if (m_entry.null()) - return lldb::ValueObjectSP(); - steps++; - if (steps > m_max_depth) - return lldb::ValueObjectSP(); + next(); + count--, steps++; + if (m_error || + m_entry.null() || + (steps > m_max_depth)) + return fail; } return m_entry.GetEntry(); } @@ -147,16 +182,39 @@ protected: void next () { - m_entry.SetEntry(increment(m_entry.GetEntry())); + if (m_entry.null()) + return; + MapEntry right(m_entry.right()); + if (right.null() == false) + { + m_entry = tree_min(std::move(right)); + return; + } + size_t steps = 0; + while (!is_left_child(m_entry)) + { + if (m_entry.error()) + { + m_error = true; + return; + } + m_entry.SetEntry(m_entry.parent()); + steps++; + if (steps > m_max_depth) + { + m_entry = MapEntry(); + return; + } + } + m_entry = MapEntry(m_entry.parent()); } private: - ValueObjectSP - tree_min (ValueObjectSP x_sp) + MapEntry + tree_min (MapEntry&& x) { - MapEntry x(x_sp); if (x.null()) - return ValueObjectSP(); + return MapEntry(); MapEntry left(x.left()); size_t steps = 0; while (left.null() == false) @@ -164,42 +222,20 @@ private: if (left.error()) { m_error = true; - return lldb::ValueObjectSP(); + return MapEntry(); } - x.SetEntry(left.GetEntry()); + x = left; left.SetEntry(x.left()); steps++; if (steps > m_max_depth) - return lldb::ValueObjectSP(); - } - return x.GetEntry(); - } - - ValueObjectSP - tree_max (ValueObjectSP x_sp) - { - MapEntry x(x_sp); - if (x.null()) - return ValueObjectSP(); - MapEntry right(x.right()); - size_t steps = 0; - while (right.null() == false) - { - if (right.error()) - return lldb::ValueObjectSP(); - x.SetEntry(right.GetEntry()); - right.SetEntry(x.right()); - steps++; - if (steps > m_max_depth) - return lldb::ValueObjectSP(); + return MapEntry(); } - return x.GetEntry(); + return x; } - + bool - is_left_child (ValueObjectSP x_sp) + is_left_child (const MapEntry& x) { - MapEntry x(x_sp); if (x.null()) return false; MapEntry rhs(x.parent()); @@ -207,31 +243,6 @@ private: return x.value() == rhs.value(); } - ValueObjectSP - increment (ValueObjectSP x_sp) - { - MapEntry node(x_sp); - if (node.null()) - return ValueObjectSP(); - MapEntry right(node.right()); - if (right.null() == false) - return tree_min(right.GetEntry()); - size_t steps = 0; - while (!is_left_child(node.GetEntry())) - { - if (node.error()) - { - m_error = true; - return lldb::ValueObjectSP(); - } - node.SetEntry(node.parent()); - steps++; - if (steps > m_max_depth) - return lldb::ValueObjectSP(); - } - return node.parent(); - } - MapEntry m_entry; size_t m_max_depth; bool m_error; @@ -302,6 +313,10 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetValueOffset (const l lldb::ValueObjectSP lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetChildAtIndex (size_t idx) { + static ConstString g___cc("__cc"); + static ConstString g___nc("__nc"); + + if (idx >= CalculateNumChildren()) return lldb::ValueObjectSP(); if (m_tree == NULL || m_root_node == NULL) @@ -375,7 +390,31 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetChildAtIndex (size_t } StreamString name; name.Printf("[%" PRIu64 "]", (uint64_t)idx); - return (m_children[idx] = ValueObject::CreateValueObjectFromData(name.GetData(), data, m_backend.GetExecutionContextRef(), m_element_type)); + auto potential_child_sp = CreateValueObjectFromData(name.GetData(), data, m_backend.GetExecutionContextRef(), m_element_type); + if (potential_child_sp) + { + switch (potential_child_sp->GetNumChildren()) + { + case 1: + { + auto child0_sp = potential_child_sp->GetChildAtIndex(0, true); + if (child0_sp && child0_sp->GetName() == g___cc) + potential_child_sp = child0_sp; + break; + } + case 2: + { + auto child0_sp = potential_child_sp->GetChildAtIndex(0, true); + auto child1_sp = potential_child_sp->GetChildAtIndex(1, true); + if (child0_sp && child0_sp->GetName() == g___cc && + child1_sp && child1_sp->GetName() == g___nc) + potential_child_sp = child0_sp; + break; + } + } + potential_child_sp->SetName(ConstString(name.GetData())); + } + return (m_children[idx] = potential_child_sp); } bool diff --git a/source/DataFormatters/LibCxxUnorderedMap.cpp b/source/DataFormatters/LibCxxUnorderedMap.cpp index bf68f20955b5..da2a88966f5e 100644 --- a/source/DataFormatters/LibCxxUnorderedMap.cpp +++ b/source/DataFormatters/LibCxxUnorderedMap.cpp @@ -25,6 +25,41 @@ using namespace lldb; using namespace lldb_private; using namespace lldb_private::formatters; +namespace lldb_private { + namespace formatters { + class LibcxxStdUnorderedMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibcxxStdUnorderedMapSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibcxxStdUnorderedMapSyntheticFrontEnd (); + private: + + ValueObject* m_tree; + size_t m_num_elements; + ValueObject* m_next_element; + std::map<size_t,lldb::ValueObjectSP> m_children; + std::vector<std::pair<ValueObject*, uint64_t> > m_elements_cache; + }; + } +} + lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::LibcxxStdUnorderedMapSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_tree(NULL), diff --git a/source/DataFormatters/LibCxxVector.cpp b/source/DataFormatters/LibCxxVector.cpp new file mode 100644 index 000000000000..26c62afbed2b --- /dev/null +++ b/source/DataFormatters/LibCxxVector.cpp @@ -0,0 +1,157 @@ +//===-- LibCxxVector.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/ValueObject.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +namespace lldb_private { + namespace formatters { + class LibcxxStdVectorSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibcxxStdVectorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibcxxStdVectorSyntheticFrontEnd (); + private: + ValueObject* m_start; + ValueObject* m_finish; + ClangASTType m_element_type; + uint32_t m_element_size; + std::map<size_t,lldb::ValueObjectSP> m_children; + }; + } +} + +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::LibcxxStdVectorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_start(NULL), +m_finish(NULL), +m_element_type(), +m_element_size(0), +m_children() +{ + if (valobj_sp) + Update(); +} + +size_t +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::CalculateNumChildren () +{ + if (!m_start || !m_finish) + return 0; + uint64_t start_val = m_start->GetValueAsUnsigned(0); + uint64_t finish_val = m_finish->GetValueAsUnsigned(0); + + if (start_val == 0 || finish_val == 0) + return 0; + + if (start_val >= finish_val) + return 0; + + size_t num_children = (finish_val - start_val); + if (num_children % m_element_size) + return 0; + return num_children/m_element_size; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (!m_start || !m_finish) + return lldb::ValueObjectSP(); + + auto cached = m_children.find(idx); + if (cached != m_children.end()) + return cached->second; + + uint64_t offset = idx * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + ValueObjectSP child_sp = CreateValueObjectFromAddress(name.GetData(), offset, m_backend.GetExecutionContextRef(), m_element_type); + m_children[idx] = child_sp; + return child_sp; +} + +bool +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::Update() +{ + m_start = m_finish = NULL; + m_children.clear(); + ValueObjectSP data_type_finder_sp(m_backend.GetChildMemberWithName(ConstString("__end_cap_"),true)); + if (!data_type_finder_sp) + return false; + data_type_finder_sp = data_type_finder_sp->GetChildMemberWithName(ConstString("__first_"),true); + if (!data_type_finder_sp) + return false; + m_element_type = data_type_finder_sp->GetClangType().GetPointeeType(); + m_element_size = m_element_type.GetByteSize(); + + if (m_element_size > 0) + { + // store raw pointers or end up with a circular dependency + m_start = m_backend.GetChildMemberWithName(ConstString("__begin_"),true).get(); + m_finish = m_backend.GetChildMemberWithName(ConstString("__end_"),true).get(); + } + return false; +} + +bool +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + if (!m_start || !m_finish) + return UINT32_MAX; + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::~LibcxxStdVectorSyntheticFrontEnd () +{ + // these need to stay around because they are child objects who will follow their parent's life cycle + // delete m_start; + // delete m_finish; +} + +lldb_private::SyntheticChildrenFrontEnd* +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibcxxStdVectorSyntheticFrontEnd(valobj_sp)); +} + diff --git a/source/DataFormatters/LibStdcpp.cpp b/source/DataFormatters/LibStdcpp.cpp index f2d617323cca..b8f031ceeb2f 100644 --- a/source/DataFormatters/LibStdcpp.cpp +++ b/source/DataFormatters/LibStdcpp.cpp @@ -276,7 +276,7 @@ lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEnd::GetChildAtIndex if (m_pair_address != 0 && m_pair_type) { if (!m_pair_sp) - m_pair_sp = ValueObject::CreateValueObjectFromAddress("pair", m_pair_address, m_exe_ctx_ref, m_pair_type); + m_pair_sp = CreateValueObjectFromAddress("pair", m_pair_address, m_exe_ctx_ref, m_pair_type); if (m_pair_sp) return m_pair_sp->GetChildAtIndex(idx, true); } diff --git a/source/DataFormatters/NSArray.cpp b/source/DataFormatters/NSArray.cpp index 16635381f94f..e242155f4fef 100644 --- a/source/DataFormatters/NSArray.cpp +++ b/source/DataFormatters/NSArray.cpp @@ -228,7 +228,7 @@ namespace lldb_private { } bool -lldb_private::formatters::NSArraySummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::NSArraySummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -341,10 +341,10 @@ lldb_private::formatters::NSArrayMSyntheticFrontEnd::GetChildAtIndex (size_t idx object_at_idx += (pyhs_idx * m_ptr_size); StreamString idx_name; idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); - lldb::ValueObjectSP retval_sp = ValueObject::CreateValueObjectFromAddress(idx_name.GetData(), - object_at_idx, - m_exe_ctx_ref, - m_id_type); + lldb::ValueObjectSP retval_sp = CreateValueObjectFromAddress(idx_name.GetData(), + object_at_idx, + m_exe_ctx_ref, + m_id_type); m_children.push_back(retval_sp); return retval_sp; } @@ -604,7 +604,10 @@ lldb_private::formatters::NSArrayISyntheticFrontEnd::GetChildAtIndex (size_t idx return lldb::ValueObjectSP(); StreamString idx_name; idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); - lldb::ValueObjectSP retval_sp = ValueObject::CreateValueObjectFromAddress(idx_name.GetData(), object_at_idx, m_exe_ctx_ref, m_id_type); + lldb::ValueObjectSP retval_sp = CreateValueObjectFromAddress(idx_name.GetData(), + object_at_idx, + m_exe_ctx_ref, + m_id_type); m_children.push_back(retval_sp); return retval_sp; } @@ -624,7 +627,7 @@ SyntheticChildrenFrontEnd* lldb_private::formatters::NSArraySyntheticFrontEndCre ClangASTType valobj_type(valobj_sp->GetClangType()); Flags flags(valobj_type.GetTypeInfo()); - if (flags.IsClear(ClangASTType::eTypeIsPointer)) + if (flags.IsClear(eTypeIsPointer)) { Error error; valobj_sp = valobj_sp->AddressOf(error); diff --git a/source/DataFormatters/NSDictionary.cpp b/source/DataFormatters/NSDictionary.cpp index ddd1e2e7ca90..fdac05192c46 100644 --- a/source/DataFormatters/NSDictionary.cpp +++ b/source/DataFormatters/NSDictionary.cpp @@ -36,52 +36,167 @@ GetLLDBNSPairType (TargetSP target_sp) if (target_ast_context) { - clang::ASTContext *ast = target_ast_context->getASTContext(); + ConstString g___lldb_autogen_nspair("__lldb_autogen_nspair"); - if (ast) + clang_type = target_ast_context->GetTypeForIdentifier<clang::CXXRecordDecl>(g___lldb_autogen_nspair); + + if (!clang_type) { - const char* type_name = "__lldb_autogen_nspair"; + clang_type = target_ast_context->CreateRecordType(NULL, lldb::eAccessPublic, g___lldb_autogen_nspair.GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); - clang::IdentifierInfo &myIdent = ast->Idents.get(type_name); - clang::DeclarationName myName = ast->DeclarationNames.getIdentifier(&myIdent); - - clang::DeclContext::lookup_const_result result = ast->getTranslationUnitDecl()->lookup(myName); - - for (clang::NamedDecl *named_decl : result) - { - if (const clang::CXXRecordDecl *record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(named_decl)) - { - clang_type.SetClangType(ast, clang::QualType(record_decl->getTypeForDecl(), 0)); - break; - } - else - { - // somebody else (the user?) has defined a type with the magic name already - fail!!! - return clang_type; - } - } - - if (!clang_type) + if (clang_type) { - clang_type = target_ast_context->CreateRecordType(NULL, lldb::eAccessPublic, type_name, clang::TTK_Struct, lldb::eLanguageTypeC); - - if (clang_type) - { - clang_type.StartTagDeclarationDefinition(); - ClangASTType id_clang_type = target_ast_context->GetBasicType (eBasicTypeObjCID); - clang_type.AddFieldToRecordType("key", id_clang_type, lldb::eAccessPublic, 0); - clang_type.AddFieldToRecordType("value", id_clang_type, lldb::eAccessPublic, 0); - clang_type.CompleteTagDeclarationDefinition(); - } + clang_type.StartTagDeclarationDefinition(); + ClangASTType id_clang_type = target_ast_context->GetBasicType (eBasicTypeObjCID); + clang_type.AddFieldToRecordType("key", id_clang_type, lldb::eAccessPublic, 0); + clang_type.AddFieldToRecordType("value", id_clang_type, lldb::eAccessPublic, 0); + clang_type.CompleteTagDeclarationDefinition(); } } } return clang_type; } +namespace lldb_private { + namespace formatters { + class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + private: + struct DataDescriptor_32 + { + uint32_t _used : 26; + uint32_t _szidx : 6; + }; + struct DataDescriptor_64 + { + uint64_t _used : 58; + uint32_t _szidx : 6; + }; + + struct DictionaryItemDescriptor + { + lldb::addr_t key_ptr; + lldb::addr_t val_ptr; + lldb::ValueObjectSP valobj_sp; + }; + + public: + NSDictionaryISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSDictionaryISyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + lldb::ByteOrder m_order; + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + lldb::addr_t m_data_ptr; + ClangASTType m_pair_type; + std::vector<DictionaryItemDescriptor> m_children; + }; + + class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + private: + struct DataDescriptor_32 + { + uint32_t _used : 26; + uint32_t _kvo : 1; + uint32_t _size; + uint32_t _mutations; + uint32_t _objs_addr; + uint32_t _keys_addr; + }; + struct DataDescriptor_64 + { + uint64_t _used : 58; + uint32_t _kvo : 1; + uint64_t _size; + uint64_t _mutations; + uint64_t _objs_addr; + uint64_t _keys_addr; + }; + struct DictionaryItemDescriptor + { + lldb::addr_t key_ptr; + lldb::addr_t val_ptr; + lldb::ValueObjectSP valobj_sp; + }; + public: + NSDictionaryMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSDictionaryMSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + lldb::ByteOrder m_order; + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + ClangASTType m_pair_type; + std::vector<DictionaryItemDescriptor> m_children; + }; + + class NSDictionaryCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + NSDictionaryCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSDictionaryCodeRunningSyntheticFrontEnd (); + }; + } +} + template<bool name_entries> bool -lldb_private::formatters::NSDictionarySummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::NSDictionarySummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -407,7 +522,10 @@ lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetChildAtIndex (size_ StreamString idx_name; idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); DataExtractor data(buffer_sp, m_order, m_ptr_size); - dict_item.valobj_sp = ValueObject::CreateValueObjectFromData(idx_name.GetData(), data, m_exe_ctx_ref, m_pair_type); + dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetData(), + data, + m_exe_ctx_ref, + m_pair_type); } return dict_item.valobj_sp; } @@ -571,13 +689,16 @@ lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::GetChildAtIndex (size_ StreamString idx_name; idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); DataExtractor data(buffer_sp, m_order, m_ptr_size); - dict_item.valobj_sp = ValueObject::CreateValueObjectFromData(idx_name.GetData(), data, m_exe_ctx_ref, m_pair_type); + dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetData(), + data, + m_exe_ctx_ref, + m_pair_type); } return dict_item.valobj_sp; } template bool -lldb_private::formatters::NSDictionarySummaryProvider<true> (ValueObject&, Stream&) ; +lldb_private::formatters::NSDictionarySummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&) ; template bool -lldb_private::formatters::NSDictionarySummaryProvider<false> (ValueObject&, Stream&) ; +lldb_private::formatters::NSDictionarySummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&) ; diff --git a/source/DataFormatters/NSIndexPath.cpp b/source/DataFormatters/NSIndexPath.cpp new file mode 100644 index 000000000000..ee9583ef4cc1 --- /dev/null +++ b/source/DataFormatters/NSIndexPath.cpp @@ -0,0 +1,309 @@ +//===-- NSIndexPath.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Symbol/ClangASTContext.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +class NSIndexPathSyntheticFrontEnd : public SyntheticChildrenFrontEnd +{ +public: + NSIndexPathSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : + SyntheticChildrenFrontEnd (*valobj_sp.get()), + m_ptr_size(0), + m_ast_ctx(nullptr), + m_uint_star_type() + { + m_ptr_size = m_backend.GetTargetSP()->GetArchitecture().GetAddressByteSize(); + } + + virtual size_t + CalculateNumChildren () + { + return m_impl.GetNumIndexes(); + } + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx) + { + return m_impl.GetIndexAtIndex(idx, m_uint_star_type); + } + + virtual bool + Update() + { + m_impl.m_mode = Mode::Invalid; + + m_ast_ctx = ClangASTContext::GetASTContext(m_backend.GetClangType().GetASTContext()); + if (!m_ast_ctx) + return false; + + m_uint_star_type = m_ast_ctx->GetPointerSizedIntType(false); + + static ConstString g__indexes("_indexes"); + static ConstString g__length("_length"); + + ProcessSP process_sp = m_backend.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(m_backend)); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + uint64_t info_bits(0),value_bits(0),payload(0); + + if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits, &payload)) + { + m_impl.m_mode = Mode::Inlined; + m_impl.m_inlined.SetIndexes(payload, *process_sp); + } + else + { + ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _indexes_id; + ObjCLanguageRuntime::ClassDescriptor::iVarDescriptor _length_id; + + bool has_indexes(false),has_length(false); + + for (size_t x = 0; + x < descriptor->GetNumIVars(); + x++) + { + const auto& ivar = descriptor->GetIVarAtIndex(x); + if (ivar.m_name == g__indexes) + { + _indexes_id = ivar; + has_indexes = true; + } + else if (ivar.m_name == g__length) + { + _length_id = ivar; + has_length = true; + } + + if (has_length && has_indexes) + break; + } + + if (has_length && has_indexes) + { + m_impl.m_outsourced.m_indexes = m_backend.GetSyntheticChildAtOffset(_indexes_id.m_offset, + m_uint_star_type.GetPointerType(), + true).get(); + ValueObjectSP length_sp(m_backend.GetSyntheticChildAtOffset(_length_id.m_offset, + m_uint_star_type, + true)); + if (length_sp) + { + m_impl.m_outsourced.m_count = length_sp->GetValueAsUnsigned(0); + if (m_impl.m_outsourced.m_indexes) + m_impl.m_mode = Mode::Outsourced; + } + } + } + return false; + } + + virtual bool + MightHaveChildren () + { + if (m_impl.m_mode == Mode::Invalid) + return false; + return true; + } + + virtual size_t + GetIndexOfChildWithName (const ConstString &name) + { + const char* item_name = name.GetCString(); + uint32_t idx = ExtractIndexFromString(item_name); + if (idx < UINT32_MAX && idx >= CalculateNumChildren()) + return UINT32_MAX; + return idx; + } + + virtual lldb::ValueObjectSP + GetSyntheticValue () { return nullptr; } + + virtual + ~NSIndexPathSyntheticFrontEnd () {} + +protected: + ObjCLanguageRuntime::ClassDescriptorSP m_descriptor_sp; + + enum class Mode { + Inlined, + Outsourced, + Invalid + }; + + struct Impl { + Mode m_mode; + + size_t + GetNumIndexes () + { + switch (m_mode) + { + case Mode::Inlined: + return m_inlined.GetNumIndexes(); + case Mode::Outsourced: + return m_outsourced.m_count; + default: + return 0; + } + } + + lldb::ValueObjectSP + GetIndexAtIndex (size_t idx, const ClangASTType& desired_type) + { + if (idx >= GetNumIndexes()) + return nullptr; + switch (m_mode) + { + default: return nullptr; + case Mode::Inlined: + return m_inlined.GetIndexAtIndex (idx, desired_type); + case Mode::Outsourced: + return m_outsourced.GetIndexAtIndex (idx); + } + } + + struct InlinedIndexes { + public: + void SetIndexes(uint64_t value, Process& p) + { + m_indexes = value; + _lengthForInlinePayload(p.GetAddressByteSize()); + m_process = &p; + } + + size_t + GetNumIndexes () + { + return m_count; + } + + lldb::ValueObjectSP + GetIndexAtIndex (size_t idx, const ClangASTType& desired_type) + { + std::pair<uint64_t, bool> value(_indexAtPositionForInlinePayload(idx)); + if (!value.second) + return nullptr; + Value v; + if (m_ptr_size == 8) + { + Scalar scalar( (unsigned long long)value.first ); + v = Value(scalar); + } + else + { + Scalar scalar( (unsigned int)value.first ); + v = Value(scalar); + } + v.SetClangType(desired_type); + StreamString idx_name; + idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return ValueObjectConstResult::Create(m_process, v, ConstString(idx_name.GetData())); + } + + private: + uint64_t m_indexes; + size_t m_count; + uint32_t m_ptr_size; + Process *m_process; + + // cfr. Foundation for the details of this code + size_t _lengthForInlinePayload(uint32_t ptr_size) { + m_ptr_size = ptr_size; + if (m_ptr_size == 8) + m_count = ((m_indexes >> 3) & 0x7); + else + m_count = ((m_indexes >> 3) & 0x3); + return m_count; + } + + std::pair<uint64_t, bool> + _indexAtPositionForInlinePayload(size_t pos) { + if (m_ptr_size == 8) + { + switch (pos) { + case 5: return {((m_indexes >> 51) & 0x1ff),true}; + case 4: return {((m_indexes >> 42) & 0x1ff),true}; + case 3: return {((m_indexes >> 33) & 0x1ff),true}; + case 2: return {((m_indexes >> 24) & 0x1ff),true}; + case 1: return {((m_indexes >> 15) & 0x1ff),true}; + case 0: return {((m_indexes >> 6) & 0x1ff),true}; + } + } + else + { + switch (pos) { + case 2: return {((m_indexes >> 23) & 0x1ff),true}; + case 1: return {((m_indexes >> 14) & 0x1ff),true}; + case 0: return {((m_indexes >> 5) & 0x1ff),true}; + } + } + return {0,false}; + } + + }; + struct OutsourcedIndexes { + ValueObject *m_indexes; + size_t m_count; + + lldb::ValueObjectSP + GetIndexAtIndex (size_t idx) + { + if (m_indexes) + { + ValueObjectSP index_sp(m_indexes->GetSyntheticArrayMemberFromPointer(idx, true)); + return index_sp; + } + return nullptr; + } + }; + + union { + struct InlinedIndexes m_inlined; + struct OutsourcedIndexes m_outsourced; + }; + } m_impl; + + uint32_t m_ptr_size; + ClangASTContext* m_ast_ctx; + ClangASTType m_uint_star_type; +}; + +namespace lldb_private { + namespace formatters { + + SyntheticChildrenFrontEnd* NSIndexPathSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) + { + if (valobj_sp) + return new NSIndexPathSyntheticFrontEnd(valobj_sp); + return nullptr; + } + } +} diff --git a/source/DataFormatters/NSSet.cpp b/source/DataFormatters/NSSet.cpp index 3c7c003ed95a..194d1bd29ea1 100644 --- a/source/DataFormatters/NSSet.cpp +++ b/source/DataFormatters/NSSet.cpp @@ -25,9 +25,165 @@ using namespace lldb; using namespace lldb_private; using namespace lldb_private::formatters; +namespace lldb_private { + namespace formatters { + class NSSetISyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + private: + struct DataDescriptor_32 + { + uint32_t _used : 26; + uint32_t _szidx : 6; + }; + struct DataDescriptor_64 + { + uint64_t _used : 58; + uint32_t _szidx : 6; + }; + + struct SetItemDescriptor + { + lldb::addr_t item_ptr; + lldb::ValueObjectSP valobj_sp; + }; + + public: + NSSetISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSSetISyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + lldb::addr_t m_data_ptr; + std::vector<SetItemDescriptor> m_children; + }; + + class NSOrderedSetSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + private: + + public: + NSOrderedSetSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSOrderedSetSyntheticFrontEnd (); + private: + uint32_t m_count; + std::map<uint32_t,lldb::ValueObjectSP> m_children; + }; + + class NSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + private: + struct DataDescriptor_32 + { + uint32_t _used : 26; + uint32_t _size; + uint32_t _mutations; + uint32_t _objs_addr; + }; + struct DataDescriptor_64 + { + uint64_t _used : 58; + uint64_t _size; + uint64_t _mutations; + uint64_t _objs_addr; + }; + struct SetItemDescriptor + { + lldb::addr_t item_ptr; + lldb::ValueObjectSP valobj_sp; + }; + public: + NSSetMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSSetMSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + std::vector<SetItemDescriptor> m_children; + }; + + class NSSetCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + NSSetCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSSetCodeRunningSyntheticFrontEnd (); + }; + } +} + template<bool cf_style> bool -lldb_private::formatters::NSSetSummaryProvider (ValueObject& valobj, Stream& stream) +lldb_private::formatters::NSSetSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) { ProcessSP process_sp = valobj.GetProcessSP(); if (!process_sp) @@ -313,10 +469,10 @@ lldb_private::formatters::NSSetISyntheticFrontEnd::GetChildAtIndex (size_t idx) process_sp->GetAddressByteSize()); set_item.valobj_sp = - ValueObject::CreateValueObjectFromData(idx_name.GetData(), - data, - m_exe_ctx_ref, - m_backend.GetClangType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID)); + CreateValueObjectFromData(idx_name.GetData(), + data, + m_exe_ctx_ref, + m_backend.GetClangType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID)); } return set_item.valobj_sp; } @@ -481,10 +637,10 @@ lldb_private::formatters::NSSetMSyntheticFrontEnd::GetChildAtIndex (size_t idx) process_sp->GetAddressByteSize()); set_item.valobj_sp = - ValueObject::CreateValueObjectFromData(idx_name.GetData(), - data, - m_exe_ctx_ref, - m_backend.GetClangType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID)); + CreateValueObjectFromData(idx_name.GetData(), + data, + m_exe_ctx_ref, + m_backend.GetClangType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID)); } return set_item.valobj_sp; } @@ -557,7 +713,7 @@ lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::~NSOrderedSetSyntheticF } template bool -lldb_private::formatters::NSSetSummaryProvider<true> (ValueObject& valobj, Stream& stream); +lldb_private::formatters::NSSetSummaryProvider<true> (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options); template bool -lldb_private::formatters::NSSetSummaryProvider<false> (ValueObject& valobj, Stream& stream); +lldb_private::formatters::NSSetSummaryProvider<false> (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options); diff --git a/source/DataFormatters/StringPrinter.cpp b/source/DataFormatters/StringPrinter.cpp new file mode 100644 index 000000000000..3af5931ad716 --- /dev/null +++ b/source/DataFormatters/StringPrinter.cpp @@ -0,0 +1,650 @@ +//===-- StringPrinter.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/DataFormatters/StringPrinter.h" + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "llvm/Support/ConvertUTF.h" + +#include <ctype.h> +#include <functional> +#include <locale> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +// I can't use a std::unique_ptr for this because the Deleter is a template argument there +// and I want the same type to represent both pointers I want to free and pointers I don't need +// to free - which is what this class essentially is +// It's very specialized to the needs of this file, and not suggested for general use +template <typename T = uint8_t, typename U = char, typename S = size_t> +struct StringPrinterBufferPointer +{ +public: + + typedef std::function<void(const T*)> Deleter; + + StringPrinterBufferPointer (std::nullptr_t ptr) : + m_data(nullptr), + m_size(0), + m_deleter() + {} + + StringPrinterBufferPointer(const T* bytes, S size, Deleter deleter = nullptr) : + m_data(bytes), + m_size(size), + m_deleter(deleter) + {} + + StringPrinterBufferPointer(const U* bytes, S size, Deleter deleter = nullptr) : + m_data((T*)bytes), + m_size(size), + m_deleter(deleter) + {} + + StringPrinterBufferPointer(StringPrinterBufferPointer&& rhs) : + m_data(rhs.m_data), + m_size(rhs.m_size), + m_deleter(rhs.m_deleter) + { + rhs.m_data = nullptr; + } + + StringPrinterBufferPointer(const StringPrinterBufferPointer& rhs) : + m_data(rhs.m_data), + m_size(rhs.m_size), + m_deleter(rhs.m_deleter) + { + rhs.m_data = nullptr; // this is why m_data has to be mutable + } + + const T* + GetBytes () const + { + return m_data; + } + + const S + GetSize () const + { + return m_size; + } + + ~StringPrinterBufferPointer () + { + if (m_data && m_deleter) + m_deleter(m_data); + m_data = nullptr; + } + + StringPrinterBufferPointer& + operator = (const StringPrinterBufferPointer& rhs) + { + if (m_data && m_deleter) + m_deleter(m_data); + m_data = rhs.m_data; + m_size = rhs.m_size; + m_deleter = rhs.m_deleter; + rhs.m_data = nullptr; + return *this; + } + +private: + mutable const T* m_data; + size_t m_size; + Deleter m_deleter; +}; + +// we define this for all values of type but only implement it for those we care about +// that's good because we get linker errors for any unsupported type +template <StringElementType type> +static StringPrinterBufferPointer<> +GetPrintableImpl(uint8_t* buffer, uint8_t* buffer_end, uint8_t*& next); + +// mimic isprint() for Unicode codepoints +static bool +isprint(char32_t codepoint) +{ + if (codepoint <= 0x1F || codepoint == 0x7F) // C0 + { + return false; + } + if (codepoint >= 0x80 && codepoint <= 0x9F) // C1 + { + return false; + } + if (codepoint == 0x2028 || codepoint == 0x2029) // line/paragraph separators + { + return false; + } + if (codepoint == 0x200E || codepoint == 0x200F || (codepoint >= 0x202A && codepoint <= 0x202E)) // bidirectional text control + { + return false; + } + if (codepoint >= 0xFFF9 && codepoint <= 0xFFFF) // interlinears and generally specials + { + return false; + } + return true; +} + +template <> +StringPrinterBufferPointer<> +GetPrintableImpl<StringElementType::ASCII> (uint8_t* buffer, uint8_t* buffer_end, uint8_t*& next) +{ + StringPrinterBufferPointer<> retval = {nullptr}; + + switch (*buffer) + { + case 0: + retval = {"\\0",2}; + break; + case '\a': + retval = {"\\a",2}; + break; + case '\b': + retval = {"\\b",2}; + break; + case '\f': + retval = {"\\f",2}; + break; + case '\n': + retval = {"\\n",2}; + break; + case '\r': + retval = {"\\r",2}; + break; + case '\t': + retval = {"\\t",2}; + break; + case '\v': + retval = {"\\v",2}; + break; + case '\"': + retval = {"\\\"",2}; + break; + case '\\': + retval = {"\\\\",2}; + break; + default: + if (isprint(*buffer)) + retval = {buffer,1}; + else + { + retval = { new uint8_t[5],4,[] (const uint8_t* c) {delete[] c;} }; + sprintf((char*)retval.GetBytes(),"\\x%02x",*buffer); + break; + } + } + + next = buffer + 1; + return retval; +} + +static char32_t +ConvertUTF8ToCodePoint (unsigned char c0, unsigned char c1) +{ + return (c0-192)*64+(c1-128); +} +static char32_t +ConvertUTF8ToCodePoint (unsigned char c0, unsigned char c1, unsigned char c2) +{ + return (c0-224)*4096+(c1-128)*64+(c2-128); +} +static char32_t +ConvertUTF8ToCodePoint (unsigned char c0, unsigned char c1, unsigned char c2, unsigned char c3) +{ + return (c0-240)*262144+(c2-128)*4096+(c2-128)*64+(c3-128); +} + +template <> +StringPrinterBufferPointer<> +GetPrintableImpl<StringElementType::UTF8> (uint8_t* buffer, uint8_t* buffer_end, uint8_t*& next) +{ + StringPrinterBufferPointer<> retval {nullptr}; + + unsigned utf8_encoded_len = getNumBytesForUTF8(*buffer); + + if (1+buffer_end-buffer < utf8_encoded_len) + { + // I don't have enough bytes - print whatever I have left + retval = {buffer,static_cast<size_t>(1+buffer_end-buffer)}; + next = buffer_end+1; + return retval; + } + + char32_t codepoint = 0; + switch (utf8_encoded_len) + { + case 1: + // this is just an ASCII byte - ask ASCII + return GetPrintableImpl<StringElementType::ASCII>(buffer, buffer_end, next); + case 2: + codepoint = ConvertUTF8ToCodePoint((unsigned char)*buffer, (unsigned char)*(buffer+1)); + break; + case 3: + codepoint = ConvertUTF8ToCodePoint((unsigned char)*buffer, (unsigned char)*(buffer+1), (unsigned char)*(buffer+2)); + break; + case 4: + codepoint = ConvertUTF8ToCodePoint((unsigned char)*buffer, (unsigned char)*(buffer+1), (unsigned char)*(buffer+2), (unsigned char)*(buffer+3)); + break; + default: + // this is probably some bogus non-character thing + // just print it as-is and hope to sync up again soon + retval = {buffer,1}; + next = buffer+1; + return retval; + } + + if (codepoint) + { + switch (codepoint) + { + case 0: + retval = {"\\0",2}; + break; + case '\a': + retval = {"\\a",2}; + break; + case '\b': + retval = {"\\b",2}; + break; + case '\f': + retval = {"\\f",2}; + break; + case '\n': + retval = {"\\n",2}; + break; + case '\r': + retval = {"\\r",2}; + break; + case '\t': + retval = {"\\t",2}; + break; + case '\v': + retval = {"\\v",2}; + break; + case '\"': + retval = {"\\\"",2}; + break; + case '\\': + retval = {"\\\\",2}; + break; + default: + if (isprint(codepoint)) + retval = {buffer,utf8_encoded_len}; + else + { + retval = { new uint8_t[11],10,[] (const uint8_t* c) {delete[] c;} }; + sprintf((char*)retval.GetBytes(),"\\U%08x",codepoint); + break; + } + } + + next = buffer + utf8_encoded_len; + return retval; + } + + // this should not happen - but just in case.. try to resync at some point + retval = {buffer,1}; + next = buffer+1; + return retval; +} + +// Given a sequence of bytes, this function returns: +// a sequence of bytes to actually print out + a length +// the following unscanned position of the buffer is in next +static StringPrinterBufferPointer<> +GetPrintable(StringElementType type, uint8_t* buffer, uint8_t* buffer_end, uint8_t*& next) +{ + if (!buffer) + return {nullptr}; + + switch (type) + { + case StringElementType::ASCII: + return GetPrintableImpl<StringElementType::ASCII>(buffer, buffer_end, next); + case StringElementType::UTF8: + return GetPrintableImpl<StringElementType::UTF8>(buffer, buffer_end, next); + default: + return {nullptr}; + } +} + +// use this call if you already have an LLDB-side buffer for the data +template<typename SourceDataType> +static bool +DumpUTFBufferToStream (ConversionResult (*ConvertFunction) (const SourceDataType**, + const SourceDataType*, + UTF8**, + UTF8*, + ConversionFlags), + const DataExtractor& data, + Stream& stream, + char prefix_token, + char quote, + uint32_t sourceSize, + bool escapeNonPrintables) +{ + if (prefix_token != 0) + stream.Printf("%c",prefix_token); + if (quote != 0) + stream.Printf("%c",quote); + if (data.GetByteSize() && data.GetDataStart() && data.GetDataEnd()) + { + const int bufferSPSize = data.GetByteSize(); + if (sourceSize == 0) + { + const int origin_encoding = 8*sizeof(SourceDataType); + sourceSize = bufferSPSize/(origin_encoding / 4); + } + + SourceDataType *data_ptr = (SourceDataType*)data.GetDataStart(); + SourceDataType *data_end_ptr = data_ptr + sourceSize; + + while (data_ptr < data_end_ptr) + { + if (!*data_ptr) + { + data_end_ptr = data_ptr; + break; + } + data_ptr++; + } + + data_ptr = (SourceDataType*)data.GetDataStart(); + + lldb::DataBufferSP utf8_data_buffer_sp; + UTF8* utf8_data_ptr = nullptr; + UTF8* utf8_data_end_ptr = nullptr; + + if (ConvertFunction) + { + utf8_data_buffer_sp.reset(new DataBufferHeap(4*bufferSPSize,0)); + utf8_data_ptr = (UTF8*)utf8_data_buffer_sp->GetBytes(); + utf8_data_end_ptr = utf8_data_ptr + utf8_data_buffer_sp->GetByteSize(); + ConvertFunction ( (const SourceDataType**)&data_ptr, data_end_ptr, &utf8_data_ptr, utf8_data_end_ptr, lenientConversion ); + utf8_data_ptr = (UTF8*)utf8_data_buffer_sp->GetBytes(); // needed because the ConvertFunction will change the value of the data_ptr + } + else + { + // just copy the pointers - the cast is necessary to make the compiler happy + // but this should only happen if we are reading UTF8 data + utf8_data_ptr = (UTF8*)data_ptr; + utf8_data_end_ptr = (UTF8*)data_end_ptr; + } + + // since we tend to accept partial data (and even partially malformed data) + // we might end up with no NULL terminator before the end_ptr + // hence we need to take a slower route and ensure we stay within boundaries + for (;utf8_data_ptr < utf8_data_end_ptr;) + { + if (!*utf8_data_ptr) + break; + + if (escapeNonPrintables) + { + uint8_t* next_data = nullptr; + auto printable = GetPrintable(StringElementType::UTF8, utf8_data_ptr, utf8_data_end_ptr, next_data); + auto printable_bytes = printable.GetBytes(); + auto printable_size = printable.GetSize(); + if (!printable_bytes || !next_data) + { + // GetPrintable() failed on us - print one byte in a desperate resync attempt + printable_bytes = utf8_data_ptr; + printable_size = 1; + next_data = utf8_data_ptr+1; + } + for (unsigned c = 0; c < printable_size; c++) + stream.Printf("%c", *(printable_bytes+c)); + utf8_data_ptr = (uint8_t*)next_data; + } + else + { + stream.Printf("%c",*utf8_data_ptr); + utf8_data_ptr++; + } + } + } + if (quote != 0) + stream.Printf("%c",quote); + return true; +} + +lldb_private::formatters::ReadStringAndDumpToStreamOptions::ReadStringAndDumpToStreamOptions (ValueObject& valobj) : + ReadStringAndDumpToStreamOptions() +{ + SetEscapeNonPrintables(valobj.GetTargetSP()->GetDebugger().GetEscapeNonPrintables()); +} + +lldb_private::formatters::ReadBufferAndDumpToStreamOptions::ReadBufferAndDumpToStreamOptions (ValueObject& valobj) : + ReadBufferAndDumpToStreamOptions() +{ + SetEscapeNonPrintables(valobj.GetTargetSP()->GetDebugger().GetEscapeNonPrintables()); +} + + +namespace lldb_private +{ + +namespace formatters +{ + +template <> +bool +ReadStringAndDumpToStream<StringElementType::ASCII> (ReadStringAndDumpToStreamOptions options) +{ + assert(options.GetStream() && "need a Stream to print the string to"); + Error my_error; + size_t my_data_read; + + ProcessSP process_sp(options.GetProcessSP()); + + if (process_sp.get() == nullptr || options.GetLocation() == 0) + return false; + + size_t size; + + if (options.GetSourceSize() == 0) + size = process_sp->GetTarget().GetMaximumSizeOfStringSummary(); + else if (!options.GetIgnoreMaxLength()) + size = std::min(options.GetSourceSize(),process_sp->GetTarget().GetMaximumSizeOfStringSummary()); + else + size = options.GetSourceSize(); + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(size,0)); + + my_data_read = process_sp->ReadCStringFromMemory(options.GetLocation(), (char*)buffer_sp->GetBytes(), size, my_error); + + if (my_error.Fail()) + return false; + + char prefix_token = options.GetPrefixToken(); + char quote = options.GetQuote(); + + if (prefix_token != 0) + options.GetStream()->Printf("%c%c",prefix_token,quote); + else if (quote != 0) + options.GetStream()->Printf("%c",quote); + + uint8_t* data_end = buffer_sp->GetBytes()+buffer_sp->GetByteSize(); + + // since we tend to accept partial data (and even partially malformed data) + // we might end up with no NULL terminator before the end_ptr + // hence we need to take a slower route and ensure we stay within boundaries + for (uint8_t* data = buffer_sp->GetBytes(); *data && (data < data_end);) + { + if (options.GetEscapeNonPrintables()) + { + uint8_t* next_data = nullptr; + auto printable = GetPrintable(StringElementType::ASCII, data, data_end, next_data); + auto printable_bytes = printable.GetBytes(); + auto printable_size = printable.GetSize(); + if (!printable_bytes || !next_data) + { + // GetPrintable() failed on us - print one byte in a desperate resync attempt + printable_bytes = data; + printable_size = 1; + next_data = data+1; + } + for (unsigned c = 0; c < printable_size; c++) + options.GetStream()->Printf("%c", *(printable_bytes+c)); + data = (uint8_t*)next_data; + } + else + { + options.GetStream()->Printf("%c",*data); + data++; + } + } + + if (quote != 0) + options.GetStream()->Printf("%c",quote); + + return true; +} + +template<typename SourceDataType> +static bool +ReadUTFBufferAndDumpToStream (const ReadStringAndDumpToStreamOptions& options, + ConversionResult (*ConvertFunction) (const SourceDataType**, + const SourceDataType*, + UTF8**, + UTF8*, + ConversionFlags)) +{ + assert(options.GetStream() && "need a Stream to print the string to"); + + if (options.GetLocation() == 0 || options.GetLocation() == LLDB_INVALID_ADDRESS) + return false; + + lldb::ProcessSP process_sp(options.GetProcessSP()); + + if (!process_sp) + return false; + + const int type_width = sizeof(SourceDataType); + const int origin_encoding = 8 * type_width ; + if (origin_encoding != 8 && origin_encoding != 16 && origin_encoding != 32) + return false; + // if not UTF8, I need a conversion function to return proper UTF8 + if (origin_encoding != 8 && !ConvertFunction) + return false; + + if (!options.GetStream()) + return false; + + uint32_t sourceSize = options.GetSourceSize(); + bool needs_zero_terminator = options.GetNeedsZeroTermination(); + + if (!sourceSize) + { + sourceSize = process_sp->GetTarget().GetMaximumSizeOfStringSummary(); + needs_zero_terminator = true; + } + else + sourceSize = std::min(sourceSize,process_sp->GetTarget().GetMaximumSizeOfStringSummary()); + + const int bufferSPSize = sourceSize * type_width; + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(bufferSPSize,0)); + + if (!buffer_sp->GetBytes()) + return false; + + Error error; + char *buffer = reinterpret_cast<char *>(buffer_sp->GetBytes()); + + size_t data_read = 0; + if (needs_zero_terminator) + data_read = process_sp->ReadStringFromMemory(options.GetLocation(), buffer, bufferSPSize, error, type_width); + else + data_read = process_sp->ReadMemoryFromInferior(options.GetLocation(), (char*)buffer_sp->GetBytes(), bufferSPSize, error); + + if (error.Fail()) + { + options.GetStream()->Printf("unable to read data"); + return true; + } + + DataExtractor data(buffer_sp, process_sp->GetByteOrder(), process_sp->GetAddressByteSize()); + + return DumpUTFBufferToStream(ConvertFunction, data, *options.GetStream(), options.GetPrefixToken(), options.GetQuote(), sourceSize, options.GetEscapeNonPrintables()); +} + +template <> +bool +ReadStringAndDumpToStream<StringElementType::UTF8> (ReadStringAndDumpToStreamOptions options) +{ + return ReadUTFBufferAndDumpToStream<UTF8>(options, + nullptr); +} + +template <> +bool +ReadStringAndDumpToStream<StringElementType::UTF16> (ReadStringAndDumpToStreamOptions options) +{ + return ReadUTFBufferAndDumpToStream<UTF16>(options, + ConvertUTF16toUTF8); +} + +template <> +bool +ReadStringAndDumpToStream<StringElementType::UTF32> (ReadStringAndDumpToStreamOptions options) +{ + return ReadUTFBufferAndDumpToStream<UTF32>(options, + ConvertUTF32toUTF8); +} + +template <> +bool +ReadBufferAndDumpToStream<StringElementType::UTF8> (ReadBufferAndDumpToStreamOptions options) +{ + assert(options.GetStream() && "need a Stream to print the string to"); + + return DumpUTFBufferToStream<UTF8>(nullptr, options.GetData(), *options.GetStream(), options.GetPrefixToken(), options.GetQuote(), options.GetSourceSize(), options.GetEscapeNonPrintables()); +} + +template <> +bool +ReadBufferAndDumpToStream<StringElementType::ASCII> (ReadBufferAndDumpToStreamOptions options) +{ + // treat ASCII the same as UTF8 + // FIXME: can we optimize ASCII some more? + return ReadBufferAndDumpToStream<StringElementType::UTF8>(options); +} + +template <> +bool +ReadBufferAndDumpToStream<StringElementType::UTF16> (ReadBufferAndDumpToStreamOptions options) +{ + assert(options.GetStream() && "need a Stream to print the string to"); + + return DumpUTFBufferToStream(ConvertUTF16toUTF8, options.GetData(), *options.GetStream(), options.GetPrefixToken(), options.GetQuote(), options.GetSourceSize(), options.GetEscapeNonPrintables()); +} + +template <> +bool +ReadBufferAndDumpToStream<StringElementType::UTF32> (ReadBufferAndDumpToStreamOptions options) +{ + assert(options.GetStream() && "need a Stream to print the string to"); + + return DumpUTFBufferToStream(ConvertUTF32toUTF8, options.GetData(), *options.GetStream(), options.GetPrefixToken(), options.GetQuote(), options.GetSourceSize(), options.GetEscapeNonPrintables()); +} + +} // namespace formatters + +} // namespace lldb_private diff --git a/source/DataFormatters/TypeCategory.cpp b/source/DataFormatters/TypeCategory.cpp index 322d1cf55437..3df6884fe679 100644 --- a/source/DataFormatters/TypeCategory.cpp +++ b/source/DataFormatters/TypeCategory.cpp @@ -27,6 +27,7 @@ m_filter_cont("filter","regex-filter",clist), #ifndef LLDB_DISABLE_PYTHON m_synth_cont("synth","regex-synth",clist), #endif +m_validator_cont("validator","regex-validator",clist), m_enabled(false), m_change_listener(clist), m_mutex(Mutex::eMutexTypeRecursive), @@ -129,6 +130,22 @@ TypeCategoryImpl::Get (ValueObject& valobj, return false; } +bool +TypeCategoryImpl::Get (ValueObject& valobj, + const FormattersMatchVector& candidates, + lldb::TypeValidatorImplSP& entry, + uint32_t* reason) +{ + if (!IsEnabled()) + return false; + if (GetTypeValidatorsContainer()->Get(candidates, entry, reason)) + return true; + bool regex = GetRegexTypeValidatorsContainer()->Get(candidates, entry, reason); + if (regex && reason) + *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionSummary; + return regex; +} + void TypeCategoryImpl::Clear (FormatCategoryItems items) { @@ -153,6 +170,11 @@ TypeCategoryImpl::Clear (FormatCategoryItems items) if ( (items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth ) GetRegexTypeSyntheticsContainer()->Clear(); #endif + + if ( (items & eFormatCategoryItemValidator) == eFormatCategoryItemValidator ) + GetTypeValidatorsContainer()->Clear(); + if ( (items & eFormatCategoryItemRegexValidator) == eFormatCategoryItemRegexValidator ) + GetRegexTypeValidatorsContainer()->Clear(); } bool @@ -182,6 +204,12 @@ TypeCategoryImpl::Delete (ConstString name, if ( (items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth ) success = GetRegexTypeSyntheticsContainer()->Delete(name) || success; #endif + + if ( (items & eFormatCategoryItemValidator) == eFormatCategoryItemValidator ) + success = GetTypeValidatorsContainer()->Delete(name) || success; + if ( (items & eFormatCategoryItemRegexValidator) == eFormatCategoryItemRegexValidator ) + success = GetRegexTypeValidatorsContainer()->Delete(name) || success; + return success; } @@ -211,6 +239,12 @@ TypeCategoryImpl::GetCount (FormatCategoryItems items) if ( (items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth ) count += GetRegexTypeSyntheticsContainer()->GetCount(); #endif + + if ( (items & eFormatCategoryItemValidator) == eFormatCategoryItemValidator ) + count += GetTypeValidatorsContainer()->GetCount(); + if ( (items & eFormatCategoryItemRegexValidator) == eFormatCategoryItemRegexValidator ) + count += GetRegexTypeValidatorsContainer()->GetCount(); + return count; } @@ -230,6 +264,7 @@ TypeCategoryImpl::AnyMatches(ConstString type_name, #ifndef LLDB_DISABLE_PYTHON ScriptedSyntheticChildren::SharedPointer synth_sp; #endif + TypeValidatorImpl::SharedPointer validator_sp; if ( (items & eFormatCategoryItemValue) == eFormatCategoryItemValue ) { @@ -324,6 +359,30 @@ TypeCategoryImpl::AnyMatches(ConstString type_name, } } #endif + + if ( (items & eFormatCategoryItemValidator) == eFormatCategoryItemValidator ) + { + if (GetTypeValidatorsContainer()->Get(type_name, validator_sp)) + { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemValidator; + return true; + } + } + if ( (items & eFormatCategoryItemRegexValidator) == eFormatCategoryItemRegexValidator ) + { + if (GetRegexTypeValidatorsContainer()->Get(type_name, validator_sp)) + { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemRegexValidator; + return true; + } + } + return false; } @@ -393,6 +452,22 @@ TypeCategoryImpl::GetSyntheticForType (lldb::TypeNameSpecifierImplSP type_sp) } #endif +TypeCategoryImpl::ValidatorContainer::MapValueType +TypeCategoryImpl::GetValidatorForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + ValidatorContainer::MapValueType retval; + + if (type_sp) + { + if (type_sp->IsRegex()) + GetRegexTypeValidatorsContainer()->GetExact(ConstString(type_sp->GetName()),retval); + else + GetTypeValidatorsContainer()->GetExact(ConstString(type_sp->GetName()),retval); + } + + return retval; +} + lldb::TypeNameSpecifierImplSP TypeCategoryImpl::GetTypeNameSpecifierForSummaryAtIndex (size_t index) { @@ -467,12 +542,30 @@ TypeCategoryImpl::GetTypeNameSpecifierForSyntheticAtIndex (size_t index) } #endif +TypeCategoryImpl::ValidatorContainer::MapValueType +TypeCategoryImpl::GetValidatorAtIndex (size_t index) +{ + if (index < GetTypeValidatorsContainer()->GetCount()) + return GetTypeValidatorsContainer()->GetAtIndex(index); + else + return GetRegexTypeValidatorsContainer()->GetAtIndex(index-GetTypeValidatorsContainer()->GetCount()); +} + +lldb::TypeNameSpecifierImplSP +TypeCategoryImpl::GetTypeNameSpecifierForValidatorAtIndex (size_t index) +{ + if (index < GetTypeValidatorsContainer()->GetCount()) + return GetTypeValidatorsContainer()->GetTypeNameSpecifierAtIndex(index); + else + return GetRegexTypeValidatorsContainer()->GetTypeNameSpecifierAtIndex(index - GetTypeValidatorsContainer()->GetCount()); +} + void TypeCategoryImpl::Enable (bool value, uint32_t position) { Mutex::Locker locker(m_mutex); - m_enabled = value; - m_enabled_position = position; + if ( (m_enabled = value) ) + m_enabled_position = position; if (m_change_listener) m_change_listener->Changed(); } diff --git a/source/DataFormatters/TypeCategoryMap.cpp b/source/DataFormatters/TypeCategoryMap.cpp index c6dba1b9f4bd..ae34d0339011 100644 --- a/source/DataFormatters/TypeCategoryMap.cpp +++ b/source/DataFormatters/TypeCategoryMap.cpp @@ -120,6 +120,46 @@ TypeCategoryMap::Disable (ValueSP category) } void +TypeCategoryMap::EnableAllCategories () +{ + Mutex::Locker locker(m_map_mutex); + std::vector<ValueSP> sorted_categories(m_map.size(), ValueSP()); + MapType::iterator iter = m_map.begin(), end = m_map.end(); + for (; iter != end; ++iter) + { + if (iter->second->IsEnabled()) + continue; + auto pos = iter->second->GetLastEnabledPosition(); + if (pos >= sorted_categories.size()) + { + auto iter = std::find_if(sorted_categories.begin(), + sorted_categories.end(), + [] (const ValueSP& sp) -> bool { + return sp.get() == nullptr; + }); + pos = std::distance(sorted_categories.begin(), iter); + } + sorted_categories.at(pos) = iter->second; + } + decltype(sorted_categories)::iterator viter = sorted_categories.begin(), vend = sorted_categories.end(); + for (; viter != vend; viter++) + if (viter->get()) + Enable(*viter, Last); +} + +void +TypeCategoryMap::DisableAllCategories () +{ + Mutex::Locker locker(m_map_mutex); + Position p = First; + for (; false == m_active_categories.empty(); p++) + { + m_active_categories.front()->SetEnabledPosition(p); + Disable(m_active_categories.front()); + } +} + +void TypeCategoryMap::Clear () { Mutex::Locker locker(m_map_mutex); @@ -266,6 +306,34 @@ TypeCategoryMap::GetSyntheticChildren (ValueObject& valobj, } #endif +lldb::TypeValidatorImplSP +TypeCategoryMap::GetValidator (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + Mutex::Locker locker(m_map_mutex); + + uint32_t reason_why; + ActiveCategoriesIterator begin, end = m_active_categories.end(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + + FormattersMatchVector matches = FormatManager::GetPossibleMatches(valobj, use_dynamic); + + for (begin = m_active_categories.begin(); begin != end; begin++) + { + lldb::TypeCategoryImplSP category_sp = *begin; + lldb::TypeValidatorImplSP current_format; + if (log) + log->Printf("\n[CategoryMap::GetValidator] Trying to use category %s", category_sp->GetName()); + if (!category_sp->Get(valobj, matches, current_format, &reason_why)) + continue; + return current_format; + } + if (log) + log->Printf("[CategoryMap::GetValidator] nothing found - returning empty SP"); + return lldb::TypeValidatorImplSP(); +} + void TypeCategoryMap::LoopThrough(CallbackType callback, void* param) { diff --git a/source/DataFormatters/TypeFormat.cpp b/source/DataFormatters/TypeFormat.cpp index 0c62daf87bbc..f07d8127d2b9 100644 --- a/source/DataFormatters/TypeFormat.cpp +++ b/source/DataFormatters/TypeFormat.cpp @@ -59,9 +59,9 @@ TypeFormatImpl_Format::FormatObject (ValueObject *valobj, { if (!valobj) return false; - if (valobj->GetClangType().IsAggregateType () == false) + if (valobj->CanProvideValue()) { - const Value& value(valobj->GetValue()); + Value& value(valobj->GetValue()); const Value::ContextType context_type = value.GetContextType(); ExecutionContext exe_ctx (valobj->GetExecutionContextRef()); DataExtractor data; @@ -92,14 +92,14 @@ TypeFormatImpl_Format::FormatObject (ValueObject *valobj, } else { - ClangASTType clang_type = valobj->GetClangType (); + ClangASTType clang_type = value.GetClangType (); if (clang_type) { // put custom bytes to display in the DataExtractor to override the default value logic if (GetFormat() == eFormatCString) { lldb_private::Flags type_flags(clang_type.GetTypeInfo(NULL)); // disambiguate w.r.t. TypeFormatImpl::Flags - if (type_flags.Test(ClangASTType::eTypeIsPointer) && !type_flags.Test(ClangASTType::eTypeIsObjC)) + if (type_flags.Test(eTypeIsPointer) && !type_flags.Test(eTypeIsObjC)) { // if we are dumping a pointer as a c-string, get the pointee data as a string TargetSP target_sp(valobj->GetTargetSP()); @@ -180,7 +180,7 @@ TypeFormatImpl_EnumType::FormatObject (ValueObject *valobj, dest.clear(); if (!valobj) return false; - if (valobj->GetClangType().IsAggregateType ()) + if (!valobj->CanProvideValue()) return false; ProcessSP process_sp; TargetSP target_sp; @@ -209,7 +209,7 @@ TypeFormatImpl_EnumType::FormatObject (ValueObject *valobj, { if (!type_sp) continue; - if ( (type_sp->GetClangForwardType().GetTypeInfo() & ClangASTType::eTypeIsEnumeration) == ClangASTType::eTypeIsEnumeration) + if ( (type_sp->GetClangForwardType().GetTypeInfo() & eTypeIsEnumeration) == eTypeIsEnumeration) { valobj_enum_type = type_sp->GetClangFullType(); m_types.emplace(valobj_key,valobj_enum_type); diff --git a/source/DataFormatters/TypeSummary.cpp b/source/DataFormatters/TypeSummary.cpp index e5d80174c3cc..ff089af58cb7 100644 --- a/source/DataFormatters/TypeSummary.cpp +++ b/source/DataFormatters/TypeSummary.cpp @@ -34,6 +34,50 @@ using namespace lldb; using namespace lldb_private; +TypeSummaryOptions::TypeSummaryOptions () : + m_lang(eLanguageTypeUnknown), + m_capping(eTypeSummaryCapped) +{} + +TypeSummaryOptions::TypeSummaryOptions (const TypeSummaryOptions& rhs) : + m_lang(rhs.m_lang), + m_capping(rhs.m_capping) +{} + +TypeSummaryOptions& +TypeSummaryOptions::operator = (const TypeSummaryOptions& rhs) +{ + m_lang = rhs.m_lang; + m_capping = rhs.m_capping; + return *this; +} + +lldb::LanguageType +TypeSummaryOptions::GetLanguage () const +{ + return m_lang; +} + +lldb::TypeSummaryCapping +TypeSummaryOptions::GetCapping () const +{ + return m_capping; +} + +TypeSummaryOptions& +TypeSummaryOptions::SetLanguage (lldb::LanguageType lang) +{ + m_lang = lang; + return *this; +} + +TypeSummaryOptions& +TypeSummaryOptions::SetCapping (lldb::TypeSummaryCapping cap) +{ + m_capping = cap; + return *this; +} + TypeSummaryImpl::TypeSummaryImpl (const TypeSummaryImpl::Flags& flags) : m_flags(flags) { @@ -51,7 +95,8 @@ m_format() bool StringSummaryFormat::FormatObject (ValueObject *valobj, - std::string& retval) + std::string& retval, + const TypeSummaryOptions& options) { if (!valobj) { @@ -115,11 +160,12 @@ m_description(description ? description : "") bool CXXFunctionSummaryFormat::FormatObject (ValueObject *valobj, - std::string& dest) + std::string& dest, + const TypeSummaryOptions& options) { dest.clear(); StreamString stream; - if (!m_impl || m_impl(*valobj,stream) == false) + if (!m_impl || m_impl(*valobj,stream,options) == false) return false; dest.assign(stream.GetData()); return true; @@ -160,7 +206,8 @@ m_script_function_sp() bool ScriptSummaryFormat::FormatObject (ValueObject *valobj, - std::string& retval) + std::string& retval, + const TypeSummaryOptions& options) { Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); @@ -190,6 +237,7 @@ ScriptSummaryFormat::FormatObject (ValueObject *valobj, return script_interpreter->GetScriptedSummary(m_function_name.c_str(), valobj->GetSP(), m_script_function_sp, + options, retval); } diff --git a/source/DataFormatters/TypeSynthetic.cpp b/source/DataFormatters/TypeSynthetic.cpp index 3949673a4be8..13c1c7508b68 100644 --- a/source/DataFormatters/TypeSynthetic.cpp +++ b/source/DataFormatters/TypeSynthetic.cpp @@ -23,6 +23,7 @@ #include "lldb/Core/StreamString.h" #include "lldb/DataFormatters/TypeSynthetic.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" #include "lldb/Symbol/ClangASTType.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" @@ -30,6 +31,59 @@ using namespace lldb; using namespace lldb_private; +void +TypeFilterImpl::AddExpressionPath (const std::string& path) +{ + bool need_add_dot = true; + if (path[0] == '.' || + (path[0] == '-' && path[1] == '>') || + path[0] == '[') + need_add_dot = false; + // add a '.' symbol to help forgetful users + if(!need_add_dot) + m_expression_paths.push_back(path); + else + m_expression_paths.push_back(std::string(".") + path); +} + +bool +TypeFilterImpl::SetExpressionPathAtIndex (size_t i, const std::string& path) +{ + if (i >= GetCount()) + return false; + bool need_add_dot = true; + if (path[0] == '.' || + (path[0] == '-' && path[1] == '>') || + path[0] == '[') + need_add_dot = false; + // add a '.' symbol to help forgetful users + if(!need_add_dot) + m_expression_paths[i] = path; + else + m_expression_paths[i] = std::string(".") + path; + return true; +} + +size_t +TypeFilterImpl::FrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + const char* name_cstr = name.GetCString(); + for (size_t i = 0; i < filter->GetCount(); i++) + { + const char* expr_cstr = filter->GetExpressionPathAtIndex(i); + if (expr_cstr) + { + if (*expr_cstr == '.') + expr_cstr++; + else if (*expr_cstr == '-' && *(expr_cstr+1) == '>') + expr_cstr += 2; + } + if (!::strcmp(name_cstr, expr_cstr)) + return i; + } + return UINT32_MAX; +} + std::string TypeFilterImpl::GetDescription() { @@ -63,6 +117,41 @@ CXXSyntheticChildren::GetDescription() return sstr.GetString(); } +lldb::ValueObjectSP +SyntheticChildrenFrontEnd::CreateValueObjectFromExpression (const char* name, + const char* expression, + const ExecutionContext& exe_ctx) +{ + ValueObjectSP valobj_sp(ValueObject::CreateValueObjectFromExpression(name, expression, exe_ctx)); + if (valobj_sp) + valobj_sp->SetSyntheticChildrenGenerated(true); + return valobj_sp; +} + +lldb::ValueObjectSP +SyntheticChildrenFrontEnd::CreateValueObjectFromAddress (const char* name, + uint64_t address, + const ExecutionContext& exe_ctx, + ClangASTType type) +{ + ValueObjectSP valobj_sp(ValueObject::CreateValueObjectFromAddress(name, address, exe_ctx, type)); + if (valobj_sp) + valobj_sp->SetSyntheticChildrenGenerated(true); + return valobj_sp; +} + +lldb::ValueObjectSP +SyntheticChildrenFrontEnd::CreateValueObjectFromData (const char* name, + const DataExtractor& data, + const ExecutionContext& exe_ctx, + ClangASTType type) +{ + ValueObjectSP valobj_sp(ValueObject::CreateValueObjectFromData(name, data, exe_ctx, type)); + if (valobj_sp) + valobj_sp->SetSyntheticChildrenGenerated(true); + return valobj_sp; +} + #ifndef LLDB_DISABLE_PYTHON ScriptedSyntheticChildren::FrontEnd::FrontEnd(std::string pclass, ValueObject &backend) : @@ -98,6 +187,55 @@ ScriptedSyntheticChildren::FrontEnd::GetChildAtIndex (size_t idx) return m_interpreter->GetChildAtIndex(m_wrapper_sp, idx); } +bool +ScriptedSyntheticChildren::FrontEnd::IsValid () +{ + return m_wrapper_sp.get() != nullptr && m_wrapper_sp->operator bool() && m_interpreter != nullptr; +} + +size_t +ScriptedSyntheticChildren::FrontEnd::CalculateNumChildren () +{ + if (!m_wrapper_sp || m_interpreter == NULL) + return 0; + return m_interpreter->CalculateNumChildren(m_wrapper_sp); +} + +bool +ScriptedSyntheticChildren::FrontEnd::Update () +{ + if (!m_wrapper_sp || m_interpreter == NULL) + return false; + + return m_interpreter->UpdateSynthProviderInstance(m_wrapper_sp); +} + +bool +ScriptedSyntheticChildren::FrontEnd::MightHaveChildren () +{ + if (!m_wrapper_sp || m_interpreter == NULL) + return false; + + return m_interpreter->MightHaveChildrenSynthProviderInstance(m_wrapper_sp); +} + +size_t +ScriptedSyntheticChildren::FrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + if (!m_wrapper_sp || m_interpreter == NULL) + return UINT32_MAX; + return m_interpreter->GetIndexOfChildWithName(m_wrapper_sp, name.GetCString()); +} + +lldb::ValueObjectSP +ScriptedSyntheticChildren::FrontEnd::GetSyntheticValue () +{ + if (!m_wrapper_sp || m_interpreter == NULL) + return nullptr; + + return m_interpreter->GetSyntheticValue(m_wrapper_sp); +} + std::string ScriptedSyntheticChildren::GetDescription() { diff --git a/source/DataFormatters/TypeValidator.cpp b/source/DataFormatters/TypeValidator.cpp new file mode 100644 index 000000000000..b5efac8f586a --- /dev/null +++ b/source/DataFormatters/TypeValidator.cpp @@ -0,0 +1,75 @@ +//===-- TypeValidator.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes + +// C++ Includes + +// Other libraries and framework includes + +// Project includes +#include "lldb/DataFormatters/TypeValidator.h" +#include "lldb/Core/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +TypeValidatorImpl::TypeValidatorImpl(const Flags &flags) : + m_flags(flags), + m_my_revision(0) +{ +} + +TypeValidatorImpl::~TypeValidatorImpl() +{ +} + +TypeValidatorImpl::ValidationResult +TypeValidatorImpl::Success () +{ + return ValidationResult { TypeValidatorResult::Success, "" }; +} + +TypeValidatorImpl::ValidationResult +TypeValidatorImpl::Failure (std::string message) +{ + return ValidationResult { TypeValidatorResult::Failure, message }; +} + +TypeValidatorImpl_CXX::TypeValidatorImpl_CXX (ValidatorFunction f, std::string d, const TypeValidatorImpl::Flags& flags) : + TypeValidatorImpl(flags), + m_description(d), + m_validator_function(f) +{ +} + +TypeValidatorImpl_CXX::~TypeValidatorImpl_CXX() +{ +} + +TypeValidatorImpl::ValidationResult +TypeValidatorImpl_CXX::FormatObject (ValueObject *valobj) const +{ + if (!valobj) + return Success(); // I guess there's nothing wrong with a null valueobject.. + + return m_validator_function(valobj); +} + +std::string +TypeValidatorImpl_CXX::GetDescription() +{ + StreamString sstr; + sstr.Printf ("%s%s%s%s", + m_description.c_str(), + Cascades() ? "" : " (not cascading)", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : ""); + return sstr.GetString(); +} diff --git a/source/DataFormatters/ValueObjectPrinter.cpp b/source/DataFormatters/ValueObjectPrinter.cpp index 65e5e3f45823..5560ce2971e7 100644 --- a/source/DataFormatters/ValueObjectPrinter.cpp +++ b/source/DataFormatters/ValueObjectPrinter.cpp @@ -66,11 +66,13 @@ ValueObjectPrinter::Init (ValueObject* valobj, bool ValueObjectPrinter::PrintValueObject () { - if (!GetDynamicValueIfNeeded () || m_valobj == nullptr) + if (!GetMostSpecializedValue () || m_valobj == nullptr) return false; if (ShouldPrintValueObject()) { + PrintValidationMarkerIfNeeded(); + PrintLocationIfNeeded(); m_stream->Indent(); @@ -89,11 +91,13 @@ ValueObjectPrinter::PrintValueObject () else m_stream->EOL(); + PrintValidationErrorIfNeeded(); + return true; } bool -ValueObjectPrinter::GetDynamicValueIfNeeded () +ValueObjectPrinter::GetMostSpecializedValue () { if (m_valobj) return true; @@ -130,6 +134,25 @@ ValueObjectPrinter::GetDynamicValueIfNeeded () else m_valobj = m_orig_valobj; } + + if (m_valobj->IsSynthetic()) + { + if (options.m_use_synthetic == false) + { + ValueObject *non_synthetic = m_valobj->GetNonSyntheticValue().get(); + if (non_synthetic) + m_valobj = non_synthetic; + } + } + else + { + if (options.m_use_synthetic == true) + { + ValueObject *synthetic = m_valobj->GetSyntheticValue().get(); + if (synthetic) + m_valobj = synthetic; + } + } } m_clang_type = m_valobj->GetClangType(); m_type_flags = m_clang_type.GetTypeInfo (); @@ -160,7 +183,7 @@ bool ValueObjectPrinter::ShouldPrintValueObject () { if (m_should_print == eLazyBoolCalculate) - m_should_print = (options.m_flat_output == false || m_type_flags.Test (ClangASTType::eTypeHasValue)) ? eLazyBoolYes : eLazyBoolNo; + m_should_print = (options.m_flat_output == false || m_type_flags.Test (eTypeHasValue)) ? eLazyBoolYes : eLazyBoolNo; return m_should_print == eLazyBoolYes; } @@ -176,7 +199,7 @@ bool ValueObjectPrinter::IsPtr () { if (m_is_ptr == eLazyBoolCalculate) - m_is_ptr = m_type_flags.Test (ClangASTType::eTypeIsPointer) ? eLazyBoolYes : eLazyBoolNo; + m_is_ptr = m_type_flags.Test (eTypeIsPointer) ? eLazyBoolYes : eLazyBoolNo; return m_is_ptr == eLazyBoolYes; } @@ -184,7 +207,7 @@ bool ValueObjectPrinter::IsRef () { if (m_is_ref == eLazyBoolCalculate) - m_is_ref = m_type_flags.Test (ClangASTType::eTypeIsReference) ? eLazyBoolYes : eLazyBoolNo; + m_is_ref = m_type_flags.Test (eTypeIsReference) ? eLazyBoolYes : eLazyBoolNo; return m_is_ref == eLazyBoolYes; } @@ -192,7 +215,7 @@ bool ValueObjectPrinter::IsAggregate () { if (m_is_aggregate == eLazyBoolCalculate) - m_is_aggregate = m_type_flags.Test (ClangASTType::eTypeHasChildren) ? eLazyBoolYes : eLazyBoolNo; + m_is_aggregate = m_type_flags.Test (eTypeHasChildren) ? eLazyBoolYes : eLazyBoolNo; return m_is_aggregate == eLazyBoolYes; } @@ -222,13 +245,13 @@ ValueObjectPrinter::PrintTypeIfNeeded () { // Some ValueObjects don't have types (like registers sets). Only print // the type if there is one to print - ConstString qualified_type_name; - if (options.m_be_raw) - qualified_type_name = m_valobj->GetQualifiedTypeName(); + ConstString type_name; + if (options.m_use_type_display_name) + type_name = m_valobj->GetDisplayTypeName(); else - qualified_type_name = m_valobj->GetDisplayTypeName(); - if (qualified_type_name) - m_stream->Printf("(%s) ", qualified_type_name.GetCString()); + type_name = m_valobj->GetQualifiedTypeName(); + if (type_name) + m_stream->Printf("(%s) ", type_name.GetCString()); else show_type = false; } @@ -438,8 +461,7 @@ ValueObjectPrinter::ShouldPrintChildren (bool is_failed_description, ValueObject* ValueObjectPrinter::GetValueObjectForChildrenGeneration () { - ValueObjectSP synth_valobj_sp = m_valobj->GetSyntheticValue (options.m_use_synthetic); - return (synth_valobj_sp ? synth_valobj_sp.get() : m_valobj); + return m_valobj; } void @@ -536,7 +558,13 @@ ValueObjectPrinter::PrintChildren (uint32_t curr_ptr_depth) { // Aggregate, no children... if (ShouldPrintValueObject()) - m_stream->PutCString(" {}\n"); + { + // if it has a synthetic value, then don't print {}, the synthetic children are probably only being used to vend a value + if (m_valobj->DoesProvideSyntheticValue()) + m_stream->PutCString( "\n"); + else + m_stream->PutCString(" {}\n"); + } } else { @@ -548,7 +576,7 @@ ValueObjectPrinter::PrintChildren (uint32_t curr_ptr_depth) bool ValueObjectPrinter::PrintChildrenOneLiner (bool hide_names) { - if (!GetDynamicValueIfNeeded () || m_valobj == nullptr) + if (!GetMostSpecializedValue () || m_valobj == nullptr) return false; ValueObject* synth_m_valobj = GetValueObjectForChildrenGeneration(); @@ -563,9 +591,8 @@ ValueObjectPrinter::PrintChildrenOneLiner (bool hide_names) for (uint32_t idx=0; idx<num_children; ++idx) { lldb::ValueObjectSP child_sp(synth_m_valobj->GetChildAtIndex(idx, true)); - lldb::ValueObjectSP child_dyn_sp = child_sp.get() ? child_sp->GetDynamicValue(options.m_use_dynamic) : child_sp; - if (child_dyn_sp) - child_sp = child_dyn_sp; + if (child_sp) + child_sp = child_sp->GetQualifiedRepresentationIfAvailable(options.m_use_dynamic, options.m_use_synthetic); if (child_sp) { if (idx) @@ -604,7 +631,7 @@ ValueObjectPrinter::PrintChildrenIfNeeded (bool value_printed, uint32_t curr_ptr_depth = m_ptr_depth; bool print_children = ShouldPrintChildren (is_failed_description,curr_ptr_depth); - bool print_oneline = (curr_ptr_depth > 0 || options.m_show_types || options.m_be_raw) ? false : DataVisualization::ShouldPrintAsOneLiner(*m_valobj); + bool print_oneline = (curr_ptr_depth > 0 || options.m_show_types || !options.m_allow_oneliner_mode) ? false : DataVisualization::ShouldPrintAsOneLiner(*m_valobj); if (print_children) { @@ -624,3 +651,44 @@ ValueObjectPrinter::PrintChildrenIfNeeded (bool value_printed, else m_stream->EOL(); } + +bool +ValueObjectPrinter::ShouldPrintValidation () +{ + return options.m_run_validator; +} + +bool +ValueObjectPrinter::PrintValidationMarkerIfNeeded () +{ + if (!ShouldPrintValidation()) + return false; + + m_validation = m_valobj->GetValidationStatus(); + + if (TypeValidatorResult::Failure == m_validation.first) + { + m_stream->Printf("! "); + return true; + } + + return false; +} + +bool +ValueObjectPrinter::PrintValidationErrorIfNeeded () +{ + if (!ShouldPrintValidation()) + return false; + + if (TypeValidatorResult::Success == m_validation.first) + return false; + + if (m_validation.second.empty()) + m_validation.second.assign("unknown error"); + + m_stream->Printf(" ! validation error: %s", m_validation.second.c_str()); + m_stream->EOL(); + + return true; +} diff --git a/source/Expression/ClangASTSource.cpp b/source/Expression/ClangASTSource.cpp index d488993b90d7..9a6d6e532255 100644 --- a/source/Expression/ClangASTSource.cpp +++ b/source/Expression/ClangASTSource.cpp @@ -708,6 +708,8 @@ ClangASTSource::FindExternalVisibleDecls (NameSearchContext &context, else m_target->GetImages().FindTypes(null_sc, name, exact_match, 1, types); + bool found_a_type = false; + if (types.GetSize()) { lldb::TypeSP type_sp = types.GetTypeAtIndex(0); @@ -736,8 +738,62 @@ ClangASTSource::FindExternalVisibleDecls (NameSearchContext &context, } context.AddTypeDecl(copied_clang_type); + + found_a_type = true; } - else + + if (!found_a_type) + { + // Try the modules next. + + do + { + if (ClangModulesDeclVendor *modules_decl_vendor = m_target->GetClangModulesDeclVendor()) + { + bool append = false; + uint32_t max_matches = 1; + std::vector <clang::NamedDecl *> decls; + + if (!modules_decl_vendor->FindDecls(name, + append, + max_matches, + decls)) + break; + + if (log) + { + log->Printf(" CAS::FEVD[%u] Matching entity found for \"%s\" in the modules", + current_id, + name.GetCString()); + } + + clang::NamedDecl *const decl_from_modules = decls[0]; + + if (llvm::isa<clang::TypeDecl>(decl_from_modules) || + llvm::isa<clang::ObjCContainerDecl>(decl_from_modules) || + llvm::isa<clang::EnumConstantDecl>(decl_from_modules)) + { + clang::Decl *copied_decl = m_ast_importer->CopyDecl(m_ast_context, &decl_from_modules->getASTContext(), decl_from_modules); + clang::NamedDecl *copied_named_decl = copied_decl ? dyn_cast<clang::NamedDecl>(copied_decl) : nullptr; + + if (!copied_named_decl) + { + if (log) + log->Printf(" CAS::FEVD[%u] - Couldn't export a type from the modules", + current_id); + + break; + } + + context.AddNamedDecl(copied_named_decl); + + found_a_type = true; + } + } + } while (0); + } + + if (!found_a_type) { do { @@ -753,19 +809,19 @@ ClangASTSource::FindExternalVisibleDecls (NameSearchContext &context, if (!language_runtime) break; - TypeVendor *type_vendor = language_runtime->GetTypeVendor(); + DeclVendor *decl_vendor = language_runtime->GetDeclVendor(); - if (!type_vendor) + if (!decl_vendor) break; bool append = false; uint32_t max_matches = 1; - std::vector <ClangASTType> types; + std::vector <clang::NamedDecl *> decls; - if (!type_vendor->FindTypes(name, + if (!decl_vendor->FindDecls(name, append, max_matches, - types)) + decls)) break; if (log) @@ -774,10 +830,11 @@ ClangASTSource::FindExternalVisibleDecls (NameSearchContext &context, current_id, name.GetCString()); } - - ClangASTType copied_clang_type (GuardedCopyType(types[0])); - - if (!copied_clang_type) + + clang::Decl *copied_decl = m_ast_importer->CopyDecl(m_ast_context, &decls[0]->getASTContext(), decls[0]); + clang::NamedDecl *copied_named_decl = copied_decl ? dyn_cast<clang::NamedDecl>(copied_decl) : nullptr; + + if (!copied_named_decl) { if (log) log->Printf(" CAS::FEVD[%u] - Couldn't export a type from the runtime", @@ -786,7 +843,7 @@ ClangASTSource::FindExternalVisibleDecls (NameSearchContext &context, break; } - context.AddTypeDecl(copied_clang_type); + context.AddNamedDecl(copied_named_decl); } while(0); } @@ -895,31 +952,44 @@ FindObjCMethodDeclsWithOrigin (unsigned int current_id, } DeclarationName original_decl_name(original_selector); - - ObjCInterfaceDecl::lookup_result result = original_interface_decl->lookup(original_decl_name); - - if (result.empty()) - return false; - - if (!result[0]) + + llvm::SmallVector<NamedDecl *, 1> methods; + + ClangASTContext::GetCompleteDecl(original_ctx, original_interface_decl); + + if (ObjCMethodDecl *instance_method_decl = original_interface_decl->lookupInstanceMethod(original_selector)) + { + methods.push_back(instance_method_decl); + } + else if (ObjCMethodDecl *class_method_decl = original_interface_decl->lookupClassMethod(original_selector)) + { + methods.push_back(class_method_decl); + } + + if (methods.empty()) + { return false; - - for (NamedDecl *named_decl : result) + } + + for (NamedDecl *named_decl : methods) { + if (!named_decl) + continue; + ObjCMethodDecl *result_method = dyn_cast<ObjCMethodDecl>(named_decl); if (!result_method) - return false; + continue; Decl *copied_decl = ast_importer->CopyDecl(ast_context, &result_method->getASTContext(), result_method); if (!copied_decl) - return false; + continue; ObjCMethodDecl *copied_method_decl = dyn_cast<ObjCMethodDecl>(copied_decl); if (!copied_method_decl) - return false; + continue; Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); @@ -1169,10 +1239,43 @@ ClangASTSource::FindObjCMethodDecls (NameSearchContext &context) return; } while (0); + + do + { + // Check the modules only if the debug information didn't have a complete interface. + + if (ClangModulesDeclVendor *modules_decl_vendor = m_target->GetClangModulesDeclVendor()) + { + ConstString interface_name(interface_decl->getNameAsString().c_str()); + bool append = false; + uint32_t max_matches = 1; + std::vector <clang::NamedDecl *> decls; + + if (!modules_decl_vendor->FindDecls(interface_name, + append, + max_matches, + decls)) + break; + + ObjCInterfaceDecl *interface_decl_from_modules = dyn_cast<ObjCInterfaceDecl>(decls[0]); + + if (!interface_decl_from_modules) + break; + + if (FindObjCMethodDeclsWithOrigin(current_id, + context, + interface_decl_from_modules, + m_ast_context, + m_ast_importer, + "in modules")) + return; + } + } + while (0); do { - // Check the runtime only if the debug information didn't have a complete interface. + // Check the runtime only if the debug information didn't have a complete interface and the modules don't get us anywhere. lldb::ProcessSP process(m_target->GetProcessSP()); @@ -1184,31 +1287,27 @@ ClangASTSource::FindObjCMethodDecls (NameSearchContext &context) if (!language_runtime) break; - TypeVendor *type_vendor = language_runtime->GetTypeVendor(); + DeclVendor *decl_vendor = language_runtime->GetDeclVendor(); - if (!type_vendor) + if (!decl_vendor) break; ConstString interface_name(interface_decl->getNameAsString().c_str()); bool append = false; uint32_t max_matches = 1; - std::vector <ClangASTType> types; + std::vector <clang::NamedDecl *> decls; - if (!type_vendor->FindTypes(interface_name, + if (!decl_vendor->FindDecls(interface_name, append, max_matches, - types)) + decls)) break; - const clang::Type *runtime_clang_type = QualType::getFromOpaquePtr(types[0].GetOpaqueQualType()).getTypePtr(); - - const ObjCInterfaceType *runtime_interface_type = dyn_cast<ObjCInterfaceType>(runtime_clang_type); - - if (!runtime_interface_type) + ObjCInterfaceDecl *runtime_interface_decl = dyn_cast<ObjCInterfaceDecl>(decls[0]); + + if (!runtime_interface_decl) break; - ObjCInterfaceDecl *runtime_interface_decl = runtime_interface_type->getDecl(); - FindObjCMethodDeclsWithOrigin(current_id, context, runtime_interface_decl, @@ -1339,10 +1438,50 @@ ClangASTSource::FindObjCPropertyAndIvarDecls (NameSearchContext &context) return; } while(0); + + do + { + // Check the modules only if the debug information didn't have a complete interface. + + ClangModulesDeclVendor *modules_decl_vendor = m_target->GetClangModulesDeclVendor(); + + if (!modules_decl_vendor) + break; + + bool append = false; + uint32_t max_matches = 1; + std::vector <clang::NamedDecl *> decls; + + if (!modules_decl_vendor->FindDecls(class_name, + append, + max_matches, + decls)) + break; + + DeclFromUser<const ObjCInterfaceDecl> interface_decl_from_modules(dyn_cast<ObjCInterfaceDecl>(decls[0])); + + if (!interface_decl_from_modules.IsValid()) + break; + + if (log) + log->Printf("CAS::FOPD[%d] trying module (ObjCInterfaceDecl*)%p/(ASTContext*)%p...", + current_id, + static_cast<const void*>(interface_decl_from_modules.decl), + static_cast<void*>(&interface_decl_from_modules->getASTContext())); + + if (FindObjCPropertyAndIvarDeclsWithOrigin(current_id, + context, + *m_ast_context, + m_ast_importer, + interface_decl_from_modules)) + return; + } + while(0); do { - // Check the runtime only if the debug information didn't have a complete interface. + // Check the runtime only if the debug information didn't have a complete interface + // and nothing was in the modules. lldb::ProcessSP process(m_target->GetProcessSP()); @@ -1354,41 +1493,37 @@ ClangASTSource::FindObjCPropertyAndIvarDecls (NameSearchContext &context) if (!language_runtime) return; - TypeVendor *type_vendor = language_runtime->GetTypeVendor(); + DeclVendor *decl_vendor = language_runtime->GetDeclVendor(); - if (!type_vendor) + if (!decl_vendor) break; bool append = false; uint32_t max_matches = 1; - std::vector <ClangASTType> types; + std::vector <clang::NamedDecl *> decls; - if (!type_vendor->FindTypes(class_name, + if (!decl_vendor->FindDecls(class_name, append, max_matches, - types)) + decls)) break; - const clang::Type *runtime_clang_type = QualType::getFromOpaquePtr(types[0].GetOpaqueQualType()).getTypePtr(); - - const ObjCInterfaceType *runtime_interface_type = dyn_cast<ObjCInterfaceType>(runtime_clang_type); - - if (!runtime_interface_type) + DeclFromUser<const ObjCInterfaceDecl> interface_decl_from_runtime(dyn_cast<ObjCInterfaceDecl>(decls[0])); + + if (!interface_decl_from_runtime.IsValid()) break; - DeclFromUser<const ObjCInterfaceDecl> runtime_iface_decl(runtime_interface_type->getDecl()); - if (log) log->Printf("CAS::FOPD[%d] trying runtime (ObjCInterfaceDecl*)%p/(ASTContext*)%p...", current_id, - static_cast<const void*>(runtime_iface_decl.decl), - static_cast<void*>(&runtime_iface_decl->getASTContext())); + static_cast<const void*>(interface_decl_from_runtime.decl), + static_cast<void*>(&interface_decl_from_runtime->getASTContext())); if (FindObjCPropertyAndIvarDeclsWithOrigin(current_id, context, *m_ast_context, m_ast_importer, - runtime_iface_decl)) + interface_decl_from_runtime)) return; } while(0); diff --git a/source/Expression/ClangExpressionDeclMap.cpp b/source/Expression/ClangExpressionDeclMap.cpp index 43c8a5fb1bcb..e3027378422f 100644 --- a/source/Expression/ClangExpressionDeclMap.cpp +++ b/source/Expression/ClangExpressionDeclMap.cpp @@ -36,6 +36,7 @@ #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/Variable.h" #include "lldb/Symbol/VariableList.h" +#include "lldb/Target/CPPLanguageRuntime.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" @@ -543,6 +544,7 @@ ClangExpressionDeclMap::GetFunctionAddress FindCodeSymbolInContext(name, m_parser_vars->m_sym_ctx, sc_list); uint32_t sc_list_size = sc_list.GetSize(); + if (sc_list_size == 0) { // We occasionally get debug information in which a const function is reported @@ -562,6 +564,25 @@ ClangExpressionDeclMap::GetFunctionAddress sc_list_size = sc_list.GetSize(); } } + + if (sc_list_size == 0) + { + // Sometimes we get a mangled name for a global function that actually should be "extern C." + // This is a hack to compensate. + + const bool is_mangled = true; + Mangled mangled(name, is_mangled); + + CPPLanguageRuntime::MethodName method_name(mangled.GetDemangledName()); + + llvm::StringRef basename = method_name.GetBasename(); + + if (!basename.empty()) + { + FindCodeSymbolInContext(ConstString(basename), m_parser_vars->m_sym_ctx, sc_list); + sc_list_size = sc_list.GetSize(); + } + } for (uint32_t i=0; i<sc_list_size; ++i) { @@ -1274,10 +1295,9 @@ ClangExpressionDeclMap::FindExternalVisibleDecls (NameSearchContext &context, { valobj = frame->GetValueForVariableExpressionPath(name_unique_cstr, eNoDynamicValues, - StackFrame::eExpressionPathOptionCheckPtrVsMember || - StackFrame::eExpressionPathOptionsAllowDirectIVarAccess || - StackFrame::eExpressionPathOptionsNoFragileObjcIvar || - StackFrame::eExpressionPathOptionsNoSyntheticChildren || + StackFrame::eExpressionPathOptionCheckPtrVsMember | + StackFrame::eExpressionPathOptionsNoFragileObjcIvar | + StackFrame::eExpressionPathOptionsNoSyntheticChildren | StackFrame::eExpressionPathOptionsNoSyntheticArrayRange, var, err); @@ -1307,6 +1327,16 @@ ClangExpressionDeclMap::FindExternalVisibleDecls (NameSearchContext &context, return; } } + + std::vector<clang::NamedDecl *> decls_from_modules; + + if (target) + { + if (ClangModulesDeclVendor *decl_vendor = target->GetClangModulesDeclVendor()) + { + decl_vendor->FindDecls(name, false, UINT32_MAX, decls_from_modules); + } + } if (!context.m_found.variable) { @@ -1384,6 +1414,19 @@ ClangExpressionDeclMap::FindExternalVisibleDecls (NameSearchContext &context, non_extern_symbol = sym_ctx.symbol; } } + + if (!context.m_found.function_with_type_info) + { + for (clang::NamedDecl *decl : decls_from_modules) + { + if (llvm::isa<clang::FunctionDecl>(decl)) + { + clang::NamedDecl *copied_decl = llvm::cast<FunctionDecl>(m_ast_importer->CopyDecl(m_ast_context, &decl->getASTContext(), decl)); + context.AddNamedDecl(copied_decl); + context.m_found.function_with_type_info = true; + } + } + } if (!context.m_found.function_with_type_info) { diff --git a/source/Expression/ClangExpressionParser.cpp b/source/Expression/ClangExpressionParser.cpp index f32ca3ae216c..4906108401af 100644 --- a/source/Expression/ClangExpressionParser.cpp +++ b/source/Expression/ClangExpressionParser.cpp @@ -22,6 +22,7 @@ #include "lldb/Expression/ClangASTSource.h" #include "lldb/Expression/ClangExpression.h" #include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/ClangModulesDeclVendor.h" #include "lldb/Expression/IRExecutionUnit.h" #include "lldb/Expression/IRDynamicChecks.h" #include "lldb/Expression/IRInterpreter.h" @@ -92,6 +93,47 @@ std::string GetBuiltinIncludePath(const char *Argv0) { return P.str(); } +class ClangExpressionParser::LLDBPreprocessorCallbacks : public PPCallbacks +{ + ClangModulesDeclVendor &m_decl_vendor; + StreamString m_error_stream; + bool m_has_errors = false; +public: + LLDBPreprocessorCallbacks(ClangModulesDeclVendor &decl_vendor) : + m_decl_vendor(decl_vendor) + { + } + + virtual void moduleImport(SourceLocation import_location, + ModuleIdPath path, + const clang::Module * /*null*/) + { + std::vector<llvm::StringRef> string_path; + + for (const std::pair<IdentifierInfo *, SourceLocation> &component : path) + { + string_path.push_back(component.first->getName()); + } + + StreamString error_stream; + + if (!m_decl_vendor.AddModule(string_path, m_error_stream)) + { + m_has_errors = true; + } + } + + bool hasErrors() + { + return m_has_errors; + } + + const std::string &getErrorString() + { + return m_error_stream.GetString(); + } +}; + //===----------------------------------------------------------------------===// // Implementation of ClangExpressionParser //===----------------------------------------------------------------------===// @@ -101,7 +143,8 @@ ClangExpressionParser::ClangExpressionParser (ExecutionContextScope *exe_scope, bool generate_debug_info) : m_expr (expr), m_compiler (), - m_code_generator () + m_code_generator (), + m_pp_callbacks(nullptr) { // 1. Create a new compiler instance. m_compiler.reset(new CompilerInstance()); @@ -165,6 +208,7 @@ ClangExpressionParser::ClangExpressionParser (ExecutionContextScope *exe_scope, case lldb::eLanguageTypeC_plus_plus: m_compiler->getLangOpts().CPlusPlus = true; m_compiler->getLangOpts().CPlusPlus11 = true; + m_compiler->getHeaderSearchOpts().UseLibcxx = true; break; case lldb::eLanguageTypeObjC_plus_plus: default: @@ -172,6 +216,7 @@ ClangExpressionParser::ClangExpressionParser (ExecutionContextScope *exe_scope, m_compiler->getLangOpts().ObjC2 = true; m_compiler->getLangOpts().CPlusPlus = true; m_compiler->getLangOpts().CPlusPlus11 = true; + m_compiler->getHeaderSearchOpts().UseLibcxx = true; break; } @@ -246,7 +291,14 @@ ClangExpressionParser::ClangExpressionParser (ExecutionContextScope *exe_scope, m_compiler->createFileManager(); m_compiler->createPreprocessor(TU_Complete); - + + if (ClangModulesDeclVendor *decl_vendor = target_sp->GetClangModulesDeclVendor()) + { + std::unique_ptr<PPCallbacks> pp_callbacks(new LLDBPreprocessorCallbacks(*decl_vendor)); + m_pp_callbacks = static_cast<LLDBPreprocessorCallbacks*>(pp_callbacks.get()); + m_compiler->getPreprocessor().addPPCallbacks(std::move(pp_callbacks)); + } + // 6. Most of this we get from the CompilerInstance, but we // also want to give the context an ExternalASTSource. m_selector_table.reset(new SelectorTable()); @@ -257,6 +309,7 @@ ClangExpressionParser::ClangExpressionParser (ExecutionContextScope *exe_scope, m_compiler->getPreprocessor().getIdentifierTable(), *m_selector_table.get(), *m_builtin_context.get())); + ast_context->InitBuiltinTypes(m_compiler->getTarget()); ClangExpressionDeclMap *decl_map = m_expr.DeclMap(); @@ -299,22 +352,23 @@ ClangExpressionParser::Parse (Stream &stream) { std::string temp_source_path; + int temp_fd = -1; + llvm::SmallString<PATH_MAX> result_path; FileSpec tmpdir_file_spec; if (HostInfo::GetLLDBPath(lldb::ePathTypeLLDBTempSystemDir, tmpdir_file_spec)) { - tmpdir_file_spec.AppendPathComponent("expr.XXXXXX"); + tmpdir_file_spec.AppendPathComponent("lldb-%%%%%%.expr"); temp_source_path = std::move(tmpdir_file_spec.GetPath()); + llvm::sys::fs::createUniqueFile(temp_source_path, temp_fd, result_path); } else { - temp_source_path = "/tmp/expr.XXXXXX"; + llvm::sys::fs::createTemporaryFile("lldb", "expr", temp_fd, result_path); } - - if (mktemp(&temp_source_path[0])) + + if (temp_fd != -1) { - lldb_private::File file (temp_source_path.c_str(), - File::eOpenOptionWrite | File::eOpenOptionCanCreateNewOnly, - lldb::eFilePermissionsFileDefault); + lldb_private::File file (temp_fd, true); const size_t expr_text_len = strlen(expr_text); size_t bytes_written = expr_text_len; if (file.Write(expr_text, bytes_written).Success()) @@ -323,7 +377,7 @@ ClangExpressionParser::Parse (Stream &stream) { file.Close(); SourceMgr.setMainFileID(SourceMgr.createFileID( - m_file_manager->getFile(temp_source_path), + m_file_manager->getFile(result_path), SourceLocation(), SrcMgr::C_User)); created_main_file = true; } @@ -351,14 +405,19 @@ ClangExpressionParser::Parse (Stream &stream) TextDiagnosticBuffer::const_iterator diag_iterator; int num_errors = 0; + + if (m_pp_callbacks && m_pp_callbacks->hasErrors()) + { + num_errors++; + + stream.PutCString(m_pp_callbacks->getErrorString().c_str()); + } for (diag_iterator = diag_buf->warn_begin(); diag_iterator != diag_buf->warn_end(); ++diag_iterator) stream.Printf("warning: %s\n", (*diag_iterator).second.c_str()); - num_errors = 0; - for (diag_iterator = diag_buf->err_begin(); diag_iterator != diag_buf->err_end(); ++diag_iterator) diff --git a/source/Expression/ClangExpressionVariable.cpp b/source/Expression/ClangExpressionVariable.cpp index c3eae41473e9..e86016e53a7d 100644 --- a/source/Expression/ClangExpressionVariable.cpp +++ b/source/Expression/ClangExpressionVariable.cpp @@ -28,6 +28,17 @@ ClangExpressionVariable::ClangExpressionVariable(ExecutionContextScope *exe_scop { } +ClangExpressionVariable::ClangExpressionVariable (ExecutionContextScope *exe_scope, + Value &value, + const ConstString &name, + uint16_t flags) : + m_parser_vars(), + m_jit_vars (), + m_flags (flags), + m_frozen_sp (ValueObjectConstResult::Create (exe_scope, value, name)) +{ +} + ClangExpressionVariable::ClangExpressionVariable (const lldb::ValueObjectSP &valobj_sp) : m_parser_vars(), m_jit_vars (), diff --git a/source/Expression/ClangFunction.cpp b/source/Expression/ClangFunction.cpp index 27afba2898ab..b438dacdfabc 100644 --- a/source/Expression/ClangFunction.cpp +++ b/source/Expression/ClangFunction.cpp @@ -57,8 +57,8 @@ ClangFunction::ClangFunction const ValueList &arg_value_list, const char *name ) : - m_parser(), m_execution_unit_sp(), + m_parser(), m_jit_module_wp(), m_name (name ? name : "<unknown>"), m_function_ptr (NULL), @@ -422,7 +422,7 @@ ClangFunction::InsertFunction (ExecutionContext &exe_ctx, lldb::addr_t &args_add return true; } -ThreadPlan * +lldb::ThreadPlanSP ClangFunction::GetThreadPlanToCallFunction (ExecutionContext &exe_ctx, lldb::addr_t args_addr, const EvaluateExpressionOptions &options, @@ -447,14 +447,14 @@ ClangFunction::GetThreadPlanToCallFunction (ExecutionContext &exe_ctx, lldb::addr_t args = { args_addr }; - ThreadPlan *new_plan = new ThreadPlanCallFunction (*thread, + lldb::ThreadPlanSP new_plan_sp (new ThreadPlanCallFunction (*thread, wrapper_address, ClangASTType(), args, - options); - new_plan->SetIsMasterPlan(true); - new_plan->SetOkayToDiscard (false); - return new_plan; + options)); + new_plan_sp->SetIsMasterPlan(true); + new_plan_sp->SetOkayToDiscard (false); + return new_plan_sp; } bool @@ -541,10 +541,10 @@ ClangFunction::ExecuteFunction( if (log) log->Printf("== [ClangFunction::ExecuteFunction] Executing function \"%s\" ==", m_name.c_str()); - lldb::ThreadPlanSP call_plan_sp (GetThreadPlanToCallFunction (exe_ctx, - args_addr, - real_options, - errors)); + lldb::ThreadPlanSP call_plan_sp = GetThreadPlanToCallFunction (exe_ctx, + args_addr, + real_options, + errors); if (!call_plan_sp) return lldb::eExpressionSetupError; diff --git a/source/Expression/ClangModulesDeclVendor.cpp b/source/Expression/ClangModulesDeclVendor.cpp new file mode 100644 index 000000000000..46adaaff33ce --- /dev/null +++ b/source/Expression/ClangModulesDeclVendor.cpp @@ -0,0 +1,372 @@ +//===-- ClangModulesDeclVendor.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/StreamString.h" +#include "lldb/Expression/ClangModulesDeclVendor.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Target/Target.h" + +#include "clang/Basic/TargetInfo.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/Parser.h" +#include "clang/Sema/Lookup.h" +#include "clang/Serialization/ASTReader.h" + +#include <mutex> + +using namespace lldb_private; + +namespace { + // Any Clang compiler requires a consumer for diagnostics. This one stores them as strings + // so we can provide them to the user in case a module failed to load. + class StoringDiagnosticConsumer : public clang::DiagnosticConsumer + { + public: + StoringDiagnosticConsumer (); + void + HandleDiagnostic (clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &info); + + void + ClearDiagnostics (); + + void + DumpDiagnostics (Stream &error_stream); + private: + typedef std::pair<clang::DiagnosticsEngine::Level, std::string> IDAndDiagnostic; + std::vector<IDAndDiagnostic> m_diagnostics; + Log * m_log; + }; + + // The private implementation of our ClangModulesDeclVendor. Contains all the Clang state required + // to load modules. + class ClangModulesDeclVendorImpl : public ClangModulesDeclVendor + { + public: + ClangModulesDeclVendorImpl(llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> &diagnostics_engine, + llvm::IntrusiveRefCntPtr<clang::CompilerInvocation> &compiler_invocation, + std::unique_ptr<clang::CompilerInstance> &&compiler_instance, + std::unique_ptr<clang::Parser> &&parser); + + virtual bool + AddModule(std::vector<llvm::StringRef> &path, + Stream &error_stream); + + virtual uint32_t + FindDecls (const ConstString &name, + bool append, + uint32_t max_matches, + std::vector <clang::NamedDecl*> &decls); + + ~ClangModulesDeclVendorImpl(); + + private: + clang::ModuleLoadResult + DoGetModule(clang::ModuleIdPath path, bool make_visible); + + llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> m_diagnostics_engine; + llvm::IntrusiveRefCntPtr<clang::CompilerInvocation> m_compiler_invocation; + std::unique_ptr<clang::CompilerInstance> m_compiler_instance; + std::unique_ptr<clang::Parser> m_parser; + }; +} + +StoringDiagnosticConsumer::StoringDiagnosticConsumer () +{ + m_log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS); +} + +void +StoringDiagnosticConsumer::HandleDiagnostic (clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &info) +{ + llvm::SmallVector<char, 256> diagnostic_string; + + info.FormatDiagnostic(diagnostic_string); + + m_diagnostics.push_back(IDAndDiagnostic(DiagLevel, std::string(diagnostic_string.data(), diagnostic_string.size()))); +} + +void +StoringDiagnosticConsumer::ClearDiagnostics () +{ + m_diagnostics.clear(); +} + +void +StoringDiagnosticConsumer::DumpDiagnostics (Stream &error_stream) +{ + for (IDAndDiagnostic &diag : m_diagnostics) + { + switch (diag.first) + { + default: + error_stream.PutCString(diag.second.c_str()); + error_stream.PutChar('\n'); + break; + case clang::DiagnosticsEngine::Level::Ignored: + break; + } + } +} + +static FileSpec +GetResourceDir () +{ + static FileSpec g_cached_resource_dir; + + static std::once_flag g_once_flag; + + std::call_once(g_once_flag, [](){ + HostInfo::GetLLDBPath (lldb::ePathTypeClangDir, g_cached_resource_dir); + }); + + return g_cached_resource_dir; +} + + +ClangModulesDeclVendor::ClangModulesDeclVendor() +{ +} + +ClangModulesDeclVendor::~ClangModulesDeclVendor() +{ +} + +ClangModulesDeclVendorImpl::ClangModulesDeclVendorImpl(llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> &diagnostics_engine, + llvm::IntrusiveRefCntPtr<clang::CompilerInvocation> &compiler_invocation, + std::unique_ptr<clang::CompilerInstance> &&compiler_instance, + std::unique_ptr<clang::Parser> &&parser) : + ClangModulesDeclVendor(), + m_diagnostics_engine(diagnostics_engine), + m_compiler_invocation(compiler_invocation), + m_compiler_instance(std::move(compiler_instance)), + m_parser(std::move(parser)) +{ +} + +bool +ClangModulesDeclVendorImpl::AddModule(std::vector<llvm::StringRef> &path, + Stream &error_stream) +{ + // Fail early. + + if (m_compiler_instance->hadModuleLoaderFatalFailure()) + { + error_stream.PutCString("error: Couldn't load a module because the module loader is in a fatal state.\n"); + return false; + } + + if (!m_compiler_instance->getPreprocessor().getHeaderSearchInfo().lookupModule(path[0])) + { + error_stream.Printf("error: Header search couldn't locate module %s\n", path[0].str().c_str()); + return false; + } + + llvm::SmallVector<std::pair<clang::IdentifierInfo *, clang::SourceLocation>, 4> clang_path; + + { + size_t source_loc_counter = 0; + clang::SourceManager &source_manager = m_compiler_instance->getASTContext().getSourceManager(); + + for (llvm::StringRef &component : path) + { + clang_path.push_back(std::make_pair(&m_compiler_instance->getASTContext().Idents.get(component), + source_manager.getLocForStartOfFile(source_manager.getMainFileID()).getLocWithOffset(source_loc_counter++))); + } + } + + StoringDiagnosticConsumer *diagnostic_consumer = static_cast<StoringDiagnosticConsumer *>(m_compiler_instance->getDiagnostics().getClient()); + + diagnostic_consumer->ClearDiagnostics(); + + clang::Module *top_level_module = DoGetModule(clang_path.front(), false); + + if (!top_level_module) + { + diagnostic_consumer->DumpDiagnostics(error_stream); + error_stream.Printf("error: Couldn't load top-level module %s\n", path[0].str().c_str()); + return false; + } + + clang::Module *submodule = top_level_module; + + for (size_t ci = 1; ci < path.size(); ++ci) + { + llvm::StringRef &component = path[ci]; + submodule = submodule->findSubmodule(component.str()); + if (!submodule) + { + diagnostic_consumer->DumpDiagnostics(error_stream); + error_stream.Printf("error: Couldn't load submodule %s\n", component.str().c_str()); + return false; + } + } + + clang::Module *requested_module = DoGetModule(clang_path, true); + + return (requested_module != nullptr); +} + +// ClangImporter::lookupValue + +uint32_t +ClangModulesDeclVendorImpl::FindDecls (const ConstString &name, + bool append, + uint32_t max_matches, + std::vector <clang::NamedDecl*> &decls) +{ + if (!append) + decls.clear(); + + clang::IdentifierInfo &ident = m_compiler_instance->getASTContext().Idents.get(name.GetStringRef()); + + clang::LookupResult lookup_result(m_compiler_instance->getSema(), + clang::DeclarationName(&ident), + clang::SourceLocation(), + clang::Sema::LookupOrdinaryName); + + m_compiler_instance->getSema().LookupName(lookup_result, m_compiler_instance->getSema().getScopeForContext(m_compiler_instance->getASTContext().getTranslationUnitDecl())); + + uint32_t num_matches = 0; + + for (clang::NamedDecl *named_decl : lookup_result) + { + if (num_matches >= max_matches) + return num_matches; + + decls.push_back(named_decl); + ++num_matches; + } + + return num_matches; +} + +ClangModulesDeclVendorImpl::~ClangModulesDeclVendorImpl() +{ +} + +clang::ModuleLoadResult +ClangModulesDeclVendorImpl::DoGetModule(clang::ModuleIdPath path, + bool make_visible) +{ + clang::Module::NameVisibilityKind visibility = make_visible ? clang::Module::AllVisible : clang::Module::Hidden; + + const bool is_inclusion_directive = false; + + return m_compiler_instance->loadModule(path.front().second, path, visibility, is_inclusion_directive); +} + +static const char *ModuleImportBufferName = "LLDBModulesMemoryBuffer"; + +lldb_private::ClangModulesDeclVendor * +ClangModulesDeclVendor::Create(Target &target) +{ + // FIXME we should insure programmatically that the expression parser's compiler and the modules runtime's + // compiler are both initialized in the same way – preferably by the same code. + + if (!target.GetPlatform()->SupportsModules()) + return nullptr; + + const ArchSpec &arch = target.GetArchitecture(); + + std::vector<std::string> compiler_invocation_arguments = + { + "-fmodules", + "-fcxx-modules", + "-fsyntax-only", + "-femit-all-decls", + "-target", arch.GetTriple().str(), + "-fmodules-validate-system-headers", + "-Werror=non-modular-include-in-framework-module" + }; + + target.GetPlatform()->AddClangModuleCompilationOptions(compiler_invocation_arguments); + + compiler_invocation_arguments.push_back(ModuleImportBufferName); + + // Add additional search paths with { "-I", path } or { "-F", path } here. + + { + llvm::SmallString<128> DefaultModuleCache; + const bool erased_on_reboot = false; + llvm::sys::path::system_temp_directory(erased_on_reboot, DefaultModuleCache); + llvm::sys::path::append(DefaultModuleCache, "org.llvm.clang"); + llvm::sys::path::append(DefaultModuleCache, "ModuleCache"); + std::string module_cache_argument("-fmodules-cache-path="); + module_cache_argument.append(DefaultModuleCache.str().str()); + compiler_invocation_arguments.push_back(module_cache_argument); + } + + { + FileSpec clang_resource_dir = GetResourceDir(); + + if (clang_resource_dir.IsDirectory()) + { + compiler_invocation_arguments.push_back("-resource-dir"); + compiler_invocation_arguments.push_back(clang_resource_dir.GetPath()); + } + } + + llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics_engine = clang::CompilerInstance::createDiagnostics(new clang::DiagnosticOptions, + new StoringDiagnosticConsumer); + + std::vector<const char *> compiler_invocation_argument_cstrs; + + for (const std::string &arg : compiler_invocation_arguments) { + compiler_invocation_argument_cstrs.push_back(arg.c_str()); + } + + llvm::IntrusiveRefCntPtr<clang::CompilerInvocation> invocation(clang::createInvocationFromCommandLine(compiler_invocation_argument_cstrs, diagnostics_engine)); + + if (!invocation) + return nullptr; + + std::unique_ptr<llvm::MemoryBuffer> source_buffer = llvm::MemoryBuffer::getMemBuffer("extern int __lldb __attribute__((unavailable));", + ModuleImportBufferName); + + invocation->getPreprocessorOpts().addRemappedFile(ModuleImportBufferName, source_buffer.release()); + + std::unique_ptr<clang::CompilerInstance> instance(new clang::CompilerInstance); + + instance->setDiagnostics(diagnostics_engine.get()); + instance->setInvocation(invocation.get()); + + std::unique_ptr<clang::FrontendAction> action(new clang::SyntaxOnlyAction); + + instance->setTarget(clang::TargetInfo::CreateTargetInfo(*diagnostics_engine, instance->getInvocation().TargetOpts)); + + if (!instance->hasTarget()) + return nullptr; + + instance->getTarget().adjust(instance->getLangOpts()); + + if (!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) + return nullptr; + + instance->getPreprocessor().enableIncrementalProcessing(); + + instance->createModuleManager(); + + instance->createSema(action->getTranslationUnitKind(), nullptr); + + const bool skipFunctionBodies = false; + std::unique_ptr<clang::Parser> parser(new clang::Parser(instance->getPreprocessor(), instance->getSema(), skipFunctionBodies)); + + instance->getPreprocessor().EnterMainSourceFile(); + parser->Initialize(); + + clang::Parser::DeclGroupPtrTy parsed; + + while (!parser->ParseTopLevelDecl(parsed)); + + return new ClangModulesDeclVendorImpl (diagnostics_engine, invocation, std::move(instance), std::move(parser)); +} diff --git a/source/Expression/ClangUserExpression.cpp b/source/Expression/ClangUserExpression.cpp index 52ef4d310352..55148462bbc0 100644 --- a/source/Expression/ClangUserExpression.cpp +++ b/source/Expression/ClangUserExpression.cpp @@ -619,15 +619,14 @@ GetObjectPointer (lldb::StackFrameSP frame_sp, valobj_sp = frame_sp->GetValueForVariableExpressionPath(object_name.AsCString(), lldb::eNoDynamicValues, - StackFrame::eExpressionPathOptionCheckPtrVsMember || - StackFrame::eExpressionPathOptionsAllowDirectIVarAccess || - StackFrame::eExpressionPathOptionsNoFragileObjcIvar || - StackFrame::eExpressionPathOptionsNoSyntheticChildren || + StackFrame::eExpressionPathOptionCheckPtrVsMember | + StackFrame::eExpressionPathOptionsNoFragileObjcIvar | + StackFrame::eExpressionPathOptionsNoSyntheticChildren | StackFrame::eExpressionPathOptionsNoSyntheticArrayRange, var_sp, err); - if (!err.Success()) + if (!err.Success() || !valobj_sp.get()) return LLDB_INVALID_ADDRESS; lldb::addr_t ret = valobj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); @@ -885,17 +884,17 @@ ClangUserExpression::Execute (Stream &error_stream, args.push_back(struct_address); - ThreadPlanCallUserExpression *user_expression_plan = - new ThreadPlanCallUserExpression (exe_ctx.GetThreadRef(), - wrapper_address, - args, - options, - shared_ptr_to_me); - lldb::ThreadPlanSP call_plan_sp(user_expression_plan); + lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallUserExpression (exe_ctx.GetThreadRef(), + wrapper_address, + args, + options, + shared_ptr_to_me)); if (!call_plan_sp || !call_plan_sp->ValidatePlan (&error_stream)) return lldb::eExpressionSetupError; + ThreadPlanCallUserExpression *user_expression_plan = static_cast<ThreadPlanCallUserExpression *>(call_plan_sp.get()); + lldb::addr_t function_stack_pointer = user_expression_plan->GetFunctionStackPointer(); function_stack_bottom = function_stack_pointer - HostInfo::GetPageSize(); @@ -1070,7 +1069,7 @@ ClangUserExpression::Evaluate (ExecutionContext &exe_ctx, user_expression_sp, expr_result); - if (options.GetResultIsInternal()) + if (options.GetResultIsInternal() && expr_result && process) { process->GetTarget().GetPersistentVariables().RemovePersistentVariable (expr_result); } diff --git a/source/Expression/DWARFExpression.cpp b/source/Expression/DWARFExpression.cpp index 44b64ab1bf29..827bddd8e7bc 100644 --- a/source/Expression/DWARFExpression.cpp +++ b/source/Expression/DWARFExpression.cpp @@ -2690,11 +2690,6 @@ DWARFExpression::Evaluate } break; - default: - if (error_ptr) - error_ptr->SetErrorStringWithFormat ("unhandled value type for DW_OP_piece(%" PRIu64 ")", piece_byte_size); - return false; - } // Check if this is the first piece? @@ -2880,7 +2875,7 @@ DWARFExpression::Evaluate if (cfa != LLDB_INVALID_ADDRESS) { stack.push_back(Scalar(cfa)); - stack.back().SetValueType (Value::eValueTypeHostAddress); + stack.back().SetValueType (Value::eValueTypeLoadAddress); } else if (error_ptr) diff --git a/source/Expression/IRExecutionUnit.cpp b/source/Expression/IRExecutionUnit.cpp index 090f88fc1bfe..e7cb728778e6 100644 --- a/source/Expression/IRExecutionUnit.cpp +++ b/source/Expression/IRExecutionUnit.cpp @@ -13,6 +13,7 @@ #include "llvm/Support/SourceMgr.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Debugger.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" @@ -199,6 +200,11 @@ IRExecutionUnit::DisassembleFunction (Stream &stream, InstructionList &instruction_list = disassembler_sp->GetInstructionList(); const uint32_t max_opcode_byte_size = instruction_list.GetMaxOpcocdeByteSize(); + const char *disassemble_format = "${addr-file-or-load}: "; + if (exe_ctx.HasTargetScope()) + { + disassemble_format = exe_ctx.GetTargetRef().GetDebugger().GetDisassemblyFormat(); + } for (size_t instruction_index = 0, num_instructions = instruction_list.GetSize(); instruction_index < num_instructions; @@ -209,7 +215,10 @@ IRExecutionUnit::DisassembleFunction (Stream &stream, max_opcode_byte_size, true, true, - &exe_ctx); + &exe_ctx, + NULL, + NULL, + disassemble_format); stream.PutChar('\n'); } // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. @@ -300,11 +309,9 @@ IRExecutionUnit::GetRunnableInfo(Error &error, builder.setEngineKind(llvm::EngineKind::JIT) .setErrorStr(&error_string) .setRelocationModel(relocModel) - .setJITMemoryManager(new MemoryManager(*this)) - .setOptLevel(llvm::CodeGenOpt::Less) - .setAllocateGVsWithCode(true) + .setMCJITMemoryManager(std::unique_ptr<MemoryManager>(new MemoryManager(*this))) .setCodeModel(codeModel) - .setUseMCJIT(true); + .setOptLevel(llvm::CodeGenOpt::Less); llvm::StringRef mArch; llvm::StringRef mCPU; @@ -425,7 +432,7 @@ IRExecutionUnit::~IRExecutionUnit () } IRExecutionUnit::MemoryManager::MemoryManager (IRExecutionUnit &parent) : - m_default_mm_ap (llvm::JITMemoryManager::CreateDefaultMemManager()), + m_default_mm_ap (new llvm::SectionMemoryManager()), m_parent (parent) { } @@ -433,60 +440,6 @@ IRExecutionUnit::MemoryManager::MemoryManager (IRExecutionUnit &parent) : IRExecutionUnit::MemoryManager::~MemoryManager () { } -void -IRExecutionUnit::MemoryManager::setMemoryWritable () -{ - m_default_mm_ap->setMemoryWritable(); -} - -void -IRExecutionUnit::MemoryManager::setMemoryExecutable () -{ - m_default_mm_ap->setMemoryExecutable(); -} - - -uint8_t * -IRExecutionUnit::MemoryManager::startFunctionBody(const llvm::Function *F, - uintptr_t &ActualSize) -{ - return m_default_mm_ap->startFunctionBody(F, ActualSize); -} - -uint8_t * -IRExecutionUnit::MemoryManager::allocateStub(const llvm::GlobalValue* F, - unsigned StubSize, - unsigned Alignment) -{ - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); - - uint8_t *return_value = m_default_mm_ap->allocateStub(F, StubSize, Alignment); - - m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, - lldb::ePermissionsReadable | lldb::ePermissionsWritable, - GetSectionTypeFromSectionName (llvm::StringRef(), AllocationKind::Stub), - StubSize, - Alignment, - eSectionIDInvalid, - NULL)); - - if (log) - { - log->Printf("IRExecutionUnit::allocateStub (F=%p, StubSize=%u, Alignment=%u) = %p", - static_cast<const void*>(F), StubSize, Alignment, - static_cast<void*>(return_value)); - } - - return return_value; -} - -void -IRExecutionUnit::MemoryManager::endFunctionBody(const llvm::Function *F, - uint8_t *FunctionStart, - uint8_t *FunctionEnd) -{ - m_default_mm_ap->endFunctionBody(F, FunctionStart, FunctionEnd); -} lldb::SectionType IRExecutionUnit::GetSectionTypeFromSectionName (const llvm::StringRef &name, IRExecutionUnit::AllocationKind alloc_kind) @@ -598,30 +551,6 @@ IRExecutionUnit::GetSectionTypeFromSectionName (const llvm::StringRef &name, IRE } uint8_t * -IRExecutionUnit::MemoryManager::allocateSpace(intptr_t Size, unsigned Alignment) -{ - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); - - uint8_t *return_value = m_default_mm_ap->allocateSpace(Size, Alignment); - - m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, - lldb::ePermissionsReadable | lldb::ePermissionsWritable, - GetSectionTypeFromSectionName (llvm::StringRef(), AllocationKind::Bytes), - Size, - Alignment, - eSectionIDInvalid, - NULL)); - - if (log) - { - log->Printf("IRExecutionUnit::allocateSpace(Size=%" PRIu64 ", Alignment=%u) = %p", - (uint64_t)Size, Alignment, return_value); - } - - return return_value; -} - -uint8_t * IRExecutionUnit::MemoryManager::allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, @@ -675,37 +604,6 @@ IRExecutionUnit::MemoryManager::allocateDataSection(uintptr_t Size, return return_value; } -uint8_t * -IRExecutionUnit::MemoryManager::allocateGlobal(uintptr_t Size, - unsigned Alignment) -{ - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); - - uint8_t *return_value = m_default_mm_ap->allocateGlobal(Size, Alignment); - - m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, - lldb::ePermissionsReadable | lldb::ePermissionsWritable, - GetSectionTypeFromSectionName (llvm::StringRef(), AllocationKind::Global), - Size, - Alignment, - eSectionIDInvalid, - NULL)); - - if (log) - { - log->Printf("IRExecutionUnit::allocateGlobal(Size=0x%" PRIx64 ", Alignment=%u) = %p", - (uint64_t)Size, Alignment, return_value); - } - - return return_value; -} - -void -IRExecutionUnit::MemoryManager::deallocateFunctionBody(void *Body) -{ - m_default_mm_ap->deallocateFunctionBody(Body); -} - lldb::addr_t IRExecutionUnit::GetRemoteAddressForLocal (lldb::addr_t local_address) { diff --git a/source/Expression/IRForTarget.cpp b/source/Expression/IRForTarget.cpp index b91e1b46f88e..8e75c32183ec 100644 --- a/source/Expression/IRForTarget.cpp +++ b/source/Expression/IRForTarget.cpp @@ -18,6 +18,7 @@ #include "llvm/IR/Module.h" #include "llvm/PassManager.h" #include "llvm/Transforms/IPO.h" +#include "llvm/IR/Metadata.h" #include "llvm/IR/ValueSymbolTable.h" #include "clang/AST/ASTContext.h" @@ -403,18 +404,17 @@ IRForTarget::DeclForGlobal (const GlobalValue *global_val, Module *module) node_index < num_nodes; ++node_index) { - MDNode *metadata_node = named_metadata->getOperand(node_index); - + llvm::MDNode *metadata_node = dyn_cast<llvm::MDNode>(named_metadata->getOperand(node_index)); if (!metadata_node) return NULL; if (metadata_node->getNumOperands() != 2) continue; - if (metadata_node->getOperand(0) != global_val) + if (mdconst::dyn_extract_or_null<GlobalValue>(metadata_node->getOperand(0)) != global_val) continue; - ConstantInt *constant_int = dyn_cast<ConstantInt>(metadata_node->getOperand(1)); + ConstantInt *constant_int = mdconst::dyn_extract<ConstantInt>(metadata_node->getOperand(1)); if (!constant_int) return NULL; @@ -639,11 +639,11 @@ IRForTarget::CreateResultVariable (llvm::Function &llvm_function) reinterpret_cast<uint64_t>(result_decl), false); - llvm::Value* values[2]; - values[0] = new_result_global; - values[1] = new_constant_int; + llvm::Metadata *values[2]; + values[0] = ConstantAsMetadata::get(new_result_global); + values[1] = ConstantAsMetadata::get(new_constant_int); - ArrayRef<Value*> value_ref(values, 2); + ArrayRef<Metadata *> value_ref(values, 2); MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref); NamedMDNode *named_metadata = m_module->getNamedMetadata("clang.global.decl.ptrs"); @@ -1037,7 +1037,7 @@ static bool IsObjCSelectorRef (Value *value) { GlobalVariable *global_variable = dyn_cast<GlobalVariable>(value); - if (!global_variable || !global_variable->hasName() || !global_variable->getName().startswith("\01L_OBJC_SELECTOR_REFERENCES_")) + if (!global_variable || !global_variable->hasName() || !global_variable->getName().startswith("OBJC_SELECTOR_REFERENCES_")) return false; return true; @@ -1056,12 +1056,12 @@ IRForTarget::RewriteObjCSelector (Instruction* selector_load) // Unpack the message name from the selector. In LLVM IR, an objc_msgSend gets represented as // - // %tmp = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_" ; <i8*> + // %tmp = load i8** @"OBJC_SELECTOR_REFERENCES_" ; <i8*> // %call = call i8* (i8*, i8*, ...)* @objc_msgSend(i8* %obj, i8* %tmp, ...) ; <i8*> // // where %obj is the object pointer and %tmp is the selector. // - // @"\01L_OBJC_SELECTOR_REFERENCES_" is a pointer to a character array called @"\01L_OBJC_llvm_moduleETH_VAR_NAllvm_moduleE_". + // @"OBJC_SELECTOR_REFERENCES_" is a pointer to a character array called @"\01L_OBJC_llvm_moduleETH_VAR_NAllvm_moduleE_". // @"\01L_OBJC_llvm_moduleETH_VAR_NAllvm_moduleE_" contains the string. // Find the pointer's initializer (a ConstantExpr with opcode GetElementPtr) and get the string from its target @@ -1215,7 +1215,7 @@ IRForTarget::RewritePersistentAlloc (llvm::Instruction *persistent_alloc) if (!alloc_md || !alloc_md->getNumOperands()) return false; - ConstantInt *constant_int = dyn_cast<ConstantInt>(alloc_md->getOperand(0)); + ConstantInt *constant_int = mdconst::dyn_extract<ConstantInt>(alloc_md->getOperand(0)); if (!constant_int) return false; @@ -1246,11 +1246,11 @@ IRForTarget::RewritePersistentAlloc (llvm::Instruction *persistent_alloc) NamedMDNode *named_metadata = m_module->getOrInsertNamedMetadata("clang.global.decl.ptrs"); - llvm::Value* values[2]; - values[0] = persistent_global; - values[1] = constant_int; + llvm::Metadata *values[2]; + values[0] = ConstantAsMetadata::get(persistent_global); + values[1] = ConstantAsMetadata::get(constant_int); - ArrayRef<llvm::Value*> value_ref(values, 2); + ArrayRef<llvm::Metadata *> value_ref(values, 2); MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref); named_metadata->addOperand(persistent_global_md); @@ -2043,8 +2043,12 @@ static bool isGuardVariableRef(Value *V) GlobalVariable *GV = dyn_cast<GlobalVariable>(Old); - if (!GV || !GV->hasName() || !GV->getName().startswith("_ZGV")) + if (!GV || !GV->hasName() || + (!GV->getName().startswith("_ZGV") && // Itanium ABI guard variable + !GV->getName().endswith("@4IA"))) // Microsoft ABI guard variable + { return false; + } return true; } @@ -2052,20 +2056,8 @@ static bool isGuardVariableRef(Value *V) void IRForTarget::TurnGuardLoadIntoZero(llvm::Instruction* guard_load) { - Constant* zero(ConstantInt::get(Type::getInt8Ty(m_module->getContext()), 0, true)); - - for (llvm::User *u : guard_load->users()) - { - if (isa<Constant>(u)) - { - // do nothing for the moment - } - else - { - u->replaceUsesOfWith(guard_load, zero); - } - } - + Constant *zero(Constant::getNullValue(guard_load->getType())); + guard_load->replaceAllUsesWith(zero); guard_load->eraseFromParent(); } diff --git a/source/Host/common/Editline.cpp b/source/Host/common/Editline.cpp index 7af9f39a7863..b82fbea90c6c 100644 --- a/source/Host/common/Editline.cpp +++ b/source/Host/common/Editline.cpp @@ -7,527 +7,913 @@ // //===----------------------------------------------------------------------===// +#include <iomanip> +#include <iostream> +#include <limits.h> #include "lldb/Host/Editline.h" - +#include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Core/Error.h" -#include "lldb/Core/StreamString.h" #include "lldb/Core/StringList.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" +#include "lldb/Host/Mutex.h" -#include <limits.h> - -using namespace lldb; using namespace lldb_private; +using namespace lldb_private::line_editor; -namespace lldb_private { - typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP; - - - // EditlineHistory objects are sometimes shared between multiple - // Editline instances with the same program name. This class allows - // multiple editline instances to - // - - class EditlineHistory - { - private: - // Use static GetHistory() function to get a EditlineHistorySP to one of these objects - EditlineHistory(const std::string &prefix, uint32_t size, bool unique_entries) : - m_history (NULL), - m_event (), - m_prefix (prefix), - m_path () +// Editline uses careful cursor management to achieve the illusion of editing a multi-line block of text +// with a single line editor. Preserving this illusion requires fairly careful management of cursor +// state. Read and understand the relationship between DisplayInput(), MoveCursor(), SetCurrentLine(), +// and SaveEditedLine() before making changes. + +#define ESCAPE "\x1b" +#define ANSI_FAINT ESCAPE "[2m" +#define ANSI_UNFAINT ESCAPE "[22m" +#define ANSI_CLEAR_BELOW ESCAPE "[J" +#define ANSI_CLEAR_RIGHT ESCAPE "[K" +#define ANSI_SET_COLUMN_N ESCAPE "[%dG" +#define ANSI_UP_N_ROWS ESCAPE "[%dA" +#define ANSI_DOWN_N_ROWS ESCAPE "[%dB" + +#if LLDB_EDITLINE_USE_WCHAR + +#define EditLineConstString(str) L##str +#define EditLineStringFormatSpec "%ls" + +#else + +#define EditLineConstString(str) str +#define EditLineStringFormatSpec "%s" + +// use #defines so wide version functions and structs will resolve to old versions +// for case of libedit not built with wide char support +#define history_w history +#define history_winit history_init +#define history_wend history_end +#define HistoryW History +#define HistEventW HistEvent +#define LineInfoW LineInfo + +#define el_wgets el_gets +#define el_wgetc el_getc +#define el_wpush el_push +#define el_wparse el_parse +#define el_wset el_set +#define el_wget el_get +#define el_wline el_line +#define el_winsertstr el_insertstr +#define el_wdeletestr el_deletestr + +#endif // #if LLDB_EDITLINE_USE_WCHAR + +bool +IsOnlySpaces (const EditLineStringType & content) +{ + for (wchar_t ch : content) + { + if (ch != EditLineCharType(' ')) + return false; + } + return true; +} + +EditLineStringType +CombineLines (const std::vector<EditLineStringType> & lines) +{ + EditLineStringStreamType combined_stream; + for (EditLineStringType line : lines) + { + combined_stream << line.c_str() << "\n"; + } + return combined_stream.str(); +} + +std::vector<EditLineStringType> +SplitLines (const EditLineStringType & input) +{ + std::vector<EditLineStringType> result; + size_t start = 0; + while (start < input.length()) + { + size_t end = input.find ('\n', start); + if (end == std::string::npos) { - m_history = ::history_init(); - ::history (m_history, &m_event, H_SETSIZE, size); - if (unique_entries) - ::history (m_history, &m_event, H_SETUNIQUE, 1); + result.insert (result.end(), input.substr (start)); + break; } + result.insert (result.end(), input.substr (start, end - start)); + start = end + 1; + } + return result; +} + +EditLineStringType +FixIndentation (const EditLineStringType & line, int indent_correction) +{ + if (indent_correction == 0) + return line; + if (indent_correction < 0) + return line.substr (-indent_correction); + return EditLineStringType (indent_correction, EditLineCharType(' ')) + line; +} + +int +GetIndentation (const EditLineStringType & line) +{ + int space_count = 0; + for (EditLineCharType ch : line) + { + if (ch != EditLineCharType(' ')) + break; + ++space_count; + } + return space_count; +} + +bool +IsInputPending (FILE * file) +{ + // FIXME: This will be broken on Windows if we ever re-enable Editline. You can't use select + // on something that isn't a socket. This will have to be re-written to not use a FILE*, but + // instead use some kind of yet-to-be-created abstraction that select-like functionality on + // non-socket objects. + const int fd = fileno (file); + fd_set fds; + FD_ZERO (&fds); + FD_SET (fd, &fds); + timeval timeout = { 0, 0 }; + return select (fd + 1, &fds, NULL, NULL, &timeout); +} + +namespace lldb_private +{ + namespace line_editor + { + typedef std::weak_ptr<EditlineHistory> EditlineHistoryWP; + + // EditlineHistory objects are sometimes shared between multiple + // Editline instances with the same program name. - const char * - GetHistoryFilePath() + class EditlineHistory { - if (m_path.empty() && m_history && !m_prefix.empty()) + private: + // Use static GetHistory() function to get a EditlineHistorySP to one of these objects + EditlineHistory (const std::string &prefix, uint32_t size, bool unique_entries) : + m_history (NULL), + m_event (), + m_prefix (prefix), + m_path () { - char history_path[PATH_MAX]; - ::snprintf (history_path, sizeof(history_path), "~/.%s-history", m_prefix.c_str()); - m_path = std::move(FileSpec(history_path, true).GetPath()); + m_history = history_winit(); + history_w (m_history, &m_event, H_SETSIZE, size); + if (unique_entries) + history_w (m_history, &m_event, H_SETUNIQUE, 1); + } + + const char * + GetHistoryFilePath() + { + if (m_path.empty() && m_history && !m_prefix.empty()) + { + std::string parent_path = FileSpec ("~/.lldb", true).GetPath(); + char history_path[PATH_MAX]; + if (FileSystem::MakeDirectory(parent_path.c_str(), lldb::eFilePermissionsDirectoryDefault).Success()) + { + snprintf (history_path, sizeof (history_path), "~/.lldb/%s-history", m_prefix.c_str()); + } + else + { + snprintf (history_path, sizeof (history_path), "~/%s-widehistory", m_prefix.c_str()); + } + m_path = std::move (FileSpec (history_path, true).GetPath()); + } + if (m_path.empty()) + return NULL; + return m_path.c_str(); } - if (m_path.empty()) - return NULL; - return m_path.c_str(); - } - - public: - - ~EditlineHistory() - { - Save (); - if (m_history) + public: + + ~EditlineHistory() { - ::history_end (m_history); - m_history = NULL; + Save(); + + if (m_history) + { + history_wend (m_history); + m_history = NULL; + } } - } - - static EditlineHistorySP - GetHistory (const std::string &prefix) - { - typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap; - static Mutex g_mutex(Mutex::eMutexTypeRecursive); - static WeakHistoryMap g_weak_map; - Mutex::Locker locker (g_mutex); - WeakHistoryMap::const_iterator pos = g_weak_map.find (prefix); - EditlineHistorySP history_sp; - if (pos != g_weak_map.end()) + + static EditlineHistorySP + GetHistory (const std::string &prefix) { - history_sp = pos->second.lock(); - if (history_sp) - return history_sp; - g_weak_map.erase(pos); + typedef std::map<std::string, EditlineHistoryWP> WeakHistoryMap; + static Mutex g_mutex (Mutex::eMutexTypeRecursive); + static WeakHistoryMap g_weak_map; + Mutex::Locker locker (g_mutex); + WeakHistoryMap::const_iterator pos = g_weak_map.find (prefix); + EditlineHistorySP history_sp; + if (pos != g_weak_map.end()) + { + history_sp = pos->second.lock(); + if (history_sp) + return history_sp; + g_weak_map.erase (pos); + } + history_sp.reset (new EditlineHistory (prefix, 800, true)); + g_weak_map[prefix] = history_sp; + return history_sp; } - history_sp.reset(new EditlineHistory(prefix, 800, true)); - g_weak_map[prefix] = history_sp; - return history_sp; - } - - bool IsValid() const - { - return m_history != NULL; - } - - ::History * - GetHistoryPtr () - { - return m_history; - } - - void - Enter (const char *line_cstr) - { - if (m_history) - ::history (m_history, &m_event, H_ENTER, line_cstr); - } - - bool - Load () - { - if (m_history) + + bool IsValid() const { - const char *path = GetHistoryFilePath(); - if (path) + return m_history != NULL; + } + + HistoryW * + GetHistoryPtr () + { + return m_history; + } + + void + Enter (const EditLineCharType *line_cstr) + { + if (m_history) + history_w (m_history, &m_event, H_ENTER, line_cstr); + } + + bool + Load () + { + if (m_history) { - ::history (m_history, &m_event, H_LOAD, path); - return true; + const char *path = GetHistoryFilePath(); + if (path) + { + history_w (m_history, &m_event, H_LOAD, path); + return true; + } } + return false; } - return false; - } - - bool - Save () - { - if (m_history) + + bool + Save () { - const char *path = GetHistoryFilePath(); - if (path) + if (m_history) { - ::history (m_history, &m_event, H_SAVE, path); - return true; + const char *path = GetHistoryFilePath(); + if (path) + { + history_w (m_history, &m_event, H_SAVE, path); + return true; + } } + return false; } - return false; - } - - protected: - ::History *m_history; // The history object - ::HistEvent m_event;// The history event needed to contain all history events - std::string m_prefix; // The prefix name (usually the editline program name) to use when loading/saving history - std::string m_path; // Path to the history file - }; + + protected: + HistoryW * m_history; // The history object + HistEventW m_event; // The history event needed to contain all history events + std::string m_prefix; // The prefix name (usually the editline program name) to use when loading/saving history + std::string m_path; // Path to the history file + }; + } } +//------------------------------------------------------------------ +// Editline private methods +//------------------------------------------------------------------ -static const char k_prompt_escape_char = '\1'; - -Editline::Editline (const char *prog, // prog can't be NULL - const char *prompt, // can be NULL for no prompt - bool configure_for_multiline, - FILE *fin, - FILE *fout, - FILE *ferr) : - m_editline (NULL), - m_history_sp (), - m_prompt (), - m_lines_prompt (), - m_getting_char (false), - m_completion_callback (NULL), - m_completion_callback_baton (NULL), - m_line_complete_callback (NULL), - m_line_complete_callback_baton (NULL), - m_lines_command (Command::None), - m_line_offset (0), - m_lines_curr_line (0), - m_lines_max_line (0), - m_file (fileno(fin), false), - m_prompt_with_line_numbers (false), - m_getting_line (false), - m_got_eof (false), - m_interrupted (false) -{ - if (prog && prog[0]) - { - m_editline = ::el_init(prog, fin, fout, ferr); - - // Get a shared history instance - m_history_sp = EditlineHistory::GetHistory(prog); +void +Editline::SetBaseLineNumber (int line_number) +{ + std::stringstream line_number_stream; + line_number_stream << line_number; + m_base_line_number = line_number; + m_line_number_digits = std::max (3, (int)line_number_stream.str().length() + 1); +} + +std::string +Editline::PromptForIndex (int line_index) +{ + bool use_line_numbers = m_multiline_enabled && m_base_line_number > 0; + std::string prompt = m_set_prompt; + if (use_line_numbers && prompt.length() == 0) + { + prompt = ": "; } - else + std::string continuation_prompt = prompt; + if (m_set_continuation_prompt.length() > 0) { - m_editline = ::el_init("lldb-tmp", fin, fout, ferr); + continuation_prompt = m_set_continuation_prompt; + + // Ensure that both prompts are the same length through space padding + while (continuation_prompt.length() < prompt.length()) + { + continuation_prompt += ' '; + } + while (prompt.length() < continuation_prompt.length()) + { + prompt += ' '; + } } - if (prompt && prompt[0]) - SetPrompt (prompt); + if (use_line_numbers) + { + StreamString prompt_stream; + prompt_stream.Printf("%*d%s", m_line_number_digits, m_base_line_number + line_index, + (line_index == 0) ? prompt.c_str() : continuation_prompt.c_str()); + return std::move (prompt_stream.GetString()); + } + return (line_index == 0) ? prompt : continuation_prompt; +} - //::el_set (m_editline, EL_BIND, "^[[A", NULL); // Print binding for up arrow key - //::el_set (m_editline, EL_BIND, "^[[B", NULL); // Print binding for up down key +void +Editline::SetCurrentLine (int line_index) +{ + m_current_line_index = line_index; + m_current_prompt = PromptForIndex (line_index); +} + +int +Editline::GetPromptWidth() +{ + return (int)PromptForIndex (0).length(); +} - assert (m_editline); - ::el_set (m_editline, EL_CLIENTDATA, this); +bool +Editline::IsEmacs() +{ + const char * editor; + el_get (m_editline, EL_EDITOR, &editor); + return editor[0] == 'e'; +} - // only defined for newer versions of editline -#ifdef EL_PROMPT_ESC - ::el_set (m_editline, EL_PROMPT_ESC, GetPromptCallback, k_prompt_escape_char); -#else - // fall back on old prompt setting code - ::el_set (m_editline, EL_PROMPT, GetPromptCallback); -#endif - ::el_set (m_editline, EL_EDITOR, "emacs"); - if (m_history_sp && m_history_sp->IsValid()) +bool +Editline::IsOnlySpaces() +{ + const LineInfoW * info = el_wline (m_editline); + for (const EditLineCharType * character = info->buffer; character < info->lastchar; character++) { - ::el_set (m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr()); + if (*character != ' ') + return false; } - ::el_set (m_editline, EL_ADDFN, "lldb-complete", "Editline completion function", Editline::CallbackComplete); - // Keep old "lldb_complete" mapping for older clients that used this in their .editrc. editline also - // has a bad bug where if you have a bind command that tries to bind to a function name that doesn't - // exist, it will corrupt the heap and probably crash your process later. - ::el_set (m_editline, EL_ADDFN, "lldb_complete", "Editline completion function", Editline::CallbackComplete); - ::el_set (m_editline, EL_ADDFN, "lldb-edit-prev-line", "Editline edit prev line", Editline::CallbackEditPrevLine); - ::el_set (m_editline, EL_ADDFN, "lldb-edit-next-line", "Editline edit next line", Editline::CallbackEditNextLine); + return true; +} - ::el_set (m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string - ::el_set (m_editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash does. - ::el_set (m_editline, EL_BIND, "\033[3~", "ed-delete-next-char", NULL); // Fix the delete key. - ::el_set (m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to be auto complete - - if (configure_for_multiline) +int +Editline::GetLineIndexForLocation (CursorLocation location, int cursor_row) +{ + int line = 0; + if (location == CursorLocation::EditingPrompt || location == CursorLocation::BlockEnd || + location == CursorLocation::EditingCursor) { - // Use escape sequences for control characters due to bugs in editline - // where "-k up" and "-k down" don't always work. - ::el_set (m_editline, EL_BIND, "^[[A", "lldb-edit-prev-line", NULL); // Map up arrow - ::el_set (m_editline, EL_BIND, "^[[B", "lldb-edit-next-line", NULL); // Map down arrow - // Bindings for next/prev history - ::el_set (m_editline, EL_BIND, "^P", "ed-prev-history", NULL); // Map up arrow - ::el_set (m_editline, EL_BIND, "^N", "ed-next-history", NULL); // Map down arrow + for (unsigned index = 0; index < m_current_line_index; index++) + { + line += CountRowsForLine (m_input_lines[index]); + } + if (location == CursorLocation::EditingCursor) + { + line += cursor_row; + } + else if (location == CursorLocation::BlockEnd) + { + for (unsigned index = m_current_line_index; index < m_input_lines.size(); index++) + { + line += CountRowsForLine (m_input_lines[index]); + } + --line; + } } - else + return line; +} + +void +Editline::MoveCursor (CursorLocation from, CursorLocation to) +{ + const LineInfoW * info = el_wline (m_editline); + int editline_cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth()); + int editline_cursor_row = editline_cursor_position / m_terminal_width; + + // Determine relative starting and ending lines + int fromLine = GetLineIndexForLocation (from, editline_cursor_row); + int toLine = GetLineIndexForLocation (to, editline_cursor_row); + if (toLine != fromLine) { - // Use escape sequences for control characters due to bugs in editline - // where "-k up" and "-k down" don't always work. - ::el_set (m_editline, EL_BIND, "^[[A", "ed-prev-history", NULL); // Map up arrow - ::el_set (m_editline, EL_BIND, "^[[B", "ed-next-history", NULL); // Map down arrow + fprintf (m_output_file, (toLine > fromLine) ? ANSI_DOWN_N_ROWS : ANSI_UP_N_ROWS, std::abs (toLine - fromLine)); } - // Source $PWD/.editrc then $HOME/.editrc - ::el_source (m_editline, NULL); - - // Always read through our callback function so we don't read - // stuff we aren't supposed to. This also stops the extra echoing - // that can happen when you have more input than editline can handle - // at once. - SetGetCharCallback(GetCharFromInputFileCallback); - - LoadHistory(); + // Determine target column + int toColumn = 1; + if (to == CursorLocation::EditingCursor) + { + toColumn = editline_cursor_position - (editline_cursor_row * m_terminal_width) + 1; + } + else if (to == CursorLocation::BlockEnd) + { + toColumn = ((m_input_lines[m_input_lines.size() - 1].length() + GetPromptWidth()) % 80) + 1; + } + fprintf (m_output_file, ANSI_SET_COLUMN_N, toColumn); } -Editline::~Editline() +void +Editline::DisplayInput (int firstIndex) { - // EditlineHistory objects are sometimes shared between multiple - // Editline instances with the same program name. So just release - // our shared pointer and if we are the last owner, it will save the - // history to the history save file automatically. - m_history_sp.reset(); + fprintf (m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1); + int line_count = (int)m_input_lines.size(); + const char *faint = m_color_prompts ? ANSI_FAINT : ""; + const char *unfaint = m_color_prompts ? ANSI_UNFAINT : ""; - // Disable edit mode to stop the terminal from flushing all input - // during the call to el_end() since we expect to have multiple editline - // instances in this program. - ::el_set (m_editline, EL_EDITMODE, 0); + for (int index = firstIndex; index < line_count; index++) + { + fprintf (m_output_file, "%s" "%s" "%s" EditLineStringFormatSpec " ", + faint, + PromptForIndex (index).c_str(), + unfaint, + m_input_lines[index].c_str()); + if (index < line_count - 1) + fprintf (m_output_file, "\n"); + } +} - ::el_end(m_editline); - m_editline = NULL; + +int +Editline::CountRowsForLine (const EditLineStringType & content) +{ + auto prompt = PromptForIndex (0); // Prompt width is constant during an edit session + int line_length = (int)(content.length() + prompt.length()); + return (line_length / m_terminal_width) + 1; } void -Editline::SetGetCharCallback (GetCharCallbackType callback) +Editline::SaveEditedLine() { - ::el_set (m_editline, EL_GETCFN, callback); + const LineInfoW * info = el_wline (m_editline); + m_input_lines[m_current_line_index] = EditLineStringType (info->buffer, info->lastchar - info->buffer); } -bool -Editline::LoadHistory () +StringList +Editline::GetInputAsStringList(int line_count) { - if (m_history_sp) - return m_history_sp->Load(); - return false; + StringList lines; + for (EditLineStringType line : m_input_lines) + { + if (line_count == 0) + break; +#if LLDB_EDITLINE_USE_WCHAR + lines.AppendString (m_utf8conv.to_bytes (line)); +#else + lines.AppendString(line); +#endif + --line_count; + } + return lines; } -bool -Editline::SaveHistory () +unsigned char +Editline::RecallHistory (bool earlier) { - if (m_history_sp) - return m_history_sp->Save(); - return false; -} + if (!m_history_sp || !m_history_sp->IsValid()) + return CC_ERROR; + + HistoryW * pHistory = m_history_sp->GetHistoryPtr(); + HistEventW history_event; + std::vector<EditLineStringType> new_input_lines; + + // Treat moving from the "live" entry differently + if (!m_in_history) + { + if (earlier == false) + return CC_ERROR; // Can't go newer than the "live" entry + if (history_w (pHistory, &history_event, H_FIRST) == -1) + return CC_ERROR; + + // Save any edits to the "live" entry in case we return by moving forward in history + // (it would be more bash-like to save over any current entry, but libedit doesn't + // offer the ability to add entries anywhere except the end.) + SaveEditedLine(); + m_live_history_lines = m_input_lines; + m_in_history = true; + } + else + { + if (history_w (pHistory, &history_event, earlier ? H_NEXT : H_PREV) == -1) + { + // Can't move earlier than the earliest entry + if (earlier) + return CC_ERROR; + // ... but moving to newer than the newest yields the "live" entry + new_input_lines = m_live_history_lines; + m_in_history = false; + } + } + + // If we're pulling the lines from history, split them apart + if (m_in_history) + new_input_lines = SplitLines (history_event.str); -Error -Editline::PrivateGetLine(std::string &line) + // Erase the current edit session and replace it with a new one + MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockStart); + m_input_lines = new_input_lines; + DisplayInput(); + + // Prepare to edit the last line when moving to previous entry, or the first line + // when moving to next entry + SetCurrentLine (m_current_line_index = earlier ? (int)m_input_lines.size() - 1 : 0); + MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); + return CC_NEWLINE; +} + +int +Editline::GetCharacter (EditLineCharType * c) { - Error error; - if (m_interrupted) + const LineInfoW * info = el_wline (m_editline); + + // Paint a faint version of the desired prompt over the version libedit draws + // (will only be requested if colors are supported) + if (m_needs_prompt_repaint) { - error.SetErrorString("interrupted"); - return error; + MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + fprintf (m_output_file, "%s" "%s" "%s", ANSI_FAINT, Prompt(), ANSI_UNFAINT); + MoveCursor (CursorLocation::EditingPrompt, CursorLocation::EditingCursor); + m_needs_prompt_repaint = false; } - line.clear(); - if (m_editline != NULL) + if (m_multiline_enabled) { - int line_len = 0; - // Call el_gets to prompt the user and read the user's input. - const char *line_cstr = ::el_gets (m_editline, &line_len); - - static int save_errno = (line_len < 0) ? errno : 0; - - if (save_errno != 0) + // Detect when the number of rows used for this input line changes due to an edit + int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth()); + int new_line_rows = (lineLength / m_terminal_width) + 1; + if (m_current_line_rows != -1 && new_line_rows != m_current_line_rows) { - error.SetError(save_errno, eErrorTypePOSIX); + // Respond by repainting the current state from this line on + MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + SaveEditedLine(); + DisplayInput (m_current_line_index); + MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingCursor); } - else if (line_cstr) + m_current_line_rows = new_line_rows; + } + + // Read an actual character + while (true) + { + lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess; + char ch = 0; + m_editor_getting_char = true; + int read_count = m_input_connection.Read(&ch, 1, UINT32_MAX, status, NULL); + m_editor_getting_char = false; + if (read_count) { - // Decrement the length so we don't have newline characters in "line" for when - // we assign the cstr into the std::string - llvm::StringRef line_ref (line_cstr); - line_ref = line_ref.rtrim("\n\r"); - - if (!line_ref.empty() && !m_interrupted) +#if LLDB_EDITLINE_USE_WCHAR + // After the initial interruptible read, this is guaranteed not to block + ungetc (ch, m_input_file); + *c = fgetwc (m_input_file); + if (*c != WEOF) + return 1; +#else + *c = ch; + if(*c != EOF) + return 1; +#endif + } + else + { + switch (status) { - // We didn't strip the newlines, we just adjusted the length, and - // we want to add the history item with the newlines - if (m_history_sp) - m_history_sp->Enter(line_cstr); - - // Copy the part of the c string that we want (removing the newline chars) - line = std::move(line_ref.str()); + case lldb::eConnectionStatusInterrupted: + m_editor_status = EditorStatus::Interrupted; + printf ("^C\n"); + return 0; + + case lldb::eConnectionStatusSuccess: // Success + break; + + case lldb::eConnectionStatusError: // Check GetError() for details + case lldb::eConnectionStatusTimedOut: // Request timed out + case lldb::eConnectionStatusEndOfFile: // End-of-file encountered + case lldb::eConnectionStatusNoConnection: // No connection + case lldb::eConnectionStatusLostConnection: // Lost connection while connected to a valid connection + m_editor_status = EditorStatus::EndOfInput; + return 0; } } } - else - { - error.SetErrorString("the EditLine instance has been deleted"); - } - return error; } +const char * +Editline::Prompt() +{ + if (m_color_prompts) + m_needs_prompt_repaint = true; + return m_current_prompt.c_str(); +} -Error -Editline::GetLine(std::string &line, bool &interrupted) +unsigned char +Editline::BreakLineCommand (int ch) { - Error error; - interrupted = false; - line.clear(); + // Preserve any content beyond the cursor, truncate and save the current line + const LineInfoW * info = el_wline (m_editline); + auto current_line = EditLineStringType (info->buffer, info->cursor - info->buffer); + auto new_line_fragment = EditLineStringType (info->cursor, info->lastchar - info->cursor); + m_input_lines[m_current_line_index] = current_line; + + // Ignore whitespace-only extra fragments when breaking a line + if (::IsOnlySpaces (new_line_fragment)) + new_line_fragment = EditLineConstString(""); - // Set arrow key bindings for up and down arrows for single line - // mode where up and down arrows do prev/next history - m_interrupted = false; + // Establish the new cursor position at the start of a line when inserting a line break + m_revert_cursor_index = 0; - if (!m_got_eof) + // Don't perform end of input detection or automatic formatting when pasting + if (!IsInputPending (m_input_file)) { - if (m_getting_line) + // If this is the end of the last line, treat this as a potential exit + if (m_current_line_index == m_input_lines.size() - 1 && new_line_fragment.length() == 0) { - error.SetErrorString("already getting a line"); - return error; + bool end_of_input = true; + if (m_is_input_complete_callback) + { + SaveEditedLine(); + auto lines = GetInputAsStringList(); + end_of_input = m_is_input_complete_callback (this, lines, m_is_input_complete_callback_baton); + + // The completion test is allowed to change the input lines when complete + if (end_of_input) + { + m_input_lines.clear(); + for (unsigned index = 0; index < lines.GetSize(); index++) + { +#if LLDB_EDITLINE_USE_WCHAR + m_input_lines.insert (m_input_lines.end(), m_utf8conv.from_bytes (lines[index])); +#else + m_input_lines.insert (m_input_lines.end(), lines[index]); +#endif + } + } + } + if (end_of_input) + { + fprintf (m_output_file, "\n"); + m_editor_status = EditorStatus::Complete; + return CC_NEWLINE; + } } - if (m_lines_curr_line > 0) + + // Apply smart indentation + if (m_fix_indentation_callback) { - error.SetErrorString("already getting lines"); - return error; + StringList lines = GetInputAsStringList (m_current_line_index + 1); +#if LLDB_EDITLINE_USE_WCHAR + lines.AppendString (m_utf8conv.to_bytes (new_line_fragment)); +#else + lines.AppendString (new_line_fragment); +#endif + + int indent_correction = m_fix_indentation_callback (this, lines, 0, m_fix_indentation_callback_baton); + new_line_fragment = FixIndentation(new_line_fragment, indent_correction); + m_revert_cursor_index = GetIndentation(new_line_fragment); } - m_getting_line = true; - error = PrivateGetLine(line); - m_getting_line = false; } + + // Insert the new line and repaint everything from the split line on down + m_input_lines.insert (m_input_lines.begin() + m_current_line_index + 1, new_line_fragment); + MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + DisplayInput (m_current_line_index); + + // Reposition the cursor to the right line and prepare to edit the new line + SetCurrentLine (m_current_line_index + 1); + MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); + return CC_NEWLINE; +} - interrupted = m_interrupted; - - if (m_got_eof && line.empty()) +unsigned char +Editline::DeleteNextCharCommand (int ch) +{ + LineInfoW * info = (LineInfoW *)el_wline (m_editline); + + // Just delete the next character normally if possible + if (info->cursor < info->lastchar) { - // Only set the error if we didn't get an error back from PrivateGetLine() - if (error.Success()) - error.SetErrorString("end of file"); + info->cursor++; + el_deletestr (m_editline, 1); + return CC_REFRESH; } - return error; + // Fail when at the end of the last line, except when ^D is pressed on + // the line is empty, in which case it is treated as EOF + if (m_current_line_index == m_input_lines.size() - 1) + { + if (ch == 4 && info->buffer == info->lastchar) + { + fprintf (m_output_file, "^D\n"); + m_editor_status = EditorStatus::EndOfInput; + return CC_EOF; + } + return CC_ERROR; + } + + // Prepare to combine this line with the one below + MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + + // Insert the next line of text at the cursor and restore the cursor position + const EditLineCharType * cursor = info->cursor; + el_winsertstr (m_editline, m_input_lines[m_current_line_index + 1].c_str()); + info->cursor = cursor; + SaveEditedLine(); + + // Delete the extra line + m_input_lines.erase (m_input_lines.begin() + m_current_line_index + 1); + + // Clear and repaint from this line on down + DisplayInput (m_current_line_index); + MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingCursor); + return CC_REFRESH; } -size_t -Editline::Push (const char *bytes, size_t len) +unsigned char +Editline::DeletePreviousCharCommand (int ch) { - if (m_editline) + LineInfoW * info = (LineInfoW *)el_wline (m_editline); + + // Just delete the previous character normally when not at the start of a line + if (info->cursor > info->buffer) { - // Must NULL terminate the string for el_push() so we stick it - // into a std::string first - ::el_push(m_editline, - const_cast<char*>(std::string (bytes, len).c_str())); - return len; + el_deletestr (m_editline, 1); + return CC_REFRESH; } - return 0; + + // No prior line and no prior character? Let the user know + if (m_current_line_index == 0) + return CC_ERROR; + + // No prior character, but prior line? Combine with the line above + SaveEditedLine(); + SetCurrentLine (m_current_line_index - 1); + auto priorLine = m_input_lines[m_current_line_index]; + m_input_lines.erase (m_input_lines.begin() + m_current_line_index); + m_input_lines[m_current_line_index] = priorLine + m_input_lines[m_current_line_index]; + + // Repaint from the new line down + fprintf (m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, CountRowsForLine (priorLine), 1); + DisplayInput (m_current_line_index); + + // Put the cursor back where libedit expects it to be before returning to editing + // by telling libedit about the newly inserted text + MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); + el_winsertstr (m_editline, priorLine.c_str()); + return CC_REDISPLAY; } - -Error -Editline::GetLines(const std::string &end_line, StringList &lines, bool &interrupted) +unsigned char +Editline::PreviousLineCommand (int ch) { - Error error; - interrupted = false; - if (m_getting_line) - { - error.SetErrorString("already getting a line"); - return error; + SaveEditedLine(); + + if (m_current_line_index == 0) { + return RecallHistory (true); } - if (m_lines_curr_line > 0) + + // Start from a known location + MoveCursor (CursorLocation::EditingCursor, CursorLocation::EditingPrompt); + + // Treat moving up from a blank last line as a deletion of that line + if (m_current_line_index == m_input_lines.size() - 1 && IsOnlySpaces()) { - error.SetErrorString("already getting lines"); - return error; + m_input_lines.erase (m_input_lines.begin() + m_current_line_index); + fprintf (m_output_file, ANSI_CLEAR_BELOW); } - // Set arrow key bindings for up and down arrows for multiple line - // mode where up and down arrows do edit prev/next line - m_interrupted = false; - - LineStatus line_status = LineStatus::Success; + SetCurrentLine (m_current_line_index - 1); + fprintf (m_output_file, ANSI_UP_N_ROWS ANSI_SET_COLUMN_N, + CountRowsForLine (m_input_lines[m_current_line_index]), 1); + return CC_NEWLINE; +} - lines.Clear(); +unsigned char +Editline::NextLineCommand (int ch) +{ + SaveEditedLine(); - FILE *out_file = GetOutputFile(); - FILE *err_file = GetErrorFile(); - m_lines_curr_line = 1; - while (line_status != LineStatus::Done) + // Handle attempts to move down from the last line + if (m_current_line_index == m_input_lines.size() - 1) { - const uint32_t line_idx = m_lines_curr_line-1; - if (line_idx >= lines.GetSize()) - lines.SetSize(m_lines_curr_line); - m_lines_max_line = lines.GetSize(); - m_lines_command = Command::None; - assert(line_idx < m_lines_max_line); - std::string &line = lines[line_idx]; - error = PrivateGetLine(line); - if (error.Fail()) + // Don't add an extra line if the existing last line is blank, move through history instead + if (IsOnlySpaces()) { - line_status = LineStatus::Error; + return RecallHistory (false); } - else if (m_interrupted) + + // Determine indentation for the new line + int indentation = 0; + if (m_fix_indentation_callback) { - interrupted = true; - line_status = LineStatus::Done; + StringList lines = GetInputAsStringList(); + lines.AppendString(""); + indentation = m_fix_indentation_callback (this, lines, 0, m_fix_indentation_callback_baton); } - else - { - switch (m_lines_command) - { - case Command::None: - if (m_line_complete_callback) - { - line_status = m_line_complete_callback (this, - lines, - line_idx, - error, - m_line_complete_callback_baton); - } - else if (line == end_line) - { - line_status = LineStatus::Done; - } + m_input_lines.insert (m_input_lines.end(), EditLineStringType (indentation, EditLineCharType(' '))); + } + + // Move down past the current line using newlines to force scrolling if needed + SetCurrentLine (m_current_line_index + 1); + const LineInfoW * info = el_wline (m_editline); + int cursor_position = (int)((info->cursor - info->buffer) + GetPromptWidth()); + int cursor_row = cursor_position / m_terminal_width; + for (int line_count = 0; line_count < m_current_line_rows - cursor_row; line_count++) + { + fprintf (m_output_file, "\n"); + } + return CC_NEWLINE; +} - if (line_status == LineStatus::Success) - { - ++m_lines_curr_line; - // If we already have content for the next line because - // we were editing previous lines, then populate the line - // with the appropriate contents - if (line_idx+1 < lines.GetSize() && !lines[line_idx+1].empty()) - ::el_push (m_editline, - const_cast<char*>(lines[line_idx+1].c_str())); - } - else if (line_status == LineStatus::Error) - { - // Clear to end of line ("ESC[K"), then print the error, - // then go to the next line ("\n") and then move cursor up - // two lines ("ESC[2A"). - fprintf (err_file, "\033[Kerror: %s\n\033[2A", error.AsCString()); - } - break; - case Command::EditPrevLine: - if (m_lines_curr_line > 1) - { - //::fprintf (out_file, "\033[1A\033[%uD\033[2K", (uint32_t)(m_lines_prompt.size() + lines[line_idx].size())); // Make cursor go up a line and clear that line - ::fprintf (out_file, "\033[1A\033[1000D\033[2K"); - if (!lines[line_idx-1].empty()) - ::el_push (m_editline, - const_cast<char*>(lines[line_idx-1].c_str())); - --m_lines_curr_line; - } - break; - case Command::EditNextLine: - // Allow the down arrow to create a new line - ++m_lines_curr_line; - //::fprintf (out_file, "\033[1B\033[%uD\033[2K", (uint32_t)(m_lines_prompt.size() + lines[line_idx].size())); - ::fprintf (out_file, "\033[1B\033[1000D\033[2K"); - if (line_idx+1 < lines.GetSize() && !lines[line_idx+1].empty()) - ::el_push (m_editline, - const_cast<char*>(lines[line_idx+1].c_str())); - break; - } +unsigned char +Editline::FixIndentationCommand (int ch) +{ + if (!m_fix_indentation_callback) + return CC_NORM; + + // Insert the character by hand prior to correction + EditLineCharType inserted[] = { (EditLineCharType)ch, 0 }; + el_winsertstr (m_editline, inserted); + SaveEditedLine(); + StringList lines = GetInputAsStringList (m_current_line_index + 1); + + // Determine the cursor position + LineInfoW * info = (LineInfoW *)el_wline (m_editline); + int cursor_position = info->cursor - info->buffer; + + int indent_correction = m_fix_indentation_callback (this, lines, cursor_position, m_fix_indentation_callback_baton); + + // Adjust the input buffer to correct indentation + if (indent_correction > 0) + { + info->cursor = info->buffer; + el_winsertstr (m_editline, EditLineStringType (indent_correction, EditLineCharType(' ')).c_str()); + } + else if (indent_correction < 0) + { + info->cursor = info->buffer - indent_correction; + el_wdeletestr (m_editline, -indent_correction); + } + info->cursor = info->buffer + cursor_position + indent_correction; + return CC_REFRESH; +} + +unsigned char +Editline::RevertLineCommand (int ch) +{ + el_winsertstr (m_editline, m_input_lines[m_current_line_index].c_str()); + if (m_revert_cursor_index >= 0) + { + LineInfoW * info = (LineInfoW *)el_wline (m_editline); + info->cursor = info->buffer + m_revert_cursor_index; + if (info->cursor > info->lastchar) + { + info->cursor = info->lastchar; } + m_revert_cursor_index = -1; } - m_lines_curr_line = 0; - m_lines_command = Command::None; + return CC_REFRESH; +} - // If we have a callback, call it one more time to let the - // user know the lines are complete - if (m_line_complete_callback && !interrupted) - m_line_complete_callback (this, - lines, - UINT32_MAX, - error, - m_line_complete_callback_baton); +unsigned char +Editline::BufferStartCommand (int ch) +{ + SaveEditedLine(); + MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockStart); + SetCurrentLine (0); + m_revert_cursor_index = 0; + return CC_NEWLINE; +} - return error; +unsigned char +Editline::BufferEndCommand (int ch) +{ + SaveEditedLine(); + MoveCursor (CursorLocation::EditingCursor, CursorLocation::BlockEnd); + SetCurrentLine ((int)m_input_lines.size() - 1); + MoveCursor (CursorLocation::BlockEnd, CursorLocation::EditingPrompt); + return CC_NEWLINE; } unsigned char -Editline::HandleCompletion (int ch) +Editline::TabCommand (int ch) { - if (m_completion_callback == NULL) + if (m_completion_callback == nullptr) return CC_ERROR; - - const LineInfo *line_info = ::el_line(m_editline); + + const LineInfo *line_info = el_line (m_editline); StringList completions; int page_size = 40; - + const int num_completions = m_completion_callback (line_info->buffer, line_info->cursor, line_info->lastchar, @@ -536,25 +922,25 @@ Editline::HandleCompletion (int ch) completions, m_completion_callback_baton); - FILE *out_file = GetOutputFile(); - -// if (num_completions == -1) -// { -// ::el_insertstr (m_editline, m_completion_key); -// return CC_REDISPLAY; -// } -// else + if (num_completions == 0) + return CC_ERROR; + // if (num_completions == -1) + // { + // el_insertstr (m_editline, m_completion_key); + // return CC_REDISPLAY; + // } + // else if (num_completions == -2) { // Replace the entire line with the first string... - ::el_deletestr (m_editline, line_info->cursor - line_info->buffer); - ::el_insertstr (m_editline, completions.GetStringAtIndex(0)); + el_deletestr (m_editline, line_info->cursor - line_info->buffer); + el_insertstr (m_editline, completions.GetStringAtIndex (0)); return CC_REDISPLAY; } // If we get a longer match display that first. - const char *completion_str = completions.GetStringAtIndex(0); - if (completion_str != NULL && *completion_str != '\0') + const char *completion_str = completions.GetStringAtIndex (0); + if (completion_str != nullptr && *completion_str != '\0') { el_insertstr (m_editline, completion_str); return CC_REDISPLAY; @@ -563,15 +949,15 @@ Editline::HandleCompletion (int ch) if (num_completions > 1) { int num_elements = num_completions + 1; - ::fprintf (out_file, "\nAvailable completions:"); + fprintf (m_output_file, "\n" ANSI_CLEAR_BELOW "Available completions:"); if (num_completions < page_size) { for (int i = 1; i < num_elements; i++) { - completion_str = completions.GetStringAtIndex(i); - ::fprintf (out_file, "\n\t%s", completion_str); + completion_str = completions.GetStringAtIndex (i); + fprintf (m_output_file, "\n\t%s", completion_str); } - ::fprintf (out_file, "\n"); + fprintf (m_output_file, "\n"); } else { @@ -585,17 +971,17 @@ Editline::HandleCompletion (int ch) endpoint = num_elements; for (; cur_pos < endpoint; cur_pos++) { - completion_str = completions.GetStringAtIndex(cur_pos); - ::fprintf (out_file, "\n\t%s", completion_str); + completion_str = completions.GetStringAtIndex (cur_pos); + fprintf (m_output_file, "\n\t%s", completion_str); } if (cur_pos >= num_elements) { - ::fprintf (out_file, "\n"); + fprintf (m_output_file, "\n"); break; } - ::fprintf (out_file, "\nMore (Y/n/a): "); + fprintf (m_output_file, "\nMore (Y/n/a): "); reply = 'n'; got_char = el_getc(m_editline, &reply); if (got_char == -1 || reply == 'n') @@ -604,247 +990,388 @@ Editline::HandleCompletion (int ch) page_size = num_elements - cur_pos; } } - + DisplayInput(); + MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); } - - if (num_completions == 0) - return CC_REFRESH_BEEP; - else - return CC_REDISPLAY; + return CC_REDISPLAY; } -Editline * -Editline::GetClientData (::EditLine *e) +void +Editline::ConfigureEditor (bool multiline) { - Editline *editline = NULL; - if (e && ::el_get(e, EL_CLIENTDATA, &editline) == 0) - return editline; - return NULL; -} + if (m_editline && m_multiline_enabled == multiline) + return; + m_multiline_enabled = multiline; + + if (m_editline) + { + // Disable edit mode to stop the terminal from flushing all input + // during the call to el_end() since we expect to have multiple editline + // instances in this program. + el_set (m_editline, EL_EDITMODE, 0); + el_end (m_editline); + } + + m_editline = el_init (m_editor_name.c_str(), m_input_file, m_output_file, m_error_file); + TerminalSizeChanged(); + + if (m_history_sp && m_history_sp->IsValid()) + { + m_history_sp->Load(); + el_wset (m_editline, EL_HIST, history, m_history_sp->GetHistoryPtr()); + } + el_set (m_editline, EL_CLIENTDATA, this); + el_set (m_editline, EL_SIGNAL, 0); + el_set (m_editline, EL_EDITOR, "emacs"); + el_set (m_editline, EL_PROMPT, (EditlinePromptCallbackType)([] (EditLine *editline) { + return Editline::InstanceFor (editline)->Prompt(); + })); -FILE * -Editline::GetInputFile () -{ - return GetFilePointer (m_editline, 0); + el_wset (m_editline, EL_GETCFN, + (EditlineGetCharCallbackType)([] (EditLine * editline, EditLineCharType * c) { + return Editline::InstanceFor (editline)->GetCharacter (c); + })); + + // Commands used for multiline support, registered whether or not they're used + el_set (m_editline, EL_ADDFN, "lldb-break-line", "Insert a line break", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->BreakLineCommand (ch); + })); + el_set (m_editline, EL_ADDFN, "lldb-delete-next-char", "Delete next character", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->DeleteNextCharCommand (ch); + })); + el_set (m_editline, EL_ADDFN, "lldb-delete-previous-char", "Delete previous character", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->DeletePreviousCharCommand (ch); + })); + el_set (m_editline, EL_ADDFN, "lldb-previous-line", "Move to previous line", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->PreviousLineCommand (ch); + })); + el_set (m_editline, EL_ADDFN, "lldb-next-line", "Move to next line", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->NextLineCommand (ch); + })); + el_set (m_editline, EL_ADDFN, "lldb-buffer-start", "Move to start of buffer", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->BufferStartCommand (ch); + })); + el_set (m_editline, EL_ADDFN, "lldb-buffer-end", "Move to end of buffer", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->BufferEndCommand (ch); + })); + el_set (m_editline, EL_ADDFN, "lldb-fix-indentation", "Fix line indentation", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->FixIndentationCommand (ch); + })); + + // Register the complete callback under two names for compatibility with older clients using + // custom .editrc files (largely becuase libedit has a bad bug where if you have a bind command + // that tries to bind to a function name that doesn't exist, it can corrupt the heap and + // crash your process later.) + EditlineCommandCallbackType complete_callback = [] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->TabCommand (ch); + }; + el_set (m_editline, EL_ADDFN, "lldb-complete", "Invoke completion", complete_callback); + el_set (m_editline, EL_ADDFN, "lldb_complete", "Invoke completion", complete_callback); + + // General bindings we don't mind being overridden + if (!multiline) { + el_set (m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string + } + el_set (m_editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash in emacs mode + el_set (m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to auto complete + + // Allow user-specific customization prior to registering bindings we absolutely require + el_source (m_editline, NULL); + + // Register an internal binding that external developers shouldn't use + el_set (m_editline, EL_ADDFN, "lldb-revert-line", "Revert line to saved state", + (EditlineCommandCallbackType)([] (EditLine * editline, int ch) { + return Editline::InstanceFor (editline)->RevertLineCommand (ch); + })); + + // Register keys that perform auto-indent correction + if (m_fix_indentation_callback && m_fix_indentation_callback_chars) + { + char bind_key[2] = { 0, 0 }; + const char * indent_chars = m_fix_indentation_callback_chars; + while (*indent_chars) + { + bind_key[0] = *indent_chars; + el_set (m_editline, EL_BIND, bind_key, "lldb-fix-indentation", NULL); + ++indent_chars; + } + } + + // Multi-line editor bindings + if (multiline) + { + el_set (m_editline, EL_BIND, "\n", "lldb-break-line", NULL); + el_set (m_editline, EL_BIND, "\r", "lldb-break-line", NULL); + el_set (m_editline, EL_BIND, "^p", "lldb-previous-line", NULL); + el_set (m_editline, EL_BIND, "^n", "lldb-next-line", NULL); + el_set (m_editline, EL_BIND, "^?", "lldb-delete-previous-char", NULL); + el_set (m_editline, EL_BIND, "^d", "lldb-delete-next-char", NULL); + el_set (m_editline, EL_BIND, ESCAPE "[3~", "lldb-delete-next-char", NULL); + el_set (m_editline, EL_BIND, ESCAPE "[\\^", "lldb-revert-line", NULL); + + // Editor-specific bindings + if (IsEmacs()) + { + el_set (m_editline, EL_BIND, ESCAPE "<", "lldb-buffer-start", NULL); + el_set (m_editline, EL_BIND, ESCAPE ">", "lldb-buffer-end", NULL); + el_set (m_editline, EL_BIND, ESCAPE "[A", "lldb-previous-line", NULL); + el_set (m_editline, EL_BIND, ESCAPE "[B", "lldb-next-line", NULL); + } + else + { + el_set (m_editline, EL_BIND, "^H", "lldb-delete-previous-char", NULL); + + el_set (m_editline, EL_BIND, "-a", ESCAPE "[A", "lldb-previous-line", NULL); + el_set (m_editline, EL_BIND, "-a", ESCAPE "[B", "lldb-next-line", NULL); + el_set (m_editline, EL_BIND, "-a", "x", "lldb-delete-next-char", NULL); + el_set (m_editline, EL_BIND, "-a", "^H", "lldb-delete-previous-char", NULL); + el_set (m_editline, EL_BIND, "-a", "^?", "lldb-delete-previous-char", NULL); + + // Escape is absorbed exiting edit mode, so re-register important sequences + // without the prefix + el_set (m_editline, EL_BIND, "-a", "[A", "lldb-previous-line", NULL); + el_set (m_editline, EL_BIND, "-a", "[B", "lldb-next-line", NULL); + el_set (m_editline, EL_BIND, "-a", "[\\^", "lldb-revert-line", NULL); + } + } } -FILE * -Editline::GetOutputFile () +//------------------------------------------------------------------ +// Editline public methods +//------------------------------------------------------------------ + +Editline * +Editline::InstanceFor (EditLine * editline) { - return GetFilePointer (m_editline, 1); + Editline * editor; + el_get (editline, EL_CLIENTDATA, &editor); + return editor; } -FILE * -Editline::GetErrorFile () +Editline::Editline (const char * editline_name, FILE * input_file, FILE * output_file, FILE * error_file, bool color_prompts) : + m_editor_status (EditorStatus::Complete), + m_color_prompts(color_prompts), + m_input_file (input_file), + m_output_file (output_file), + m_error_file (error_file), + m_input_connection (fileno(input_file), false) { - return GetFilePointer (m_editline, 2); + // Get a shared history instance + m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name; + m_history_sp = EditlineHistory::GetHistory (m_editor_name); } -const char * -Editline::GetPrompt() +Editline::~Editline() { - if (m_prompt_with_line_numbers && m_lines_curr_line > 0) + if (m_editline) { - StreamString strm; - strm.Printf("%3u: ", m_lines_curr_line); - m_lines_prompt = std::move(strm.GetString()); - return m_lines_prompt.c_str(); - } - else - { - return m_prompt.c_str(); + // Disable edit mode to stop the terminal from flushing all input + // during the call to el_end() since we expect to have multiple editline + // instances in this program. + el_set (m_editline, EL_EDITMODE, 0); + el_end (m_editline); + m_editline = nullptr; } + + // EditlineHistory objects are sometimes shared between multiple + // Editline instances with the same program name. So just release + // our shared pointer and if we are the last owner, it will save the + // history to the history save file automatically. + m_history_sp.reset(); } void -Editline::SetPrompt (const char *p) +Editline::SetPrompt (const char * prompt) { - if (p && p[0]) - m_prompt = p; - else - m_prompt.clear(); - size_t start_pos = 0; - size_t escape_pos; - while ((escape_pos = m_prompt.find('\033', start_pos)) != std::string::npos) - { - m_prompt.insert(escape_pos, 1, k_prompt_escape_char); - start_pos += 2; - } + m_set_prompt = prompt == nullptr ? "" : prompt; } -FILE * -Editline::GetFilePointer (::EditLine *e, int fd) +void +Editline::SetContinuationPrompt (const char * continuation_prompt) { - FILE *file_ptr = NULL; - if (e && ::el_get(e, EL_GETFP, fd, &file_ptr) == 0) - return file_ptr; - return NULL; + m_set_continuation_prompt = continuation_prompt == nullptr ? "" : continuation_prompt; } -unsigned char -Editline::CallbackEditPrevLine (::EditLine *e, int ch) +void +Editline::TerminalSizeChanged() { - Editline *editline = GetClientData (e); - if (editline->m_lines_curr_line > 1) + if (m_editline != nullptr) { - editline->m_lines_command = Command::EditPrevLine; - return CC_NEWLINE; + el_resize (m_editline); + int columns; + // Despite the man page claiming non-zero indicates success, it's actually zero + if (el_get (m_editline, EL_GETTC, "co", &columns) == 0) + { + m_terminal_width = columns; + if (m_current_line_rows != -1) + { + const LineInfoW * info = el_wline (m_editline); + int lineLength = (int)((info->lastchar - info->buffer) + GetPromptWidth()); + m_current_line_rows = (lineLength / columns) + 1; + } + } + else + { + m_terminal_width = INT_MAX; + m_current_line_rows = 1; + } } - return CC_ERROR; } -unsigned char -Editline::CallbackEditNextLine (::EditLine *e, int ch) + +const char * +Editline::GetPrompt() { - Editline *editline = GetClientData (e); - if (editline->m_lines_curr_line < editline->m_lines_max_line) - { - editline->m_lines_command = Command::EditNextLine; - return CC_NEWLINE; - } - return CC_ERROR; + return m_set_prompt.c_str(); } -unsigned char -Editline::CallbackComplete (::EditLine *e, int ch) +uint32_t +Editline::GetCurrentLine() { - Editline *editline = GetClientData (e); - if (editline) - return editline->HandleCompletion (ch); - return CC_ERROR; + return m_current_line_index; } -const char * -Editline::GetPromptCallback (::EditLine *e) +void +Editline::Hide() { - Editline *editline = GetClientData (e); - if (editline) - return editline->GetPrompt(); - return ""; + // Make sure we're at a stable location waiting for input + while (m_editor_status == EditorStatus::Editing && !m_editor_getting_char) + { + usleep(100000); + } + + // Clear the existing input + if (m_editor_status == EditorStatus::Editing) + { + MoveCursor(CursorLocation::EditingCursor, CursorLocation::BlockStart); + fprintf(m_output_file, ANSI_CLEAR_BELOW); + } } -int -Editline::GetCharFromInputFileCallback (EditLine *e, char *c) +void +Editline::Refresh() { - Editline *editline = GetClientData (e); - if (editline && editline->m_got_eof == false) + if (m_editor_status == EditorStatus::Editing) { - FILE *f = editline->GetInputFile(); - if (f == NULL) - { - editline->m_got_eof = true; - return 0; - } - - - while (1) - { - lldb::ConnectionStatus status = eConnectionStatusSuccess; - char ch = 0; - // When we start to call el_gets() the editline library needs to - // output the prompt - editline->m_getting_char.SetValue(true, eBroadcastAlways); - const size_t n = editline->m_file.Read(&ch, 1, UINT32_MAX, status, NULL); - editline->m_getting_char.SetValue(false, eBroadcastAlways); - if (n) - { - if (ch == '\x04') - { - // Only turn a CTRL+D into a EOF if we receive the - // CTRL+D an empty line, otherwise it will forward - // delete the character at the cursor - const LineInfo *line_info = ::el_line(e); - if (line_info != NULL && - line_info->buffer == line_info->cursor && - line_info->cursor == line_info->lastchar) - { - editline->m_got_eof = true; - break; - } - } - - if (status == eConnectionStatusEndOfFile) - { - editline->m_got_eof = true; - break; - } - else - { - *c = ch; - return 1; - } - } - else - { - switch (status) - { - case eConnectionStatusInterrupted: - editline->m_interrupted = true; - *c = '\n'; - return 1; - - case eConnectionStatusSuccess: // Success - break; - - case eConnectionStatusError: // Check GetError() for details - case eConnectionStatusTimedOut: // Request timed out - case eConnectionStatusEndOfFile: // End-of-file encountered - case eConnectionStatusNoConnection: // No connection - case eConnectionStatusLostConnection: // Lost connection while connected to a valid connection - editline->m_got_eof = true; - break; - } - } - } + DisplayInput(); + MoveCursor(CursorLocation::BlockEnd, CursorLocation::EditingCursor); } - return 0; } -void -Editline::Hide () +bool +Editline::Interrupt() { - if (m_getting_line) + if (m_editor_status == EditorStatus::Editing) { - // If we are getting a line, we might have started to call el_gets() and - // it might be printing the prompt. Here we make sure we are actually getting - // a character. This way we know the entire prompt has been printed. - TimeValue timeout = TimeValue::Now(); - timeout.OffsetWithSeconds(1); - if (m_getting_char.WaitForValueEqualTo(true, &timeout)) - { - FILE *out_file = GetOutputFile(); - if (out_file) - { - const LineInfo *line_info = ::el_line(m_editline); - if (line_info) - ::fprintf (out_file, "\033[%uD\033[K", (uint32_t)(strlen(GetPrompt()) + line_info->cursor - line_info->buffer)); - } - } + return m_input_connection.InterruptRead(); } + return false; // Interrupt not handled as we weren't getting a line or lines } +void +Editline::SetAutoCompleteCallback (CompleteCallbackType callback, void * baton) +{ + m_completion_callback = callback; + m_completion_callback_baton = baton; +} void -Editline::Refresh() +Editline::SetIsInputCompleteCallback (IsInputCompleteCallbackType callback, void * baton) +{ + m_is_input_complete_callback = callback; + m_is_input_complete_callback_baton = baton; +} + +bool +Editline::SetFixIndentationCallback (FixIndentationCallbackType callback, + void * baton, + const char * indent_chars) { - if (m_getting_line) + m_fix_indentation_callback = callback; + m_fix_indentation_callback_baton = baton; + m_fix_indentation_callback_chars = indent_chars; + return false; +} + +bool +Editline::GetLine (std::string &line, bool &interrupted) +{ + ConfigureEditor (false); + m_input_lines = std::vector<EditLineStringType>(); + m_input_lines.insert (m_input_lines.begin(), EditLineConstString("")); + + SetCurrentLine (0); + m_in_history = false; + m_editor_status = EditorStatus::Editing; + m_editor_getting_char = false; + m_revert_cursor_index = -1; + + int count; + auto input = el_wgets (m_editline, &count); + + interrupted = m_editor_status == EditorStatus::Interrupted; + if (!interrupted) { - // If we are getting a line, we might have started to call el_gets() and - // it might be printing the prompt. Here we make sure we are actually getting - // a character. This way we know the entire prompt has been printed. - TimeValue timeout = TimeValue::Now(); - timeout.OffsetWithSeconds(1); - if (m_getting_char.WaitForValueEqualTo(true, &timeout)) + if (input == nullptr) { - ::el_set (m_editline, EL_REFRESH); + fprintf (m_output_file, "\n"); + m_editor_status = EditorStatus::EndOfInput; + } + else + { + m_history_sp->Enter (input); +#if LLDB_EDITLINE_USE_WCHAR + line = m_utf8conv.to_bytes (SplitLines (input)[0]); +#else + line = SplitLines (input)[0]; +#endif + m_editor_status = EditorStatus::Complete; } } + return m_editor_status != EditorStatus::EndOfInput; } bool -Editline::Interrupt () +Editline::GetLines (int first_line_number, StringList &lines, bool &interrupted) { - m_interrupted = true; - if (m_getting_line || m_lines_curr_line > 0) - return m_file.InterruptRead(); - return false; // Interrupt not handled as we weren't getting a line or lines + ConfigureEditor (true); + + // Print the initial input lines, then move the cursor back up to the start of input + SetBaseLineNumber (first_line_number); + m_input_lines = std::vector<EditLineStringType>(); + m_input_lines.insert (m_input_lines.begin(), EditLineConstString("")); + + // Begin the line editing loop + DisplayInput(); + SetCurrentLine (0); + MoveCursor (CursorLocation::BlockEnd, CursorLocation::BlockStart); + m_editor_status = EditorStatus::Editing; + m_editor_getting_char = false; + m_in_history = false; + + m_revert_cursor_index = -1; + while (m_editor_status == EditorStatus::Editing) + { + int count; + m_current_line_rows = -1; + el_wpush (m_editline, EditLineConstString("\x1b[^")); // Revert to the existing line content + el_wgets (m_editline, &count); + } + + interrupted = m_editor_status == EditorStatus::Interrupted; + if (!interrupted) + { + // Save the completed entry in history before returning + m_history_sp->Enter (CombineLines (m_input_lines).c_str()); + + lines = GetInputAsStringList(); + } + return m_editor_status != EditorStatus::EndOfInput; } diff --git a/source/Host/common/File.cpp b/source/Host/common/File.cpp index 50513af2dc14..c3c77835ce86 100644 --- a/source/Host/common/File.cpp +++ b/source/Host/common/File.cpp @@ -896,7 +896,7 @@ File::CalculateInteractiveAndTerminal () { m_is_interactive = eLazyBoolNo; m_is_real_terminal = eLazyBoolNo; -#ifdef _WIN32 +#if (defined(_WIN32) || defined(__ANDROID_NDK__)) if (_isatty(fd)) { m_is_interactive = eLazyBoolYes; diff --git a/source/Host/common/FileSpec.cpp b/source/Host/common/FileSpec.cpp index 8c4014c074f5..0af0556d30c9 100644 --- a/source/Host/common/FileSpec.cpp +++ b/source/Host/common/FileSpec.cpp @@ -56,7 +56,8 @@ GetFileStats (const FileSpec *file_spec, struct stat *stats_ptr) } // Resolves the username part of a path of the form ~user/other/directories, and -// writes the result into dst_path. +// writes the result into dst_path. This will also resolve "~" to the current user. +// If you want to complete "~" to the list of users, pass it to ResolvePartialUsername. void FileSpec::ResolveUsername (llvm::SmallVectorImpl<char> &path) { @@ -66,9 +67,9 @@ FileSpec::ResolveUsername (llvm::SmallVectorImpl<char> &path) llvm::StringRef path_str(path.data()); size_t slash_pos = path_str.find_first_of("/", 1); - if (slash_pos == 1) + if (slash_pos == 1 || path.size() == 1) { - // A path of the form ~/ resolves to the current user's home dir + // A path of ~/ resolves to the current user's home dir llvm::SmallString<64> home_dir; if (!llvm::sys::path::home_directory(home_dir)) return; @@ -166,10 +167,10 @@ FileSpec::Resolve (llvm::SmallVectorImpl<char> &path) llvm::sys::fs::make_absolute(path); } -FileSpec::FileSpec() - : m_directory() - , m_filename() - , m_syntax(FileSystem::GetNativePathSyntax()) +FileSpec::FileSpec() : + m_directory(), + m_filename(), + m_syntax(FileSystem::GetNativePathSyntax()) { } @@ -180,7 +181,8 @@ FileSpec::FileSpec() FileSpec::FileSpec(const char *pathname, bool resolve_path, PathSyntax syntax) : m_directory(), m_filename(), - m_is_resolved(false) + m_is_resolved(false), + m_syntax(syntax) { if (pathname && pathname[0]) SetFile(pathname, resolve_path, syntax); @@ -266,14 +268,18 @@ FileSpec::SetFile (const char *pathname, bool resolve, PathSyntax syntax) return; llvm::SmallString<64> normalized(pathname); - Normalize(normalized, syntax); if (resolve) { FileSpec::Resolve (normalized); m_is_resolved = true; } - + + // Only normalize after resolving the path. Resolution will modify the path + // string, potentially adding wrong kinds of slashes to the path, that need + // to be re-normalized. + Normalize(normalized, syntax); + llvm::StringRef resolve_path_ref(normalized.c_str()); llvm::StringRef filename_ref = llvm::sys::path::filename(resolve_path_ref); if (!filename_ref.empty()) @@ -446,15 +452,129 @@ FileSpec::Compare(const FileSpec& a, const FileSpec& b, bool full) } bool -FileSpec::Equal (const FileSpec& a, const FileSpec& b, bool full) +FileSpec::Equal (const FileSpec& a, const FileSpec& b, bool full, bool remove_backups) { if (!full && (a.GetDirectory().IsEmpty() || b.GetDirectory().IsEmpty())) return a.m_filename == b.m_filename; - else + else if (remove_backups == false) return a == b; + else + { + if (a.m_filename != b.m_filename) + return false; + if (a.m_directory == b.m_directory) + return true; + ConstString a_without_dots; + ConstString b_without_dots; + + RemoveBackupDots (a.m_directory, a_without_dots); + RemoveBackupDots (b.m_directory, b_without_dots); + return a_without_dots == b_without_dots; + } } +void +FileSpec::RemoveBackupDots (const ConstString &input_const_str, ConstString &result_const_str) +{ + const char *input = input_const_str.GetCString(); + result_const_str.Clear(); + if (!input || input[0] == '\0') + return; + + const char win_sep = '\\'; + const char unix_sep = '/'; + char found_sep; + const char *win_backup = "\\.."; + const char *unix_backup = "/.."; + + bool is_win = false; + + // Determine the platform for the path (win or unix): + + if (input[0] == win_sep) + is_win = true; + else if (input[0] == unix_sep) + is_win = false; + else if (input[1] == ':') + is_win = true; + else if (strchr(input, unix_sep) != nullptr) + is_win = false; + else if (strchr(input, win_sep) != nullptr) + is_win = true; + else + { + // No separators at all, no reason to do any work here. + result_const_str = input_const_str; + return; + } + + llvm::StringRef backup_sep; + if (is_win) + { + found_sep = win_sep; + backup_sep = win_backup; + } + else + { + found_sep = unix_sep; + backup_sep = unix_backup; + } + + llvm::StringRef input_ref(input); + llvm::StringRef curpos(input); + + bool had_dots = false; + std::string result; + + while (1) + { + // Start of loop + llvm::StringRef before_sep; + std::pair<llvm::StringRef, llvm::StringRef> around_sep = curpos.split(backup_sep); + + before_sep = around_sep.first; + curpos = around_sep.second; + + if (curpos.empty()) + { + if (had_dots) + { + if (!before_sep.empty()) + { + result.append(before_sep.data(), before_sep.size()); + } + } + break; + } + had_dots = true; + unsigned num_backups = 1; + while (curpos.startswith(backup_sep)) + { + num_backups++; + curpos = curpos.slice(backup_sep.size(), curpos.size()); + } + + size_t end_pos = before_sep.size(); + while (num_backups-- > 0) + { + end_pos = before_sep.rfind(found_sep, end_pos); + if (end_pos == llvm::StringRef::npos) + { + result_const_str = input_const_str; + return; + } + } + result.append(before_sep.data(), end_pos); + } + + if (had_dots) + result_const_str.SetCString(result.c_str()); + else + result_const_str = input_const_str; + + return; +} //------------------------------------------------------------------ // Dump the object to the supplied stream. If the object contains @@ -502,7 +622,10 @@ FileSpec::ResolveExecutableLocation () if (file_cstr) { const std::string file_str (file_cstr); - std::string path = llvm::sys::FindProgramByName (file_str); + llvm::ErrorOr<std::string> error_or_path = llvm::sys::findProgramByName (file_str); + if (!error_or_path) + return false; + std::string path = error_or_path.get(); llvm::StringRef dir_ref = llvm::sys::path::parent_path(path); if (!dir_ref.empty()) { @@ -946,6 +1069,8 @@ FileSpec::EnumerateDirectory lldb_utility::CleanUp <DIR *, int> dir_path_dir(opendir(dir_path), NULL, closedir); if (dir_path_dir.is_valid()) { + char dir_path_last_char = dir_path[strlen(dir_path) - 1]; + long path_max = fpathconf (dirfd (dir_path_dir.get()), _PC_NAME_MAX); #if defined (__APPLE_) && defined (__DARWIN_MAXPATHLEN) if (path_max < __DARWIN_MAXPATHLEN) @@ -990,7 +1115,14 @@ FileSpec::EnumerateDirectory if (call_callback) { char child_path[PATH_MAX]; - const int child_path_len = ::snprintf (child_path, sizeof(child_path), "%s/%s", dir_path, dp->d_name); + + // Don't make paths with "/foo//bar", that just confuses everybody. + int child_path_len; + if (dir_path_last_char == '/') + child_path_len = ::snprintf (child_path, sizeof(child_path), "%s%s", dir_path, dp->d_name); + else + child_path_len = ::snprintf (child_path, sizeof(child_path), "%s/%s", dir_path, dp->d_name); + if (child_path_len < (int)(sizeof(child_path) - 1)) { // Don't resolve the file type or path @@ -1208,18 +1340,31 @@ FileSpec::IsSourceImplementationFile () const bool FileSpec::IsRelativeToCurrentWorkingDirectory () const { - const char *directory = m_directory.GetCString(); - if (directory && directory[0]) + const char *dir = m_directory.GetCString(); + llvm::StringRef directory(dir ? dir : ""); + + if (directory.size() > 0) { - // If the path doesn't start with '/' or '~', return true - switch (directory[0]) + if (m_syntax == ePathSyntaxWindows) { - case '/': - case '~': - return false; - default: + if (directory.size() >= 2 && directory[1] == ':') + return false; + if (directory[0] == '/') + return false; return true; } + else + { + // If the path doesn't start with '/' or '~', return true + switch (directory[0]) + { + case '/': + case '~': + return false; + default: + return true; + } + } } else if (m_filename) { diff --git a/source/Host/common/Host.cpp b/source/Host/common/Host.cpp index 00c2fa37b383..c8daa175d1bd 100644 --- a/source/Host/common/Host.cpp +++ b/source/Host/common/Host.cpp @@ -14,11 +14,7 @@ #include <limits.h> #include <stdlib.h> #include <sys/types.h> -#ifdef _WIN32 -#include "lldb/Host/windows/windows.h" -#include <winsock2.h> -#include <ws2tcpip.h> -#else +#ifndef _WIN32 #include <unistd.h> #include <dlfcn.h> #include <grp.h> @@ -27,11 +23,6 @@ #include <sys/stat.h> #endif -#if !defined (__GNU__) && !defined (_WIN32) -// Does not exist under GNU/HURD or Windows -#include <sys/sysctl.h> -#endif - #if defined (__APPLE__) #include <mach/mach_port.h> #include <mach/mach_init.h> @@ -39,7 +30,9 @@ #endif #if defined (__linux__) || defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__APPLE__) || defined(__NetBSD__) +#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) #include <spawn.h> +#endif #include <sys/wait.h> #include <sys/syscall.h> #endif @@ -51,33 +44,30 @@ // C++ includes #include <limits> +#include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Core/ArchSpec.h" -#include "lldb/Core/ConstString.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" -#include "lldb/Core/StreamString.h" -#include "lldb/Core/ThreadSafeSTLMap.h" -#include "lldb/Host/Config.h" -#include "lldb/Host/Endian.h" #include "lldb/Host/FileSpec.h" -#include "lldb/Host/FileSystem.h" -#include "lldb/Host/Mutex.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/MonitoringProcessLauncher.h" +#include "lldb/Host/ProcessLauncher.h" +#include "lldb/Host/ThreadLauncher.h" #include "lldb/lldb-private-forward.h" #include "lldb/Target/FileAction.h" -#include "lldb/Target/Process.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Target/TargetList.h" #include "lldb/Utility/CleanUp.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/Support/Host.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/raw_ostream.h" +#if defined(_WIN32) +#include "lldb/Host/windows/ProcessLauncherWindows.h" +#else +#include "lldb/Host/posix/ProcessLauncherPosix.h" +#endif #if defined (__APPLE__) #ifndef _POSIX_SPAWN_DISABLE_ASLR @@ -95,13 +85,6 @@ extern "C" using namespace lldb; using namespace lldb_private; -// Define maximum thread name length -#if defined (__linux__) || defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__NetBSD__) -uint32_t const Host::MAX_THREAD_NAME_LENGTH = 16; -#else -uint32_t const Host::MAX_THREAD_NAME_LENGTH = std::numeric_limits<uint32_t>::max (); -#endif - #if !defined (__APPLE__) && !defined (_WIN32) struct MonitorInfo { @@ -114,16 +97,9 @@ struct MonitorInfo static thread_result_t MonitorChildProcessThreadFunction (void *arg); -lldb::thread_t -Host::StartMonitoringChildProcess -( - Host::MonitorChildProcessCallback callback, - void *callback_baton, - lldb::pid_t pid, - bool monitor_signals -) +HostThread +Host::StartMonitoringChildProcess(Host::MonitorChildProcessCallback callback, void *callback_baton, lldb::pid_t pid, bool monitor_signals) { - lldb::thread_t thread = LLDB_INVALID_HOST_THREAD; MonitorInfo * info_ptr = new MonitorInfo(); info_ptr->pid = pid; @@ -132,26 +108,11 @@ Host::StartMonitoringChildProcess info_ptr->monitor_signals = monitor_signals; char thread_name[256]; - - if (Host::MAX_THREAD_NAME_LENGTH <= 16) - { - // On some platforms, the thread name is limited to 16 characters. We need to - // abbreviate there or the pid info would get truncated. - ::snprintf (thread_name, sizeof(thread_name), "wait4(%" PRIu64 ")", pid); - } - else - { - ::snprintf (thread_name, sizeof(thread_name), "<lldb.host.wait4(pid=%" PRIu64 ")>", pid); - } - - thread = ThreadCreate (thread_name, - MonitorChildProcessThreadFunction, - info_ptr, - NULL); - - return thread; + ::snprintf(thread_name, sizeof(thread_name), "<lldb.host.wait4(pid=%" PRIu64 ")>", pid); + return ThreadLauncher::LaunchThread(thread_name, MonitorChildProcessThreadFunction, info_ptr, NULL); } +#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) //------------------------------------------------------------------ // Scoped class that will disable thread canceling when it is // constructed, and exception safely restore the previous value it @@ -166,7 +127,6 @@ public: int err = ::pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &m_old_state); if (err != 0) m_old_state = -1; - } ~ScopedPThreadCancelDisabler() @@ -179,6 +139,7 @@ public: private: int m_old_state; // Save the old cancelability state. }; +#endif // __ANDROID_NDK__ static thread_result_t MonitorChildProcessThreadFunction (void *arg) @@ -212,11 +173,14 @@ MonitorChildProcessThreadFunction (void *arg) log->Printf("%s ::wait_pid (pid = %" PRIi32 ", &status, options = %i)...", function, pid, options); // Wait for all child processes +#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) ::pthread_testcancel (); +#endif // Get signals from all children with same process group of pid const ::pid_t wait_pid = ::waitpid (pid, &status, options); +#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) ::pthread_testcancel (); - +#endif if (wait_pid == -1) { if (errno == EINTR) @@ -261,7 +225,9 @@ MonitorChildProcessThreadFunction (void *arg) // Scope for pthread_cancel_disabler { +#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) ScopedPThreadCancelDisabler pthread_cancel_disabler; +#endif log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); if (log) @@ -349,6 +315,8 @@ Host::GetCurrentThreadID() return thread_self; #elif defined(__FreeBSD__) return lldb::tid_t(pthread_getthreadid_np()); +#elif defined(__ANDROID_NDK__) + return lldb::tid_t(gettid()); #elif defined(__linux__) return lldb::tid_t(syscall(SYS_gettid)); #else @@ -429,11 +397,6 @@ Host::WillTerminate () #if !defined (__APPLE__) && !defined (__FreeBSD__) && !defined (__FreeBSD_kernel__) && !defined (__linux__) // see macosx/Host.mm void -Host::ThreadCreated (const char *thread_name) -{ -} - -void Host::Backtrace (Stream &strm, uint32_t max_frames) { // TODO: Is there a way to backtrace the current process on other systems? @@ -448,101 +411,8 @@ Host::GetEnvironment (StringList &env) #endif // #if !defined (__APPLE__) && !defined (__FreeBSD__) && !defined (__FreeBSD_kernel__) && !defined (__linux__) -struct HostThreadCreateInfo -{ - std::string thread_name; - thread_func_t thread_fptr; - thread_arg_t thread_arg; - - HostThreadCreateInfo (const char *name, thread_func_t fptr, thread_arg_t arg) : - thread_name (name ? name : ""), - thread_fptr (fptr), - thread_arg (arg) - { - } -}; - -static thread_result_t -#ifdef _WIN32 -__stdcall -#endif -ThreadCreateTrampoline (thread_arg_t arg) -{ - HostThreadCreateInfo *info = (HostThreadCreateInfo *)arg; - Host::ThreadCreated (info->thread_name.c_str()); - thread_func_t thread_fptr = info->thread_fptr; - thread_arg_t thread_arg = info->thread_arg; - - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); - if (log) - log->Printf("thread created"); - - delete info; - return thread_fptr (thread_arg); -} - -lldb::thread_t -Host::ThreadCreate -( - const char *thread_name, - thread_func_t thread_fptr, - thread_arg_t thread_arg, - Error *error -) -{ - lldb::thread_t thread = LLDB_INVALID_HOST_THREAD; - - // Host::ThreadCreateTrampoline will delete this pointer for us. - HostThreadCreateInfo *info_ptr = new HostThreadCreateInfo (thread_name, thread_fptr, thread_arg); - -#ifdef _WIN32 - thread = ::_beginthreadex(0, 0, ThreadCreateTrampoline, info_ptr, 0, NULL); - int err = thread <= 0 ? GetLastError() : 0; -#else - int err = ::pthread_create (&thread, NULL, ThreadCreateTrampoline, info_ptr); -#endif - if (err == 0) - { - if (error) - error->Clear(); - return thread; - } - - if (error) - error->SetError (err, eErrorTypePOSIX); - - return LLDB_INVALID_HOST_THREAD; -} - #ifndef _WIN32 -bool -Host::ThreadCancel (lldb::thread_t thread, Error *error) -{ - int err = ::pthread_cancel (thread); - if (error) - error->SetError(err, eErrorTypePOSIX); - return err == 0; -} - -bool -Host::ThreadDetach (lldb::thread_t thread, Error *error) -{ - int err = ::pthread_detach (thread); - if (error) - error->SetError(err, eErrorTypePOSIX); - return err == 0; -} - -bool -Host::ThreadJoin (lldb::thread_t thread, thread_result_t *thread_result_ptr, Error *error) -{ - int err = ::pthread_join (thread, thread_result_ptr); - if (error) - error->SetError(err, eErrorTypePOSIX); - return err == 0; -} - lldb::thread_key_t Host::ThreadLocalStorageCreate(ThreadLocalStorageCleanupCallback callback) { @@ -563,99 +433,6 @@ Host::ThreadLocalStorageSet(lldb::thread_key_t key, void *value) ::pthread_setspecific (key, value); } -bool -Host::SetThreadName (lldb::pid_t pid, lldb::tid_t tid, const char *name) -{ -#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 - lldb::pid_t curr_pid = Host::GetCurrentProcessID(); - lldb::tid_t curr_tid = Host::GetCurrentThreadID(); - if (pid == LLDB_INVALID_PROCESS_ID) - pid = curr_pid; - - if (tid == LLDB_INVALID_THREAD_ID) - tid = curr_tid; - - // Set the pthread name if possible - if (pid == curr_pid && tid == curr_tid) - { - if (::pthread_setname_np (name) == 0) - return true; - } - return false; -#elif defined (__FreeBSD__) - lldb::pid_t curr_pid = Host::GetCurrentProcessID(); - lldb::tid_t curr_tid = Host::GetCurrentThreadID(); - if (pid == LLDB_INVALID_PROCESS_ID) - pid = curr_pid; - - if (tid == LLDB_INVALID_THREAD_ID) - tid = curr_tid; - - // Set the pthread name if possible - if (pid == curr_pid && tid == curr_tid) - { - ::pthread_set_name_np (::pthread_self(), name); - return true; - } - return false; -#elif defined (__linux__) || defined (__GLIBC__) - void *fn = dlsym (RTLD_DEFAULT, "pthread_setname_np"); - if (fn) - { - lldb::pid_t curr_pid = Host::GetCurrentProcessID(); - lldb::tid_t curr_tid = Host::GetCurrentThreadID(); - if (pid == LLDB_INVALID_PROCESS_ID) - pid = curr_pid; - - if (tid == LLDB_INVALID_THREAD_ID) - tid = curr_tid; - - if (pid == curr_pid && tid == curr_tid) - { - int (*pthread_setname_np_func)(pthread_t thread, const char *name); - *reinterpret_cast<void **> (&pthread_setname_np_func) = fn; - - if (pthread_setname_np_func (::pthread_self(), name) == 0) - return true; - } - } - return false; -#else - return false; -#endif -} - -bool -Host::SetShortThreadName (lldb::pid_t pid, lldb::tid_t tid, - const char *thread_name, size_t len) -{ - std::unique_ptr<char[]> namebuf(new char[len+1]); - - // Thread names are coming in like '<lldb.comm.debugger.edit>' and - // '<lldb.comm.debugger.editline>'. So just chopping the end of the string - // off leads to a lot of similar named threads. Go through the thread name - // and search for the last dot and use that. - const char *lastdot = ::strrchr (thread_name, '.'); - - if (lastdot && lastdot != thread_name) - thread_name = lastdot + 1; - ::strncpy (namebuf.get(), thread_name, len); - namebuf[len] = 0; - - int namebuflen = strlen(namebuf.get()); - if (namebuflen > 0) - { - if (namebuf[namebuflen - 1] == '(' || namebuf[namebuflen - 1] == '>') - { - // Trim off trailing '(' and '>' characters for a bit more cleanup. - namebuflen--; - namebuf[namebuflen] = 0; - } - return Host::SetThreadName (pid, tid, namebuf.get()); - } - return false; -} - #endif #if !defined (__APPLE__) // see Host.mm @@ -680,12 +457,16 @@ FileSpec Host::GetModuleFileSpecForHostAddress (const void *host_addr) { FileSpec module_filespec; +#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) Dl_info info; if (::dladdr (host_addr, &info)) { if (info.dli_fname) module_filespec.SetFile(info.dli_fname, true); } +#else + assert(false && "dladdr() not supported on Android"); +#endif return module_filespec; } @@ -699,28 +480,6 @@ Host::FindProcessThreads (const lldb::pid_t pid, TidMap &tids_to_attach) } #endif -lldb::TargetSP -Host::GetDummyTarget (lldb_private::Debugger &debugger) -{ - static TargetSP g_dummy_target_sp; - - // FIXME: Maybe the dummy target should be per-Debugger - if (!g_dummy_target_sp || !g_dummy_target_sp->IsValid()) - { - ArchSpec arch(Target::GetDefaultArchitecture()); - if (!arch.IsValid()) - arch = HostInfo::GetArchitecture(); - Error err = debugger.GetTargetList().CreateTarget(debugger, - NULL, - arch.GetTriple().getTriple().c_str(), - false, - NULL, - g_dummy_target_sp); - } - - return g_dummy_target_sp; -} - struct ShellInfo { ShellInfo () : @@ -770,14 +529,15 @@ Host::RunShellCommand (const char *command, int *signo_ptr, std::string *command_output_ptr, uint32_t timeout_sec, - const char *shell) + bool run_in_default_shell) { Error error; ProcessLaunchInfo launch_info; - if (shell && shell[0]) + launch_info.SetArchitecture(HostInfo::GetArchitecture()); + if (run_in_default_shell) { // Run the command in a shell - launch_info.SetShell(shell); + launch_info.SetShell(HostInfo::GetDefaultShell()); launch_info.GetArguments().AppendArgument(command); const bool localhost = true; const bool will_debug = false; @@ -798,9 +558,8 @@ Host::RunShellCommand (const char *command, if (working_dir) launch_info.SetWorkingDirectory(working_dir); - char output_file_path_buffer[PATH_MAX]; - const char *output_file_path = NULL; - + llvm::SmallString<PATH_MAX> output_file_path; + if (command_output_ptr) { // Create a temporary file to get the stdout/stderr and redirect the @@ -809,21 +568,19 @@ Host::RunShellCommand (const char *command, FileSpec tmpdir_file_spec; if (HostInfo::GetLLDBPath(ePathTypeLLDBTempSystemDir, tmpdir_file_spec)) { - tmpdir_file_spec.AppendPathComponent("lldb-shell-output.XXXXXX"); - strncpy(output_file_path_buffer, tmpdir_file_spec.GetPath().c_str(), sizeof(output_file_path_buffer)); + tmpdir_file_spec.AppendPathComponent("lldb-shell-output.%%%%%%"); + llvm::sys::fs::createUniqueFile(tmpdir_file_spec.GetPath().c_str(), output_file_path); } else { - strncpy(output_file_path_buffer, "/tmp/lldb-shell-output.XXXXXX", sizeof(output_file_path_buffer)); + llvm::sys::fs::createTemporaryFile("lldb-shell-output.%%%%%%", "", output_file_path); } - - output_file_path = ::mktemp(output_file_path_buffer); } launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false); - if (output_file_path) + if (!output_file_path.empty()) { - launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_path, false, true); + launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_path.c_str(), false, true); launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO); } else @@ -882,7 +639,7 @@ Host::RunShellCommand (const char *command, if (command_output_ptr) { command_output_ptr->clear(); - FileSpec file_spec(output_file_path, File::eOpenOptionRead); + FileSpec file_spec(output_file_path.c_str(), File::eOpenOptionRead); uint64_t file_size = file_spec.GetByteSize(); if (file_size > 0) { @@ -901,8 +658,9 @@ Host::RunShellCommand (const char *command, shell_info->can_delete.SetValue(true, eBroadcastAlways); } - if (output_file_path) - ::unlink (output_file_path); + FileSpec output_file_spec(output_file_path.c_str(), false); + if (FileSystem::GetFileExists(output_file_spec)) + FileSystem::Unlink(output_file_path.c_str()); // Handshake with the monitor thread, or just let it know in advance that // it can delete "shell_info" in case we timed out and were not able to kill // the process... @@ -914,13 +672,13 @@ Host::RunShellCommand (const char *command, // systems #if defined (__APPLE__) || defined (__linux__) || defined (__FreeBSD__) || defined (__GLIBC__) || defined(__NetBSD__) - // this method needs to be visible to macosx/Host.cpp and // common/Host.cpp. short -Host::GetPosixspawnFlags (ProcessLaunchInfo &launch_info) +Host::GetPosixspawnFlags(const ProcessLaunchInfo &launch_info) { +#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; #if defined (__APPLE__) @@ -963,12 +721,17 @@ Host::GetPosixspawnFlags (ProcessLaunchInfo &launch_info) #endif #endif // #if defined (__APPLE__) return flags; +#else + assert(false && "Host::GetPosixspawnFlags() not supported on Android"); + return 0; +#endif } Error -Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_info, ::pid_t &pid) +Host::LaunchProcessPosixSpawn(const char *exe_path, const ProcessLaunchInfo &launch_info, lldb::pid_t &pid) { Error error; +#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); posix_spawnattr_t attr; @@ -1086,6 +849,7 @@ Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_i #endif } + ::pid_t result_pid = LLDB_INVALID_PROCESS_ID; const size_t num_file_actions = launch_info.GetNumFileActions (); if (num_file_actions > 0) { @@ -1110,21 +874,13 @@ Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_i } } - error.SetError (::posix_spawnp (&pid, - exe_path, - &file_actions, - &attr, - argv, - envp), - eErrorTypePOSIX); + error.SetError(::posix_spawnp(&result_pid, exe_path, &file_actions, &attr, argv, envp), eErrorTypePOSIX); if (error.Fail() || log) { - error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", - pid, exe_path, static_cast<void*>(&file_actions), - static_cast<void*>(&attr), - reinterpret_cast<const void*>(argv), - reinterpret_cast<const void*>(envp)); + error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )", result_pid, + exe_path, static_cast<void *>(&file_actions), static_cast<void *>(&attr), reinterpret_cast<const void *>(argv), + reinterpret_cast<const void *>(envp)); if (log) { for (int ii=0; argv[ii]; ++ii) @@ -1135,20 +891,13 @@ Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_i } else { - error.SetError (::posix_spawnp (&pid, - exe_path, - NULL, - &attr, - argv, - envp), - eErrorTypePOSIX); + error.SetError(::posix_spawnp(&result_pid, exe_path, NULL, &attr, argv, envp), eErrorTypePOSIX); if (error.Fail() || log) { error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = NULL, attr = %p, argv = %p, envp = %p )", - pid, exe_path, static_cast<void*>(&attr), - reinterpret_cast<const void*>(argv), - reinterpret_cast<const void*>(envp)); + result_pid, exe_path, static_cast<void *>(&attr), reinterpret_cast<const void *>(argv), + reinterpret_cast<const void *>(envp)); if (log) { for (int ii=0; argv[ii]; ++ii) @@ -1156,6 +905,7 @@ Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_i } } } + pid = result_pid; if (working_dir) { @@ -1171,6 +921,9 @@ Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_i } #endif } +#else + error.SetErrorString("Host::LaunchProcessPosixSpawn() not supported on Android"); +#endif return error; } @@ -1178,6 +931,7 @@ Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_i bool Host::AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, Log *log, Error &error) { +#if !defined(__ANDROID__) && !defined(__ANDROID_NDK__) if (info == NULL) return false; @@ -1240,97 +994,40 @@ Host::AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, Log * break; } return error.Success(); +#else + error.SetErrorString("Host::AddPosixSpawnFileAction() not supported on Android"); + return false; +#endif } - #endif // LaunchProcedssPosixSpawn: Apple, Linux, FreeBSD and other GLIBC systems - -#if defined(__linux__) || defined(__FreeBSD__) || defined(__GLIBC__) || defined(__NetBSD__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__GLIBC__) || defined(__NetBSD__) || defined(_WIN32) // The functions below implement process launching via posix_spawn() for Linux, // FreeBSD and NetBSD. Error Host::LaunchProcess (ProcessLaunchInfo &launch_info) { - Error error; - char exe_path[PATH_MAX]; - - PlatformSP host_platform_sp (Platform::GetDefaultPlatform ()); - - const ArchSpec &arch_spec = launch_info.GetArchitecture(); - - FileSpec exe_spec(launch_info.GetExecutableFile()); - - FileSpec::FileType file_type = exe_spec.GetFileType(); - if (file_type != FileSpec::eFileTypeRegular) - { - lldb::ModuleSP exe_module_sp; - error = host_platform_sp->ResolveExecutable (exe_spec, - arch_spec, - exe_module_sp, - NULL); - - if (error.Fail()) - return error; - - if (exe_module_sp) - exe_spec = exe_module_sp->GetFileSpec(); - } - - if (exe_spec.Exists()) - { - exe_spec.GetPath (exe_path, sizeof(exe_path)); - } - else - { - launch_info.GetExecutableFile().GetPath (exe_path, sizeof(exe_path)); - error.SetErrorStringWithFormat ("executable doesn't exist: '%s'", exe_path); - return error; - } - - assert(!launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY)); - - ::pid_t pid = LLDB_INVALID_PROCESS_ID; - - error = LaunchProcessPosixSpawn(exe_path, launch_info, pid); + std::unique_ptr<ProcessLauncher> delegate_launcher; +#if defined(_WIN32) + delegate_launcher.reset(new ProcessLauncherWindows()); +#else + delegate_launcher.reset(new ProcessLauncherPosix()); +#endif + MonitoringProcessLauncher launcher(std::move(delegate_launcher)); - if (pid != LLDB_INVALID_PROCESS_ID) - { - // If all went well, then set the process ID into the launch info - launch_info.SetProcessID(pid); + Error error; + HostProcess process = launcher.LaunchProcess(launch_info, error); - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + // TODO(zturner): It would be better if the entire HostProcess were returned instead of writing + // it into this structure. + launch_info.SetProcessID(process.GetProcessId()); - // Make sure we reap any processes we spawn or we will have zombies. - if (!launch_info.MonitorProcess()) - { - const bool monitor_signals = false; - StartMonitoringChildProcess (Process::SetProcessExitStatus, - NULL, - pid, - monitor_signals); - if (log) - log->PutCString ("monitored child process with default Process::SetProcessExitStatus."); - } - else - { - if (log) - log->PutCString ("monitored child process with user-specified process monitor."); - } - } - else - { - // Invalid process ID, something didn't go well - if (error.Success()) - error.SetErrorString ("process launch failed for unknown reasons"); - } return error; } - #endif // defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) #ifndef _WIN32 - void Host::Kill(lldb::pid_t pid, int signo) { diff --git a/source/Host/common/HostInfoBase.cpp b/source/Host/common/HostInfoBase.cpp index 4eb43bfaf6ff..d65b79698384 100644 --- a/source/Host/common/HostInfoBase.cpp +++ b/source/Host/common/HostInfoBase.cpp @@ -18,7 +18,9 @@ #include "lldb/Host/HostInfoBase.h" #include "llvm/ADT/Triple.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Support/Host.h" +#include "llvm/Support/raw_ostream.h" #include <thread> @@ -54,6 +56,7 @@ struct HostInfoBaseFields FileSpec m_lldb_support_exe_dir; FileSpec m_lldb_headers_dir; FileSpec m_lldb_python_dir; + FileSpec m_lldb_clang_resource_dir; FileSpec m_lldb_system_plugin_dir; FileSpec m_lldb_user_plugin_dir; FileSpec m_lldb_tmp_dir; @@ -94,6 +97,12 @@ HostInfoBase::GetNumberCPUS() return g_fields->m_number_cpus; } +uint32_t +HostInfoBase::GetMaxThreadNameLength() +{ + return 0; +} + llvm::StringRef HostInfoBase::GetVendorString() { @@ -190,6 +199,11 @@ HostInfoBase::GetLLDBPath(lldb::PathType type, FileSpec &file_spec) if (log) log->Printf("HostInfoBase::GetLLDBPath(ePathTypePythonDir) => '%s'", g_fields->m_lldb_python_dir.GetPath().c_str()); break; + case lldb::ePathTypeClangDir: + COMPUTE_LLDB_PATH(ComputeClangDirectory, g_fields->m_lldb_clang_resource_dir) + if (log) + log->Printf("HostInfoBase::GetLLDBPath(ePathTypeClangResourceDir) => '%s'", g_fields->m_lldb_clang_resource_dir.GetPath().c_str()); + break; case lldb::ePathTypeLLDBSystemPlugins: COMPUTE_LLDB_PATH(ComputeSystemPluginsDirectory, g_fields->m_lldb_system_plugin_dir) if (log) @@ -252,19 +266,23 @@ HostInfoBase::ComputeTempFileDirectory(FileSpec &file_spec) if (!tmpdir_cstr) return false; - StreamString pid_tmpdir; - pid_tmpdir.Printf("%s/lldb", tmpdir_cstr); - if (!FileSystem::MakeDirectory(pid_tmpdir.GetString().c_str(), eFilePermissionsDirectoryDefault).Success()) + FileSpec temp_file_spec(tmpdir_cstr, false); + temp_file_spec.AppendPathComponent("lldb"); + if (!FileSystem::MakeDirectory(temp_file_spec.GetPath().c_str(), eFilePermissionsDirectoryDefault).Success()) return false; - pid_tmpdir.Printf("/%" PRIu64, Host::GetCurrentProcessID()); - if (!FileSystem::MakeDirectory(pid_tmpdir.GetString().c_str(), eFilePermissionsDirectoryDefault).Success()) + std::string pid_str; + llvm::raw_string_ostream pid_stream(pid_str); + pid_stream << Host::GetCurrentProcessID(); + temp_file_spec.AppendPathComponent(pid_stream.str().c_str()); + std::string final_path = temp_file_spec.GetPath(); + if (!FileSystem::MakeDirectory(final_path.c_str(), eFilePermissionsDirectoryDefault).Success()) return false; // Make an atexit handler to clean up the process specify LLDB temp dir // and all of its contents. ::atexit(CleanupProcessSpecificLLDBTempDir); - file_spec.GetDirectory().SetCStringWithLength(pid_tmpdir.GetString().c_str(), pid_tmpdir.GetString().size()); + file_spec.GetDirectory().SetCStringWithLength(final_path.c_str(), final_path.size()); return true; } @@ -283,6 +301,12 @@ HostInfoBase::ComputeSystemPluginsDirectory(FileSpec &file_spec) } bool +HostInfoBase::ComputeClangDirectory(FileSpec &file_spec) +{ + return false; +} + +bool HostInfoBase::ComputeUserPluginsDirectory(FileSpec &file_spec) { // TODO(zturner): Figure out how to compute the user plugins directory for all platforms. diff --git a/source/Host/common/HostNativeThreadBase.cpp b/source/Host/common/HostNativeThreadBase.cpp new file mode 100644 index 000000000000..9fea54d1e3fa --- /dev/null +++ b/source/Host/common/HostNativeThreadBase.cpp @@ -0,0 +1,82 @@ +//===-- HostNativeThreadBase.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Log.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostNativeThreadBase.h" +#include "lldb/Host/ThisThread.h" +#include "lldb/Host/ThreadLauncher.h" +#include "llvm/ADT/StringExtras.h" + +using namespace lldb; +using namespace lldb_private; + +HostNativeThreadBase::HostNativeThreadBase() + : m_thread(LLDB_INVALID_HOST_THREAD) + , m_result(0) +{ +} + +HostNativeThreadBase::HostNativeThreadBase(thread_t thread) + : m_thread(thread) + , m_result(0) +{ +} + +lldb::thread_t +HostNativeThreadBase::GetSystemHandle() const +{ + return m_thread; +} + +lldb::thread_result_t +HostNativeThreadBase::GetResult() const +{ + return m_result; +} + +bool +HostNativeThreadBase::IsJoinable() const +{ + return m_thread != LLDB_INVALID_HOST_THREAD; +} + +void +HostNativeThreadBase::Reset() +{ + m_thread = LLDB_INVALID_HOST_THREAD; + m_result = 0; +} + +lldb::thread_t +HostNativeThreadBase::Release() +{ + lldb::thread_t result = m_thread; + m_thread = LLDB_INVALID_HOST_THREAD; + m_result = 0; + + return result; +} + +lldb::thread_result_t +HostNativeThreadBase::ThreadCreateTrampoline(lldb::thread_arg_t arg) +{ + ThreadLauncher::HostThreadCreateInfo *info = (ThreadLauncher::HostThreadCreateInfo *)arg; + ThisThread::SetName(info->thread_name.c_str(), HostInfo::GetMaxThreadNameLength()); + + thread_func_t thread_fptr = info->thread_fptr; + thread_arg_t thread_arg = info->thread_arg; + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + if (log) + log->Printf("thread created"); + + delete info; + return thread_fptr(thread_arg); +} diff --git a/source/Host/common/HostProcess.cpp b/source/Host/common/HostProcess.cpp new file mode 100644 index 000000000000..58a214693a52 --- /dev/null +++ b/source/Host/common/HostProcess.cpp @@ -0,0 +1,65 @@ +//===-- HostProcess.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/HostNativeProcess.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/HostThread.h" + +using namespace lldb; +using namespace lldb_private; + +HostProcess::HostProcess() + : m_native_process(new HostNativeProcess) +{ +} + +HostProcess::HostProcess(lldb::process_t process) + : m_native_process(new HostNativeProcess(process)) +{ +} + +HostProcess::~HostProcess() +{ +} + +Error HostProcess::Terminate() +{ + return m_native_process->Terminate(); +} + +Error HostProcess::GetMainModule(FileSpec &file_spec) const +{ + return m_native_process->GetMainModule(file_spec); +} + +lldb::pid_t HostProcess::GetProcessId() const +{ + return m_native_process->GetProcessId(); +} + +bool HostProcess::IsRunning() const +{ + return m_native_process->IsRunning(); +} + +HostThread +HostProcess::StartMonitoring(HostProcess::MonitorCallback callback, void *callback_baton, bool monitor_signals) +{ + return m_native_process->StartMonitoring(callback, callback_baton, monitor_signals); +} + +HostNativeProcessBase &HostProcess::GetNativeProcess() +{ + return *m_native_process; +} + +const HostNativeProcessBase &HostProcess::GetNativeProcess() const +{ + return *m_native_process; +} diff --git a/source/Host/common/HostThread.cpp b/source/Host/common/HostThread.cpp new file mode 100644 index 000000000000..7757477126c4 --- /dev/null +++ b/source/Host/common/HostThread.cpp @@ -0,0 +1,78 @@ +//===-- HostThread.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/HostNativeThread.h" +#include "lldb/Host/HostThread.h" + +using namespace lldb; +using namespace lldb_private; + +HostThread::HostThread() + : m_native_thread(new HostNativeThread) +{ +} + +HostThread::HostThread(lldb::thread_t thread) + : m_native_thread(new HostNativeThread(thread)) +{ +} + +Error +HostThread::Join(lldb::thread_result_t *result) +{ + return m_native_thread->Join(result); +} + +Error +HostThread::Cancel() +{ + return m_native_thread->Cancel(); +} + +void +HostThread::Reset() +{ + return m_native_thread->Reset(); +} + +lldb::thread_t +HostThread::Release() +{ + return m_native_thread->Release(); +} + +bool +HostThread::IsJoinable() const +{ + return m_native_thread->IsJoinable(); +} + +HostNativeThread & +HostThread::GetNativeThread() +{ + return static_cast<HostNativeThread &>(*m_native_thread); +} + +const HostNativeThread & +HostThread::GetNativeThread() const +{ + return static_cast<const HostNativeThread &>(*m_native_thread); +} + +lldb::thread_result_t +HostThread::GetResult() const +{ + return m_native_thread->GetResult(); +} + +bool +HostThread::EqualsThread(lldb::thread_t thread) const +{ + return m_native_thread->GetSystemHandle() == thread; +} diff --git a/source/Host/common/MonitoringProcessLauncher.cpp b/source/Host/common/MonitoringProcessLauncher.cpp new file mode 100644 index 000000000000..0fad44a9ec08 --- /dev/null +++ b/source/Host/common/MonitoringProcessLauncher.cpp @@ -0,0 +1,102 @@ +//===-- ProcessLauncherWindows.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/MonitoringProcessLauncher.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/ProcessLaunchInfo.h" + +using namespace lldb; +using namespace lldb_private; + +MonitoringProcessLauncher::MonitoringProcessLauncher(std::unique_ptr<ProcessLauncher> delegate_launcher) + : m_delegate_launcher(std::move(delegate_launcher)) +{ +} + +HostProcess +MonitoringProcessLauncher::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error) +{ + ProcessLaunchInfo resolved_info(launch_info); + + error.Clear(); + char exe_path[PATH_MAX]; + + PlatformSP host_platform_sp(Platform::GetHostPlatform()); + + const ArchSpec &arch_spec = resolved_info.GetArchitecture(); + + FileSpec exe_spec(resolved_info.GetExecutableFile()); + + FileSpec::FileType file_type = exe_spec.GetFileType(); + if (file_type != FileSpec::eFileTypeRegular) + { + ModuleSpec module_spec(exe_spec, arch_spec); + lldb::ModuleSP exe_module_sp; + error = host_platform_sp->ResolveExecutable(module_spec, exe_module_sp, NULL); + + if (error.Fail()) + return HostProcess(); + + if (exe_module_sp) + exe_spec = exe_module_sp->GetFileSpec(); + } + + if (exe_spec.Exists()) + { + exe_spec.GetPath(exe_path, sizeof(exe_path)); + } + else + { + resolved_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); + error.SetErrorStringWithFormat("executable doesn't exist: '%s'", exe_path); + return HostProcess(); + } + + resolved_info.SetExecutableFile(exe_spec, false); + assert(!resolved_info.GetFlags().Test(eLaunchFlagLaunchInTTY)); + + HostProcess process = m_delegate_launcher->LaunchProcess(resolved_info, error); + + if (process.GetProcessId() != LLDB_INVALID_PROCESS_ID) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + Host::MonitorChildProcessCallback callback = launch_info.GetMonitorProcessCallback(); + + void *baton = nullptr; + bool monitor_signals = false; + if (callback) + { + // If the ProcessLaunchInfo specified a callback, use that. + baton = launch_info.GetMonitorProcessBaton(); + monitor_signals = launch_info.GetMonitorSignals(); + } + else + { + callback = Process::SetProcessExitStatus; + } + + process.StartMonitoring(callback, baton, monitor_signals); + if (log) + log->PutCString("started monitoring child process."); + } + else + { + // Invalid process ID, something didn't go well + if (error.Success()) + error.SetErrorString("process launch failed for unknown reasons"); + } + return process; +} diff --git a/source/Host/common/NativeProcessProtocol.cpp b/source/Host/common/NativeProcessProtocol.cpp index b7a77266c58c..e192f19a8896 100644 --- a/source/Host/common/NativeProcessProtocol.cpp +++ b/source/Host/common/NativeProcessProtocol.cpp @@ -13,6 +13,7 @@ #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Log.h" #include "lldb/Core/State.h" +#include "lldb/Host/Host.h" #include "lldb/Target/NativeRegisterContext.h" #include "NativeThreadProtocol.h" @@ -44,6 +45,18 @@ NativeProcessProtocol::NativeProcessProtocol (lldb::pid_t pid) : } lldb_private::Error +NativeProcessProtocol::Interrupt () +{ + Error error; +#if !defined (SIGSTOP) + error.SetErrorString ("local host does not support signaling"); + return error; +#else + return Signal (SIGSTOP); +#endif +} + +lldb_private::Error NativeProcessProtocol::GetMemoryRegionInfo (lldb::addr_t load_addr, MemoryRegionInfo &range_info) { // Default: not implemented. @@ -110,9 +123,8 @@ NativeProcessProtocol::GetThreadAtIndex (uint32_t idx) } NativeThreadProtocolSP -NativeProcessProtocol::GetThreadByID (lldb::tid_t tid) +NativeProcessProtocol::GetThreadByIDUnlocked (lldb::tid_t tid) { - Mutex::Locker locker (m_threads_mutex); for (auto thread_sp : m_threads) { if (thread_sp->GetID() == tid) @@ -121,6 +133,13 @@ NativeProcessProtocol::GetThreadByID (lldb::tid_t tid) return NativeThreadProtocolSP (); } +NativeThreadProtocolSP +NativeProcessProtocol::GetThreadByID (lldb::tid_t tid) +{ + Mutex::Locker locker (m_threads_mutex); + return GetThreadByIDUnlocked (tid); +} + bool NativeProcessProtocol::IsAlive () const { diff --git a/source/Host/common/NativeProcessProtocol.h b/source/Host/common/NativeProcessProtocol.h index 035a264e172e..19d8f353b26f 100644 --- a/source/Host/common/NativeProcessProtocol.h +++ b/source/Host/common/NativeProcessProtocol.h @@ -59,17 +59,25 @@ namespace lldb_private //------------------------------------------------------------------ /// Sends a process a UNIX signal \a signal. /// - /// Implementer note: the WillSignal ()/DidSignal () calls - /// from the Process class are not replicated here since no - /// concrete classes implemented any behavior for those and - /// put all the work in DoSignal (...). - /// /// @return /// Returns an error object. //------------------------------------------------------------------ virtual Error Signal (int signo) = 0; + //------------------------------------------------------------------ + /// Tells a process to interrupt all operations as if by a Ctrl-C. + /// + /// The default implementation will send a local host's equivalent of + /// a SIGSTOP to the process via the NativeProcessProtocol::Signal() + /// operation. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + Interrupt (); + virtual Error Kill () = 0; @@ -296,7 +304,7 @@ namespace lldb_private void SetState (lldb::StateType state, bool notify_delegates = true); - // Derived classes need not impelment this. It can be used as a + // Derived classes need not implement this. It can be used as a // hook to clear internal caches that should be invalidated when // stop ids change. // @@ -323,6 +331,9 @@ namespace lldb_private void NotifyDidExec (); + NativeThreadProtocolSP + GetThreadByIDUnlocked (lldb::tid_t tid); + private: void diff --git a/source/Host/common/NativeThreadProtocol.h b/source/Host/common/NativeThreadProtocol.h index 9b404be500b9..15ecffe8b82d 100644 --- a/source/Host/common/NativeThreadProtocol.h +++ b/source/Host/common/NativeThreadProtocol.h @@ -31,7 +31,7 @@ namespace lldb_private { } - virtual const char * + virtual std::string GetName() = 0; virtual lldb::StateType diff --git a/source/Host/common/Pipe.cpp b/source/Host/common/Pipe.cpp deleted file mode 100644 index 4db0e32c93b7..000000000000 --- a/source/Host/common/Pipe.cpp +++ /dev/null @@ -1,171 +0,0 @@ -//===-- Pipe.cpp ------------------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "lldb/Host/Pipe.h" - -#if defined(_WIN32) -#include <io.h> -#include <fcntl.h> -#else -#include <unistd.h> -#endif - -using namespace lldb_private; - -int Pipe::kInvalidDescriptor = -1; - -enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE - -Pipe::Pipe() -{ - m_fds[READ] = Pipe::kInvalidDescriptor; - m_fds[WRITE] = Pipe::kInvalidDescriptor; -} - -Pipe::~Pipe() -{ - Close(); -} - -bool -Pipe::Open() -{ - if (IsValid()) - return true; - -#ifdef _WIN32 - if (::_pipe(m_fds, 256, O_BINARY) == 0) - return true; -#else - if (::pipe(m_fds) == 0) - return true; -#endif - m_fds[READ] = Pipe::kInvalidDescriptor; - m_fds[WRITE] = Pipe::kInvalidDescriptor; - return false; -} - -int -Pipe::GetReadFileDescriptor() const -{ - return m_fds[READ]; -} - -int -Pipe::GetWriteFileDescriptor() const -{ - return m_fds[WRITE]; -} - -int -Pipe::ReleaseReadFileDescriptor() -{ - const int fd = m_fds[READ]; - m_fds[READ] = Pipe::kInvalidDescriptor; - return fd; -} - -int -Pipe::ReleaseWriteFileDescriptor() -{ - const int fd = m_fds[WRITE]; - m_fds[WRITE] = Pipe::kInvalidDescriptor; - return fd; -} - -void -Pipe::Close() -{ - CloseReadFileDescriptor(); - CloseWriteFileDescriptor(); -} - -bool -Pipe::ReadDescriptorIsValid() const -{ - return m_fds[READ] != Pipe::kInvalidDescriptor; -} - -bool -Pipe::WriteDescriptorIsValid() const -{ - return m_fds[WRITE] != Pipe::kInvalidDescriptor; -} - -bool -Pipe::IsValid() const -{ - return ReadDescriptorIsValid() && WriteDescriptorIsValid(); -} - -bool -Pipe::CloseReadFileDescriptor() -{ - if (ReadDescriptorIsValid()) - { - int err; -#ifdef _WIN32 - err = _close(m_fds[READ]); -#else - err = close(m_fds[READ]); -#endif - m_fds[READ] = Pipe::kInvalidDescriptor; - return err == 0; - } - return true; -} - -bool -Pipe::CloseWriteFileDescriptor() -{ - if (WriteDescriptorIsValid()) - { - int err; -#ifdef _WIN32 - err = _close(m_fds[WRITE]); -#else - err = close(m_fds[WRITE]); -#endif - m_fds[WRITE] = Pipe::kInvalidDescriptor; - return err == 0; - } - return true; -} - - -size_t -Pipe::Read (void *buf, size_t num_bytes) -{ - if (ReadDescriptorIsValid()) - { - const int fd = GetReadFileDescriptor(); -#ifdef _WIN32 - return _read (fd, (char *)buf, num_bytes); -#else - return read (fd, buf, num_bytes); -#endif - } - return 0; // Return 0 since errno won't be set if we didn't call read -} - -size_t -Pipe::Write (const void *buf, size_t num_bytes) -{ - if (WriteDescriptorIsValid()) - { - const int fd = GetWriteFileDescriptor(); -#ifdef _WIN32 - return _write (fd, (char *)buf, num_bytes); -#else - return write (fd, buf, num_bytes); -#endif - } - return 0; // Return 0 since errno won't be set if we didn't call write -} - diff --git a/source/Host/common/PipeBase.cpp b/source/Host/common/PipeBase.cpp new file mode 100644 index 000000000000..a9d6e6f46c86 --- /dev/null +++ b/source/Host/common/PipeBase.cpp @@ -0,0 +1,27 @@ +//===-- source/Host/common/PipeBase.cpp -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/PipeBase.h" + +using namespace lldb_private; + + +PipeBase::~PipeBase() = default; + +Error +PipeBase::OpenAsWriter(llvm::StringRef name, bool child_process_inherit) +{ + return OpenAsWriterWithTimeout(name, child_process_inherit, std::chrono::microseconds::zero()); +} + +Error +PipeBase::Read(void *buf, size_t size, size_t &bytes_read) +{ + return ReadWithTimeout(buf, size, std::chrono::microseconds::zero(), bytes_read); +} diff --git a/source/Host/common/Socket.cpp b/source/Host/common/Socket.cpp index 31e3228497ec..a6118eef7b79 100644 --- a/source/Host/common/Socket.cpp +++ b/source/Host/common/Socket.cpp @@ -18,6 +18,14 @@ #include "lldb/Host/TimeValue.h" #include "lldb/Interpreter/Args.h" +#ifdef __ANDROID_NDK__ +#include <linux/tcp.h> +#include <bits/error_constants.h> +#include <asm-generic/errno-base.h> +#include <errno.h> +#include <arpa/inet.h> +#endif + #ifndef LLDB_DISABLE_POSIX #include <arpa/inet.h> #include <netdb.h> @@ -40,6 +48,40 @@ typedef void * get_socket_option_arg_type; const NativeSocket Socket::kInvalidSocketValue = -1; #endif // #if defined(_WIN32) +#ifdef __ANDROID__ +// Android does not have SUN_LEN +#ifndef SUN_LEN +#define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) + strlen((ptr)->sun_path)) +#endif +#endif // #ifdef __ANDROID__ + +namespace { + +NativeSocket CreateSocket(const int domain, const int type, const int protocol, bool child_processes_inherit) +{ + auto socketType = type; +#ifdef SOCK_CLOEXEC + if (!child_processes_inherit) { + socketType |= SOCK_CLOEXEC; + } +#endif + return ::socket (domain, socketType, protocol); +} + +NativeSocket Accept(NativeSocket sockfd, struct sockaddr *addr, socklen_t *addrlen, bool child_processes_inherit) +{ +#ifdef SOCK_CLOEXEC + int flags = 0; + if (!child_processes_inherit) { + flags |= SOCK_CLOEXEC; + } + return ::accept4 (sockfd, addr, addrlen, flags); +#else + return ::accept (sockfd, addr, addrlen); +#endif +} +} + Socket::Socket(NativeSocket socket, SocketProtocol protocol, bool should_close) : IOObject(eFDTypeSocket, should_close) , m_protocol(protocol) @@ -53,7 +95,7 @@ Socket::~Socket() Close(); } -Error Socket::TcpConnect(llvm::StringRef host_and_port, Socket *&socket) +Error Socket::TcpConnect(llvm::StringRef host_and_port, bool child_processes_inherit, Socket *&socket) { // Store the result in a unique_ptr in case we error out, the memory will get correctly freed. std::unique_ptr<Socket> final_socket; @@ -71,7 +113,7 @@ Error Socket::TcpConnect(llvm::StringRef host_and_port, Socket *&socket) return error; // Create the socket - sock = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + sock = CreateSocket (AF_INET, SOCK_STREAM, IPPROTO_TCP, child_processes_inherit); if (sock == kInvalidSocketValue) { // TODO: On Windows, use WSAGetLastError(). @@ -125,7 +167,7 @@ Error Socket::TcpConnect(llvm::StringRef host_and_port, Socket *&socket) return error; } -Error Socket::TcpListen(llvm::StringRef host_and_port, Socket *&socket, Predicate<uint16_t>* predicate) +Error Socket::TcpListen(llvm::StringRef host_and_port, bool child_processes_inherit, Socket *&socket, Predicate<uint16_t>* predicate) { std::unique_ptr<Socket> listen_socket; NativeSocket listen_sock = kInvalidSocketValue; @@ -134,7 +176,7 @@ Error Socket::TcpListen(llvm::StringRef host_and_port, Socket *&socket, Predicat const sa_family_t family = AF_INET; const int socktype = SOCK_STREAM; const int protocol = IPPROTO_TCP; - listen_sock = ::socket (family, socktype, protocol); + listen_sock = ::CreateSocket (family, socktype, protocol, child_processes_inherit); if (listen_sock == kInvalidSocketValue) { error.SetErrorToErrno(); @@ -196,7 +238,7 @@ Error Socket::TcpListen(llvm::StringRef host_and_port, Socket *&socket, Predicat return error; } -Error Socket::BlockingAccept(llvm::StringRef host_and_port, Socket *&socket) +Error Socket::BlockingAccept(llvm::StringRef host_and_port, bool child_processes_inherit, Socket *&socket) { Error error; std::string host_str; @@ -235,7 +277,10 @@ Error Socket::BlockingAccept(llvm::StringRef host_and_port, Socket *&socket) #endif socklen_t accept_addr_len = sizeof accept_addr; - int sock = ::accept (this->GetNativeSocket(), (struct sockaddr *)&accept_addr, &accept_addr_len); + int sock = Accept (this->GetNativeSocket(), + (struct sockaddr *)&accept_addr, + &accept_addr_len, + child_processes_inherit); if (sock == kInvalidSocketValue) { @@ -280,7 +325,7 @@ Error Socket::BlockingAccept(llvm::StringRef host_and_port, Socket *&socket) } -Error Socket::UdpConnect(llvm::StringRef host_and_port, Socket *&send_socket, Socket *&recv_socket) +Error Socket::UdpConnect(llvm::StringRef host_and_port, bool child_processes_inherit, Socket *&send_socket, Socket *&recv_socket) { std::unique_ptr<Socket> final_send_socket; std::unique_ptr<Socket> final_recv_socket; @@ -300,7 +345,7 @@ Error Socket::UdpConnect(llvm::StringRef host_and_port, Socket *&send_socket, So // Setup the receiving end of the UDP connection on this localhost // on port zero. After we bind to port zero we can read the port. - final_recv_fd = ::socket (AF_INET, SOCK_DGRAM, 0); + final_recv_fd = ::CreateSocket (AF_INET, SOCK_DGRAM, 0, child_processes_inherit); if (final_recv_fd == kInvalidSocketValue) { // Socket creation failed... @@ -351,9 +396,10 @@ Error Socket::UdpConnect(llvm::StringRef host_and_port, Socket *&send_socket, So service_info_ptr != NULL; service_info_ptr = service_info_ptr->ai_next) { - final_send_fd = ::socket (service_info_ptr->ai_family, - service_info_ptr->ai_socktype, - service_info_ptr->ai_protocol); + final_send_fd = ::CreateSocket (service_info_ptr->ai_family, + service_info_ptr->ai_socktype, + service_info_ptr->ai_protocol, + child_processes_inherit); if (final_send_fd != kInvalidSocketValue) { @@ -380,7 +426,7 @@ Error Socket::UdpConnect(llvm::StringRef host_and_port, Socket *&send_socket, So return error; } -Error Socket::UnixDomainConnect(llvm::StringRef name, Socket *&socket) +Error Socket::UnixDomainConnect(llvm::StringRef name, bool child_processes_inherit, Socket *&socket) { Error error; #ifndef LLDB_DISABLE_POSIX @@ -388,7 +434,7 @@ Error Socket::UnixDomainConnect(llvm::StringRef name, Socket *&socket) // Open the socket that was passed in as an option struct sockaddr_un saddr_un; - int fd = ::socket (AF_UNIX, SOCK_STREAM, 0); + int fd = ::CreateSocket (AF_UNIX, SOCK_STREAM, 0, child_processes_inherit); if (fd == kInvalidSocketValue) { error.SetErrorToErrno(); @@ -417,7 +463,7 @@ Error Socket::UnixDomainConnect(llvm::StringRef name, Socket *&socket) return error; } -Error Socket::UnixDomainAccept(llvm::StringRef name, Socket *&socket) +Error Socket::UnixDomainAccept(llvm::StringRef name, bool child_processes_inherit, Socket *&socket) { Error error; #ifndef LLDB_DISABLE_POSIX @@ -427,7 +473,7 @@ Error Socket::UnixDomainAccept(llvm::StringRef name, Socket *&socket) NativeSocket listen_fd = kInvalidSocketValue; NativeSocket socket_fd = kInvalidSocketValue; - listen_fd = ::socket (AF_UNIX, SOCK_STREAM, 0); + listen_fd = ::CreateSocket (AF_UNIX, SOCK_STREAM, 0, child_processes_inherit); if (listen_fd == kInvalidSocketValue) { error.SetErrorToErrno(); @@ -449,7 +495,7 @@ Error Socket::UnixDomainAccept(llvm::StringRef name, Socket *&socket) { if (::listen (listen_fd, 5) == 0) { - socket_fd = ::accept (listen_fd, NULL, 0); + socket_fd = Accept (listen_fd, NULL, 0, child_processes_inherit); if (socket_fd > 0) { final_socket.reset(new Socket(socket_fd, ProtocolUnixDomain, true)); diff --git a/source/Host/common/SocketAddress.cpp b/source/Host/common/SocketAddress.cpp index a952a83185fb..6231631934df 100644 --- a/source/Host/common/SocketAddress.cpp +++ b/source/Host/common/SocketAddress.cpp @@ -214,6 +214,8 @@ SocketAddress::getaddrinfo (const char *host, int ai_protocol, int ai_flags) { + Clear (); + struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = ai_family; @@ -221,15 +223,17 @@ SocketAddress::getaddrinfo (const char *host, hints.ai_protocol = ai_protocol; hints.ai_flags = ai_flags; + bool result = false; struct addrinfo *service_info_list = NULL; int err = ::getaddrinfo (host, service, &hints, &service_info_list); if (err == 0 && service_info_list) + { *this = service_info_list; - else - Clear(); + result = IsValid (); + } :: freeaddrinfo (service_info_list); - return IsValid(); + return result; } diff --git a/source/Host/common/SoftwareBreakpoint.cpp b/source/Host/common/SoftwareBreakpoint.cpp index fe2f504ebc71..d9d1fa67156f 100644 --- a/source/Host/common/SoftwareBreakpoint.cpp +++ b/source/Host/common/SoftwareBreakpoint.cpp @@ -119,6 +119,16 @@ SoftwareBreakpoint::EnableSoftwareBreakpoint (NativeProcessProtocol &process, ll return Error ("SoftwareBreakpoint::%s failed to read memory while attempting to set breakpoint: attempted to read %lu bytes but only read %" PRIu64, __FUNCTION__, bp_opcode_size, bytes_read); } + // Log what we read. + if (log) + { + int i = 0; + for (const uint8_t *read_byte = saved_opcode_bytes; read_byte < saved_opcode_bytes + bp_opcode_size; ++read_byte) + { + log->Printf ("SoftwareBreakpoint::%s addr = 0x%" PRIx64 " ovewriting byte index %d (was 0x%x)", __FUNCTION__, addr, i++, static_cast<int> (*read_byte)); + } + } + // Write a software breakpoint in place of the original opcode. lldb::addr_t bytes_written = 0; error = process.WriteMemory (addr, bp_opcode_bytes, static_cast<lldb::addr_t> (bp_opcode_size), bytes_written); @@ -207,7 +217,7 @@ SoftwareBreakpoint::DoDisable () if (m_opcode_size > 0) { - // Clear a software breakoint instruction + // Clear a software breakpoint instruction uint8_t curr_break_op [MAX_TRAP_OPCODE_SIZE]; bool break_op_found = false; assert (m_opcode_size <= sizeof (curr_break_op)); @@ -265,7 +275,14 @@ SoftwareBreakpoint::DoDisable () { // SUCCESS if (log) + { + int i = 0; + for (const uint8_t *verify_byte = verify_opcode; verify_byte < verify_opcode + m_opcode_size; ++verify_byte) + { + log->Printf ("SoftwareBreakpoint::%s addr = 0x%" PRIx64 " replaced byte index %d with 0x%x", __FUNCTION__, m_addr, i++, static_cast<int> (*verify_byte)); + } log->Printf ("SoftwareBreakpoint::%s addr = 0x%" PRIx64 " -- SUCCESS", __FUNCTION__, m_addr); + } return error; } else diff --git a/source/Host/common/ThisThread.cpp b/source/Host/common/ThisThread.cpp new file mode 100644 index 000000000000..289ec780e9fb --- /dev/null +++ b/source/Host/common/ThisThread.cpp @@ -0,0 +1,52 @@ +//===-- ThisThread.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Error.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/ThisThread.h" + +#include "llvm/ADT/STLExtras.h" + +#include <algorithm> + +using namespace lldb; +using namespace lldb_private; + +void +ThisThread::SetName(llvm::StringRef name, int max_length) +{ + std::string truncated_name(name.data()); + + // Thread names are coming in like '<lldb.comm.debugger.edit>' and + // '<lldb.comm.debugger.editline>'. So just chopping the end of the string + // off leads to a lot of similar named threads. Go through the thread name + // and search for the last dot and use that. + + if (max_length > 0 && truncated_name.length() > static_cast<size_t>(max_length)) + { + // First see if we can get lucky by removing any initial or final braces. + std::string::size_type begin = truncated_name.find_first_not_of("(<"); + std::string::size_type end = truncated_name.find_last_not_of(")>."); + if (end - begin > static_cast<size_t>(max_length)) + { + // We're still too long. Since this is a dotted component, use everything after the last + // dot, up to a maximum of |length| characters. + std::string::size_type last_dot = truncated_name.find_last_of("."); + if (last_dot != std::string::npos) + begin = last_dot + 1; + + end = std::min(end, begin + max_length); + } + + std::string::size_type count = end - begin + 1; + truncated_name = truncated_name.substr(begin, count); + } + + SetName(truncated_name.c_str()); +} diff --git a/source/Host/common/ThreadLauncher.cpp b/source/Host/common/ThreadLauncher.cpp new file mode 100644 index 000000000000..ec7da325bf92 --- /dev/null +++ b/source/Host/common/ThreadLauncher.cpp @@ -0,0 +1,74 @@ +//===-- ThreadLauncher.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// lldb Includes +#include "lldb/Core/Log.h" +#include "lldb/Host/HostNativeThread.h" +#include "lldb/Host/HostThread.h" +#include "lldb/Host/ThisThread.h" +#include "lldb/Host/ThreadLauncher.h" + +#if defined(_WIN32) +#include "lldb/Host/windows/windows.h" +#endif + +using namespace lldb; +using namespace lldb_private; + +HostThread +ThreadLauncher::LaunchThread(llvm::StringRef name, lldb::thread_func_t thread_function, lldb::thread_arg_t thread_arg, Error *error_ptr, size_t min_stack_byte_size) +{ + Error error; + if (error_ptr) + error_ptr->Clear(); + + // Host::ThreadCreateTrampoline will delete this pointer for us. + HostThreadCreateInfo *info_ptr = new HostThreadCreateInfo(name.data(), thread_function, thread_arg); + lldb::thread_t thread; +#ifdef _WIN32 + thread = + (lldb::thread_t)::_beginthreadex(0, (unsigned)min_stack_byte_size, HostNativeThread::ThreadCreateTrampoline, info_ptr, 0, NULL); + if (thread == (lldb::thread_t)(-1L)) + error.SetError(::GetLastError(), eErrorTypeWin32); +#else + + pthread_attr_t *thread_attr_ptr = NULL; + pthread_attr_t thread_attr; + bool destroy_attr = false; + if (min_stack_byte_size > 0) + { + if (::pthread_attr_init (&thread_attr) == 0) + { + destroy_attr = true; + size_t default_min_stack_byte_size = 0; + if (::pthread_attr_getstacksize(&thread_attr, &default_min_stack_byte_size) == 0) + { + if (default_min_stack_byte_size < min_stack_byte_size) + { + if (::pthread_attr_setstacksize (&thread_attr, min_stack_byte_size) == 0) + thread_attr_ptr = &thread_attr; + } + } + + } + } + int err = ::pthread_create(&thread, thread_attr_ptr, HostNativeThread::ThreadCreateTrampoline, info_ptr); + + if (destroy_attr) + ::pthread_attr_destroy(&thread_attr); + + error.SetError(err, eErrorTypePOSIX); +#endif + if (error_ptr) + *error_ptr = error; + if (!error.Success()) + thread = LLDB_INVALID_HOST_THREAD; + + return HostThread(thread); +} diff --git a/source/Host/freebsd/Host.cpp b/source/Host/freebsd/Host.cpp index dc092e86d78b..2cbf4d8f4696 100644 --- a/source/Host/freebsd/Host.cpp +++ b/source/Host/freebsd/Host.cpp @@ -50,80 +50,6 @@ extern "C" { using namespace lldb; using namespace lldb_private; -class FreeBSDThread -{ -public: - FreeBSDThread(const char *thread_name) - { - Host::SetThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name); - } - static void PThreadDestructor (void *v) - { - delete (FreeBSDThread*)v; - } -}; - -static pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT; -static pthread_key_t g_thread_create_key = 0; - -static void -InitThreadCreated() -{ - ::pthread_key_create (&g_thread_create_key, FreeBSDThread::PThreadDestructor); -} - -void -Host::ThreadCreated (const char *thread_name) -{ - ::pthread_once (&g_thread_create_once, InitThreadCreated); - if (g_thread_create_key) - { - ::pthread_setspecific (g_thread_create_key, new FreeBSDThread(thread_name)); - } - - Host::SetShortThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name, 16); -} - -std::string -Host::GetThreadName (lldb::pid_t pid, lldb::tid_t tid) -{ - struct kinfo_proc *kp = nullptr, *nkp; - size_t len = 0; - int error; - int name[4] = { - CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_INC_THREAD, (int)pid - }; - - while (1) { - error = sysctl(name, 4, kp, &len, nullptr, 0); - if (kp == nullptr || (error != 0 && errno == ENOMEM)) { - // Add extra space in case threads are added before next call. - len += sizeof(*kp) + len / 10; - nkp = (struct kinfo_proc *)realloc(kp, len); - if (nkp == nullptr) - { - free(kp); - return std::string(); - } - kp = nkp; - continue; - } - if (error != 0) - len = 0; - break; - } - - std::string thread_name; - for (size_t i = 0; i < len / sizeof(*kp); i++) { - if (kp[i].ki_tid == (int)tid) { - thread_name = kp[i].ki_tdname; - break; - } - } - free(kp); - return thread_name; -} - void Host::Backtrace (Stream &strm, uint32_t max_frames) { @@ -363,43 +289,19 @@ Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) lldb::DataBufferSP Host::GetAuxvData(lldb_private::Process *process) { - int mib[2] = { CTL_KERN, KERN_PS_STRINGS }; - void *ps_strings_addr, *auxv_addr; - size_t ps_strings_size = sizeof(void *); - Elf_Auxinfo aux_info[AT_COUNT]; - struct ps_strings ps_strings; - struct ptrace_io_desc pid; + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_AUXV, 0 }; + size_t auxv_size = AT_COUNT * sizeof(Elf_Auxinfo); DataBufferSP buf_sp; - std::unique_ptr<DataBufferHeap> buf_ap(new DataBufferHeap(1024, 0)); - - if (::sysctl(mib, 2, &ps_strings_addr, &ps_strings_size, NULL, 0) == 0) { - pid.piod_op = PIOD_READ_D; - pid.piod_addr = &ps_strings; - pid.piod_offs = ps_strings_addr; - pid.piod_len = sizeof(ps_strings); - if (::ptrace(PT_IO, process->GetID(), (caddr_t)&pid, 0)) { - perror("failed to fetch ps_strings"); - buf_ap.release(); - goto done; - } - - auxv_addr = ps_strings.ps_envstr + ps_strings.ps_nenvstr + 1; - - pid.piod_addr = aux_info; - pid.piod_offs = auxv_addr; - pid.piod_len = sizeof(aux_info); - if (::ptrace(PT_IO, process->GetID(), (caddr_t)&pid, 0)) { - perror("failed to fetch aux_info"); - buf_ap.release(); - goto done; - } - memcpy(buf_ap->GetBytes(), aux_info, pid.piod_len); + + std::unique_ptr<DataBufferHeap> buf_ap(new DataBufferHeap(auxv_size, 0)); + + mib[3] = process->GetID(); + if (::sysctl(mib, 4, buf_ap->GetBytes(), &auxv_size, NULL, 0) == 0) { buf_sp.reset(buf_ap.release()); } else { - perror("sysctl failed on ps_strings"); + perror("sysctl failed on auxv"); } - done: return buf_sp; } diff --git a/source/Host/freebsd/HostInfoFreeBSD.cpp b/source/Host/freebsd/HostInfoFreeBSD.cpp index 78458259f0a2..d51109320c3b 100644 --- a/source/Host/freebsd/HostInfoFreeBSD.cpp +++ b/source/Host/freebsd/HostInfoFreeBSD.cpp @@ -17,6 +17,12 @@ using namespace lldb_private; +uint32_t +HostInfoFreeBSD::GetMaxThreadNameLength() +{ + return 16; +} + bool HostInfoFreeBSD::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) { diff --git a/source/Host/freebsd/HostThreadFreeBSD.cpp b/source/Host/freebsd/HostThreadFreeBSD.cpp new file mode 100644 index 000000000000..7d611bb6894b --- /dev/null +++ b/source/Host/freebsd/HostThreadFreeBSD.cpp @@ -0,0 +1,77 @@ +//===-- HostThreadFreeBSD.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// lldb Includes +#include "lldb/Host/freebsd/HostThreadFreeBSD.h" +#include "lldb/Host/Host.h" + +// C includes +#include <errno.h> +#include <pthread.h> +#include <pthread_np.h> +#include <stdlib.h> +#include <sys/sysctl.h> +#include <sys/user.h> + +// C++ includes +#include <string> + +using namespace lldb_private; + +HostThreadFreeBSD::HostThreadFreeBSD() +{ +} + +HostThreadFreeBSD::HostThreadFreeBSD(lldb::thread_t thread) + : HostThreadPosix(thread) +{ +} + +void +HostThreadFreeBSD::GetName(lldb::tid_t tid, llvm::SmallVectorImpl<char> &name) +{ + name.clear(); + int pid = Host::GetCurrentProcessID(); + + struct kinfo_proc *kp = nullptr, *nkp; + size_t len = 0; + int error; + int ctl[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_INC_THREAD, (int)pid}; + + while (1) + { + error = sysctl(ctl, 4, kp, &len, nullptr, 0); + if (kp == nullptr || (error != 0 && errno == ENOMEM)) + { + // Add extra space in case threads are added before next call. + len += sizeof(*kp) + len / 10; + nkp = (struct kinfo_proc *)realloc(kp, len); + if (nkp == nullptr) + { + free(kp); + return; + } + kp = nkp; + continue; + } + if (error != 0) + len = 0; + break; + } + + for (size_t i = 0; i < len / sizeof(*kp); i++) + { + if (kp[i].ki_tid == (lwpid_t)tid) + { + name.append(kp[i].ki_tdname, kp[i].ki_tdname + strlen(kp[i].ki_tdname)); + break; + } + } + free(kp); +} diff --git a/source/Host/freebsd/ThisThread.cpp b/source/Host/freebsd/ThisThread.cpp new file mode 100644 index 000000000000..fb25847be24f --- /dev/null +++ b/source/Host/freebsd/ThisThread.cpp @@ -0,0 +1,30 @@ +//===-- ThisThread.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/HostNativeThread.h" +#include "lldb/Host/ThisThread.h" + +#include "llvm/ADT/SmallVector.h" + +#include <pthread.h> +#include <pthread_np.h> + +using namespace lldb_private; + +void +ThisThread::SetName(llvm::StringRef name) +{ + ::pthread_set_name_np(::pthread_self(), name.data()); +} + +void +ThisThread::GetName(llvm::SmallVectorImpl<char> &name) +{ + HostNativeThread::GetName(::pthread_getthreadid_np(), name); +} diff --git a/source/Core/ConnectionFileDescriptor.cpp b/source/Host/posix/ConnectionFileDescriptorPosix.cpp index 7c8e98a21129..38ddc0a49220 100644 --- a/source/Core/ConnectionFileDescriptor.cpp +++ b/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -1,4 +1,4 @@ -//===-- ConnectionFileDescriptor.cpp ----------------------------*- C++ -*-===// +//===-- ConnectionFileDescriptorPosix.cpp -----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -14,7 +14,7 @@ #define _DARWIN_UNLIMITED_SELECT #endif -#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" #include "lldb/Host/Config.h" #include "lldb/Host/IOObject.h" #include "lldb/Host/SocketAddress.h" @@ -46,99 +46,94 @@ #include "lldb/Host/Socket.h" #include "lldb/Interpreter/Args.h" - using namespace lldb; using namespace lldb_private; -ConnectionFileDescriptor::ConnectionFileDescriptor () : - Connection(), - m_pipe (), - m_mutex (Mutex::eMutexTypeRecursive), - m_shutting_down (false), - m_waiting_for_accept (false) +ConnectionFileDescriptor::ConnectionFileDescriptor(bool child_processes_inherit) + : Connection() + , m_pipe() + , m_mutex(Mutex::eMutexTypeRecursive) + , m_shutting_down(false) + , m_waiting_for_accept(false) + , m_child_processes_inherit(child_processes_inherit) { - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); if (log) - log->Printf ("%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", - static_cast<void*>(this)); + log->Printf("%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", static_cast<void *>(this)); } -ConnectionFileDescriptor::ConnectionFileDescriptor (int fd, bool owns_fd) : - Connection(), - m_pipe (), - m_mutex (Mutex::eMutexTypeRecursive), - m_shutting_down (false), - m_waiting_for_accept (false) +ConnectionFileDescriptor::ConnectionFileDescriptor(int fd, bool owns_fd) + : Connection() + , m_pipe() + , m_mutex(Mutex::eMutexTypeRecursive) + , m_shutting_down(false) + , m_waiting_for_accept(false) + , m_child_processes_inherit(false) { m_write_sp.reset(new File(fd, owns_fd)); m_read_sp.reset(new File(fd, false)); - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); if (log) - log->Printf ("%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = %i, owns_fd = %i)", - static_cast<void*>(this), fd, owns_fd); - OpenCommandPipe (); + log->Printf("%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = %i, owns_fd = %i)", static_cast<void *>(this), fd, + owns_fd); + OpenCommandPipe(); } - -ConnectionFileDescriptor::~ConnectionFileDescriptor () +ConnectionFileDescriptor::~ConnectionFileDescriptor() { - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); if (log) - log->Printf ("%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", - static_cast<void*>(this)); - Disconnect (NULL); - CloseCommandPipe (); + log->Printf("%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", static_cast<void *>(this)); + Disconnect(NULL); + CloseCommandPipe(); } void -ConnectionFileDescriptor::OpenCommandPipe () +ConnectionFileDescriptor::OpenCommandPipe() { CloseCommandPipe(); - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); // Make the command file descriptor here: - if (!m_pipe.Open()) + Error result = m_pipe.CreateNew(m_child_processes_inherit); + if (!result.Success()) { if (log) - log->Printf ("%p ConnectionFileDescriptor::OpenCommandPipe () - could not make pipe: %s", - static_cast<void*>(this), strerror(errno)); + log->Printf("%p ConnectionFileDescriptor::OpenCommandPipe () - could not make pipe: %s", static_cast<void *>(this), + result.AsCString()); } else { if (log) - log->Printf ("%p ConnectionFileDescriptor::OpenCommandPipe() - success readfd=%d writefd=%d", - static_cast<void*>(this), - m_pipe.GetReadFileDescriptor(), - m_pipe.GetWriteFileDescriptor()); + log->Printf("%p ConnectionFileDescriptor::OpenCommandPipe() - success readfd=%d writefd=%d", static_cast<void *>(this), + m_pipe.GetReadFileDescriptor(), m_pipe.GetWriteFileDescriptor()); } } void -ConnectionFileDescriptor::CloseCommandPipe () +ConnectionFileDescriptor::CloseCommandPipe() { - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) - log->Printf ("%p ConnectionFileDescriptor::CloseCommandPipe()", - static_cast<void*>(this)); + log->Printf("%p ConnectionFileDescriptor::CloseCommandPipe()", static_cast<void *>(this)); m_pipe.Close(); } bool -ConnectionFileDescriptor::IsConnected () const +ConnectionFileDescriptor::IsConnected() const { return (m_read_sp && m_read_sp->IsValid()) || (m_write_sp && m_write_sp->IsValid()); } ConnectionStatus -ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) +ConnectionFileDescriptor::Connect(const char *s, Error *error_ptr) { - Mutex::Locker locker (m_mutex); - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + Mutex::Locker locker(m_mutex); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) - log->Printf ("%p ConnectionFileDescriptor::Connect (url = '%s')", - static_cast<void*>(this), s); + log->Printf("%p ConnectionFileDescriptor::Connect (url = '%s')", static_cast<void *>(this), s); OpenCommandPipe(); @@ -147,54 +142,51 @@ ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) if (strstr(s, "listen://") == s) { // listen://HOST:PORT - return SocketListen (s + strlen("listen://"), error_ptr); + return SocketListen(s + strlen("listen://"), error_ptr); } else if (strstr(s, "accept://") == s) { // unix://SOCKNAME - return NamedSocketAccept (s + strlen("accept://"), error_ptr); + return NamedSocketAccept(s + strlen("accept://"), error_ptr); } else if (strstr(s, "unix-accept://") == s) { // unix://SOCKNAME - return NamedSocketAccept (s + strlen("unix-accept://"), error_ptr); + return NamedSocketAccept(s + strlen("unix-accept://"), error_ptr); } else if (strstr(s, "connect://") == s) { - return ConnectTCP (s + strlen("connect://"), error_ptr); + return ConnectTCP(s + strlen("connect://"), error_ptr); } else if (strstr(s, "tcp-connect://") == s) { - return ConnectTCP (s + strlen("tcp-connect://"), error_ptr); + return ConnectTCP(s + strlen("tcp-connect://"), error_ptr); } else if (strstr(s, "udp://") == s) { - return ConnectUDP (s + strlen("udp://"), error_ptr); + return ConnectUDP(s + strlen("udp://"), error_ptr); } #ifndef LLDB_DISABLE_POSIX else if (strstr(s, "fd://") == s) { - if (error_ptr) - error_ptr->SetErrorStringWithFormat ("Protocol is not supported on non-posix hosts '%s'", s); - return eConnectionStatusError; // Just passing a native file descriptor within this current process // that is already opened (possibly from a service or other source). - s += strlen ("fd://"); + s += strlen("fd://"); bool success = false; - int fd = Args::StringToSInt32 (s, -1, 0, &success); + int fd = Args::StringToSInt32(s, -1, 0, &success); if (success) { - // We have what looks to be a valid file descriptor, but we + // We have what looks to be a valid file descriptor, but we // should make sure it is. We currently are doing this by trying to - // get the flags from the file descriptor and making sure it + // get the flags from the file descriptor and making sure it // isn't a bad fd. errno = 0; - int flags = ::fcntl (fd, F_GETFL, 0); + int flags = ::fcntl(fd, F_GETFL, 0); if (flags == -1 || errno == EBADF) { if (error_ptr) - error_ptr->SetErrorStringWithFormat ("stale file descriptor: %s", s); + error_ptr->SetErrorStringWithFormat("stale file descriptor: %s", s); m_read_sp.reset(); m_write_sp.reset(); return eConnectionStatusError; @@ -203,17 +195,17 @@ ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) { // Don't take ownership of a file descriptor that gets passed // to us since someone else opened the file descriptor and - // handed it to us. - // TODO: Since are using a URL to open connection we should + // handed it to us. + // TODO: Since are using a URL to open connection we should // eventually parse options using the web standard where we // have "fd://123?opt1=value;opt2=value" and we can have an // option be "owns=1" or "owns=0" or something like this to - // allow us to specify this. For now, we assume we must + // allow us to specify this. For now, we assume we must // assume we don't own it. std::unique_ptr<Socket> tcp_socket; tcp_socket.reset(new Socket(fd, Socket::ProtocolTcp, false)); - // Try and get a socket option from this file descriptor to + // Try and get a socket option from this file descriptor to // see if this is a socket and set m_is_socket accordingly. int resuse; bool is_socket = !!tcp_socket->GetOption(SOL_SOCKET, SO_REUSEADDR, resuse); @@ -232,7 +224,7 @@ ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) } if (error_ptr) - error_ptr->SetErrorStringWithFormat ("invalid file descriptor: \"fd://%s\"", s); + error_ptr->SetErrorStringWithFormat("invalid file descriptor: \"fd://%s\"", s); m_read_sp.reset(); m_write_sp.reset(); return eConnectionStatusError; @@ -244,7 +236,7 @@ ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) int fd = -1; do { - fd = ::open (path, O_RDWR); + fd = ::open(path, O_RDWR); } while (fd == -1 && errno == EINTR); if (fd == -1) @@ -258,29 +250,29 @@ ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) { // Set up serial terminal emulation struct termios options; - ::tcgetattr (fd, &options); + ::tcgetattr(fd, &options); // Set port speed to maximum - ::cfsetospeed (&options, B115200); - ::cfsetispeed (&options, B115200); + ::cfsetospeed(&options, B115200); + ::cfsetispeed(&options, B115200); // Raw input, disable echo and signals options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // Make sure only one character is needed to return from a read - options.c_cc[VMIN] = 1; + options.c_cc[VMIN] = 1; options.c_cc[VTIME] = 0; - ::tcsetattr (fd, TCSANOW, &options); + ::tcsetattr(fd, TCSANOW, &options); } - int flags = ::fcntl (fd, F_GETFL, 0); + int flags = ::fcntl(fd, F_GETFL, 0); if (flags >= 0) { if ((flags & O_NONBLOCK) == 0) { flags |= O_NONBLOCK; - ::fcntl (fd, F_SETFL, flags); + ::fcntl(fd, F_SETFL, flags); } } m_read_sp.reset(new File(fd, true)); @@ -289,7 +281,7 @@ ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) } #endif if (error_ptr) - error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s); + error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'", s); return eConnectionStatusError; } if (error_ptr) @@ -300,29 +292,29 @@ ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) bool ConnectionFileDescriptor::InterruptRead() { - return m_pipe.Write("i", 1) == 1; + size_t bytes_written = 0; + Error result = m_pipe.Write("i", 1, bytes_written); + return result.Success(); } ConnectionStatus -ConnectionFileDescriptor::Disconnect (Error *error_ptr) +ConnectionFileDescriptor::Disconnect(Error *error_ptr) { - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) - log->Printf ("%p ConnectionFileDescriptor::Disconnect ()", - static_cast<void*>(this)); + log->Printf("%p ConnectionFileDescriptor::Disconnect ()", static_cast<void *>(this)); ConnectionStatus status = eConnectionStatusSuccess; if (!IsConnected()) { if (log) - log->Printf ("%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect", - static_cast<void*>(this)); + log->Printf("%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect", static_cast<void *>(this)); return eConnectionStatusSuccess; } if (m_read_sp && m_read_sp->IsValid() && m_read_sp->GetFdType() == IOObject::eFDTypeSocket) - static_cast<Socket&>(*m_read_sp).PreDisconnect(); + static_cast<Socket &>(*m_read_sp).PreDisconnect(); // Try to get the ConnectionFileDescriptor's mutex. If we fail, that is quite likely // because somebody is doing a blocking read on our file descriptor. If that's the case, @@ -332,24 +324,24 @@ ConnectionFileDescriptor::Disconnect (Error *error_ptr) m_shutting_down = true; Mutex::Locker locker; - bool got_lock = locker.TryLock (m_mutex); + bool got_lock = locker.TryLock(m_mutex); if (!got_lock) { - if (m_pipe.WriteDescriptorIsValid()) + if (m_pipe.CanWrite()) { - int result; - result = m_pipe.Write("q", 1) == 1; + size_t bytes_written = 0; + Error result = m_pipe.Write("q", 1, bytes_written); if (log) - log->Printf ("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the lock, sent 'q' to %d, result = %d.", - static_cast<void*>(this), m_pipe.GetWriteFileDescriptor(), result); + log->Printf("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the lock, sent 'q' to %d, error = '%s'.", + static_cast<void *>(this), m_pipe.GetWriteFileDescriptor(), result.AsCString()); } else if (log) { - log->Printf ("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the lock, but no command pipe is available.", - static_cast<void*>(this)); + log->Printf("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the lock, but no command pipe is available.", + static_cast<void *>(this)); } - locker.Lock (m_mutex); + locker.Lock(m_mutex); } Error error = m_read_sp->Close(); @@ -364,23 +356,18 @@ ConnectionFileDescriptor::Disconnect (Error *error_ptr) } size_t -ConnectionFileDescriptor::Read (void *dst, - size_t dst_len, - uint32_t timeout_usec, - ConnectionStatus &status, - Error *error_ptr) +ConnectionFileDescriptor::Read(void *dst, size_t dst_len, uint32_t timeout_usec, ConnectionStatus &status, Error *error_ptr) { - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); Mutex::Locker locker; - bool got_lock = locker.TryLock (m_mutex); + bool got_lock = locker.TryLock(m_mutex); if (!got_lock) { if (log) - log->Printf ("%p ConnectionFileDescriptor::Read () failed to get the connection lock.", - static_cast<void*>(this)); + log->Printf("%p ConnectionFileDescriptor::Read () failed to get the connection lock.", static_cast<void *>(this)); if (error_ptr) - error_ptr->SetErrorString ("failed to get the connection lock for read."); + error_ptr->SetErrorString("failed to get the connection lock for read."); status = eConnectionStatusTimedOut; return 0; @@ -388,7 +375,7 @@ ConnectionFileDescriptor::Read (void *dst, else if (m_shutting_down) return eConnectionStatusError; - status = BytesAvailable (timeout_usec, error_ptr); + status = BytesAvailable(timeout_usec, error_ptr); if (status != eConnectionStatusSuccess) return 0; @@ -399,12 +386,8 @@ ConnectionFileDescriptor::Read (void *dst, if (log) { log->Printf("%p ConnectionFileDescriptor::Read() fd = %" PRIu64 ", dst = %p, dst_len = %" PRIu64 ") => %" PRIu64 ", error = %s", - static_cast<void*>(this), - static_cast<uint64_t>(m_read_sp->GetWaitableHandle()), - static_cast<void*>(dst), - static_cast<uint64_t>(dst_len), - static_cast<uint64_t>(bytes_read), - error.AsCString()); + static_cast<void *>(this), static_cast<uint64_t>(m_read_sp->GetWaitableHandle()), static_cast<void *>(dst), + static_cast<uint64_t>(dst_len), static_cast<uint64_t>(bytes_read), error.AsCString()); } if (bytes_read == 0) @@ -421,49 +404,48 @@ ConnectionFileDescriptor::Read (void *dst, uint32_t error_value = error.GetError(); switch (error_value) { - case EAGAIN: // The file was marked for non-blocking I/O, and no data were ready to be read. - if (m_read_sp->GetFdType() == IOObject::eFDTypeSocket) + case EAGAIN: // The file was marked for non-blocking I/O, and no data were ready to be read. + if (m_read_sp->GetFdType() == IOObject::eFDTypeSocket) + status = eConnectionStatusTimedOut; + else + status = eConnectionStatusSuccess; + return 0; + + case EFAULT: // Buf points outside the allocated address space. + case EINTR: // A read from a slow device was interrupted before any data arrived by the delivery of a signal. + case EINVAL: // The pointer associated with fildes was negative. + case EIO: // An I/O error occurred while reading from the file system. + // The process group is orphaned. + // The file is a regular file, nbyte is greater than 0, + // the starting position is before the end-of-file, and + // the starting position is greater than or equal to the + // offset maximum established for the open file + // descriptor associated with fildes. + case EISDIR: // An attempt is made to read a directory. + case ENOBUFS: // An attempt to allocate a memory buffer fails. + case ENOMEM: // Insufficient memory is available. + status = eConnectionStatusError; + break; // Break to close.... + + case ENOENT: // no such file or directory + case EBADF: // fildes is not a valid file or socket descriptor open for reading. + case ENXIO: // An action is requested of a device that does not exist.. + // A requested action cannot be performed by the device. + case ECONNRESET: // The connection is closed by the peer during a read attempt on a socket. + case ENOTCONN: // A read is attempted on an unconnected socket. + status = eConnectionStatusLostConnection; + break; // Break to close.... + + case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a socket. status = eConnectionStatusTimedOut; - else - status = eConnectionStatusSuccess; - return 0; - - case EFAULT: // Buf points outside the allocated address space. - case EINTR: // A read from a slow device was interrupted before any data arrived by the delivery of a signal. - case EINVAL: // The pointer associated with fildes was negative. - case EIO: // An I/O error occurred while reading from the file system. - // The process group is orphaned. - // The file is a regular file, nbyte is greater than 0, - // the starting position is before the end-of-file, and - // the starting position is greater than or equal to the - // offset maximum established for the open file - // descriptor associated with fildes. - case EISDIR: // An attempt is made to read a directory. - case ENOBUFS: // An attempt to allocate a memory buffer fails. - case ENOMEM: // Insufficient memory is available. - status = eConnectionStatusError; - break; // Break to close.... - - case ENOENT: // no such file or directory - case EBADF: // fildes is not a valid file or socket descriptor open for reading. - case ENXIO: // An action is requested of a device that does not exist.. - // A requested action cannot be performed by the device. - case ECONNRESET:// The connection is closed by the peer during a read attempt on a socket. - case ENOTCONN: // A read is attempted on an unconnected socket. - status = eConnectionStatusLostConnection; - break; // Break to close.... - - case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a socket. - status = eConnectionStatusTimedOut; - return 0; - - default: - if (log) - log->Printf("%p ConnectionFileDescriptor::Read (), unexpected error: %s", - static_cast<void*>(this), strerror(error_value)); - status = eConnectionStatusError; - break; // Break to close.... - + return 0; + + default: + if (log) + log->Printf("%p ConnectionFileDescriptor::Read (), unexpected error: %s", static_cast<void *>(this), + strerror(error_value)); + status = eConnectionStatusError; + break; // Break to close.... } return 0; @@ -472,15 +454,14 @@ ConnectionFileDescriptor::Read (void *dst, } size_t -ConnectionFileDescriptor::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) +ConnectionFileDescriptor::Write(const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) { - Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) - log->Printf ("%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64 ")", - static_cast<void*>(this), static_cast<const void*>(src), - static_cast<uint64_t>(src_len)); + log->Printf("%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64 ")", static_cast<void *>(this), + static_cast<const void *>(src), static_cast<uint64_t>(src_len)); - if (!IsConnected ()) + if (!IsConnected()) { if (error_ptr) error_ptr->SetErrorString("not connected"); @@ -488,7 +469,6 @@ ConnectionFileDescriptor::Write (const void *src, size_t src_len, ConnectionStat return 0; } - Error error; size_t bytes_sent = src_len; @@ -496,13 +476,9 @@ ConnectionFileDescriptor::Write (const void *src, size_t src_len, ConnectionStat if (log) { - log->Printf ("%p ConnectionFileDescriptor::Write(fd = %" PRIu64 ", src = %p, src_len = %" PRIu64 ") => %" PRIu64 " (error = %s)", - static_cast<void*>(this), - static_cast<uint64_t>(m_write_sp->GetWaitableHandle()), - static_cast<const void*>(src), - static_cast<uint64_t>(src_len), - static_cast<uint64_t>(bytes_sent), - error.AsCString()); + log->Printf("%p ConnectionFileDescriptor::Write(fd = %" PRIu64 ", src = %p, src_len = %" PRIu64 ") => %" PRIu64 " (error = %s)", + static_cast<void *>(this), static_cast<uint64_t>(m_write_sp->GetWaitableHandle()), static_cast<const void *>(src), + static_cast<uint64_t>(src_len), static_cast<uint64_t>(bytes_sent), error.AsCString()); } if (error_ptr) @@ -512,19 +488,19 @@ ConnectionFileDescriptor::Write (const void *src, size_t src_len, ConnectionStat { switch (error.GetError()) { - case EAGAIN: - case EINTR: - status = eConnectionStatusSuccess; - return 0; - - case ECONNRESET:// The connection is closed by the peer during a read attempt on a socket. - case ENOTCONN: // A read is attempted on an unconnected socket. - status = eConnectionStatusLostConnection; - break; // Break to close.... - - default: - status = eConnectionStatusError; - break; // Break to close.... + case EAGAIN: + case EINTR: + status = eConnectionStatusSuccess; + return 0; + + case ECONNRESET: // The connection is closed by the peer during a read attempt on a socket. + case ENOTCONN: // A read is attempted on an unconnected socket. + status = eConnectionStatusLostConnection; + break; // Break to close.... + + default: + status = eConnectionStatusError; + break; // Break to close.... } return 0; @@ -534,8 +510,6 @@ ConnectionFileDescriptor::Write (const void *src, size_t src_len, ConnectionStat return bytes_sent; } - - // This ConnectionFileDescriptor::BytesAvailable() uses select(). // // PROS: @@ -557,15 +531,14 @@ ConnectionFileDescriptor::Write (const void *src, size_t src_len, ConnectionStat #endif ConnectionStatus -ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +ConnectionFileDescriptor::BytesAvailable(uint32_t timeout_usec, Error *error_ptr) { // Don't need to take the mutex here separately since we are only called from Read. If we // ever get used more generally we will need to lock here as well. - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_CONNECTION)); + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) - log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", - static_cast<void*>(this), timeout_usec); + log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", static_cast<void *>(this), timeout_usec); struct timeval *tv_ptr; struct timeval tv; @@ -577,7 +550,7 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt else { TimeValue time_value; - time_value.OffsetWithMicroSeconds (timeout_usec); + time_value.OffsetWithMicroSeconds(timeout_usec); tv.tv_sec = time_value.seconds(); tv.tv_usec = time_value.microseconds(); tv_ptr = &tv; @@ -599,9 +572,9 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt #else const bool have_pipe_fd = pipe_fd >= 0; #if !defined(__APPLE__) - assert (handle < FD_SETSIZE); + assert(handle < FD_SETSIZE); if (have_pipe_fd) - assert (pipe_fd < FD_SETSIZE); + assert(pipe_fd < FD_SETSIZE); #endif #endif while (handle == m_read_sp->GetWaitableHandle()) @@ -609,35 +582,34 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt const int nfds = std::max<int>(handle, pipe_fd) + 1; #if defined(__APPLE__) llvm::SmallVector<fd_set, 1> read_fds; - read_fds.resize((nfds/FD_SETSIZE) + 1); - for (size_t i=0; i<read_fds.size(); ++i) - FD_ZERO (&read_fds[i]); - // FD_SET doesn't bounds check, it just happily walks off the end - // but we have taken care of making the extra storage with our - // SmallVector of fd_set objects + read_fds.resize((nfds / FD_SETSIZE) + 1); + for (size_t i = 0; i < read_fds.size(); ++i) + FD_ZERO(&read_fds[i]); +// FD_SET doesn't bounds check, it just happily walks off the end +// but we have taken care of making the extra storage with our +// SmallVector of fd_set objects #else fd_set read_fds; - FD_ZERO (&read_fds); + FD_ZERO(&read_fds); #endif - FD_SET (handle, FD_SET_DATA(read_fds)); + FD_SET(handle, FD_SET_DATA(read_fds)); if (have_pipe_fd) - FD_SET (pipe_fd, FD_SET_DATA(read_fds)); + FD_SET(pipe_fd, FD_SET_DATA(read_fds)); Error error; if (log) { if (have_pipe_fd) - log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...", - static_cast<void*>(this), nfds, handle, pipe_fd, - static_cast<void*>(tv_ptr)); + log->Printf( + "%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...", + static_cast<void *>(this), nfds, handle, pipe_fd, static_cast<void *>(tv_ptr)); else log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...", - static_cast<void*>(this), nfds, handle, - static_cast<void*>(tv_ptr)); + static_cast<void *>(this), nfds, handle, static_cast<void *>(tv_ptr)); } - const int num_set_fds = ::select (nfds, FD_SET_DATA(read_fds), NULL, NULL, tv_ptr); + const int num_set_fds = ::select(nfds, FD_SET_DATA(read_fds), NULL, NULL, tv_ptr); if (num_set_fds < 0) error.SetErrorToErrno(); else @@ -646,15 +618,14 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt if (log) { if (have_pipe_fd) - log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p) => %d, error = %s", - static_cast<void*>(this), nfds, handle, - pipe_fd, static_cast<void*>(tv_ptr), num_set_fds, + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p) " + "=> %d, error = %s", + static_cast<void *>(this), nfds, handle, pipe_fd, static_cast<void *>(tv_ptr), num_set_fds, error.AsCString()); else - log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p) => %d, error = %s", - static_cast<void*>(this), nfds, handle, - static_cast<void*>(tv_ptr), num_set_fds, - error.AsCString()); + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p) => " + "%d, error = %s", + static_cast<void *>(this), nfds, handle, static_cast<void *>(tv_ptr), num_set_fds, error.AsCString()); } if (error_ptr) @@ -664,20 +635,20 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt { switch (error.GetError()) { - case EBADF: // One of the descriptor sets specified an invalid descriptor. + case EBADF: // One of the descriptor sets specified an invalid descriptor. return eConnectionStatusLostConnection; - case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. - default: // Other unknown error + case EINVAL: // The specified time limit is invalid. One of its components is negative or too large. + default: // Other unknown error return eConnectionStatusError; - case EAGAIN: // The kernel was (perhaps temporarily) unable to - // allocate the requested number of file descriptors, - // or we have non-blocking IO - case EINTR: // A signal was delivered before the time limit + case EAGAIN: // The kernel was (perhaps temporarily) unable to + // allocate the requested number of file descriptors, + // or we have non-blocking IO + case EINTR: // A signal was delivered before the time limit // expired and before any of the selected events // occurred. - break; // Lets keep reading to until we timeout + break; // Lets keep reading to until we timeout } } else if (num_set_fds == 0) @@ -696,20 +667,19 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt do { - bytes_read = ::read (pipe_fd, buffer, sizeof(buffer)); + bytes_read = ::read(pipe_fd, buffer, sizeof(buffer)); } while (bytes_read < 0 && errno == EINTR); - + switch (buffer[0]) { - case 'q': - if (log) - log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", - static_cast<void*>(this), - static_cast<int>(bytes_read), buffer); - return eConnectionStatusEndOfFile; - case 'i': - // Interrupt the current read - return eConnectionStatusInterrupted; + case 'q': + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", + static_cast<void *>(this), static_cast<int>(bytes_read), buffer); + return eConnectionStatusEndOfFile; + case 'i': + // Interrupt the current read + return eConnectionStatusInterrupted; } } } @@ -722,10 +692,10 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt } ConnectionStatus -ConnectionFileDescriptor::NamedSocketAccept (const char *socket_name, Error *error_ptr) +ConnectionFileDescriptor::NamedSocketAccept(const char *socket_name, Error *error_ptr) { - Socket* socket = nullptr; - Error error = Socket::UnixDomainAccept(socket_name, socket); + Socket *socket = nullptr; + Error error = Socket::UnixDomainAccept(socket_name, m_child_processes_inherit, socket); if (error_ptr) *error_ptr = error; m_write_sp.reset(socket); @@ -734,10 +704,10 @@ ConnectionFileDescriptor::NamedSocketAccept (const char *socket_name, Error *err } ConnectionStatus -ConnectionFileDescriptor::NamedSocketConnect (const char *socket_name, Error *error_ptr) +ConnectionFileDescriptor::NamedSocketConnect(const char *socket_name, Error *error_ptr) { - Socket* socket = nullptr; - Error error = Socket::UnixDomainConnect(socket_name, socket); + Socket *socket = nullptr; + Error error = Socket::UnixDomainConnect(socket_name, m_child_processes_inherit, socket); if (error_ptr) *error_ptr = error; m_write_sp.reset(socket); @@ -750,9 +720,9 @@ ConnectionFileDescriptor::SocketListen(const char *s, Error *error_ptr) { m_port_predicate.SetValue(0, eBroadcastNever); - Socket* socket = nullptr; + Socket *socket = nullptr; m_waiting_for_accept = true; - Error error = Socket::TcpListen(s, socket, &m_port_predicate); + Error error = Socket::TcpListen(s, m_child_processes_inherit, socket, &m_port_predicate); if (error_ptr) *error_ptr = error; if (error.Fail()) @@ -762,7 +732,7 @@ ConnectionFileDescriptor::SocketListen(const char *s, Error *error_ptr) listening_socket_up.reset(socket); socket = nullptr; - error = listening_socket_up->BlockingAccept(s, socket); + error = listening_socket_up->BlockingAccept(s, m_child_processes_inherit, socket); listening_socket_up.reset(); if (error_ptr) *error_ptr = error; @@ -777,8 +747,8 @@ ConnectionFileDescriptor::SocketListen(const char *s, Error *error_ptr) ConnectionStatus ConnectionFileDescriptor::ConnectTCP(const char *s, Error *error_ptr) { - Socket* socket = nullptr; - Error error = Socket::TcpConnect(s, socket); + Socket *socket = nullptr; + Error error = Socket::TcpConnect(s, m_child_processes_inherit, socket); if (error_ptr) *error_ptr = error; m_write_sp.reset(socket); @@ -789,9 +759,9 @@ ConnectionFileDescriptor::ConnectTCP(const char *s, Error *error_ptr) ConnectionStatus ConnectionFileDescriptor::ConnectUDP(const char *s, Error *error_ptr) { - Socket* send_socket = nullptr; - Socket* recv_socket = nullptr; - Error error = Socket::UdpConnect(s, send_socket, recv_socket); + Socket *send_socket = nullptr; + Socket *recv_socket = nullptr; + Error error = Socket::UdpConnect(s, m_child_processes_inherit, send_socket, recv_socket); if (error_ptr) *error_ptr = error; m_write_sp.reset(send_socket); @@ -799,17 +769,29 @@ ConnectionFileDescriptor::ConnectUDP(const char *s, Error *error_ptr) return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError; } - -uint16_t ConnectionFileDescriptor::GetListeningPort(uint32_t timeout_sec) +uint16_t +ConnectionFileDescriptor::GetListeningPort(uint32_t timeout_sec) { uint16_t bound_port = 0; if (timeout_sec == UINT32_MAX) - m_port_predicate.WaitForValueNotEqualTo (0, bound_port); + m_port_predicate.WaitForValueNotEqualTo(0, bound_port); else { TimeValue timeout = TimeValue::Now(); timeout.OffsetWithSeconds(timeout_sec); - m_port_predicate.WaitForValueNotEqualTo (0, bound_port, &timeout); + m_port_predicate.WaitForValueNotEqualTo(0, bound_port, &timeout); } return bound_port; } + +bool +ConnectionFileDescriptor::GetChildProcessesInherit() const +{ + return m_child_processes_inherit; +} + +void +ConnectionFileDescriptor::SetChildProcessesInherit(bool child_processes_inherit) +{ + m_child_processes_inherit = child_processes_inherit; +} diff --git a/source/Host/posix/HostInfoPosix.cpp b/source/Host/posix/HostInfoPosix.cpp index 77fdc2b61a31..018d423ee9d3 100644 --- a/source/Host/posix/HostInfoPosix.cpp +++ b/source/Host/posix/HostInfoPosix.cpp @@ -69,6 +69,7 @@ HostInfoPosix::LookupUserName(uint32_t uid, std::string &user_name) const char * HostInfoPosix::LookupGroupName(uint32_t gid, std::string &group_name) { +#ifndef __ANDROID__ char group_buffer[PATH_MAX]; size_t group_buffer_size = sizeof(group_buffer); struct group group_info; @@ -94,6 +95,9 @@ HostInfoPosix::LookupGroupName(uint32_t gid, std::string &group_name) } } group_name.clear(); +#else + assert(false && "getgrgid_r() not supported on Android"); +#endif return NULL; } @@ -121,6 +125,12 @@ HostInfoPosix::GetEffectiveGroupID() return getegid(); } +FileSpec +HostInfoPosix::GetDefaultShell() +{ + return FileSpec("/bin/sh", false); +} + bool HostInfoPosix::ComputeSupportExeDirectory(FileSpec &file_spec) { @@ -173,6 +183,7 @@ HostInfoPosix::ComputeHeaderDirectory(FileSpec &file_spec) bool HostInfoPosix::ComputePythonDirectory(FileSpec &file_spec) { +#ifndef LLDB_DISABLE_PYTHON FileSpec lldb_file_spec; if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) return false; @@ -190,4 +201,7 @@ HostInfoPosix::ComputePythonDirectory(FileSpec &file_spec) file_spec.GetDirectory().SetCString(raw_path); return true; +#else + return false; +#endif } diff --git a/source/Host/posix/HostProcessPosix.cpp b/source/Host/posix/HostProcessPosix.cpp index 4618de4711de..8e19add048ee 100644 --- a/source/Host/posix/HostProcessPosix.cpp +++ b/source/Host/posix/HostProcessPosix.cpp @@ -1,4 +1,4 @@ -//===-- HostProcessWindows.cpp ----------------------------------*- C++ -*-===// +//===-- HostProcessPosix.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "lldb/Host/Host.h" #include "lldb/Host/posix/HostProcessPosix.h" #include "lldb/Host/FileSystem.h" @@ -16,49 +17,52 @@ using namespace lldb_private; -const lldb::pid_t HostProcessPosix::kInvalidProcessId = 0; +namespace +{ + const int kInvalidPosixProcess = 0; +} HostProcessPosix::HostProcessPosix() -: m_pid(kInvalidProcessId) + : HostNativeProcessBase(kInvalidPosixProcess) { } -HostProcessPosix::~HostProcessPosix() +HostProcessPosix::HostProcessPosix(lldb::process_t process) + : HostNativeProcessBase(process) { } -Error HostProcessPosix::Create(lldb::pid_t pid) +HostProcessPosix::~HostProcessPosix() { - Error error; - if (pid == kInvalidProcessId) - error.SetErrorString("Attempt to create an invalid process"); - - m_pid = pid; - return error; } Error HostProcessPosix::Signal(int signo) const { - if (m_pid <= 0) + if (m_process == kInvalidPosixProcess) { Error error; error.SetErrorString("HostProcessPosix refers to an invalid process"); return error; } - return HostProcessPosix::Signal(m_pid, signo); + return HostProcessPosix::Signal(m_process, signo); } -Error HostProcessPosix::Signal(lldb::pid_t pid, int signo) +Error HostProcessPosix::Signal(lldb::process_t process, int signo) { Error error; - if (-1 == ::kill(pid, signo)) + if (-1 == ::kill(process, signo)) error.SetErrorToErrno(); return error; } +Error HostProcessPosix::Terminate() +{ + return Signal(SIGKILL); +} + Error HostProcessPosix::GetMainModule(FileSpec &file_spec) const { Error error; @@ -66,7 +70,7 @@ Error HostProcessPosix::GetMainModule(FileSpec &file_spec) const // Use special code here because proc/[pid]/exe is a symbolic link. char link_path[PATH_MAX]; char exe_path[PATH_MAX] = ""; - if (snprintf (link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", m_pid) <= 0) + if (snprintf (link_path, PATH_MAX, "/proc/%" PRIu64 "/exe", m_process) <= 0) { error.SetErrorString("Unable to build /proc/<pid>/exe string"); return error; @@ -92,12 +96,21 @@ Error HostProcessPosix::GetMainModule(FileSpec &file_spec) const lldb::pid_t HostProcessPosix::GetProcessId() const { - return m_pid; + return m_process; } bool HostProcessPosix::IsRunning() const { + if (m_process == kInvalidPosixProcess) + return false; + // Send this process the null signal. If it succeeds the process is running. Error error = Signal(0); return error.Success(); } + +HostThread +HostProcessPosix::StartMonitoring(HostProcess::MonitorCallback callback, void *callback_baton, bool monitor_signals) +{ + return Host::StartMonitoringChildProcess(callback, callback_baton, m_process, monitor_signals); +} diff --git a/source/Host/posix/HostThreadPosix.cpp b/source/Host/posix/HostThreadPosix.cpp new file mode 100644 index 000000000000..9c4892431938 --- /dev/null +++ b/source/Host/posix/HostThreadPosix.cpp @@ -0,0 +1,74 @@ +//===-- HostThreadPosix.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Error.h" +#include "lldb/Host/posix/HostThreadPosix.h" + +#include <errno.h> +#include <pthread.h> + +using namespace lldb; +using namespace lldb_private; + +HostThreadPosix::HostThreadPosix() +{ +} + +HostThreadPosix::HostThreadPosix(lldb::thread_t thread) + : HostNativeThreadBase(thread) +{ +} + +HostThreadPosix::~HostThreadPosix() +{ +} + +Error +HostThreadPosix::Join(lldb::thread_result_t *result) +{ + Error error; + if (IsJoinable()) + { + int err = ::pthread_join(m_thread, result); + error.SetError(err, lldb::eErrorTypePOSIX); + } + else + { + if (result) + *result = NULL; + error.SetError(EINVAL, eErrorTypePOSIX); + } + + Reset(); + return error; +} + +Error +HostThreadPosix::Cancel() +{ + Error error; +#ifndef __ANDROID__ + int err = ::pthread_cancel(m_thread); + error.SetError(err, eErrorTypePOSIX); +#else + error.SetErrorString("HostThreadPosix::Cancel() not supported on Android"); +#endif + + return error; +} + +Error +HostThreadPosix::Detach() +{ + Error error; + int err = ::pthread_detach(m_thread); + error.SetError(err, eErrorTypePOSIX); + Reset(); + return error; +} diff --git a/source/Host/posix/PipePosix.cpp b/source/Host/posix/PipePosix.cpp new file mode 100644 index 000000000000..02838ec5124e --- /dev/null +++ b/source/Host/posix/PipePosix.cpp @@ -0,0 +1,378 @@ +//===-- PipePosix.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/posix/PipePosix.h" +#include "lldb/Host/FileSystem.h" + +#include <functional> +#include <thread> + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +using namespace lldb; +using namespace lldb_private; + +int PipePosix::kInvalidDescriptor = -1; + +enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE + +// pipe2 is supported by Linux, FreeBSD v10 and higher. +// TODO: Add more platforms that support pipe2. +#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10) +#define PIPE2_SUPPORTED 1 +#else +#define PIPE2_SUPPORTED 0 +#endif + +namespace +{ + +constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100; + +#if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED +bool SetCloexecFlag(int fd) +{ + int flags = ::fcntl(fd, F_GETFD); + if (flags == -1) + return false; + return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0); +} +#endif + +std::chrono::time_point<std::chrono::steady_clock> +Now() +{ + return std::chrono::steady_clock::now(); +} + +Error +SelectIO(int handle, bool is_read, const std::function<Error(bool&)> &io_handler, const std::chrono::microseconds &timeout) +{ + Error error; + fd_set fds; + bool done = false; + + using namespace std::chrono; + + const auto finish_time = Now() + timeout; + + while (!done) + { + struct timeval tv = {0, 0}; + if (timeout != microseconds::zero()) + { + const auto remaining_dur = duration_cast<microseconds>(finish_time - Now()); + if (remaining_dur.count() <= 0) + { + error.SetErrorString("timeout exceeded"); + break; + } + const auto dur_secs = duration_cast<seconds>(remaining_dur); + const auto dur_usecs = remaining_dur % seconds(1); + + tv.tv_sec = dur_secs.count(); + tv.tv_usec = dur_usecs.count(); + } + else + tv.tv_sec = 1; + + FD_ZERO(&fds); + FD_SET(handle, &fds); + + const auto retval = ::select(handle + 1, + (is_read) ? &fds : nullptr, + (is_read) ? nullptr : &fds, + nullptr, &tv); + if (retval == -1) + { + if (errno == EINTR) + continue; + error.SetErrorToErrno(); + break; + } + if (retval == 0) + { + error.SetErrorString("timeout exceeded"); + break; + } + if (!FD_ISSET(handle, &fds)) + { + error.SetErrorString("invalid state"); + break; + } + + error = io_handler(done); + if (error.Fail()) + { + if (error.GetError() == EINTR) + continue; + break; + } + } + return error; +} + +} + +PipePosix::PipePosix() +{ + m_fds[READ] = PipePosix::kInvalidDescriptor; + m_fds[WRITE] = PipePosix::kInvalidDescriptor; +} + +PipePosix::~PipePosix() +{ + Close(); +} + +Error +PipePosix::CreateNew(bool child_processes_inherit) +{ + if (CanRead() || CanWrite()) + return Error(EINVAL, eErrorTypePOSIX); + + Error error; +#if PIPE2_SUPPORTED + if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0) + return error; +#else + if (::pipe(m_fds) == 0) + { +#ifdef FD_CLOEXEC + if (!child_processes_inherit) + { + if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) + { + error.SetErrorToErrno(); + Close(); + return error; + } + } +#endif + return error; + } +#endif + + error.SetErrorToErrno(); + m_fds[READ] = PipePosix::kInvalidDescriptor; + m_fds[WRITE] = PipePosix::kInvalidDescriptor; + return error; +} + +Error +PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) +{ + if (CanRead() || CanWrite()) + return Error("Pipe is already opened"); + + Error error; + if (::mkfifo(name.data(), 0660) != 0) + error.SetErrorToErrno(); + + return error; +} + +Error +PipePosix::OpenAsReader(llvm::StringRef name, bool child_process_inherit) +{ + if (CanRead() || CanWrite()) + return Error("Pipe is already opened"); + + int flags = O_RDONLY | O_NONBLOCK; + if (!child_process_inherit) + flags |= O_CLOEXEC; + + Error error; + int fd = ::open(name.data(), flags); + if (fd != -1) + m_fds[READ] = fd; + else + error.SetErrorToErrno(); + + return error; +} + +Error +PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit, const std::chrono::microseconds &timeout) +{ + if (CanRead() || CanWrite()) + return Error("Pipe is already opened"); + + int flags = O_WRONLY | O_NONBLOCK; + if (!child_process_inherit) + flags |= O_CLOEXEC; + + using namespace std::chrono; + const auto finish_time = Now() + timeout; + + while (!CanWrite()) + { + if (timeout != microseconds::zero()) + { + const auto dur = duration_cast<microseconds>(finish_time - Now()).count(); + if (dur <= 0) + return Error("timeout exceeded - reader hasn't opened so far"); + } + + errno = 0; + int fd = ::open(name.data(), flags); + if (fd == -1) + { + const auto errno_copy = errno; + // We may get ENXIO if a reader side of the pipe hasn't opened yet. + if (errno_copy != ENXIO) + return Error(errno_copy, eErrorTypePOSIX); + + std::this_thread::sleep_for(milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS)); + } + else + { + m_fds[WRITE] = fd; + } + } + + return Error(); +} + +int +PipePosix::GetReadFileDescriptor() const +{ + return m_fds[READ]; +} + +int +PipePosix::GetWriteFileDescriptor() const +{ + return m_fds[WRITE]; +} + +int +PipePosix::ReleaseReadFileDescriptor() +{ + const int fd = m_fds[READ]; + m_fds[READ] = PipePosix::kInvalidDescriptor; + return fd; +} + +int +PipePosix::ReleaseWriteFileDescriptor() +{ + const int fd = m_fds[WRITE]; + m_fds[WRITE] = PipePosix::kInvalidDescriptor; + return fd; +} + +void +PipePosix::Close() +{ + CloseReadFileDescriptor(); + CloseWriteFileDescriptor(); +} + +Error +PipePosix::Delete(llvm::StringRef name) +{ + return FileSystem::Unlink(name.data()); +} + +bool +PipePosix::CanRead() const +{ + return m_fds[READ] != PipePosix::kInvalidDescriptor; +} + +bool +PipePosix::CanWrite() const +{ + return m_fds[WRITE] != PipePosix::kInvalidDescriptor; +} + +void +PipePosix::CloseReadFileDescriptor() +{ + if (CanRead()) + { + close(m_fds[READ]); + m_fds[READ] = PipePosix::kInvalidDescriptor; + } +} + +void +PipePosix::CloseWriteFileDescriptor() +{ + if (CanWrite()) + { + close(m_fds[WRITE]); + m_fds[WRITE] = PipePosix::kInvalidDescriptor; + } +} + +Error +PipePosix::ReadWithTimeout(void *buf, size_t size, const std::chrono::microseconds &timeout, size_t &bytes_read) +{ + bytes_read = 0; + if (!CanRead()) + return Error(EINVAL, eErrorTypePOSIX); + + auto handle = GetReadFileDescriptor(); + return SelectIO(handle, + true, + [=, &bytes_read](bool &done) + { + Error error; + auto result = ::read(handle, + reinterpret_cast<char*>(buf) + bytes_read, + size - bytes_read); + if (result != -1) + { + bytes_read += result; + if (bytes_read == size || result == 0) + done = true; + } + else + error.SetErrorToErrno(); + + return error; + }, + timeout); +} + +Error +PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) +{ + bytes_written = 0; + if (!CanWrite()) + return Error(EINVAL, eErrorTypePOSIX); + + auto handle = GetWriteFileDescriptor(); + return SelectIO(handle, + false, + [=, &bytes_written](bool &done) + { + Error error; + auto result = ::write(handle, + reinterpret_cast<const char*>(buf) + bytes_written, + size - bytes_written); + if (result != -1) + { + bytes_written += result; + if (bytes_written == size) + done = true; + } + else + error.SetErrorToErrno(); + + return error; + }, + std::chrono::microseconds::zero()); +} diff --git a/source/Host/posix/ProcessLauncherPosix.cpp b/source/Host/posix/ProcessLauncherPosix.cpp new file mode 100644 index 000000000000..dd5c5121a97b --- /dev/null +++ b/source/Host/posix/ProcessLauncherPosix.cpp @@ -0,0 +1,33 @@ +//===-- ProcessLauncherPosix.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Host.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/posix/ProcessLauncherPosix.h" + +#include "lldb/Target/ProcessLaunchInfo.h" + +#include <limits.h> + +using namespace lldb; +using namespace lldb_private; + +HostProcess +ProcessLauncherPosix::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error) +{ + lldb::pid_t pid; + char exe_path[PATH_MAX]; + + launch_info.GetExecutableFile().GetPath(exe_path, sizeof(exe_path)); + + // TODO(zturner): Move the code from LaunchProcessPosixSpawn to here, and make MacOSX re-use this + // ProcessLauncher when it wants a posix_spawn launch. + error = Host::LaunchProcessPosixSpawn(exe_path, launch_info, pid); + return HostProcess(pid); +} diff --git a/source/Interpreter/Args.cpp b/source/Interpreter/Args.cpp index 4831eaaac348..93933802f57c 100644 --- a/source/Interpreter/Args.cpp +++ b/source/Interpreter/Args.cpp @@ -685,7 +685,7 @@ Args::ParseOptions (Options &options) } } // Call the callback with the option - if (long_options_index >= 0) + if (long_options_index >= 0 && long_options[long_options_index].definition) { const OptionDefinition *def = long_options[long_options_index].definition; CommandInterpreter &interpreter = options.GetInterpreter(); @@ -829,15 +829,18 @@ Args::StringToAddress (const ExecutionContext *exe_ctx, const char *s, lldb::add options.SetTryAllThreads(true); ExpressionResults expr_result = target->EvaluateExpression(s, - exe_ctx->GetFramePtr(), - valobj_sp, - options); + exe_ctx->GetFramePtr(), + valobj_sp, + options); bool success = false; if (expr_result == eExpressionCompleted) { + if (valobj_sp) + valobj_sp = valobj_sp->GetQualifiedRepresentationIfAvailable(valobj_sp->GetDynamicValueType(), true); // Get the address to watch. - addr = valobj_sp->GetValueAsUnsigned(fail_value, &success); + if (valobj_sp) + addr = valobj_sp->GetValueAsUnsigned(fail_value, &success); if (success) { if (error_ptr) @@ -966,6 +969,26 @@ Args::StringToBoolean (const char *s, bool fail_value, bool *success_ptr) return fail_value; } +char +Args::StringToChar(const char *s, char fail_value, bool *success_ptr) +{ + bool success = false; + char result = fail_value; + + if (s) + { + size_t length = strlen(s); + if (length == 1) + { + success = true; + result = s[0]; + } + } + if (success_ptr) + *success_ptr = success; + return result; +} + const char * Args::StringToVersion (const char *s, uint32_t &major, uint32_t &minor, uint32_t &update) { diff --git a/source/Interpreter/CommandInterpreter.cpp b/source/Interpreter/CommandInterpreter.cpp index 56c8f8c0ad6a..176a1fc9a3b0 100644 --- a/source/Interpreter/CommandInterpreter.cpp +++ b/source/Interpreter/CommandInterpreter.cpp @@ -49,7 +49,9 @@ #include "lldb/Core/StreamFile.h" #include "lldb/Core/Timer.h" +#ifndef LLDB_DISABLE_LIBEDIT #include "lldb/Host/Editline.h" +#endif #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" @@ -117,7 +119,11 @@ CommandInterpreter::CommandInterpreter m_comment_char ('#'), m_batch_command_mode (false), m_truncation_warning(eNoTruncation), - m_command_source_depth (0) + m_command_source_depth (0), + m_num_errors(0), + m_quit_requested(false), + m_stopped_for_crash(false) + { debugger.SetScriptLanguage (script_language); SetEventName (eBroadcastBitThreadShouldExit, "thread-should-exit"); @@ -333,7 +339,11 @@ CommandInterpreter::Initialize () #if defined (__arm__) || defined (__arm64__) || defined (__aarch64__) ProcessAliasOptionsArgs (cmd_obj_sp, "--", alias_arguments_vector_sp); #else - ProcessAliasOptionsArgs (cmd_obj_sp, "--shell=" LLDB_DEFAULT_SHELL " --", alias_arguments_vector_sp); + std::string shell_option; + shell_option.append("--shell="); + shell_option.append(HostInfo::GetDefaultShell().GetPath()); + shell_option.append(" --"); + ProcessAliasOptionsArgs (cmd_obj_sp, shell_option.c_str(), alias_arguments_vector_sp); #endif AddAlias ("r", cmd_obj_sp); AddAlias ("run", cmd_obj_sp); @@ -414,6 +424,7 @@ CommandInterpreter::LoadCommandDictionary () m_command_dict["watchpoint"]= CommandObjectSP (new CommandObjectMultiwordWatchpoint (*this)); const char *break_regexes[][2] = {{"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$", "breakpoint set --file '%1' --line %2"}, + {"^/([^/]+)/$", "breakpoint set --source-pattern-regexp '%1'"}, {"^([[:digit:]]+)[[:space:]]*$", "breakpoint set --line %1"}, {"^\\*?(0x[[:xdigit:]]+)[[:space:]]*$", "breakpoint set --address %1"}, {"^[\"']?([-+]?\\[.*\\])[\"']?[[:space:]]*$", "breakpoint set --name '%1'"}, @@ -431,7 +442,8 @@ CommandInterpreter::LoadCommandDictionary () "_regexp-break [<filename>:<linenum>]\n_regexp-break [<linenum>]\n_regexp-break [<address>]\n_regexp-break <...>", 2, CommandCompletions::eSymbolCompletion | - CommandCompletions::eSourceFileCompletion)); + CommandCompletions::eSourceFileCompletion, + false)); if (break_regex_cmd_ap.get()) { @@ -458,7 +470,8 @@ CommandInterpreter::LoadCommandDictionary () "_regexp-tbreak [<filename>:<linenum>]\n_regexp-break [<linenum>]\n_regexp-break [<address>]\n_regexp-break <...>", 2, CommandCompletions::eSymbolCompletion | - CommandCompletions::eSourceFileCompletion)); + CommandCompletions::eSourceFileCompletion, + false)); if (tbreak_regex_cmd_ap.get()) { @@ -469,6 +482,8 @@ CommandInterpreter::LoadCommandDictionary () char buffer[1024]; int num_printed = snprintf(buffer, 1024, "%s %s", break_regexes[i][1], "-o"); assert (num_printed < 1024); + // Quiet unused variable warning for release builds. + (void) num_printed; success = tbreak_regex_cmd_ap->AddRegexCommand (break_regexes[i][0], buffer); if (!success) break; @@ -487,7 +502,9 @@ CommandInterpreter::LoadCommandDictionary () "_regexp-attach", "Attach to a process id if in decimal, otherwise treat the argument as a process name to attach to.", "_regexp-attach [<pid>]\n_regexp-attach [<process-name>]", - 2)); + 2, + 0, + false)); if (attach_regex_cmd_ap.get()) { if (attach_regex_cmd_ap->AddRegexCommand("^([0-9]+)[[:space:]]*$", "process attach --pid %1") && @@ -504,7 +521,10 @@ CommandInterpreter::LoadCommandDictionary () down_regex_cmd_ap(new CommandObjectRegexCommand (*this, "_regexp-down", "Go down \"n\" frames in the stack (1 frame by default).", - "_regexp-down [n]", 2)); + "_regexp-down [n]", + 2, + 0, + false)); if (down_regex_cmd_ap.get()) { if (down_regex_cmd_ap->AddRegexCommand("^$", "frame select -r -1") && @@ -519,7 +539,10 @@ CommandInterpreter::LoadCommandDictionary () up_regex_cmd_ap(new CommandObjectRegexCommand (*this, "_regexp-up", "Go up \"n\" frames in the stack (1 frame by default).", - "_regexp-up [n]", 2)); + "_regexp-up [n]", + 2, + 0, + false)); if (up_regex_cmd_ap.get()) { if (up_regex_cmd_ap->AddRegexCommand("^$", "frame select -r 1") && @@ -534,7 +557,10 @@ CommandInterpreter::LoadCommandDictionary () display_regex_cmd_ap(new CommandObjectRegexCommand (*this, "_regexp-display", "Add an expression evaluation stop-hook.", - "_regexp-display expression", 2)); + "_regexp-display expression", + 2, + 0, + false)); if (display_regex_cmd_ap.get()) { if (display_regex_cmd_ap->AddRegexCommand("^(.+)$", "target stop-hook add -o \"expr -- %1\"")) @@ -548,7 +574,10 @@ CommandInterpreter::LoadCommandDictionary () undisplay_regex_cmd_ap(new CommandObjectRegexCommand (*this, "_regexp-undisplay", "Remove an expression evaluation stop-hook.", - "_regexp-undisplay stop-hook-number", 2)); + "_regexp-undisplay stop-hook-number", + 2, + 0, + false)); if (undisplay_regex_cmd_ap.get()) { if (undisplay_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "target stop-hook delete %1")) @@ -562,7 +591,10 @@ CommandInterpreter::LoadCommandDictionary () connect_gdb_remote_cmd_ap(new CommandObjectRegexCommand (*this, "gdb-remote", "Connect to a remote GDB server. If no hostname is provided, localhost is assumed.", - "gdb-remote [<hostname>:]<portnum>", 2)); + "gdb-remote [<hostname>:]<portnum>", + 2, + 0, + false)); if (connect_gdb_remote_cmd_ap.get()) { if (connect_gdb_remote_cmd_ap->AddRegexCommand("^([^:]+:[[:digit:]]+)$", "process connect --plugin gdb-remote connect://%1") && @@ -577,7 +609,10 @@ CommandInterpreter::LoadCommandDictionary () connect_kdp_remote_cmd_ap(new CommandObjectRegexCommand (*this, "kdp-remote", "Connect to a remote KDP server. udp port 41139 is the default port number.", - "kdp-remote <hostname>[:<portnum>]", 2)); + "kdp-remote <hostname>[:<portnum>]", + 2, + 0, + false)); if (connect_kdp_remote_cmd_ap.get()) { if (connect_kdp_remote_cmd_ap->AddRegexCommand("^([^:]+:[[:digit:]]+)$", "process connect --plugin kdp-remote udp://%1") && @@ -590,9 +625,12 @@ CommandInterpreter::LoadCommandDictionary () std::unique_ptr<CommandObjectRegexCommand> bt_regex_cmd_ap(new CommandObjectRegexCommand (*this, - "_regexp-bt", - "Show a backtrace. An optional argument is accepted; if that argument is a number, it specifies the number of frames to display. If that argument is 'all', full backtraces of all threads are displayed.", - "bt [<digit>|all]", 2)); + "_regexp-bt", + "Show a backtrace. An optional argument is accepted; if that argument is a number, it specifies the number of frames to display. If that argument is 'all', full backtraces of all threads are displayed.", + "bt [<digit>|all]", + 2, + 0, + false)); if (bt_regex_cmd_ap.get()) { // accept but don't document "bt -c <number>" -- before bt was a regex command if you wanted to backtrace @@ -614,7 +652,8 @@ CommandInterpreter::LoadCommandDictionary () "Implements the GDB 'list' command in all of its forms except FILE:FUNCTION and maps them to the appropriate 'source list' commands.", "_regexp-list [<line>]\n_regexp-list [<file>:<line>]\n_regexp-list [<file>:<line>]", 2, - CommandCompletions::eSourceFileCompletion)); + CommandCompletions::eSourceFileCompletion, + false)); if (list_regex_cmd_ap.get()) { if (list_regex_cmd_ap->AddRegexCommand("^([0-9]+)[[:space:]]*$", "source list --line %1") && @@ -634,7 +673,10 @@ CommandInterpreter::LoadCommandDictionary () env_regex_cmd_ap(new CommandObjectRegexCommand (*this, "_regexp-env", "Implements a shortcut to viewing and setting environment variables.", - "_regexp-env\n_regexp-env FOO=BAR", 2)); + "_regexp-env\n_regexp-env FOO=BAR", + 2, + 0, + false)); if (env_regex_cmd_ap.get()) { if (env_regex_cmd_ap->AddRegexCommand("^$", "settings show target.env-vars") && @@ -652,7 +694,10 @@ CommandInterpreter::LoadCommandDictionary () "_regexp-jump [<line>]\n" "_regexp-jump [<+-lineoffset>]\n" "_regexp-jump [<file>:<line>]\n" - "_regexp-jump [*<addr>]\n", 2)); + "_regexp-jump [*<addr>]\n", + 2, + 0, + false)); if (jump_regex_cmd_ap.get()) { if (jump_regex_cmd_ap->AddRegexCommand("^\\*(.*)$", "thread jump --addr %1") && @@ -1043,6 +1088,22 @@ CommandInterpreter::RemoveAlias (const char *alias_name) } return false; } + +bool +CommandInterpreter::RemoveCommand (const char *cmd) +{ + auto pos = m_command_dict.find(cmd); + if (pos != m_command_dict.end()) + { + if (pos->second->IsRemovable()) + { + // Only regular expression objects or python commands are removable + m_command_dict.erase(pos); + return true; + } + } + return false; +} bool CommandInterpreter::RemoveUser (const char *alias_name) { @@ -1448,7 +1509,7 @@ CommandInterpreter::PreprocessCommand (std::string &command) // Get a dummy target to allow for calculator mode while processing backticks. // This also helps break the infinite loop caused when target is null. if (!target) - target = Host::GetDummyTarget(GetDebugger()).get(); + target = m_debugger.GetDummyTarget(); if (target) { ValueObjectSP expr_result_valobj_sp; @@ -1462,13 +1523,15 @@ CommandInterpreter::PreprocessCommand (std::string &command) options.SetTimeoutUsec(0); ExpressionResults expr_result = target->EvaluateExpression (expr_str.c_str(), - exe_ctx.GetFramePtr(), - expr_result_valobj_sp, - options); + exe_ctx.GetFramePtr(), + expr_result_valobj_sp, + options); if (expr_result == eExpressionCompleted) { Scalar scalar; + if (expr_result_valobj_sp) + expr_result_valobj_sp = expr_result_valobj_sp->GetQualifiedRepresentationIfAvailable(expr_result_valobj_sp->GetDynamicValueType(), true); if (expr_result_valobj_sp->ResolveValue (scalar)) { command.erase (start_backtick, end_backtick - start_backtick + 1); @@ -2415,13 +2478,14 @@ CommandInterpreter::SourceInitFile (bool in_cwd, CommandReturnObject &result) if (init_file.Exists()) { const bool saved_batch = SetBatchCommandMode (true); + CommandInterpreterRunOptions options; + options.SetSilent (true); + options.SetStopOnError (false); + options.SetStopOnContinue (true); + HandleCommandsFromFile (init_file, nullptr, // Execution context - eLazyBoolYes, // Stop on continue - eLazyBoolNo, // Stop on error - eLazyBoolNo, // Don't echo commands - eLazyBoolNo, // Don't print command output - eLazyBoolNo, // Don't add the commands that are sourced into the history buffer + options, result); SetBatchCommandMode (saved_batch); } @@ -2451,12 +2515,8 @@ CommandInterpreter::GetPlatform (bool prefer_target_platform) void CommandInterpreter::HandleCommands (const StringList &commands, - ExecutionContext *override_context, - bool stop_on_continue, - bool stop_on_error, - bool echo_commands, - bool print_results, - LazyBool add_to_history, + ExecutionContext *override_context, + CommandInterpreterRunOptions &options, CommandReturnObject &result) { size_t num_lines = commands.GetSize(); @@ -2472,7 +2532,7 @@ CommandInterpreter::HandleCommands (const StringList &commands, if (override_context != nullptr) UpdateExecutionContext (override_context); - if (!stop_on_continue) + if (!options.GetStopOnContinue()) { m_debugger.SetAsyncExecution (false); } @@ -2483,7 +2543,7 @@ CommandInterpreter::HandleCommands (const StringList &commands, if (cmd[0] == '\0') continue; - if (echo_commands) + if (options.GetEchoCommands()) { result.AppendMessageWithFormat ("%s %s\n", m_debugger.GetPrompt(), @@ -2496,16 +2556,16 @@ CommandInterpreter::HandleCommands (const StringList &commands, // We might call into a regex or alias command, in which case the add_to_history will get lost. This // m_command_source_depth dingus is the way we turn off adding to the history in that case, so set it up here. - if (!add_to_history) + if (!options.GetAddToHistory()) m_command_source_depth++; - bool success = HandleCommand(cmd, add_to_history, tmp_result, + bool success = HandleCommand(cmd, options.m_add_to_history, tmp_result, nullptr, /* override_context */ true, /* repeat_on_empty_command */ override_context != nullptr /* no_context_switching */); - if (!add_to_history) + if (!options.GetAddToHistory()) m_command_source_depth--; - if (print_results) + if (options.GetPrintResults()) { if (tmp_result.Succeeded()) result.AppendMessageWithFormat("%s", tmp_result.GetOutputData()); @@ -2516,7 +2576,7 @@ CommandInterpreter::HandleCommands (const StringList &commands, const char *error_msg = tmp_result.GetErrorData(); if (error_msg == nullptr || error_msg[0] == '\0') error_msg = "<unknown error>.\n"; - if (stop_on_error) + if (options.GetStopOnError()) { result.AppendErrorWithFormat("Aborting reading of commands after command #%" PRIu64 ": '%s' failed with %s", (uint64_t)idx, cmd, error_msg); @@ -2524,7 +2584,7 @@ CommandInterpreter::HandleCommands (const StringList &commands, m_debugger.SetAsyncExecution (old_async_execution); return; } - else if (print_results) + else if (options.GetPrintResults()) { result.AppendMessageWithFormat ("Command #%" PRIu64 " '%s' failed with %s", (uint64_t)idx + 1, @@ -2545,7 +2605,7 @@ CommandInterpreter::HandleCommands (const StringList &commands, if ((tmp_result.GetStatus() == eReturnStatusSuccessContinuingNoResult) || (tmp_result.GetStatus() == eReturnStatusSuccessContinuingResult)) { - if (stop_on_continue) + if (options.GetStopOnContinue()) { // If we caused the target to proceed, and we're going to stop in that case, set the // status in our real result before returning. This is an error if the continue was not the @@ -2562,6 +2622,42 @@ CommandInterpreter::HandleCommands (const StringList &commands, return; } } + + // Also check for "stop on crash here: + bool should_stop = false; + if (tmp_result.GetDidChangeProcessState() && options.GetStopOnCrash()) + { + TargetSP target_sp (m_debugger.GetTargetList().GetSelectedTarget()); + if (target_sp) + { + ProcessSP process_sp (target_sp->GetProcessSP()); + if (process_sp) + { + for (ThreadSP thread_sp : process_sp->GetThreadList().Threads()) + { + StopReason reason = thread_sp->GetStopReason(); + if (reason == eStopReasonSignal || reason == eStopReasonException || reason == eStopReasonInstrumentation) + { + should_stop = true; + break; + } + } + } + } + if (should_stop) + { + if (idx != num_lines - 1) + result.AppendErrorWithFormat("Aborting reading of commands after command #%" PRIu64 ": '%s' stopped with a signal or exception.\n", + (uint64_t)idx + 1, cmd); + else + result.AppendMessageWithFormat("Command #%" PRIu64 " '%s' stopped with a signal or exception.\n", (uint64_t)idx + 1, cmd); + + result.SetStatus(tmp_result.GetStatus()); + m_debugger.SetAsyncExecution (old_async_execution); + + return; + } + } } @@ -2576,17 +2672,14 @@ enum { eHandleCommandFlagStopOnContinue = (1u << 0), eHandleCommandFlagStopOnError = (1u << 1), eHandleCommandFlagEchoCommand = (1u << 2), - eHandleCommandFlagPrintResult = (1u << 3) + eHandleCommandFlagPrintResult = (1u << 3), + eHandleCommandFlagStopOnCrash = (1u << 4) }; void CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file, ExecutionContext *context, - LazyBool stop_on_continue, - LazyBool stop_on_error, - LazyBool echo_command, - LazyBool print_result, - LazyBool add_to_history, + CommandInterpreterRunOptions &options, CommandReturnObject &result) { if (cmd_file.Exists()) @@ -2602,7 +2695,7 @@ CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file, uint32_t flags = 0; - if (stop_on_continue == eLazyBoolCalculate) + if (options.m_stop_on_continue == eLazyBoolCalculate) { if (m_command_source_flags.empty()) { @@ -2614,12 +2707,12 @@ CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file, flags |= eHandleCommandFlagStopOnContinue; } } - else if (stop_on_continue == eLazyBoolYes) + else if (options.m_stop_on_continue == eLazyBoolYes) { flags |= eHandleCommandFlagStopOnContinue; } - if (stop_on_error == eLazyBoolCalculate) + if (options.m_stop_on_error == eLazyBoolCalculate) { if (m_command_source_flags.empty()) { @@ -2631,12 +2724,25 @@ CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file, flags |= eHandleCommandFlagStopOnError; } } - else if (stop_on_error == eLazyBoolYes) + else if (options.m_stop_on_error == eLazyBoolYes) { flags |= eHandleCommandFlagStopOnError; } - if (echo_command == eLazyBoolCalculate) + if (options.GetStopOnCrash()) + { + if (m_command_source_flags.empty()) + { + // Echo command by default + flags |= eHandleCommandFlagStopOnCrash; + } + else if (m_command_source_flags.back() & eHandleCommandFlagStopOnCrash) + { + flags |= eHandleCommandFlagStopOnCrash; + } + } + + if (options.m_echo_commands == eLazyBoolCalculate) { if (m_command_source_flags.empty()) { @@ -2648,12 +2754,12 @@ CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file, flags |= eHandleCommandFlagEchoCommand; } } - else if (echo_command == eLazyBoolYes) + else if (options.m_echo_commands == eLazyBoolYes) { flags |= eHandleCommandFlagEchoCommand; } - if (print_result == eLazyBoolCalculate) + if (options.m_print_results == eLazyBoolCalculate) { if (m_command_source_flags.empty()) { @@ -2665,7 +2771,7 @@ CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file, flags |= eHandleCommandFlagPrintResult; } } - else if (print_result == eLazyBoolYes) + else if (options.m_print_results == eLazyBoolYes) { flags |= eHandleCommandFlagPrintResult; } @@ -2680,18 +2786,21 @@ CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file, lldb::StreamFileSP empty_stream_sp; m_command_source_flags.push_back(flags); IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger, + IOHandler::Type::CommandInterpreter, input_file_sp, empty_stream_sp, // Pass in an empty stream so we inherit the top input reader output stream empty_stream_sp, // Pass in an empty stream so we inherit the top input reader error stream flags, nullptr, // Pass in NULL for "editline_name" so no history is saved, or written debugger.GetPrompt(), + NULL, false, // Not multi-line + debugger.GetUseColor(), 0, *this)); const bool old_async_execution = debugger.GetAsyncExecution(); - // Set synchronous execution if we not stopping when we continue + // Set synchronous execution if we are not stopping on continue if ((flags & eHandleCommandFlagStopOnContinue) == 0) debugger.SetAsyncExecution (false); @@ -2715,7 +2824,7 @@ CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file, else { result.AppendErrorWithFormat ("Error reading commands from file %s - file not found.\n", - cmd_file.GetFilename().AsCString()); + cmd_file.GetFilename().AsCString("<Unknown>")); result.SetStatus (eReturnStatusFailed); return; } @@ -3056,14 +3165,46 @@ CommandInterpreter::IOHandlerInputComplete (IOHandler &io_handler, std::string & break; case eReturnStatusFailed: + m_num_errors++; if (io_handler.GetFlags().Test(eHandleCommandFlagStopOnError)) io_handler.SetIsDone(true); break; case eReturnStatusQuit: + m_quit_requested = true; io_handler.SetIsDone(true); break; } + + // Finally, if we're going to stop on crash, check that here: + if (!m_quit_requested + && result.GetDidChangeProcessState() + && io_handler.GetFlags().Test(eHandleCommandFlagStopOnCrash)) + { + bool should_stop = false; + TargetSP target_sp (m_debugger.GetTargetList().GetSelectedTarget()); + if (target_sp) + { + ProcessSP process_sp (target_sp->GetProcessSP()); + if (process_sp) + { + for (ThreadSP thread_sp : process_sp->GetThreadList().Threads()) + { + StopReason reason = thread_sp->GetStopReason(); + if (reason == eStopReasonSignal || reason == eStopReasonException || reason == eStopReasonInstrumentation) + { + should_stop = true; + break; + } + } + } + } + if (should_stop) + { + io_handler.SetIsDone(true); + m_stopped_for_crash = true; + } + } } bool @@ -3092,9 +3233,12 @@ CommandInterpreter::GetLLDBCommandsFromIOHandler (const char *prompt, { Debugger &debugger = GetDebugger(); IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger, + IOHandler::Type::CommandList, "lldb", // Name of input reader for history prompt, // Prompt + NULL, // Continuation prompt true, // Get multiple lines + debugger.GetUseColor(), 0, // Don't show line numbers delegate)); // IOHandlerDelegate @@ -3118,9 +3262,12 @@ CommandInterpreter::GetPythonCommandsFromIOHandler (const char *prompt, { Debugger &debugger = GetDebugger(); IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger, + IOHandler::Type::PythonCode, "lldb-python", // Name of input reader for history prompt, // Prompt + NULL, // Continuation prompt true, // Get multiple lines + debugger.GetUseColor(), 0, // Don't show line numbers delegate)); // IOHandlerDelegate @@ -3141,28 +3288,64 @@ CommandInterpreter::IsActive () return m_debugger.IsTopIOHandler (m_command_io_handler_sp); } -void -CommandInterpreter::RunCommandInterpreter(bool auto_handle_events, - bool spawn_thread) +lldb::IOHandlerSP +CommandInterpreter::GetIOHandler(bool force_create, CommandInterpreterRunOptions *options) { - // Only get one line at a time - const bool multiple_lines = false; - // Always re-create the IOHandlerEditline in case the input // changed. The old instance might have had a non-interactive // input and now it does or vice versa. - m_command_io_handler_sp.reset(new IOHandlerEditline (m_debugger, - m_debugger.GetInputFile(), - m_debugger.GetOutputFile(), - m_debugger.GetErrorFile(), - eHandleCommandFlagEchoCommand | eHandleCommandFlagPrintResult, - "lldb", - m_debugger.GetPrompt(), - multiple_lines, - 0, // Don't show line numbers - *this)); - - m_debugger.PushIOHandler(m_command_io_handler_sp); + if (force_create || !m_command_io_handler_sp) + { + // Always re-create the IOHandlerEditline in case the input + // changed. The old instance might have had a non-interactive + // input and now it does or vice versa. + uint32_t flags = 0; + + if (options) + { + if (options->m_stop_on_continue == eLazyBoolYes) + flags |= eHandleCommandFlagStopOnContinue; + if (options->m_stop_on_error == eLazyBoolYes) + flags |= eHandleCommandFlagStopOnError; + if (options->m_stop_on_crash == eLazyBoolYes) + flags |= eHandleCommandFlagStopOnCrash; + if (options->m_echo_commands != eLazyBoolNo) + flags |= eHandleCommandFlagEchoCommand; + if (options->m_print_results != eLazyBoolNo) + flags |= eHandleCommandFlagPrintResult; + } + else + { + flags = eHandleCommandFlagEchoCommand | eHandleCommandFlagPrintResult; + } + + m_command_io_handler_sp.reset(new IOHandlerEditline (m_debugger, + IOHandler::Type::CommandInterpreter, + m_debugger.GetInputFile(), + m_debugger.GetOutputFile(), + m_debugger.GetErrorFile(), + flags, + "lldb", + m_debugger.GetPrompt(), + NULL, // Continuation prompt + false, // Don't enable multiple line input, just single line commands + m_debugger.GetUseColor(), + 0, // Don't show line numbers + *this)); + } + return m_command_io_handler_sp; +} + +void +CommandInterpreter::RunCommandInterpreter(bool auto_handle_events, + bool spawn_thread, + CommandInterpreterRunOptions &options) +{ + // Always re-create the command intepreter when we run it in case + // any file handles have changed. + bool force_create = true; + m_debugger.PushIOHandler(GetIOHandler(force_create, &options)); + m_stopped_for_crash = false; if (auto_handle_events) m_debugger.StartEventHandlerThread(); @@ -3174,10 +3357,10 @@ CommandInterpreter::RunCommandInterpreter(bool auto_handle_events, else { m_debugger.ExecuteIOHanders(); - + if (auto_handle_events) m_debugger.StopEventHandlerThread(); } - + } diff --git a/source/Interpreter/CommandObject.cpp b/source/Interpreter/CommandObject.cpp index 3fdbf994fe7a..753e720b0f6e 100644 --- a/source/Interpreter/CommandObject.cpp +++ b/source/Interpreter/CommandObject.cpp @@ -49,7 +49,7 @@ CommandObject::CommandObject uint32_t flags ) : m_interpreter (interpreter), - m_cmd_name (name), + m_cmd_name (name ? name : ""), m_cmd_help_short (), m_cmd_help_long (), m_cmd_syntax (), @@ -236,19 +236,34 @@ CommandObject::CheckRequirements (CommandReturnObject &result) if ((flags & eFlagRequiresProcess) && !m_exe_ctx.HasProcessScope()) { - result.AppendError (GetInvalidProcessDescription()); + if (!m_exe_ctx.HasTargetScope()) + result.AppendError (GetInvalidTargetDescription()); + else + result.AppendError (GetInvalidProcessDescription()); return false; } if ((flags & eFlagRequiresThread) && !m_exe_ctx.HasThreadScope()) { - result.AppendError (GetInvalidThreadDescription()); + if (!m_exe_ctx.HasTargetScope()) + result.AppendError (GetInvalidTargetDescription()); + else if (!m_exe_ctx.HasProcessScope()) + result.AppendError (GetInvalidProcessDescription()); + else + result.AppendError (GetInvalidThreadDescription()); return false; } if ((flags & eFlagRequiresFrame) && !m_exe_ctx.HasFrameScope()) { - result.AppendError (GetInvalidFrameDescription()); + if (!m_exe_ctx.HasTargetScope()) + result.AppendError (GetInvalidTargetDescription()); + else if (!m_exe_ctx.HasProcessScope()) + result.AppendError (GetInvalidProcessDescription()); + else if (!m_exe_ctx.HasThreadScope()) + result.AppendError (GetInvalidThreadDescription()); + else + result.AppendError (GetInvalidFrameDescription()); return false; } @@ -733,6 +748,22 @@ BreakpointIDRangeHelpTextCallback () } static const char * +BreakpointNameHelpTextCallback () +{ + return "A name that can be added to a breakpoint when it is created, or later " + "on with the \"breakpoint name add\" command. " + "Breakpoint names can be used to specify breakpoints in all the places breakpoint ID's " + "and breakpoint ID ranges can be used. As such they provide a convenient way to group breakpoints, " + "and to operate on breakpoints you create without having to track the breakpoint number. " + "Note, the attributes you set when using a breakpoint name in a breakpoint command don't " + "adhere to the name, but instead are set individually on all the breakpoints currently tagged with that name. Future breakpoints " + "tagged with that name will not pick up the attributes previously given using that name. " + "In order to distinguish breakpoint names from breakpoint ID's and ranges, " + "names must start with a letter from a-z or A-Z and cannot contain spaces, \".\" or \"-\". " + "Also, breakpoint names can only be applied to breakpoints, not to breakpoint locations."; +} + +static const char * GDBFormatHelpTextCallback () { return "A GDB format consists of a repeat count, a format letter and a size letter. " @@ -1003,6 +1034,18 @@ CommandObject::GetArgumentDescriptionAsCString (const lldb::CommandArgumentType return nullptr; } +Target * +CommandObject::GetDummyTarget() +{ + return m_interpreter.GetDebugger().GetDummyTarget(); +} + +Target * +CommandObject::GetSelectedOrDummyTarget(bool prefer_dummy) +{ + return m_interpreter.GetDebugger().GetSelectedOrDummyTarget(prefer_dummy); +} + bool CommandObjectParsed::Execute (const char *args_string, CommandReturnObject &result) { @@ -1085,6 +1128,7 @@ CommandObject::g_arguments_data[] = { eArgTypeBoolean, "boolean", CommandCompletions::eNoCompletion, { nullptr, false }, "A Boolean value: 'true' or 'false'" }, { eArgTypeBreakpointID, "breakpt-id", CommandCompletions::eNoCompletion, { BreakpointIDHelpTextCallback, false }, nullptr }, { eArgTypeBreakpointIDRange, "breakpt-id-list", CommandCompletions::eNoCompletion, { BreakpointIDRangeHelpTextCallback, false }, nullptr }, + { eArgTypeBreakpointName, "breakpoint-name", CommandCompletions::eNoCompletion, { BreakpointNameHelpTextCallback, false }, nullptr }, { eArgTypeByteSize, "byte-size", CommandCompletions::eNoCompletion, { nullptr, false }, "Number of bytes to use." }, { eArgTypeClassName, "class-name", CommandCompletions::eNoCompletion, { nullptr, false }, "Then name of a class from the debug information in the program." }, { eArgTypeCommandName, "cmd-name", CommandCompletions::eNoCompletion, { nullptr, false }, "A debugger command (may be multiple words), without any options or arguments." }, @@ -1103,6 +1147,7 @@ CommandObject::g_arguments_data[] = { eArgTypeFunctionName, "function-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a function." }, { eArgTypeFunctionOrSymbol, "function-or-symbol", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a function or symbol." }, { eArgTypeGDBFormat, "gdb-format", CommandCompletions::eNoCompletion, { GDBFormatHelpTextCallback, true }, nullptr }, + { eArgTypeHelpText, "help-text", CommandCompletions::eNoCompletion, { nullptr, false }, "Text to be used as help for some other entity in LLDB" }, { eArgTypeIndex, "index", CommandCompletions::eNoCompletion, { nullptr, false }, "An index into a list." }, { eArgTypeLanguage, "language", CommandCompletions::eNoCompletion, { LanguageTypeHelpTextCallback, true }, nullptr }, { eArgTypeLineNum, "linenum", CommandCompletions::eNoCompletion, { nullptr, false }, "Line number in a source file." }, diff --git a/source/Interpreter/CommandObjectRegexCommand.cpp b/source/Interpreter/CommandObjectRegexCommand.cpp index d27320dd1f3d..efc7c33fa8f6 100644 --- a/source/Interpreter/CommandObjectRegexCommand.cpp +++ b/source/Interpreter/CommandObjectRegexCommand.cpp @@ -31,12 +31,14 @@ CommandObjectRegexCommand::CommandObjectRegexCommand const char *help, const char *syntax, uint32_t max_matches, - uint32_t completion_type_mask + uint32_t completion_type_mask, + bool is_removable ) : CommandObjectRaw (interpreter, name, help, syntax), m_max_matches (max_matches), m_completion_type_mask (completion_type_mask), - m_entries () + m_entries (), + m_is_removable (is_removable) { } diff --git a/source/Interpreter/OptionGroupPlatform.cpp b/source/Interpreter/OptionGroupPlatform.cpp index 9ffd5f072d90..7e5e1245a79f 100644 --- a/source/Interpreter/OptionGroupPlatform.cpp +++ b/source/Interpreter/OptionGroupPlatform.cpp @@ -33,7 +33,7 @@ OptionGroupPlatform::CreatePlatformWithOptions (CommandInterpreter &interpreter, if (!m_platform_name.empty()) { - platform_sp = Platform::Create (m_platform_name.c_str(), error); + platform_sp = Platform::Create (ConstString(m_platform_name.c_str()), error); if (platform_sp) { if (platform_arch.IsValid() && !platform_sp->IsCompatibleArchitecture(arch, false, &platform_arch)) diff --git a/source/Interpreter/OptionGroupValueObjectDisplay.cpp b/source/Interpreter/OptionGroupValueObjectDisplay.cpp index 125e5fb0a5d6..b6c63fa44c40 100644 --- a/source/Interpreter/OptionGroupValueObjectDisplay.cpp +++ b/source/Interpreter/OptionGroupValueObjectDisplay.cpp @@ -45,6 +45,7 @@ g_option_table[] = { LLDB_OPT_SET_1, false, "no-summary-depth", 'Y', OptionParser::eOptionalArgument, nullptr, nullptr, 0, eArgTypeCount, "Set the depth at which omitting summary information stops (default is 1)."}, { LLDB_OPT_SET_1, false, "raw-output", 'R', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Don't use formatting options."}, { LLDB_OPT_SET_1, false, "show-all-children", 'A', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Ignore the upper bound on the number of children to show."}, + { LLDB_OPT_SET_1, false, "validate", 'V', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeBoolean, "Show results of type validators."}, { 0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr } }; @@ -115,6 +116,13 @@ OptionGroupValueObjectDisplay::SetOptionValue (CommandInterpreter &interpreter, if (!success) error.SetErrorStringWithFormat("invalid synthetic-type '%s'", option_arg); break; + + case 'V': + run_validator = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid validate '%s'", option_arg); + break; + default: error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); break; @@ -137,6 +145,7 @@ OptionGroupValueObjectDisplay::OptionParsingStarting (CommandInterpreter &interp use_synth = true; be_raw = false; ignore_cap = false; + run_validator = false; Target *target = interpreter.GetExecutionContext().GetTargetPtr(); if (target != nullptr) @@ -176,7 +185,9 @@ OptionGroupValueObjectDisplay::GetAsDumpOptions (LanguageRuntimeDescriptionDispl .SetHideValue(use_objc); if (be_raw) - options.SetRawDisplay(true); + options.SetRawDisplay(); + + options.SetRunValidator(run_validator); return options; } diff --git a/source/Interpreter/OptionValue.cpp b/source/Interpreter/OptionValue.cpp index bc1e1c4c4779..a08a6127db15 100644 --- a/source/Interpreter/OptionValue.cpp +++ b/source/Interpreter/OptionValue.cpp @@ -71,6 +71,21 @@ OptionValue::GetAsBoolean () const return nullptr; } +const OptionValueChar * +OptionValue::GetAsChar () const +{ + if (GetType () == OptionValue::eTypeChar) + return static_cast<const OptionValueChar *>(this); + return nullptr; +} + +OptionValueChar * +OptionValue::GetAsChar () +{ + if (GetType () == OptionValue::eTypeChar) + return static_cast<OptionValueChar *>(this); + return nullptr; +} OptionValueFileSpec * OptionValue::GetAsFileSpec () @@ -342,6 +357,27 @@ OptionValue::SetBooleanValue (bool new_value) return false; } +char +OptionValue::GetCharValue(char fail_value) const +{ + const OptionValueChar *option_value = GetAsChar(); + if (option_value) + return option_value->GetCurrentValue(); + return fail_value; +} + +char +OptionValue::SetCharValue(char new_value) +{ + OptionValueChar *option_value = GetAsChar(); + if (option_value) + { + option_value->SetCurrentValue(new_value); + return true; + } + return false; +} + int64_t OptionValue::GetEnumerationValue (int64_t fail_value) const { @@ -520,6 +556,8 @@ OptionValue::GetBuiltinTypeAsCString (Type t) case eTypeArgs: return "arguments"; case eTypeArray: return "array"; case eTypeBoolean: return "boolean"; + case eTypeChar: + return "char"; case eTypeDictionary: return "dictionary"; case eTypeEnum: return "enum"; case eTypeFileSpec: return "file"; @@ -547,6 +585,7 @@ OptionValue::CreateValueFromCStringForTypeMask (const char *value_cstr, uint32_t { case 1u << eTypeArch: value_sp.reset(new OptionValueArch()); break; case 1u << eTypeBoolean: value_sp.reset(new OptionValueBoolean(false)); break; + case 1u << eTypeChar: value_sp.reset(new OptionValueChar('\0')); break; case 1u << eTypeFileSpec: value_sp.reset(new OptionValueFileSpec()); break; case 1u << eTypeFormat: value_sp.reset(new OptionValueFormat(eFormatInvalid)); break; case 1u << eTypeSInt64: value_sp.reset(new OptionValueSInt64()); break; diff --git a/source/Interpreter/OptionValueArch.cpp b/source/Interpreter/OptionValueArch.cpp index 6d283d6b9275..7df149234bda 100644 --- a/source/Interpreter/OptionValueArch.cpp +++ b/source/Interpreter/OptionValueArch.cpp @@ -50,6 +50,7 @@ OptionValueArch::SetValueFromCString (const char *value_cstr, VarSetOperationTyp { case eVarSetOperationClear: Clear(); + NotifyValueChanged(); break; case eVarSetOperationReplace: @@ -57,7 +58,10 @@ OptionValueArch::SetValueFromCString (const char *value_cstr, VarSetOperationTyp if (value_cstr && value_cstr[0]) { if (m_current_value.SetTriple (value_cstr)) + { m_value_was_set = true; + NotifyValueChanged(); + } else error.SetErrorStringWithFormat("unsupported architecture '%s'", value_cstr); } diff --git a/source/Interpreter/OptionValueArray.cpp b/source/Interpreter/OptionValueArray.cpp index 769aadd7b308..c0d48c1e7bd2 100644 --- a/source/Interpreter/OptionValueArray.cpp +++ b/source/Interpreter/OptionValueArray.cpp @@ -53,6 +53,7 @@ OptionValueArray::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint break; case eTypeBoolean: + case eTypeChar: case eTypeEnum: case eTypeFileSpec: case eTypeFormat: @@ -75,6 +76,7 @@ Error OptionValueArray::SetValueFromCString (const char *value, VarSetOperationType op) { Args args(value); + NotifyValueChanged(); return SetArgs (args, op); } diff --git a/source/Interpreter/OptionValueBoolean.cpp b/source/Interpreter/OptionValueBoolean.cpp index bf153a1442c7..71cc2afb98e1 100644 --- a/source/Interpreter/OptionValueBoolean.cpp +++ b/source/Interpreter/OptionValueBoolean.cpp @@ -45,6 +45,7 @@ OptionValueBoolean::SetValueFromCString (const char *value_cstr, { case eVarSetOperationClear: Clear(); + NotifyValueChanged(); break; case eVarSetOperationReplace: @@ -56,6 +57,7 @@ OptionValueBoolean::SetValueFromCString (const char *value_cstr, { m_value_was_set = true; m_current_value = value; + NotifyValueChanged(); } else { diff --git a/source/Interpreter/OptionValueChar.cpp b/source/Interpreter/OptionValueChar.cpp new file mode 100644 index 000000000000..7a0135314c81 --- /dev/null +++ b/source/Interpreter/OptionValueChar.cpp @@ -0,0 +1,80 @@ +//===-- OptionValueChar.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueChar.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Interpreter/Args.h" +#include "llvm/ADT/STLExtras.h" + +using namespace lldb; +using namespace lldb_private; + +void +OptionValueChar::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) +{ + if (dump_mask & eDumpOptionType) + strm.Printf ("(%s)", GetTypeAsCString ()); + + if (dump_mask & eDumpOptionValue) + { + if (dump_mask & eDumpOptionType) + strm.PutCString (" = "); + if (m_current_value != '\0') + strm.PutChar(m_current_value); + else + strm.PutCString("(null)"); + } +} + +Error +OptionValueChar::SetValueFromCString (const char *value_cstr, + VarSetOperationType op) +{ + Error error; + switch (op) + { + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + { + bool success = false; + char char_value = Args::StringToChar(value_cstr, '\0', &success); + if (success) + { + m_current_value = char_value; + m_value_was_set = true; + } + else + error.SetErrorStringWithFormat("'%s' cannot be longer than 1 character", value_cstr); + } + break; + + default: + error = OptionValue::SetValueFromCString (value_cstr, op); + break; + } + return error; +} + +lldb::OptionValueSP +OptionValueChar::DeepCopy () const +{ + return OptionValueSP(new OptionValueChar(*this)); +} + + diff --git a/source/Interpreter/OptionValueDictionary.cpp b/source/Interpreter/OptionValueDictionary.cpp index b560937141b3..e5299f8cc390 100644 --- a/source/Interpreter/OptionValueDictionary.cpp +++ b/source/Interpreter/OptionValueDictionary.cpp @@ -64,6 +64,7 @@ OptionValueDictionary::DumpValue (const ExecutionContext *exe_ctx, Stream &strm, break; case eTypeBoolean: + case eTypeChar: case eTypeEnum: case eTypeFileSpec: case eTypeFormat: @@ -220,7 +221,10 @@ Error OptionValueDictionary::SetValueFromCString (const char *value_cstr, VarSetOperationType op) { Args args(value_cstr); - return SetArgs (args, op); + Error error = SetArgs (args, op); + if (error.Success()) + NotifyValueChanged(); + return error; } lldb::OptionValueSP diff --git a/source/Interpreter/OptionValueEnumeration.cpp b/source/Interpreter/OptionValueEnumeration.cpp index 7aceac91b601..dbaeb185fa3a 100644 --- a/source/Interpreter/OptionValueEnumeration.cpp +++ b/source/Interpreter/OptionValueEnumeration.cpp @@ -62,6 +62,7 @@ OptionValueEnumeration::SetValueFromCString (const char *value, VarSetOperationT { case eVarSetOperationClear: Clear (); + NotifyValueChanged(); break; case eVarSetOperationReplace: @@ -73,6 +74,7 @@ OptionValueEnumeration::SetValueFromCString (const char *value, VarSetOperationT if (enumerator_entry) { m_current_value = enumerator_entry->value.value; + NotifyValueChanged(); } else { diff --git a/source/Interpreter/OptionValueFileSpec.cpp b/source/Interpreter/OptionValueFileSpec.cpp index c8aaadef23bc..3f466985a83a 100644 --- a/source/Interpreter/OptionValueFileSpec.cpp +++ b/source/Interpreter/OptionValueFileSpec.cpp @@ -78,6 +78,7 @@ OptionValueFileSpec::SetValueFromCString (const char *value_cstr, { case eVarSetOperationClear: Clear (); + NotifyValueChanged(); break; case eVarSetOperationReplace: @@ -100,6 +101,7 @@ OptionValueFileSpec::SetValueFromCString (const char *value_cstr, m_value_was_set = true; m_current_value.SetFile(filepath.c_str(), true); m_data_sp.reset(); + NotifyValueChanged(); } else { diff --git a/source/Interpreter/OptionValueFileSpecLIst.cpp b/source/Interpreter/OptionValueFileSpecLIst.cpp index e493c70d5a55..7150ad474643 100644 --- a/source/Interpreter/OptionValueFileSpecLIst.cpp +++ b/source/Interpreter/OptionValueFileSpecLIst.cpp @@ -51,6 +51,7 @@ OptionValueFileSpecList::SetValueFromCString (const char *value, VarSetOperation { case eVarSetOperationClear: Clear (); + NotifyValueChanged(); break; case eVarSetOperationReplace: @@ -72,6 +73,7 @@ OptionValueFileSpecList::SetValueFromCString (const char *value, VarSetOperation else m_current_value.Append(file); } + NotifyValueChanged(); } } else @@ -94,6 +96,7 @@ OptionValueFileSpecList::SetValueFromCString (const char *value, VarSetOperation FileSpec file (args.GetArgumentAtIndex(i), false); m_current_value.Append(file); } + NotifyValueChanged(); } else { @@ -120,6 +123,7 @@ OptionValueFileSpecList::SetValueFromCString (const char *value, VarSetOperation FileSpec file (args.GetArgumentAtIndex(i), false); m_current_value.Insert (idx, file); } + NotifyValueChanged(); } } else @@ -155,6 +159,7 @@ OptionValueFileSpecList::SetValueFromCString (const char *value, VarSetOperation m_current_value.Remove (j); } } + NotifyValueChanged(); } else { @@ -172,9 +177,6 @@ OptionValueFileSpecList::SetValueFromCString (const char *value, VarSetOperation break; } return error; - - m_value_was_set = true; - return Error(); } lldb::OptionValueSP diff --git a/source/Interpreter/OptionValueFormat.cpp b/source/Interpreter/OptionValueFormat.cpp index 296dd983208c..d91f10b0edeb 100644 --- a/source/Interpreter/OptionValueFormat.cpp +++ b/source/Interpreter/OptionValueFormat.cpp @@ -43,6 +43,7 @@ OptionValueFormat::SetValueFromCString (const char *value_cstr, VarSetOperationT { case eVarSetOperationClear: Clear(); + NotifyValueChanged(); break; case eVarSetOperationReplace: @@ -54,6 +55,7 @@ OptionValueFormat::SetValueFromCString (const char *value_cstr, VarSetOperationT { m_value_was_set = true; m_current_value = new_format; + NotifyValueChanged(); } } break; diff --git a/source/Interpreter/OptionValuePathMappings.cpp b/source/Interpreter/OptionValuePathMappings.cpp index 88a0eb7d6a3b..56f2ecf4f5fe 100644 --- a/source/Interpreter/OptionValuePathMappings.cpp +++ b/source/Interpreter/OptionValuePathMappings.cpp @@ -43,6 +43,7 @@ OptionValuePathMappings::SetValueFromCString (const char *value, VarSetOperation { case eVarSetOperationClear: Clear (); + NotifyValueChanged(); break; case eVarSetOperationReplace: @@ -64,6 +65,7 @@ OptionValuePathMappings::SetValueFromCString (const char *value, VarSetOperation if (!m_path_mappings.Replace (a, b, idx, m_notify_changes)) m_path_mappings.Append(a, b, m_notify_changes); } + NotifyValueChanged(); } } else @@ -97,6 +99,7 @@ OptionValuePathMappings::SetValueFromCString (const char *value, VarSetOperation m_path_mappings.Append(a, b, m_notify_changes); m_value_was_set = true; } + NotifyValueChanged(); } break; @@ -121,6 +124,7 @@ OptionValuePathMappings::SetValueFromCString (const char *value, VarSetOperation ConstString b(args.GetArgumentAtIndex(i+1)); m_path_mappings.Insert (a, b, idx, m_notify_changes); } + NotifyValueChanged(); } } else @@ -156,6 +160,7 @@ OptionValuePathMappings::SetValueFromCString (const char *value, VarSetOperation m_path_mappings.Remove (j, m_notify_changes); } } + NotifyValueChanged(); } else { @@ -173,9 +178,6 @@ OptionValuePathMappings::SetValueFromCString (const char *value, VarSetOperation break; } return error; - - m_value_was_set = true; - return Error(); } lldb::OptionValueSP diff --git a/source/Interpreter/OptionValueProperties.cpp b/source/Interpreter/OptionValueProperties.cpp index 0497ee1c15d7..6ec2aa569fa3 100644 --- a/source/Interpreter/OptionValueProperties.cpp +++ b/source/Interpreter/OptionValueProperties.cpp @@ -81,6 +81,16 @@ OptionValueProperties::Initialize (const PropertyDefinition *defs) } void +OptionValueProperties::SetValueChangedCallback (uint32_t property_idx, + OptionValueChangedCallback callback, + void *baton) +{ + Property *property = ProtectedGetPropertyAtIndex (property_idx); + if (property) + property->SetValueChangedCallback (callback, baton); +} + +void OptionValueProperties::AppendProperty(const ConstString &name, const ConstString &desc, bool is_global, diff --git a/source/Interpreter/OptionValueRegex.cpp b/source/Interpreter/OptionValueRegex.cpp index f1ba0ed04d6c..f51cf02edf56 100644 --- a/source/Interpreter/OptionValueRegex.cpp +++ b/source/Interpreter/OptionValueRegex.cpp @@ -57,6 +57,7 @@ OptionValueRegex::SetValueFromCString (const char *value_cstr, case eVarSetOperationClear: Clear(); + NotifyValueChanged(); break; case eVarSetOperationReplace: @@ -64,6 +65,7 @@ OptionValueRegex::SetValueFromCString (const char *value_cstr, if (m_regex.Compile (value_cstr, m_regex.GetCompileFlags())) { m_value_was_set = true; + NotifyValueChanged(); } else { diff --git a/source/Interpreter/OptionValueSInt64.cpp b/source/Interpreter/OptionValueSInt64.cpp index 04bf9306ade6..1827cc1d873f 100644 --- a/source/Interpreter/OptionValueSInt64.cpp +++ b/source/Interpreter/OptionValueSInt64.cpp @@ -44,6 +44,7 @@ OptionValueSInt64::SetValueFromCString (const char *value_cstr, VarSetOperationT { case eVarSetOperationClear: Clear(); + NotifyValueChanged(); break; case eVarSetOperationReplace: @@ -57,6 +58,7 @@ OptionValueSInt64::SetValueFromCString (const char *value_cstr, VarSetOperationT { m_value_was_set = true; m_current_value = value; + NotifyValueChanged(); } else error.SetErrorStringWithFormat ("%" PRIi64 " is out of range, valid values must be between %" PRIi64 " and %" PRIi64 ".", diff --git a/source/Interpreter/OptionValueString.cpp b/source/Interpreter/OptionValueString.cpp index df047bd98996..a1b80d8fc4f6 100644 --- a/source/Interpreter/OptionValueString.cpp +++ b/source/Interpreter/OptionValueString.cpp @@ -94,30 +94,32 @@ OptionValueString::SetValueFromCString (const char *value_cstr, case eVarSetOperationAppend: { - std::string new_value(m_current_value); - if (value_cstr && value_cstr[0]) - { - if (m_options.Test (eOptionEncodeCharacterEscapeSequences)) + std::string new_value(m_current_value); + if (value_cstr && value_cstr[0]) { - std::string str; - Args::EncodeEscapeSequences (value_cstr, str); - new_value.append(str); + if (m_options.Test (eOptionEncodeCharacterEscapeSequences)) + { + std::string str; + Args::EncodeEscapeSequences (value_cstr, str); + new_value.append(str); + } + else + new_value.append(value_cstr); } - else - new_value.append(value_cstr); - } - if (m_validator) - { - error = m_validator(new_value.c_str(),m_validator_baton); - if (error.Fail()) - return error; - } - m_current_value.assign(new_value); + if (m_validator) + { + error = m_validator(new_value.c_str(),m_validator_baton); + if (error.Fail()) + return error; + } + m_current_value.assign(new_value); + NotifyValueChanged(); } break; case eVarSetOperationClear: Clear (); + NotifyValueChanged(); break; case eVarSetOperationReplace: @@ -137,6 +139,7 @@ OptionValueString::SetValueFromCString (const char *value_cstr, { SetCurrentValue (value_cstr); } + NotifyValueChanged(); break; } return error; diff --git a/source/Interpreter/OptionValueUInt64.cpp b/source/Interpreter/OptionValueUInt64.cpp index 56b3a1c74702..3e12c030255c 100644 --- a/source/Interpreter/OptionValueUInt64.cpp +++ b/source/Interpreter/OptionValueUInt64.cpp @@ -51,6 +51,7 @@ OptionValueUInt64::SetValueFromCString (const char *value_cstr, VarSetOperationT { case eVarSetOperationClear: Clear (); + NotifyValueChanged(); break; case eVarSetOperationReplace: @@ -62,6 +63,7 @@ OptionValueUInt64::SetValueFromCString (const char *value_cstr, VarSetOperationT { m_value_was_set = true; m_current_value = value; + NotifyValueChanged(); } else { diff --git a/source/Interpreter/OptionValueUUID.cpp b/source/Interpreter/OptionValueUUID.cpp index 0141911d97ad..c228cf6e415e 100644 --- a/source/Interpreter/OptionValueUUID.cpp +++ b/source/Interpreter/OptionValueUUID.cpp @@ -45,6 +45,7 @@ OptionValueUUID::SetValueFromCString (const char *value_cstr, { case eVarSetOperationClear: Clear(); + NotifyValueChanged(); break; case eVarSetOperationReplace: @@ -53,7 +54,10 @@ OptionValueUUID::SetValueFromCString (const char *value_cstr, if (m_uuid.SetFromCString(value_cstr) == 0) error.SetErrorStringWithFormat ("invalid uuid string value '%s'", value_cstr); else + { m_value_was_set = true; + NotifyValueChanged(); + } } break; diff --git a/source/Interpreter/Property.cpp b/source/Interpreter/Property.cpp index 49376266b077..7f7219fc0d50 100644 --- a/source/Interpreter/Property.cpp +++ b/source/Interpreter/Property.cpp @@ -60,7 +60,11 @@ Property::Property (const PropertyDefinition &definition) : else m_value_sp.reset (new OptionValueBoolean(definition.default_uint_value != 0)); break; - + + case OptionValue::eTypeChar: + m_value_sp.reset(new OptionValueChar(Args::StringToChar(definition.default_cstr_value, '\0', nullptr))); + break; + case OptionValue::eTypeDictionary: // "definition.default_uint_value" is always a OptionValue::Type m_value_sp.reset (new OptionValueDictionary(OptionValue::ConvertTypeToMask((OptionValue::Type)definition.default_uint_value))); @@ -273,3 +277,12 @@ Property::DumpDescription (CommandInterpreter &interpreter, } } + +void +Property::SetValueChangedCallback (OptionValueChangedCallback callback, void *baton) +{ + if (m_value_sp) + m_value_sp->SetValueChangedCallback (callback, baton); +} + + diff --git a/source/Interpreter/ScriptInterpreter.cpp b/source/Interpreter/ScriptInterpreter.cpp index b6c46f83bd92..45e3b44cc0ac 100644 --- a/source/Interpreter/ScriptInterpreter.cpp +++ b/source/Interpreter/ScriptInterpreter.cpp @@ -124,6 +124,7 @@ ScriptInterpreter::InitializeInterpreter (SWIGInitCallback python_swig_init_call SWIGPythonGetValueObjectSPFromSBValue swig_get_valobj_sp_from_sbvalue, SWIGPythonUpdateSynthProviderInstance swig_update_provider, SWIGPythonMightHaveChildrenSynthProviderInstance swig_mighthavechildren_provider, + SWIGPythonGetValueSynthProviderInstance swig_getvalue_provider, SWIGPythonCallCommand swig_call_command, SWIGPythonCallModuleInit swig_call_module_init, SWIGPythonCreateOSPlugin swig_create_os_plugin, @@ -131,7 +132,10 @@ ScriptInterpreter::InitializeInterpreter (SWIGInitCallback python_swig_init_call SWIGPythonScriptKeyword_Thread swig_run_script_keyword_thread, SWIGPythonScriptKeyword_Target swig_run_script_keyword_target, SWIGPythonScriptKeyword_Frame swig_run_script_keyword_frame, - SWIGPython_GetDynamicSetting swig_plugin_get) + SWIGPythonScriptKeyword_Value swig_run_script_keyword_value, + SWIGPython_GetDynamicSetting swig_plugin_get, + SWIGPythonCreateScriptedThreadPlan swig_thread_plan_script, + SWIGPythonCallThreadPlan swig_call_thread_plan) { #ifndef LLDB_DISABLE_PYTHON ScriptInterpreterPython::InitializeInterpreter (python_swig_init_callback, @@ -146,6 +150,7 @@ ScriptInterpreter::InitializeInterpreter (SWIGInitCallback python_swig_init_call swig_get_valobj_sp_from_sbvalue, swig_update_provider, swig_mighthavechildren_provider, + swig_getvalue_provider, swig_call_command, swig_call_module_init, swig_create_os_plugin, @@ -153,6 +158,9 @@ ScriptInterpreter::InitializeInterpreter (SWIGInitCallback python_swig_init_call swig_run_script_keyword_thread, swig_run_script_keyword_target, swig_run_script_keyword_frame, - swig_plugin_get); + swig_run_script_keyword_value, + swig_plugin_get, + swig_thread_plan_script, + swig_call_thread_plan); #endif // #ifndef LLDB_DISABLE_PYTHON } diff --git a/source/Interpreter/ScriptInterpreterPython.cpp b/source/Interpreter/ScriptInterpreterPython.cpp index 1b24fea7c218..ab151073f9e9 100644 --- a/source/Interpreter/ScriptInterpreterPython.cpp +++ b/source/Interpreter/ScriptInterpreterPython.cpp @@ -28,15 +28,20 @@ #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Breakpoint/WatchpointOptions.h" #include "lldb/Core/Communication.h" -#include "lldb/Core/ConnectionFileDescriptor.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Timer.h" +#include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/Pipe.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/PythonDataObjects.h" #include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +#if defined(_WIN32) +#include "lldb/Host/windows/ConnectionGenericFileWindows.h" +#endif using namespace lldb; using namespace lldb_private; @@ -54,6 +59,7 @@ static ScriptInterpreter::SWIGPythonCastPyObjectToSBValue g_swig_cast_to_sbvalue static ScriptInterpreter::SWIGPythonGetValueObjectSPFromSBValue g_swig_get_valobj_sp_from_sbvalue = nullptr; static ScriptInterpreter::SWIGPythonUpdateSynthProviderInstance g_swig_update_provider = nullptr; static ScriptInterpreter::SWIGPythonMightHaveChildrenSynthProviderInstance g_swig_mighthavechildren_provider = nullptr; +static ScriptInterpreter::SWIGPythonGetValueSynthProviderInstance g_swig_getvalue_provider = nullptr; static ScriptInterpreter::SWIGPythonCallCommand g_swig_call_command = nullptr; static ScriptInterpreter::SWIGPythonCallModuleInit g_swig_call_module_init = nullptr; static ScriptInterpreter::SWIGPythonCreateOSPlugin g_swig_create_os_plugin = nullptr; @@ -61,7 +67,10 @@ static ScriptInterpreter::SWIGPythonScriptKeyword_Process g_swig_run_script_keyw static ScriptInterpreter::SWIGPythonScriptKeyword_Thread g_swig_run_script_keyword_thread = nullptr; static ScriptInterpreter::SWIGPythonScriptKeyword_Target g_swig_run_script_keyword_target = nullptr; static ScriptInterpreter::SWIGPythonScriptKeyword_Frame g_swig_run_script_keyword_frame = nullptr; +static ScriptInterpreter::SWIGPythonScriptKeyword_Value g_swig_run_script_keyword_value = nullptr; static ScriptInterpreter::SWIGPython_GetDynamicSetting g_swig_plugin_get = nullptr; +static ScriptInterpreter::SWIGPythonCreateScriptedThreadPlan g_swig_thread_plan_script = nullptr; +static ScriptInterpreter::SWIGPythonCallThreadPlan g_swig_call_thread_plan = nullptr; static std::string ReadPythonBacktrace (PyObject* py_backtrace); @@ -435,19 +444,23 @@ ScriptInterpreterPython::EnterSession (uint16_t on_entry_flags, if (in == nullptr || out == nullptr || err == nullptr) m_interpreter.GetDebugger().AdoptTopIOHandlerFilesIfInvalid (in_sp, out_sp, err_sp); - if (in == nullptr && in_sp && (on_entry_flags & Locker::NoSTDIN) == 0) - in = in_sp->GetFile().GetStream(); - if (in) - { - m_saved_stdin.Reset(sys_module_dict.GetItemForKey("stdin")); + m_saved_stdin.Reset(); - PyObject *new_file = PyFile_FromFile (in, (char *) "", (char *) "r", nullptr); - sys_module_dict.SetItemForKey ("stdin", new_file); - Py_DECREF (new_file); + if ((on_entry_flags & Locker::NoSTDIN) == 0) + { + // STDIN is enabled + if (in == nullptr && in_sp) + in = in_sp->GetFile().GetStream(); + if (in) + { + m_saved_stdin.Reset(sys_module_dict.GetItemForKey("stdin")); + // This call can deadlock your process if the file is locked + PyObject *new_file = PyFile_FromFile (in, (char *) "", (char *) "r", nullptr); + sys_module_dict.SetItemForKey ("stdin", new_file); + Py_DECREF (new_file); + } } - else - m_saved_stdin.Reset(); - + if (out == nullptr && out_sp) out = out_sp->GetFile().GetStream(); if (out) @@ -594,9 +607,16 @@ ScriptInterpreterPython::ExecuteOneLine (const char *command, CommandReturnObjec // Set output to a temporary file so we can forward the results on to the result object Pipe pipe; - if (pipe.Open()) + Error pipe_result = pipe.CreateNew(false); + if (pipe_result.Success()) { +#if defined(_WIN32) + lldb::file_t read_file = pipe.GetReadNativeHandle(); + pipe.ReleaseReadFileDescriptor(); + std::unique_ptr<ConnectionGenericFile> conn_ap(new ConnectionGenericFile(read_file, true)); +#else std::unique_ptr<ConnectionFileDescriptor> conn_ap(new ConnectionFileDescriptor(pipe.ReleaseReadFileDescriptor(), true)); +#endif if (conn_ap->IsConnected()) { output_comm.SetConnection(conn_ap.release()); @@ -632,7 +652,8 @@ ScriptInterpreterPython::ExecuteOneLine (const char *command, CommandReturnObjec Locker locker(this, ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | - (options.GetSetLLDBGlobals() ? ScriptInterpreterPython::Locker::InitGlobals : 0), + (options.GetSetLLDBGlobals() ? ScriptInterpreterPython::Locker::InitGlobals : 0) | + ((result && result->GetInteractive()) ? 0: Locker::NoSTDIN), ScriptInterpreterPython::Locker::FreeAcquiredLock | ScriptInterpreterPython::Locker::TearDownSession, in_file, @@ -708,7 +729,7 @@ public: IOHandlerPythonInterpreter (Debugger &debugger, ScriptInterpreterPython *python) : - IOHandler (debugger), + IOHandler (debugger, IOHandler::Type::PythonInterpreter), m_python(python) { @@ -1617,6 +1638,87 @@ ScriptInterpreterPython::OSPlugin_CreateThread (lldb::ScriptInterpreterObjectSP } lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::CreateScriptedThreadPlan (const char *class_name, + lldb::ThreadPlanSP thread_plan_sp) +{ + if (class_name == nullptr || class_name[0] == '\0') + return lldb::ScriptInterpreterObjectSP(); + + if (!thread_plan_sp.get()) + return lldb::ScriptInterpreterObjectSP(); + + Debugger &debugger = thread_plan_sp->GetTarget().GetDebugger(); + ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); + ScriptInterpreterPython *python_interpreter = static_cast<ScriptInterpreterPython *>(script_interpreter); + + if (!script_interpreter) + return lldb::ScriptInterpreterObjectSP(); + + void* ret_val; + + { + Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); + + ret_val = g_swig_thread_plan_script (class_name, + python_interpreter->m_dictionary_name.c_str(), + thread_plan_sp); + } + + return MakeScriptObject(ret_val); +} + +bool +ScriptInterpreterPython::ScriptedThreadPlanExplainsStop (lldb::ScriptInterpreterObjectSP implementor_sp, + Event *event, + bool &script_error) +{ + bool explains_stop = true; + if (implementor_sp) + { + Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); + explains_stop = g_swig_call_thread_plan (implementor_sp->GetObject(), "explains_stop", event, script_error); + if (script_error) + return true; + } + return explains_stop; +} + +bool +ScriptInterpreterPython::ScriptedThreadPlanShouldStop (lldb::ScriptInterpreterObjectSP implementor_sp, + Event *event, + bool &script_error) +{ + bool should_stop = true; + if (implementor_sp) + { + Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); + should_stop = g_swig_call_thread_plan (implementor_sp->GetObject(), "should_stop", event, script_error); + if (script_error) + return true; + } + return should_stop; +} + +lldb::StateType +ScriptInterpreterPython::ScriptedThreadPlanGetRunState (lldb::ScriptInterpreterObjectSP implementor_sp, + bool &script_error) +{ + bool should_step = false; + if (implementor_sp) + { + Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); + should_step = g_swig_call_thread_plan (implementor_sp->GetObject(), "should_step", NULL, script_error); + if (script_error) + should_step = true; + } + if (should_step) + return lldb::eStateStepping; + else + return lldb::eStateRunning; +} + + +lldb::ScriptInterpreterObjectSP ScriptInterpreterPython::LoadPluginModule (const FileSpec& file_spec, lldb_private::Error& error) { @@ -1759,6 +1861,7 @@ bool ScriptInterpreterPython::GetScriptedSummary (const char *python_function_name, lldb::ValueObjectSP valobj, lldb::ScriptInterpreterObjectSP& callee_wrapper_sp, + const TypeSummaryOptions& options, std::string& retval) { @@ -1780,11 +1883,14 @@ ScriptInterpreterPython::GetScriptedSummary (const char *python_function_name, { Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); { + TypeSummaryOptionsSP options_sp(new TypeSummaryOptions(options)); + Timer scoped_timer ("g_swig_typescript_callback","g_swig_typescript_callback"); ret_val = g_swig_typescript_callback (python_function_name, GetSessionDictionary().get(), valobj, &new_callee, + options_sp, retval); } } @@ -2056,6 +2162,42 @@ ScriptInterpreterPython::MightHaveChildrenSynthProviderInstance (const lldb::Scr return ret_val; } +lldb::ValueObjectSP +ScriptInterpreterPython::GetSyntheticValue (const lldb::ScriptInterpreterObjectSP& implementor_sp) +{ + lldb::ValueObjectSP ret_val(nullptr); + + if (!implementor_sp) + return ret_val; + + void* implementor = implementor_sp->GetObject(); + + if (!implementor) + return ret_val; + + if (!g_swig_getvalue_provider || !g_swig_cast_to_sbvalue || !g_swig_get_valobj_sp_from_sbvalue) + return ret_val; + + { + Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); + void* child_ptr = g_swig_getvalue_provider (implementor); + if (child_ptr != nullptr && child_ptr != Py_None) + { + lldb::SBValue* sb_value_ptr = (lldb::SBValue*)g_swig_cast_to_sbvalue(child_ptr); + if (sb_value_ptr == nullptr) + Py_XDECREF(child_ptr); + else + ret_val = g_swig_get_valobj_sp_from_sbvalue (sb_value_ptr); + } + else + { + Py_XDECREF(child_ptr); + } + } + + return ret_val; +} + static std::string ReadPythonBacktrace (PyObject* py_backtrace) { @@ -2240,6 +2382,38 @@ ScriptInterpreterPython::RunScriptFormatKeyword (const char* impl_function, } return ret_val; } + +bool +ScriptInterpreterPython::RunScriptFormatKeyword (const char* impl_function, + ValueObject *value, + std::string& output, + Error& error) +{ + bool ret_val; + if (!value) + { + error.SetErrorString("no value"); + return false; + } + if (!impl_function || !impl_function[0]) + { + error.SetErrorString("no function to execute"); + return false; + } + if (!g_swig_run_script_keyword_value) + { + error.SetErrorString("internal helper function missing"); + return false; + } + { + ValueObjectSP value_sp(value->GetSP()); + Locker py_lock(this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); + ret_val = g_swig_run_script_keyword_value (impl_function, m_dictionary_name.c_str(), value_sp, output); + if (!ret_val) + error.SetErrorString("python script evaluation failed"); + } + return ret_val; +} uint64_t replace_all(std::string& str, const std::string& oldStr, const std::string& newStr) { @@ -2426,7 +2600,8 @@ ScriptInterpreterPython::RunScriptBasedCommand(const char* impl_function, const char* args, ScriptedCommandSynchronicity synchronicity, lldb_private::CommandReturnObject& cmd_retobj, - Error& error) + Error& error, + const lldb_private::ExecutionContext& exe_ctx) { if (!impl_function) { @@ -2441,6 +2616,7 @@ ScriptInterpreterPython::RunScriptBasedCommand(const char* impl_function, } lldb::DebuggerSP debugger_sp = m_interpreter.GetDebugger().shared_from_this(); + lldb::ExecutionContextRefSP exe_ctx_ref_sp(new ExecutionContextRef(exe_ctx)); if (!debugger_sp.get()) { @@ -2464,7 +2640,8 @@ ScriptInterpreterPython::RunScriptBasedCommand(const char* impl_function, m_dictionary_name.c_str(), debugger_sp, args, - cmd_retobj); + cmd_retobj, + exe_ctx_ref_sp); } if (!ret_val) @@ -2529,6 +2706,7 @@ ScriptInterpreterPython::InitializeInterpreter (SWIGInitCallback swig_init_callb SWIGPythonGetValueObjectSPFromSBValue swig_get_valobj_sp_from_sbvalue, SWIGPythonUpdateSynthProviderInstance swig_update_provider, SWIGPythonMightHaveChildrenSynthProviderInstance swig_mighthavechildren_provider, + SWIGPythonGetValueSynthProviderInstance swig_getvalue_provider, SWIGPythonCallCommand swig_call_command, SWIGPythonCallModuleInit swig_call_module_init, SWIGPythonCreateOSPlugin swig_create_os_plugin, @@ -2536,7 +2714,10 @@ ScriptInterpreterPython::InitializeInterpreter (SWIGInitCallback swig_init_callb SWIGPythonScriptKeyword_Thread swig_run_script_keyword_thread, SWIGPythonScriptKeyword_Target swig_run_script_keyword_target, SWIGPythonScriptKeyword_Frame swig_run_script_keyword_frame, - SWIGPython_GetDynamicSetting swig_plugin_get) + SWIGPythonScriptKeyword_Value swig_run_script_keyword_value, + SWIGPython_GetDynamicSetting swig_plugin_get, + SWIGPythonCreateScriptedThreadPlan swig_thread_plan_script, + SWIGPythonCallThreadPlan swig_call_thread_plan) { g_swig_init_callback = swig_init_callback; g_swig_breakpoint_callback = swig_breakpoint_callback; @@ -2550,6 +2731,7 @@ ScriptInterpreterPython::InitializeInterpreter (SWIGInitCallback swig_init_callb g_swig_get_valobj_sp_from_sbvalue = swig_get_valobj_sp_from_sbvalue; g_swig_update_provider = swig_update_provider; g_swig_mighthavechildren_provider = swig_mighthavechildren_provider; + g_swig_getvalue_provider = swig_getvalue_provider; g_swig_call_command = swig_call_command; g_swig_call_module_init = swig_call_module_init; g_swig_create_os_plugin = swig_create_os_plugin; @@ -2557,7 +2739,10 @@ ScriptInterpreterPython::InitializeInterpreter (SWIGInitCallback swig_init_callb g_swig_run_script_keyword_thread = swig_run_script_keyword_thread; g_swig_run_script_keyword_target = swig_run_script_keyword_target; g_swig_run_script_keyword_frame = swig_run_script_keyword_frame; + g_swig_run_script_keyword_value = swig_run_script_keyword_value; g_swig_plugin_get = swig_plugin_get; + g_swig_thread_plan_script = swig_thread_plan_script; + g_swig_call_thread_plan = swig_call_thread_plan; } void diff --git a/source/Interpreter/embedded_interpreter.py b/source/Interpreter/embedded_interpreter.py index 51a971690d67..10186f5a9d50 100644 --- a/source/Interpreter/embedded_interpreter.py +++ b/source/Interpreter/embedded_interpreter.py @@ -61,7 +61,7 @@ def get_terminal_size(fd): def readfunc_stdio(prompt): sys.stdout.write(prompt) - return sys.stdin.readline() + return sys.stdin.readline().rstrip() def run_python_interpreter (local_dict): # Pass in the dictionary, for continuity from one session to the next. diff --git a/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp b/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp index a9f8f3b668dc..5073b13f09ab 100644 --- a/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp +++ b/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp @@ -801,6 +801,36 @@ ABIMacOSX_arm::RegisterIsVolatile (const RegisterInfo *reg_info) break; } } + else if (name[0] == 'q') + { + switch (name[1]) + { + case '1': + switch (name[2]) + { + case '\0': + return true; // q1 is volatile + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + return true; // q10-q15 are volatile + default: + break; + }; + case '0': + case '2': + case '3': + return name[2] == '\0'; // q0-q3 are volatile + case '8': + case '9': + return name[2] == '\0'; // q8-q9 are volatile + default: + break; + } + } else if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') return true; } diff --git a/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h b/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h index 6f7b339e28a2..eee43943d73a 100644 --- a/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h +++ b/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h @@ -53,13 +53,7 @@ public: virtual bool RegisterIsVolatile (const lldb_private::RegisterInfo *reg_info); - - virtual bool - StackUsesFrames () - { - return true; - } - + virtual bool CallFrameAddressIsValid (lldb::addr_t cfa) { @@ -88,12 +82,6 @@ public: return pc & ~(lldb::addr_t)1; } - virtual bool - FunctionCallsChangeCFA () - { - return false; - } - virtual const lldb_private::RegisterInfo * GetRegisterInfoArray (uint32_t &count); diff --git a/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.cpp b/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.cpp index 8f7962d095fc..465257db31d8 100644 --- a/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.cpp +++ b/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.cpp @@ -440,11 +440,11 @@ ABIMacOSX_arm64::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueO } const uint32_t type_flags = return_value_type.GetTypeInfo (NULL); - if (type_flags & ClangASTType::eTypeIsScalar || - type_flags & ClangASTType::eTypeIsPointer) + if (type_flags & eTypeIsScalar || + type_flags & eTypeIsPointer) { - if (type_flags & ClangASTType::eTypeIsInteger || - type_flags & ClangASTType::eTypeIsPointer ) + if (type_flags & eTypeIsInteger || + type_flags & eTypeIsPointer ) { // Extract the register context so we can read arguments from registers lldb::offset_t offset = 0; @@ -477,9 +477,9 @@ ABIMacOSX_arm64::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueO error.SetErrorString("We don't support returning longer than 128 bit integer values at present."); } } - else if (type_flags & ClangASTType::eTypeIsFloat) + else if (type_flags & eTypeIsFloat) { - if (type_flags & ClangASTType::eTypeIsComplex) + if (type_flags & eTypeIsComplex) { // Don't handle complex yet. error.SetErrorString ("returning complex float values are not supported"); @@ -519,7 +519,7 @@ ABIMacOSX_arm64::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueO } } } - else if (type_flags & ClangASTType::eTypeIsVector) + else if (type_flags & eTypeIsVector) { if (byte_size > 0) { @@ -874,14 +874,14 @@ ABIMacOSX_arm64::GetReturnValueObjectImpl (Thread &thread, ClangASTType &return_ const size_t byte_size = return_clang_type.GetByteSize(); const uint32_t type_flags = return_clang_type.GetTypeInfo (NULL); - if (type_flags & ClangASTType::eTypeIsScalar || - type_flags & ClangASTType::eTypeIsPointer) + if (type_flags & eTypeIsScalar || + type_flags & eTypeIsPointer) { value.SetValueType(Value::eValueTypeScalar); bool success = false; - if (type_flags & ClangASTType::eTypeIsInteger || - type_flags & ClangASTType::eTypeIsPointer ) + if (type_flags & eTypeIsInteger || + type_flags & eTypeIsPointer ) { // Extract the register context so we can read arguments from registers if (byte_size <= 8) @@ -890,7 +890,7 @@ ABIMacOSX_arm64::GetReturnValueObjectImpl (Thread &thread, ClangASTType &return_ if (x0_reg_info) { uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(x0_reg_info, 0); - const bool is_signed = (type_flags & ClangASTType::eTypeIsSigned) != 0; + const bool is_signed = (type_flags & eTypeIsSigned) != 0; switch (byte_size) { default: @@ -965,9 +965,9 @@ ABIMacOSX_arm64::GetReturnValueObjectImpl (Thread &thread, ClangASTType &return_ } } } - else if (type_flags & ClangASTType::eTypeIsFloat) + else if (type_flags & eTypeIsFloat) { - if (type_flags & ClangASTType::eTypeIsComplex) + if (type_flags & eTypeIsComplex) { // Don't handle complex yet. } @@ -1010,7 +1010,7 @@ ABIMacOSX_arm64::GetReturnValueObjectImpl (Thread &thread, ClangASTType &return_ ConstString("")); } - else if (type_flags & ClangASTType::eTypeIsVector) + else if (type_flags & eTypeIsVector) { if (byte_size > 0) { @@ -1046,8 +1046,8 @@ ABIMacOSX_arm64::GetReturnValueObjectImpl (Thread &thread, ClangASTType &return_ } } } - else if (type_flags & ClangASTType::eTypeIsStructUnion || - type_flags & ClangASTType::eTypeIsClass) + else if (type_flags & eTypeIsStructUnion || + type_flags & eTypeIsClass) { DataExtractor data; diff --git a/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h b/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h index 0753b23ce2a2..6cce6a6f1174 100644 --- a/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h +++ b/source/Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h @@ -46,15 +46,7 @@ public: virtual bool RegisterIsVolatile (const lldb_private::RegisterInfo *reg_info); - - - virtual bool - StackUsesFrames () - { - // MacOSX uses frame pointers. - return true; - } - + // The arm64 ABI requires that stack frames be 16 byte aligned. // When there is a trap handler on the stack, e.g. _sigtramp in userland // code, we've seen that the stack pointer is often not aligned properly @@ -87,12 +79,6 @@ public: return true; } - virtual bool - FunctionCallsChangeCFA () - { - return false; - } - virtual const lldb_private::RegisterInfo * GetRegisterInfoArray (uint32_t &count); diff --git a/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h b/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h index 43784a5c9111..d81b7a7e684b 100644 --- a/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h +++ b/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h @@ -64,13 +64,7 @@ public: virtual bool RegisterIsVolatile (const lldb_private::RegisterInfo *reg_info); - - virtual bool - StackUsesFrames () - { - return true; - } - + // The Darwin i386 ABI requires that stack frames be 16 byte aligned. // When there is a trap handler on the stack, e.g. _sigtramp in userland // code, we've seen that the stack pointer is often not aligned properly @@ -102,12 +96,6 @@ public: return pc <= UINT32_MAX; } - virtual bool - FunctionCallsChangeCFA () - { - return true; - } - virtual const lldb_private::RegisterInfo * GetRegisterInfoArray (uint32_t &count); diff --git a/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h b/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h index 989c4a16710a..1550a38c4f3b 100644 --- a/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h +++ b/source/Plugins/ABI/SysV-hexagon/ABISysV_hexagon.h @@ -75,13 +75,7 @@ public: virtual bool RegisterIsVolatile ( const lldb_private::RegisterInfo *reg_info ); - - virtual bool - StackUsesFrames ( void ) - { - return true; - } - + virtual bool CallFrameAddressIsValid ( lldb::addr_t cfa ) { @@ -101,12 +95,6 @@ public: return true; } - virtual bool - FunctionCallsChangeCFA ( void ) - { - return true; - } - virtual const lldb_private::RegisterInfo * GetRegisterInfoArray ( uint32_t &count ); diff --git a/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.cpp b/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.cpp new file mode 100644 index 000000000000..adb3313d1a30 --- /dev/null +++ b/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.cpp @@ -0,0 +1,1120 @@ +//===-- ABISysV_ppc.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ABISysV_ppc.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectRegister.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Triple.h" + +using namespace lldb; +using namespace lldb_private; + +enum gcc_dwarf_regnums +{ + gcc_dwarf_r0 = 0, + gcc_dwarf_r1, + gcc_dwarf_r2, + gcc_dwarf_r3, + gcc_dwarf_r4, + gcc_dwarf_r5, + gcc_dwarf_r6, + gcc_dwarf_r7, + gcc_dwarf_r8, + gcc_dwarf_r9, + gcc_dwarf_r10, + gcc_dwarf_r11, + gcc_dwarf_r12, + gcc_dwarf_r13, + gcc_dwarf_r14, + gcc_dwarf_r15, + gcc_dwarf_r16, + gcc_dwarf_r17, + gcc_dwarf_r18, + gcc_dwarf_r19, + gcc_dwarf_r20, + gcc_dwarf_r21, + gcc_dwarf_r22, + gcc_dwarf_r23, + gcc_dwarf_r24, + gcc_dwarf_r25, + gcc_dwarf_r26, + gcc_dwarf_r27, + gcc_dwarf_r28, + gcc_dwarf_r29, + gcc_dwarf_r30, + gcc_dwarf_r31, + gcc_dwarf_f0, + gcc_dwarf_f1, + gcc_dwarf_f2, + gcc_dwarf_f3, + gcc_dwarf_f4, + gcc_dwarf_f5, + gcc_dwarf_f6, + gcc_dwarf_f7, + gcc_dwarf_f8, + gcc_dwarf_f9, + gcc_dwarf_f10, + gcc_dwarf_f11, + gcc_dwarf_f12, + gcc_dwarf_f13, + gcc_dwarf_f14, + gcc_dwarf_f15, + gcc_dwarf_f16, + gcc_dwarf_f17, + gcc_dwarf_f18, + gcc_dwarf_f19, + gcc_dwarf_f20, + gcc_dwarf_f21, + gcc_dwarf_f22, + gcc_dwarf_f23, + gcc_dwarf_f24, + gcc_dwarf_f25, + gcc_dwarf_f26, + gcc_dwarf_f27, + gcc_dwarf_f28, + gcc_dwarf_f29, + gcc_dwarf_f30, + gcc_dwarf_f31, + gcc_dwarf_cr, + gcc_dwarf_fpscr, + gcc_dwarf_xer = 101, + gcc_dwarf_lr = 108, + gcc_dwarf_ctr, + gcc_dwarf_pc, + gcc_dwarf_cfa, +}; + +enum gdb_regnums +{ + gdb_r0 = 0, + gdb_r1, + gdb_r2, + gdb_r3, + gdb_r4, + gdb_r5, + gdb_r6, + gdb_r7, + gdb_r8, + gdb_r9, + gdb_r10, + gdb_r11, + gdb_r12, + gdb_r13, + gdb_r14, + gdb_r15, + gdb_r16, + gdb_r17, + gdb_r18, + gdb_r19, + gdb_r20, + gdb_r21, + gdb_r22, + gdb_r23, + gdb_r24, + gdb_r25, + gdb_r26, + gdb_r27, + gdb_r28, + gdb_r29, + gdb_r30, + gdb_r31, + gdb_lr, + gdb_cr, + gdb_xer, + gdb_ctr, + gdb_pc, +}; + + +// Note that the size and offset will be updated by platform-specific classes. +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, 8, 0, eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4}, NULL, NULL } +static const RegisterInfo +g_register_infos[] = +{ + // General purpose registers. GCC, DWARF, Generic, GDB + DEFINE_GPR(r0, NULL, gcc_dwarf_r0, gcc_dwarf_r0, LLDB_INVALID_REGNUM, gdb_r0), + DEFINE_GPR(r1, "sp", gcc_dwarf_r1, gcc_dwarf_r1, LLDB_REGNUM_GENERIC_SP, gdb_r1), + DEFINE_GPR(r2, NULL, gcc_dwarf_r2, gcc_dwarf_r2, LLDB_INVALID_REGNUM, gdb_r2), + DEFINE_GPR(r3, "arg1",gcc_dwarf_r3, gcc_dwarf_r3, LLDB_REGNUM_GENERIC_ARG1, gdb_r3), + DEFINE_GPR(r4, "arg2",gcc_dwarf_r4, gcc_dwarf_r4, LLDB_REGNUM_GENERIC_ARG2 ,gdb_r4), + DEFINE_GPR(r5, "arg3",gcc_dwarf_r5, gcc_dwarf_r5, LLDB_REGNUM_GENERIC_ARG3, gdb_r5), + DEFINE_GPR(r6, "arg4",gcc_dwarf_r6, gcc_dwarf_r6, LLDB_REGNUM_GENERIC_ARG4, gdb_r6), + DEFINE_GPR(r7, "arg5",gcc_dwarf_r7, gcc_dwarf_r7, LLDB_REGNUM_GENERIC_ARG5, gdb_r7), + DEFINE_GPR(r8, "arg6",gcc_dwarf_r8, gcc_dwarf_r8, LLDB_REGNUM_GENERIC_ARG6, gdb_r8), + DEFINE_GPR(r9, "arg7",gcc_dwarf_r9, gcc_dwarf_r9, LLDB_REGNUM_GENERIC_ARG7, gdb_r9), + DEFINE_GPR(r10, "arg8",gcc_dwarf_r10, gcc_dwarf_r10, LLDB_REGNUM_GENERIC_ARG8, gdb_r10), + DEFINE_GPR(r11, NULL, gcc_dwarf_r11, gcc_dwarf_r11, LLDB_INVALID_REGNUM, gdb_r11), + DEFINE_GPR(r12, NULL, gcc_dwarf_r12, gcc_dwarf_r12, LLDB_INVALID_REGNUM, gdb_r12), + DEFINE_GPR(r13, NULL, gcc_dwarf_r13, gcc_dwarf_r13, LLDB_INVALID_REGNUM, gdb_r13), + DEFINE_GPR(r14, NULL, gcc_dwarf_r14, gcc_dwarf_r14, LLDB_INVALID_REGNUM, gdb_r14), + DEFINE_GPR(r15, NULL, gcc_dwarf_r15, gcc_dwarf_r15, LLDB_INVALID_REGNUM, gdb_r15), + DEFINE_GPR(r16, NULL, gcc_dwarf_r16, gcc_dwarf_r16, LLDB_INVALID_REGNUM, gdb_r16), + DEFINE_GPR(r17, NULL, gcc_dwarf_r17, gcc_dwarf_r17, LLDB_INVALID_REGNUM, gdb_r17), + DEFINE_GPR(r18, NULL, gcc_dwarf_r18, gcc_dwarf_r18, LLDB_INVALID_REGNUM, gdb_r18), + DEFINE_GPR(r19, NULL, gcc_dwarf_r19, gcc_dwarf_r19, LLDB_INVALID_REGNUM, gdb_r19), + DEFINE_GPR(r20, NULL, gcc_dwarf_r20, gcc_dwarf_r20, LLDB_INVALID_REGNUM, gdb_r20), + DEFINE_GPR(r21, NULL, gcc_dwarf_r21, gcc_dwarf_r21, LLDB_INVALID_REGNUM, gdb_r21), + DEFINE_GPR(r22, NULL, gcc_dwarf_r22, gcc_dwarf_r22, LLDB_INVALID_REGNUM, gdb_r22), + DEFINE_GPR(r23, NULL, gcc_dwarf_r23, gcc_dwarf_r23, LLDB_INVALID_REGNUM, gdb_r23), + DEFINE_GPR(r24, NULL, gcc_dwarf_r24, gcc_dwarf_r24, LLDB_INVALID_REGNUM, gdb_r24), + DEFINE_GPR(r25, NULL, gcc_dwarf_r25, gcc_dwarf_r25, LLDB_INVALID_REGNUM, gdb_r25), + DEFINE_GPR(r26, NULL, gcc_dwarf_r26, gcc_dwarf_r26, LLDB_INVALID_REGNUM, gdb_r26), + DEFINE_GPR(r27, NULL, gcc_dwarf_r27, gcc_dwarf_r27, LLDB_INVALID_REGNUM, gdb_r27), + DEFINE_GPR(r28, NULL, gcc_dwarf_r28, gcc_dwarf_r28, LLDB_INVALID_REGNUM, gdb_r28), + DEFINE_GPR(r29, NULL, gcc_dwarf_r29, gcc_dwarf_r29, LLDB_INVALID_REGNUM, gdb_r29), + DEFINE_GPR(r30, NULL, gcc_dwarf_r30, gcc_dwarf_r30, LLDB_INVALID_REGNUM, gdb_r30), + DEFINE_GPR(r31, NULL, gcc_dwarf_r31, gcc_dwarf_r31, LLDB_INVALID_REGNUM, gdb_r31), + DEFINE_GPR(lr, "lr", gcc_dwarf_lr, gcc_dwarf_lr, LLDB_REGNUM_GENERIC_RA, gdb_lr), + DEFINE_GPR(cr, "cr", gcc_dwarf_cr, gcc_dwarf_cr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR(xer, "xer", gcc_dwarf_xer, gcc_dwarf_xer, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ctr, "ctr", gcc_dwarf_ctr, gcc_dwarf_ctr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(pc, "pc", gcc_dwarf_pc, gcc_dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + { NULL, NULL, 8, 0, eEncodingUint, eFormatHex, { gcc_dwarf_cfa, gcc_dwarf_cfa, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, NULL, NULL}, +}; + +static const uint32_t k_num_register_infos = llvm::array_lengthof(g_register_infos); + +const lldb_private::RegisterInfo * +ABISysV_ppc::GetRegisterInfoArray (uint32_t &count) +{ + count = k_num_register_infos; + return g_register_infos; +} + + +size_t +ABISysV_ppc::GetRedZoneSize () const +{ + return 224; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +ABISP +ABISysV_ppc::CreateInstance (const ArchSpec &arch) +{ + static ABISP g_abi_sp; + if (arch.GetTriple().getArch() == llvm::Triple::ppc) + { + if (!g_abi_sp) + g_abi_sp.reset (new ABISysV_ppc); + return g_abi_sp; + } + return ABISP(); +} + +bool +ABISysV_ppc::PrepareTrivialCall (Thread &thread, + addr_t sp, + addr_t func_addr, + addr_t return_addr, + llvm::ArrayRef<addr_t> args) const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + StreamString s; + s.Printf("ABISysV_ppc::PrepareTrivialCall (tid = 0x%" PRIx64 ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64 ", return_addr = 0x%" PRIx64, + thread.GetID(), + (uint64_t)sp, + (uint64_t)func_addr, + (uint64_t)return_addr); + + for (size_t i = 0; i < args.size(); ++i) + s.Printf (", arg%zd = 0x%" PRIx64, i + 1, args[i]); + s.PutCString (")"); + log->PutCString(s.GetString().c_str()); + } + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return false; + + const RegisterInfo *reg_info = NULL; + + if (args.size() > 8) // TODO handle more than 8 arguments + return false; + + for (size_t i = 0; i < args.size(); ++i) + { + reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i); + if (log) + log->Printf("About to write arg%zd (0x%" PRIx64 ") into %s", i + 1, args[i], reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i])) + return false; + } + + // First, align the SP + + if (log) + log->Printf("16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64, (uint64_t)sp, (uint64_t)(sp & ~0xfull)); + + sp &= ~(0xfull); // 16-byte alignment + + sp -= 8; + + Error error; + const RegisterInfo *pc_reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + const RegisterInfo *sp_reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + ProcessSP process_sp (thread.GetProcess()); + + RegisterValue reg_value; + +#if 0 + // This code adds an extra frame so that we don't lose the function that we came from + // by pushing the PC and the FP and then writing the current FP to point to the FP value + // we just pushed. It is disabled for now until the stack backtracing code can be debugged. + + // Save current PC + const RegisterInfo *fp_reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + if (reg_ctx->ReadRegister(pc_reg_info, reg_value)) + { + if (log) + log->Printf("Pushing the current PC onto the stack: 0x%" PRIx64 ": 0x%" PRIx64, (uint64_t)sp, reg_value.GetAsUInt64()); + + if (!process_sp->WritePointerToMemory(sp, reg_value.GetAsUInt64(), error)) + return false; + + sp -= 8; + + // Save current FP + if (reg_ctx->ReadRegister(fp_reg_info, reg_value)) + { + if (log) + log->Printf("Pushing the current FP onto the stack: 0x%" PRIx64 ": 0x%" PRIx64, (uint64_t)sp, reg_value.GetAsUInt64()); + + if (!process_sp->WritePointerToMemory(sp, reg_value.GetAsUInt64(), error)) + return false; + } + // Setup FP backchain + reg_value.SetUInt64 (sp); + + if (log) + log->Printf("Writing FP: 0x%" PRIx64 " (for FP backchain)", reg_value.GetAsUInt64()); + + if (!reg_ctx->WriteRegister(fp_reg_info, reg_value)) + { + return false; + } + + sp -= 8; + } +#endif + + if (log) + log->Printf("Pushing the return address onto the stack: 0x%" PRIx64 ": 0x%" PRIx64, (uint64_t)sp, (uint64_t)return_addr); + + // Save return address onto the stack + if (!process_sp->WritePointerToMemory(sp, return_addr, error)) + return false; + + // %r1 is set to the actual stack value. + + if (log) + log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp); + + if (!reg_ctx->WriteRegisterFromUnsigned (sp_reg_info, sp)) + return false; + + // %pc is set to the address of the called function. + + if (log) + log->Printf("Writing IP: 0x%" PRIx64, (uint64_t)func_addr); + + if (!reg_ctx->WriteRegisterFromUnsigned (pc_reg_info, func_addr)) + return false; + + return true; +} + +static bool ReadIntegerArgument(Scalar &scalar, + unsigned int bit_width, + bool is_signed, + Thread &thread, + uint32_t *argument_register_ids, + unsigned int ¤t_argument_register, + addr_t ¤t_stack_argument) +{ + if (bit_width > 64) + return false; // Scalar can't hold large integer arguments + + if (current_argument_register < 6) + { + scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned(argument_register_ids[current_argument_register], 0); + current_argument_register++; + if (is_signed) + scalar.SignExtend (bit_width); + } + else + { + uint32_t byte_size = (bit_width + (8-1))/8; + Error error; + if (thread.GetProcess()->ReadScalarIntegerFromMemory(current_stack_argument, byte_size, is_signed, scalar, error)) + { + current_stack_argument += byte_size; + return true; + } + return false; + } + return true; +} + +bool +ABISysV_ppc::GetArgumentValues (Thread &thread, + ValueList &values) const +{ + unsigned int num_values = values.GetSize(); + unsigned int value_index; + + // Extract the register context so we can read arguments from registers + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + + if (!reg_ctx) + return false; + + // Get the pointer to the first stack argument so we have a place to start + // when reading data + + addr_t sp = reg_ctx->GetSP(0); + + if (!sp) + return false; + + addr_t current_stack_argument = sp + 48; // jump over return address + + uint32_t argument_register_ids[8]; + + argument_register_ids[0] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1)->kinds[eRegisterKindLLDB]; + argument_register_ids[1] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2)->kinds[eRegisterKindLLDB]; + argument_register_ids[2] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG3)->kinds[eRegisterKindLLDB]; + argument_register_ids[3] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG4)->kinds[eRegisterKindLLDB]; + argument_register_ids[4] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG5)->kinds[eRegisterKindLLDB]; + argument_register_ids[5] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG6)->kinds[eRegisterKindLLDB]; + argument_register_ids[6] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG7)->kinds[eRegisterKindLLDB]; + argument_register_ids[7] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG8)->kinds[eRegisterKindLLDB]; + + unsigned int current_argument_register = 0; + + for (value_index = 0; + value_index < num_values; + ++value_index) + { + Value *value = values.GetValueAtIndex(value_index); + + if (!value) + return false; + + // We currently only support extracting values with Clang QualTypes. + // Do we care about others? + ClangASTType clang_type = value->GetClangType(); + if (!clang_type) + return false; + bool is_signed; + + if (clang_type.IsIntegerType (is_signed)) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + is_signed, + thread, + argument_register_ids, + current_argument_register, + current_stack_argument); + } + else if (clang_type.IsPointerType ()) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + false, + thread, + argument_register_ids, + current_argument_register, + current_stack_argument); + } + } + + return true; +} + +Error +ABISysV_ppc::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value_sp) +{ + Error error; + if (!new_value_sp) + { + error.SetErrorString("Empty value object for return value."); + return error; + } + + ClangASTType clang_type = new_value_sp->GetClangType(); + if (!clang_type) + { + error.SetErrorString ("Null clang type for return value."); + return error; + } + + Thread *thread = frame_sp->GetThread().get(); + + bool is_signed; + uint32_t count; + bool is_complex; + + RegisterContext *reg_ctx = thread->GetRegisterContext().get(); + + bool set_it_simple = false; + if (clang_type.IsIntegerType (is_signed) || clang_type.IsPointerType()) + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("r3", 0); + + DataExtractor data; + Error data_error; + size_t num_bytes = new_value_sp->GetData(data, data_error); + if (data_error.Fail()) + { + error.SetErrorStringWithFormat("Couldn't convert return value to raw data: %s", data_error.AsCString()); + return error; + } + lldb::offset_t offset = 0; + if (num_bytes <= 8) + { + uint64_t raw_value = data.GetMaxU64(&offset, num_bytes); + + if (reg_ctx->WriteRegisterFromUnsigned (reg_info, raw_value)) + set_it_simple = true; + } + else + { + error.SetErrorString("We don't support returning longer than 64 bit integer values at present."); + } + + } + else if (clang_type.IsFloatingPointType (count, is_complex)) + { + if (is_complex) + error.SetErrorString ("We don't support returning complex values at present"); + else + { + size_t bit_width = clang_type.GetBitSize(); + if (bit_width <= 64) + { + DataExtractor data; + Error data_error; + size_t num_bytes = new_value_sp->GetData(data, data_error); + if (data_error.Fail()) + { + error.SetErrorStringWithFormat("Couldn't convert return value to raw data: %s", data_error.AsCString()); + return error; + } + + unsigned char buffer[16]; + ByteOrder byte_order = data.GetByteOrder(); + + data.CopyByteOrderedData (0, num_bytes, buffer, 16, byte_order); + set_it_simple = true; + } + else + { + // FIXME - don't know how to do 80 bit long doubles yet. + error.SetErrorString ("We don't support returning float values > 64 bits at present"); + } + } + } + + if (!set_it_simple) + { + // Okay we've got a structure or something that doesn't fit in a simple register. + // We should figure out where it really goes, but we don't support this yet. + error.SetErrorString ("We only support setting simple integer and float return types at present."); + } + + return error; +} + + +ValueObjectSP +ABISysV_ppc::GetReturnValueObjectSimple (Thread &thread, + ClangASTType &return_clang_type) const +{ + ValueObjectSP return_valobj_sp; + Value value; + + if (!return_clang_type) + return return_valobj_sp; + + //value.SetContext (Value::eContextTypeClangType, return_value_type); + value.SetClangType (return_clang_type); + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return return_valobj_sp; + + const uint32_t type_flags = return_clang_type.GetTypeInfo (); + if (type_flags & eTypeIsScalar) + { + value.SetValueType(Value::eValueTypeScalar); + + bool success = false; + if (type_flags & eTypeIsInteger) + { + // Extract the register context so we can read arguments from registers + + const size_t byte_size = return_clang_type.GetByteSize(); + uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg_ctx->GetRegisterInfoByName("r3", 0), 0); + const bool is_signed = (type_flags & eTypeIsSigned) != 0; + switch (byte_size) + { + default: + break; + + case sizeof(uint64_t): + if (is_signed) + value.GetScalar() = (int64_t)(raw_value); + else + value.GetScalar() = (uint64_t)(raw_value); + success = true; + break; + + case sizeof(uint32_t): + if (is_signed) + value.GetScalar() = (int32_t)(raw_value & UINT32_MAX); + else + value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX); + success = true; + break; + + case sizeof(uint16_t): + if (is_signed) + value.GetScalar() = (int16_t)(raw_value & UINT16_MAX); + else + value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX); + success = true; + break; + + case sizeof(uint8_t): + if (is_signed) + value.GetScalar() = (int8_t)(raw_value & UINT8_MAX); + else + value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX); + success = true; + break; + } + } + else if (type_flags & eTypeIsFloat) + { + if (type_flags & eTypeIsComplex) + { + // Don't handle complex yet. + } + else + { + const size_t byte_size = return_clang_type.GetByteSize(); + if (byte_size <= sizeof(long double)) + { + const RegisterInfo *f1_info = reg_ctx->GetRegisterInfoByName("f1", 0); + RegisterValue f1_value; + if (reg_ctx->ReadRegister (f1_info, f1_value)) + { + DataExtractor data; + if (f1_value.GetData(data)) + { + lldb::offset_t offset = 0; + if (byte_size == sizeof(float)) + { + value.GetScalar() = (float) data.GetFloat(&offset); + success = true; + } + else if (byte_size == sizeof(double)) + { + value.GetScalar() = (double) data.GetDouble(&offset); + success = true; + } + } + } + } + } + } + + if (success) + return_valobj_sp = ValueObjectConstResult::Create (thread.GetStackFrameAtIndex(0).get(), + value, + ConstString("")); + + } + else if (type_flags & eTypeIsPointer) + { + unsigned r3_id = reg_ctx->GetRegisterInfoByName("r3", 0)->kinds[eRegisterKindLLDB]; + value.GetScalar() = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r3_id, 0); + value.SetValueType(Value::eValueTypeScalar); + return_valobj_sp = ValueObjectConstResult::Create (thread.GetStackFrameAtIndex(0).get(), + value, + ConstString("")); + } + else if (type_flags & eTypeIsVector) + { + const size_t byte_size = return_clang_type.GetByteSize(); + if (byte_size > 0) + { + + const RegisterInfo *altivec_reg = reg_ctx->GetRegisterInfoByName("v0", 0); + if (altivec_reg == NULL) + altivec_reg = reg_ctx->GetRegisterInfoByName("mm0", 0); + if (altivec_reg) + { + if (byte_size <= altivec_reg->byte_size) + { + ProcessSP process_sp (thread.GetProcess()); + if (process_sp) + { + std::unique_ptr<DataBufferHeap> heap_data_ap (new DataBufferHeap(byte_size, 0)); + const ByteOrder byte_order = process_sp->GetByteOrder(); + RegisterValue reg_value; + if (reg_ctx->ReadRegister(altivec_reg, reg_value)) + { + Error error; + if (reg_value.GetAsMemoryData (altivec_reg, + heap_data_ap->GetBytes(), + heap_data_ap->GetByteSize(), + byte_order, + error)) + { + DataExtractor data (DataBufferSP (heap_data_ap.release()), + byte_order, + process_sp->GetTarget().GetArchitecture().GetAddressByteSize()); + return_valobj_sp = ValueObjectConstResult::Create (&thread, + return_clang_type, + ConstString(""), + data); + } + } + } + } + } + } + } + + return return_valobj_sp; +} + +ValueObjectSP +ABISysV_ppc::GetReturnValueObjectImpl (Thread &thread, ClangASTType &return_clang_type) const +{ + ValueObjectSP return_valobj_sp; + + if (!return_clang_type) + return return_valobj_sp; + + ExecutionContext exe_ctx (thread.shared_from_this()); + return_valobj_sp = GetReturnValueObjectSimple(thread, return_clang_type); + if (return_valobj_sp) + return return_valobj_sp; + + RegisterContextSP reg_ctx_sp = thread.GetRegisterContext(); + if (!reg_ctx_sp) + return return_valobj_sp; + + const size_t bit_width = return_clang_type.GetBitSize(); + if (return_clang_type.IsAggregateType()) + { + Target *target = exe_ctx.GetTargetPtr(); + bool is_memory = true; + if (bit_width <= 128) + { + ByteOrder target_byte_order = target->GetArchitecture().GetByteOrder(); + DataBufferSP data_sp (new DataBufferHeap(16, 0)); + DataExtractor return_ext (data_sp, + target_byte_order, + target->GetArchitecture().GetAddressByteSize()); + + const RegisterInfo *r3_info = reg_ctx_sp->GetRegisterInfoByName("r3", 0); + const RegisterInfo *rdx_info = reg_ctx_sp->GetRegisterInfoByName("rdx", 0); + + RegisterValue r3_value, rdx_value; + reg_ctx_sp->ReadRegister (r3_info, r3_value); + reg_ctx_sp->ReadRegister (rdx_info, rdx_value); + + DataExtractor r3_data, rdx_data; + + r3_value.GetData(r3_data); + rdx_value.GetData(rdx_data); + + uint32_t fp_bytes = 0; // Tracks how much of the xmm registers we've consumed so far + uint32_t integer_bytes = 0; // Tracks how much of the r3/rds registers we've consumed so far + + const uint32_t num_children = return_clang_type.GetNumFields (); + + // Since we are in the small struct regime, assume we are not in memory. + is_memory = false; + + for (uint32_t idx = 0; idx < num_children; idx++) + { + std::string name; + uint64_t field_bit_offset = 0; + bool is_signed; + bool is_complex; + uint32_t count; + + ClangASTType field_clang_type = return_clang_type.GetFieldAtIndex (idx, name, &field_bit_offset, NULL, NULL); + const size_t field_bit_width = field_clang_type.GetBitSize(); + + // If there are any unaligned fields, this is stored in memory. + if (field_bit_offset % field_bit_width != 0) + { + is_memory = true; + break; + } + + uint32_t field_byte_width = field_bit_width/8; + uint32_t field_byte_offset = field_bit_offset/8; + + + DataExtractor *copy_from_extractor = NULL; + uint32_t copy_from_offset = 0; + + if (field_clang_type.IsIntegerType (is_signed) || field_clang_type.IsPointerType ()) + { + if (integer_bytes < 8) + { + if (integer_bytes + field_byte_width <= 8) + { + // This is in RAX, copy from register to our result structure: + copy_from_extractor = &r3_data; + copy_from_offset = integer_bytes; + integer_bytes += field_byte_width; + } + else + { + // The next field wouldn't fit in the remaining space, so we pushed it to rdx. + copy_from_extractor = &rdx_data; + copy_from_offset = 0; + integer_bytes = 8 + field_byte_width; + + } + } + else if (integer_bytes + field_byte_width <= 16) + { + copy_from_extractor = &rdx_data; + copy_from_offset = integer_bytes - 8; + integer_bytes += field_byte_width; + } + else + { + // The last field didn't fit. I can't see how that would happen w/o the overall size being + // greater than 16 bytes. For now, return a NULL return value object. + return return_valobj_sp; + } + } + else if (field_clang_type.IsFloatingPointType (count, is_complex)) + { + // Structs with long doubles are always passed in memory. + if (field_bit_width == 128) + { + is_memory = true; + break; + } + else if (field_bit_width == 64) + { + copy_from_offset = 0; + fp_bytes += field_byte_width; + } + else if (field_bit_width == 32) + { + // This one is kind of complicated. If we are in an "eightbyte" with another float, we'll + // be stuffed into an xmm register with it. If we are in an "eightbyte" with one or more ints, + // then we will be stuffed into the appropriate GPR with them. + bool in_gpr; + if (field_byte_offset % 8 == 0) + { + // We are at the beginning of one of the eightbytes, so check the next element (if any) + if (idx == num_children - 1) + in_gpr = false; + else + { + uint64_t next_field_bit_offset = 0; + ClangASTType next_field_clang_type = return_clang_type.GetFieldAtIndex (idx + 1, + name, + &next_field_bit_offset, + NULL, + NULL); + if (next_field_clang_type.IsIntegerType (is_signed)) + in_gpr = true; + else + { + copy_from_offset = 0; + in_gpr = false; + } + } + + } + else if (field_byte_offset % 4 == 0) + { + // We are inside of an eightbyte, so see if the field before us is floating point: + // This could happen if somebody put padding in the structure. + if (idx == 0) + in_gpr = false; + else + { + uint64_t prev_field_bit_offset = 0; + ClangASTType prev_field_clang_type = return_clang_type.GetFieldAtIndex (idx - 1, + name, + &prev_field_bit_offset, + NULL, + NULL); + if (prev_field_clang_type.IsIntegerType (is_signed)) + in_gpr = true; + else + { + copy_from_offset = 4; + in_gpr = false; + } + } + + } + else + { + is_memory = true; + continue; + } + + // Okay, we've figured out whether we are in GPR or XMM, now figure out which one. + if (in_gpr) + { + if (integer_bytes < 8) + { + // This is in RAX, copy from register to our result structure: + copy_from_extractor = &r3_data; + copy_from_offset = integer_bytes; + integer_bytes += field_byte_width; + } + else + { + copy_from_extractor = &rdx_data; + copy_from_offset = integer_bytes - 8; + integer_bytes += field_byte_width; + } + } + else + { + fp_bytes += field_byte_width; + } + } + } + + // These two tests are just sanity checks. If I somehow get the + // type calculation wrong above it is better to just return nothing + // than to assert or crash. + if (!copy_from_extractor) + return return_valobj_sp; + if (copy_from_offset + field_byte_width > copy_from_extractor->GetByteSize()) + return return_valobj_sp; + + copy_from_extractor->CopyByteOrderedData (copy_from_offset, + field_byte_width, + data_sp->GetBytes() + field_byte_offset, + field_byte_width, + target_byte_order); + } + + if (!is_memory) + { + // The result is in our data buffer. Let's make a variable object out of it: + return_valobj_sp = ValueObjectConstResult::Create (&thread, + return_clang_type, + ConstString(""), + return_ext); + } + } + + + // FIXME: This is just taking a guess, r3 may very well no longer hold the return storage location. + // If we are going to do this right, when we make a new frame we should check to see if it uses a memory + // return, and if we are at the first instruction and if so stash away the return location. Then we would + // only return the memory return value if we know it is valid. + + if (is_memory) + { + unsigned r3_id = reg_ctx_sp->GetRegisterInfoByName("r3", 0)->kinds[eRegisterKindLLDB]; + lldb::addr_t storage_addr = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r3_id, 0); + return_valobj_sp = ValueObjectMemory::Create (&thread, + "", + Address (storage_addr, NULL), + return_clang_type); + } + } + + return return_valobj_sp; +} + +bool +ABISysV_ppc::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) +{ + unwind_plan.Clear(); + unwind_plan.SetRegisterKind (eRegisterKindDWARF); + + uint32_t lr_reg_num = gcc_dwarf_lr; + uint32_t sp_reg_num = gcc_dwarf_r1; + uint32_t pc_reg_num = gcc_dwarf_pc; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + + // Our Call Frame Address is the stack pointer value + row->SetCFARegister (sp_reg_num); + + // The previous PC is in the LR + row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true); + unwind_plan.AppendRow (row); + + // All other registers are the same. + + unwind_plan.SetSourceName ("ppc at-func-entry default"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + + return true; +} + +bool +ABISysV_ppc::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) +{ + unwind_plan.Clear(); + unwind_plan.SetRegisterKind (eRegisterKindDWARF); + + uint32_t sp_reg_num = gcc_dwarf_r1; + uint32_t pc_reg_num = gcc_dwarf_lr; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + + const int32_t ptr_size = 4; + row->SetCFARegister (sp_reg_num); + row->SetCFAType(lldb_private::UnwindPlan::Row::CFAIsRegisterDereferenced); + + row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * 1, true); + row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true); + + unwind_plan.AppendRow (row); + unwind_plan.SetSourceName ("ppc default unwind plan"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); + unwind_plan.SetReturnAddressRegister(gcc_dwarf_lr); + return true; +} + +bool +ABISysV_ppc::RegisterIsVolatile (const RegisterInfo *reg_info) +{ + return !RegisterIsCalleeSaved (reg_info); +} + + + +// See "Register Usage" in the +// "System V Application Binary Interface" +// "64-bit PowerPC ELF Application Binary Interface Supplement" +// current version is 1.9 released 2004 at http://refspecs.linuxfoundation.org/ELF/ppc/PPC-elf64abi-1.9.pdf + +bool +ABISysV_ppc::RegisterIsCalleeSaved (const RegisterInfo *reg_info) +{ + if (reg_info) + { + // Preserved registers are : + // r1,r2,r13-r31 + // f14-f31 (not yet) + // v20-v31 (not yet) + // vrsave (not yet) + + const char *name = reg_info->name; + if (name[0] == 'r') + { + if ((name[1] == '1' || name[1] == '2') && name[2] == '\0') + return true; + if (name[1] == '1' && name[2] > '2') + return true; + if ((name[1] == '2' || name[1] == '3') && name[2] != '\0') + return true; + } + + if (name[0] == 'f' && name[1] >= '0' && name[1] <= '9') + { + if (name[3] == '1' && name[4] >= '4') + return true; + if ((name[3] == '2' || name[3] == '3') && name[4] != '\0') + return true; + } + + if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp + return true; + if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp + return true; + if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc + return true; + } + return false; +} + + + +void +ABISysV_ppc::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "System V ABI for ppc targets", + CreateInstance); +} + +void +ABISysV_ppc::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +ABISysV_ppc::GetPluginNameStatic() +{ + static ConstString g_name("sysv-ppc"); + return g_name; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +ABISysV_ppc::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ABISysV_ppc::GetPluginVersion() +{ + return 1; +} + diff --git a/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.h b/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.h new file mode 100644 index 000000000000..a7aad300e297 --- /dev/null +++ b/source/Plugins/ABI/SysV-ppc/ABISysV_ppc.h @@ -0,0 +1,143 @@ +//===-- ABISysV_ppc.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABISysV_ppc_h_ +#define liblldb_ABISysV_ppc_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" + +class ABISysV_ppc : + public lldb_private::ABI +{ +public: + + ~ABISysV_ppc() + { + } + + virtual size_t + GetRedZoneSize () const; + + virtual bool + PrepareTrivialCall (lldb_private::Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + llvm::ArrayRef<lldb::addr_t> args) const; + + virtual bool + GetArgumentValues (lldb_private::Thread &thread, + lldb_private::ValueList &values) const; + + virtual lldb_private::Error + SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value); + +protected: + lldb::ValueObjectSP + GetReturnValueObjectSimple (lldb_private::Thread &thread, + lldb_private::ClangASTType &ast_type) const; + +public: + virtual lldb::ValueObjectSP + GetReturnValueObjectImpl (lldb_private::Thread &thread, + lldb_private::ClangASTType &type) const; + + virtual bool + CreateFunctionEntryUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + CreateDefaultUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + RegisterIsVolatile (const lldb_private::RegisterInfo *reg_info); + + virtual bool + StackUsesFrames () + { + return true; + } + + // The SysV ppc ABI requires that stack frames be 16 byte aligned. + // When there is a trap handler on the stack, e.g. _sigtramp in userland + // code, we've seen that the stack pointer is often not aligned properly + // before the handler is invoked. This means that lldb will stop the unwind + // early -- before the function which caused the trap. + // + // To work around this, we relax that alignment to be just word-size (8-bytes). + // Whitelisting the trap handlers for user space would be easy (_sigtramp) but + // in other environments there can be a large number of different functions + // involved in async traps. + virtual bool + CallFrameAddressIsValid (lldb::addr_t cfa) + { + // Make sure the stack call frame addresses are 8 byte aligned + if (cfa & (8ull - 1ull)) + return false; // Not 8 byte aligned + if (cfa == 0) + return false; // Zero is not a valid stack address + return true; + } + + virtual bool + CodeAddressIsValid (lldb::addr_t pc) + { + // We have a 64 bit address space, so anything is valid as opcodes + // aren't fixed width... + return true; + } + + virtual bool + FunctionCallsChangeCFA () + { + return true; + } + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoArray (uint32_t &count); + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb::ABISP + CreateInstance (const lldb_private::ArchSpec &arch); + + static lldb_private::ConstString + GetPluginNameStatic(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + void + CreateRegisterMapIfNeeded (); + + bool + RegisterIsCalleeSaved (const lldb_private::RegisterInfo *reg_info); + +private: + ABISysV_ppc() : lldb_private::ABI() { } // Call CreateInstance instead. +}; + +#endif // liblldb_ABI_h_ diff --git a/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.cpp b/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.cpp new file mode 100644 index 000000000000..46f1e1023f0a --- /dev/null +++ b/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.cpp @@ -0,0 +1,1124 @@ +//===-- ABISysV_ppc64.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ABISysV_ppc64.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectRegister.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Triple.h" + +using namespace lldb; +using namespace lldb_private; + +enum gcc_dwarf_regnums +{ + gcc_dwarf_r0 = 0, + gcc_dwarf_r1, + gcc_dwarf_r2, + gcc_dwarf_r3, + gcc_dwarf_r4, + gcc_dwarf_r5, + gcc_dwarf_r6, + gcc_dwarf_r7, + gcc_dwarf_r8, + gcc_dwarf_r9, + gcc_dwarf_r10, + gcc_dwarf_r11, + gcc_dwarf_r12, + gcc_dwarf_r13, + gcc_dwarf_r14, + gcc_dwarf_r15, + gcc_dwarf_r16, + gcc_dwarf_r17, + gcc_dwarf_r18, + gcc_dwarf_r19, + gcc_dwarf_r20, + gcc_dwarf_r21, + gcc_dwarf_r22, + gcc_dwarf_r23, + gcc_dwarf_r24, + gcc_dwarf_r25, + gcc_dwarf_r26, + gcc_dwarf_r27, + gcc_dwarf_r28, + gcc_dwarf_r29, + gcc_dwarf_r30, + gcc_dwarf_r31, + gcc_dwarf_f0, + gcc_dwarf_f1, + gcc_dwarf_f2, + gcc_dwarf_f3, + gcc_dwarf_f4, + gcc_dwarf_f5, + gcc_dwarf_f6, + gcc_dwarf_f7, + gcc_dwarf_f8, + gcc_dwarf_f9, + gcc_dwarf_f10, + gcc_dwarf_f11, + gcc_dwarf_f12, + gcc_dwarf_f13, + gcc_dwarf_f14, + gcc_dwarf_f15, + gcc_dwarf_f16, + gcc_dwarf_f17, + gcc_dwarf_f18, + gcc_dwarf_f19, + gcc_dwarf_f20, + gcc_dwarf_f21, + gcc_dwarf_f22, + gcc_dwarf_f23, + gcc_dwarf_f24, + gcc_dwarf_f25, + gcc_dwarf_f26, + gcc_dwarf_f27, + gcc_dwarf_f28, + gcc_dwarf_f29, + gcc_dwarf_f30, + gcc_dwarf_f31, + gcc_dwarf_cr, + gcc_dwarf_fpscr, + gcc_dwarf_xer = 101, + gcc_dwarf_lr = 108, + gcc_dwarf_ctr, + gcc_dwarf_pc, + gcc_dwarf_cfa, +}; + +enum gdb_regnums +{ + gdb_r0 = 0, + gdb_r1, + gdb_r2, + gdb_r3, + gdb_r4, + gdb_r5, + gdb_r6, + gdb_r7, + gdb_r8, + gdb_r9, + gdb_r10, + gdb_r11, + gdb_r12, + gdb_r13, + gdb_r14, + gdb_r15, + gdb_r16, + gdb_r17, + gdb_r18, + gdb_r19, + gdb_r20, + gdb_r21, + gdb_r22, + gdb_r23, + gdb_r24, + gdb_r25, + gdb_r26, + gdb_r27, + gdb_r28, + gdb_r29, + gdb_r30, + gdb_r31, + gdb_lr, + gdb_cr, + gdb_xer, + gdb_ctr, + gdb_pc, +}; + + +// Note that the size and offset will be updated by platform-specific classes. +#define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ + { #reg, alt, 8, 0, eEncodingUint, \ + eFormatHex, { kind1, kind2, kind3, kind4}, NULL, NULL } +static const RegisterInfo +g_register_infos[] = +{ + // General purpose registers. GCC, DWARF, Generic, GDB + DEFINE_GPR(r0, NULL, gcc_dwarf_r0, gcc_dwarf_r0, LLDB_INVALID_REGNUM, gdb_r0), + DEFINE_GPR(r1, "sp", gcc_dwarf_r1, gcc_dwarf_r1, LLDB_REGNUM_GENERIC_SP, gdb_r1), + DEFINE_GPR(r2, NULL, gcc_dwarf_r2, gcc_dwarf_r2, LLDB_INVALID_REGNUM, gdb_r2), + DEFINE_GPR(r3, "arg1",gcc_dwarf_r3, gcc_dwarf_r3, LLDB_REGNUM_GENERIC_ARG1, gdb_r3), + DEFINE_GPR(r4, "arg2",gcc_dwarf_r4, gcc_dwarf_r4, LLDB_REGNUM_GENERIC_ARG2 ,gdb_r4), + DEFINE_GPR(r5, "arg3",gcc_dwarf_r5, gcc_dwarf_r5, LLDB_REGNUM_GENERIC_ARG3, gdb_r5), + DEFINE_GPR(r6, "arg4",gcc_dwarf_r6, gcc_dwarf_r6, LLDB_REGNUM_GENERIC_ARG4, gdb_r6), + DEFINE_GPR(r7, "arg5",gcc_dwarf_r7, gcc_dwarf_r7, LLDB_REGNUM_GENERIC_ARG5, gdb_r7), + DEFINE_GPR(r8, "arg6",gcc_dwarf_r8, gcc_dwarf_r8, LLDB_REGNUM_GENERIC_ARG6, gdb_r8), + DEFINE_GPR(r9, "arg7",gcc_dwarf_r9, gcc_dwarf_r9, LLDB_REGNUM_GENERIC_ARG7, gdb_r9), + DEFINE_GPR(r10, "arg8",gcc_dwarf_r10, gcc_dwarf_r10, LLDB_REGNUM_GENERIC_ARG8, gdb_r10), + DEFINE_GPR(r11, NULL, gcc_dwarf_r11, gcc_dwarf_r11, LLDB_INVALID_REGNUM, gdb_r11), + DEFINE_GPR(r12, NULL, gcc_dwarf_r12, gcc_dwarf_r12, LLDB_INVALID_REGNUM, gdb_r12), + DEFINE_GPR(r13, NULL, gcc_dwarf_r13, gcc_dwarf_r13, LLDB_INVALID_REGNUM, gdb_r13), + DEFINE_GPR(r14, NULL, gcc_dwarf_r14, gcc_dwarf_r14, LLDB_INVALID_REGNUM, gdb_r14), + DEFINE_GPR(r15, NULL, gcc_dwarf_r15, gcc_dwarf_r15, LLDB_INVALID_REGNUM, gdb_r15), + DEFINE_GPR(r16, NULL, gcc_dwarf_r16, gcc_dwarf_r16, LLDB_INVALID_REGNUM, gdb_r16), + DEFINE_GPR(r17, NULL, gcc_dwarf_r17, gcc_dwarf_r17, LLDB_INVALID_REGNUM, gdb_r17), + DEFINE_GPR(r18, NULL, gcc_dwarf_r18, gcc_dwarf_r18, LLDB_INVALID_REGNUM, gdb_r18), + DEFINE_GPR(r19, NULL, gcc_dwarf_r19, gcc_dwarf_r19, LLDB_INVALID_REGNUM, gdb_r19), + DEFINE_GPR(r20, NULL, gcc_dwarf_r20, gcc_dwarf_r20, LLDB_INVALID_REGNUM, gdb_r20), + DEFINE_GPR(r21, NULL, gcc_dwarf_r21, gcc_dwarf_r21, LLDB_INVALID_REGNUM, gdb_r21), + DEFINE_GPR(r22, NULL, gcc_dwarf_r22, gcc_dwarf_r22, LLDB_INVALID_REGNUM, gdb_r22), + DEFINE_GPR(r23, NULL, gcc_dwarf_r23, gcc_dwarf_r23, LLDB_INVALID_REGNUM, gdb_r23), + DEFINE_GPR(r24, NULL, gcc_dwarf_r24, gcc_dwarf_r24, LLDB_INVALID_REGNUM, gdb_r24), + DEFINE_GPR(r25, NULL, gcc_dwarf_r25, gcc_dwarf_r25, LLDB_INVALID_REGNUM, gdb_r25), + DEFINE_GPR(r26, NULL, gcc_dwarf_r26, gcc_dwarf_r26, LLDB_INVALID_REGNUM, gdb_r26), + DEFINE_GPR(r27, NULL, gcc_dwarf_r27, gcc_dwarf_r27, LLDB_INVALID_REGNUM, gdb_r27), + DEFINE_GPR(r28, NULL, gcc_dwarf_r28, gcc_dwarf_r28, LLDB_INVALID_REGNUM, gdb_r28), + DEFINE_GPR(r29, NULL, gcc_dwarf_r29, gcc_dwarf_r29, LLDB_INVALID_REGNUM, gdb_r29), + DEFINE_GPR(r30, NULL, gcc_dwarf_r30, gcc_dwarf_r30, LLDB_INVALID_REGNUM, gdb_r30), + DEFINE_GPR(r31, NULL, gcc_dwarf_r31, gcc_dwarf_r31, LLDB_INVALID_REGNUM, gdb_r31), + DEFINE_GPR(lr, "lr", gcc_dwarf_lr, gcc_dwarf_lr, LLDB_REGNUM_GENERIC_RA, gdb_lr), + DEFINE_GPR(cr, "cr", gcc_dwarf_cr, gcc_dwarf_cr, LLDB_REGNUM_GENERIC_FLAGS, LLDB_INVALID_REGNUM), + DEFINE_GPR(xer, "xer", gcc_dwarf_xer, gcc_dwarf_xer, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(ctr, "ctr", gcc_dwarf_ctr, gcc_dwarf_ctr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM), + DEFINE_GPR(pc, "pc", gcc_dwarf_pc, gcc_dwarf_pc, LLDB_REGNUM_GENERIC_PC, LLDB_INVALID_REGNUM), + { NULL, NULL, 8, 0, eEncodingUint, eFormatHex, { gcc_dwarf_cfa, gcc_dwarf_cfa, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM}, NULL, NULL}, +}; + +static const uint32_t k_num_register_infos = llvm::array_lengthof(g_register_infos); + +const lldb_private::RegisterInfo * +ABISysV_ppc64::GetRegisterInfoArray (uint32_t &count) +{ + count = k_num_register_infos; + return g_register_infos; +} + + +size_t +ABISysV_ppc64::GetRedZoneSize () const +{ + return 224; +} + +//------------------------------------------------------------------ +// Static Functions +//------------------------------------------------------------------ +ABISP +ABISysV_ppc64::CreateInstance (const ArchSpec &arch) +{ + static ABISP g_abi_sp; + if (arch.GetTriple().getArch() == llvm::Triple::ppc64) + { + if (!g_abi_sp) + g_abi_sp.reset (new ABISysV_ppc64); + return g_abi_sp; + } + return ABISP(); +} + +bool +ABISysV_ppc64::PrepareTrivialCall (Thread &thread, + addr_t sp, + addr_t func_addr, + addr_t return_addr, + llvm::ArrayRef<addr_t> args) const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + StreamString s; + s.Printf("ABISysV_ppc64::PrepareTrivialCall (tid = 0x%" PRIx64 ", sp = 0x%" PRIx64 ", func_addr = 0x%" PRIx64 ", return_addr = 0x%" PRIx64, + thread.GetID(), + (uint64_t)sp, + (uint64_t)func_addr, + (uint64_t)return_addr); + + for (size_t i = 0; i < args.size(); ++i) + s.Printf (", arg%zd = 0x%" PRIx64, i + 1, args[i]); + s.PutCString (")"); + log->PutCString(s.GetString().c_str()); + } + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return false; + + const RegisterInfo *reg_info = NULL; + + if (args.size() > 8) // TODO handle more than 8 arguments + return false; + + for (size_t i = 0; i < args.size(); ++i) + { + reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i); + if (log) + log->Printf("About to write arg%zd (0x%" PRIx64 ") into %s", i + 1, args[i], reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i])) + return false; + } + + // First, align the SP + + if (log) + log->Printf("16-byte aligning SP: 0x%" PRIx64 " to 0x%" PRIx64, (uint64_t)sp, (uint64_t)(sp & ~0xfull)); + + sp &= ~(0xfull); // 16-byte alignment + + sp -= 8; + + Error error; + const RegisterInfo *pc_reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + const RegisterInfo *sp_reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + ProcessSP process_sp (thread.GetProcess()); + + RegisterValue reg_value; + +#if 0 + // This code adds an extra frame so that we don't lose the function that we came from + // by pushing the PC and the FP and then writing the current FP to point to the FP value + // we just pushed. It is disabled for now until the stack backtracing code can be debugged. + + // Save current PC + const RegisterInfo *fp_reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + if (reg_ctx->ReadRegister(pc_reg_info, reg_value)) + { + if (log) + log->Printf("Pushing the current PC onto the stack: 0x%" PRIx64 ": 0x%" PRIx64, (uint64_t)sp, reg_value.GetAsUInt64()); + + if (!process_sp->WritePointerToMemory(sp, reg_value.GetAsUInt64(), error)) + return false; + + sp -= 8; + + // Save current FP + if (reg_ctx->ReadRegister(fp_reg_info, reg_value)) + { + if (log) + log->Printf("Pushing the current FP onto the stack: 0x%" PRIx64 ": 0x%" PRIx64, (uint64_t)sp, reg_value.GetAsUInt64()); + + if (!process_sp->WritePointerToMemory(sp, reg_value.GetAsUInt64(), error)) + return false; + } + // Setup FP backchain + reg_value.SetUInt64 (sp); + + if (log) + log->Printf("Writing FP: 0x%" PRIx64 " (for FP backchain)", reg_value.GetAsUInt64()); + + if (!reg_ctx->WriteRegister(fp_reg_info, reg_value)) + { + return false; + } + + sp -= 8; + } +#endif + + if (log) + log->Printf("Pushing the return address onto the stack: 0x%" PRIx64 ": 0x%" PRIx64, (uint64_t)sp, (uint64_t)return_addr); + + // Save return address onto the stack + if (!process_sp->WritePointerToMemory(sp, return_addr, error)) + return false; + + // %r1 is set to the actual stack value. + + if (log) + log->Printf("Writing SP: 0x%" PRIx64, (uint64_t)sp); + + if (!reg_ctx->WriteRegisterFromUnsigned (sp_reg_info, sp)) + return false; + + // %pc is set to the address of the called function. + + if (log) + log->Printf("Writing IP: 0x%" PRIx64, (uint64_t)func_addr); + + if (!reg_ctx->WriteRegisterFromUnsigned (pc_reg_info, func_addr)) + return false; + + return true; +} + +static bool ReadIntegerArgument(Scalar &scalar, + unsigned int bit_width, + bool is_signed, + Thread &thread, + uint32_t *argument_register_ids, + unsigned int ¤t_argument_register, + addr_t ¤t_stack_argument) +{ + if (bit_width > 64) + return false; // Scalar can't hold large integer arguments + + if (current_argument_register < 6) + { + scalar = thread.GetRegisterContext()->ReadRegisterAsUnsigned(argument_register_ids[current_argument_register], 0); + current_argument_register++; + if (is_signed) + scalar.SignExtend (bit_width); + } + else + { + uint32_t byte_size = (bit_width + (8-1))/8; + Error error; + if (thread.GetProcess()->ReadScalarIntegerFromMemory(current_stack_argument, byte_size, is_signed, scalar, error)) + { + current_stack_argument += byte_size; + return true; + } + return false; + } + return true; +} + +bool +ABISysV_ppc64::GetArgumentValues (Thread &thread, + ValueList &values) const +{ + unsigned int num_values = values.GetSize(); + unsigned int value_index; + + // Extract the register context so we can read arguments from registers + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + + if (!reg_ctx) + return false; + + // Get the pointer to the first stack argument so we have a place to start + // when reading data + + addr_t sp = reg_ctx->GetSP(0); + + if (!sp) + return false; + + addr_t current_stack_argument = sp + 48; // jump over return address + + uint32_t argument_register_ids[8]; + + argument_register_ids[0] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1)->kinds[eRegisterKindLLDB]; + argument_register_ids[1] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2)->kinds[eRegisterKindLLDB]; + argument_register_ids[2] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG3)->kinds[eRegisterKindLLDB]; + argument_register_ids[3] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG4)->kinds[eRegisterKindLLDB]; + argument_register_ids[4] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG5)->kinds[eRegisterKindLLDB]; + argument_register_ids[5] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG6)->kinds[eRegisterKindLLDB]; + argument_register_ids[6] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG7)->kinds[eRegisterKindLLDB]; + argument_register_ids[7] = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG8)->kinds[eRegisterKindLLDB]; + + unsigned int current_argument_register = 0; + + for (value_index = 0; + value_index < num_values; + ++value_index) + { + Value *value = values.GetValueAtIndex(value_index); + + if (!value) + return false; + + // We currently only support extracting values with Clang QualTypes. + // Do we care about others? + ClangASTType clang_type = value->GetClangType(); + if (!clang_type) + return false; + bool is_signed; + + if (clang_type.IsIntegerType (is_signed)) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + is_signed, + thread, + argument_register_ids, + current_argument_register, + current_stack_argument); + } + else if (clang_type.IsPointerType ()) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + false, + thread, + argument_register_ids, + current_argument_register, + current_stack_argument); + } + } + + return true; +} + +Error +ABISysV_ppc64::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value_sp) +{ + Error error; + if (!new_value_sp) + { + error.SetErrorString("Empty value object for return value."); + return error; + } + + ClangASTType clang_type = new_value_sp->GetClangType(); + if (!clang_type) + { + error.SetErrorString ("Null clang type for return value."); + return error; + } + + Thread *thread = frame_sp->GetThread().get(); + + bool is_signed; + uint32_t count; + bool is_complex; + + RegisterContext *reg_ctx = thread->GetRegisterContext().get(); + + bool set_it_simple = false; + if (clang_type.IsIntegerType (is_signed) || clang_type.IsPointerType()) + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("r3", 0); + + DataExtractor data; + Error data_error; + size_t num_bytes = new_value_sp->GetData(data, data_error); + if (data_error.Fail()) + { + error.SetErrorStringWithFormat("Couldn't convert return value to raw data: %s", data_error.AsCString()); + return error; + } + lldb::offset_t offset = 0; + if (num_bytes <= 8) + { + uint64_t raw_value = data.GetMaxU64(&offset, num_bytes); + + if (reg_ctx->WriteRegisterFromUnsigned (reg_info, raw_value)) + set_it_simple = true; + } + else + { + error.SetErrorString("We don't support returning longer than 64 bit integer values at present."); + } + + } + else if (clang_type.IsFloatingPointType (count, is_complex)) + { + if (is_complex) + error.SetErrorString ("We don't support returning complex values at present"); + else + { + size_t bit_width = clang_type.GetBitSize(); + if (bit_width <= 64) + { + DataExtractor data; + Error data_error; + size_t num_bytes = new_value_sp->GetData(data, data_error); + if (data_error.Fail()) + { + error.SetErrorStringWithFormat("Couldn't convert return value to raw data: %s", data_error.AsCString()); + return error; + } + + unsigned char buffer[16]; + ByteOrder byte_order = data.GetByteOrder(); + + data.CopyByteOrderedData (0, num_bytes, buffer, 16, byte_order); + set_it_simple = true; + } + else + { + // FIXME - don't know how to do 80 bit long doubles yet. + error.SetErrorString ("We don't support returning float values > 64 bits at present"); + } + } + } + + if (!set_it_simple) + { + // Okay we've got a structure or something that doesn't fit in a simple register. + // We should figure out where it really goes, but we don't support this yet. + error.SetErrorString ("We only support setting simple integer and float return types at present."); + } + + return error; +} + + +ValueObjectSP +ABISysV_ppc64::GetReturnValueObjectSimple (Thread &thread, + ClangASTType &return_clang_type) const +{ + ValueObjectSP return_valobj_sp; + Value value; + + if (!return_clang_type) + return return_valobj_sp; + + //value.SetContext (Value::eContextTypeClangType, return_value_type); + value.SetClangType (return_clang_type); + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return return_valobj_sp; + + const uint32_t type_flags = return_clang_type.GetTypeInfo (); + if (type_flags & eTypeIsScalar) + { + value.SetValueType(Value::eValueTypeScalar); + + bool success = false; + if (type_flags & eTypeIsInteger) + { + // Extract the register context so we can read arguments from registers + + const size_t byte_size = return_clang_type.GetByteSize(); + uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg_ctx->GetRegisterInfoByName("r3", 0), 0); + const bool is_signed = (type_flags & eTypeIsSigned) != 0; + switch (byte_size) + { + default: + break; + + case sizeof(uint64_t): + if (is_signed) + value.GetScalar() = (int64_t)(raw_value); + else + value.GetScalar() = (uint64_t)(raw_value); + success = true; + break; + + case sizeof(uint32_t): + if (is_signed) + value.GetScalar() = (int32_t)(raw_value & UINT32_MAX); + else + value.GetScalar() = (uint32_t)(raw_value & UINT32_MAX); + success = true; + break; + + case sizeof(uint16_t): + if (is_signed) + value.GetScalar() = (int16_t)(raw_value & UINT16_MAX); + else + value.GetScalar() = (uint16_t)(raw_value & UINT16_MAX); + success = true; + break; + + case sizeof(uint8_t): + if (is_signed) + value.GetScalar() = (int8_t)(raw_value & UINT8_MAX); + else + value.GetScalar() = (uint8_t)(raw_value & UINT8_MAX); + success = true; + break; + } + } + else if (type_flags & eTypeIsFloat) + { + if (type_flags & eTypeIsComplex) + { + // Don't handle complex yet. + } + else + { + const size_t byte_size = return_clang_type.GetByteSize(); + if (byte_size <= sizeof(long double)) + { + const RegisterInfo *f1_info = reg_ctx->GetRegisterInfoByName("f1", 0); + RegisterValue f1_value; + if (reg_ctx->ReadRegister (f1_info, f1_value)) + { + DataExtractor data; + if (f1_value.GetData(data)) + { + lldb::offset_t offset = 0; + if (byte_size == sizeof(float)) + { + value.GetScalar() = (float) data.GetFloat(&offset); + success = true; + } + else if (byte_size == sizeof(double)) + { + value.GetScalar() = (double) data.GetDouble(&offset); + success = true; + } + } + } + } + } + } + + if (success) + return_valobj_sp = ValueObjectConstResult::Create (thread.GetStackFrameAtIndex(0).get(), + value, + ConstString("")); + + } + else if (type_flags & eTypeIsPointer) + { + unsigned r3_id = reg_ctx->GetRegisterInfoByName("r3", 0)->kinds[eRegisterKindLLDB]; + value.GetScalar() = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r3_id, 0); + value.SetValueType(Value::eValueTypeScalar); + return_valobj_sp = ValueObjectConstResult::Create (thread.GetStackFrameAtIndex(0).get(), + value, + ConstString("")); + } + else if (type_flags & eTypeIsVector) + { + const size_t byte_size = return_clang_type.GetByteSize(); + if (byte_size > 0) + { + + const RegisterInfo *altivec_reg = reg_ctx->GetRegisterInfoByName("v0", 0); + if (altivec_reg == NULL) + altivec_reg = reg_ctx->GetRegisterInfoByName("mm0", 0); + if (altivec_reg) + { + if (byte_size <= altivec_reg->byte_size) + { + ProcessSP process_sp (thread.GetProcess()); + if (process_sp) + { + std::unique_ptr<DataBufferHeap> heap_data_ap (new DataBufferHeap(byte_size, 0)); + const ByteOrder byte_order = process_sp->GetByteOrder(); + RegisterValue reg_value; + if (reg_ctx->ReadRegister(altivec_reg, reg_value)) + { + Error error; + if (reg_value.GetAsMemoryData (altivec_reg, + heap_data_ap->GetBytes(), + heap_data_ap->GetByteSize(), + byte_order, + error)) + { + DataExtractor data (DataBufferSP (heap_data_ap.release()), + byte_order, + process_sp->GetTarget().GetArchitecture().GetAddressByteSize()); + return_valobj_sp = ValueObjectConstResult::Create (&thread, + return_clang_type, + ConstString(""), + data); + } + } + } + } + } + } + } + + return return_valobj_sp; +} + +ValueObjectSP +ABISysV_ppc64::GetReturnValueObjectImpl (Thread &thread, ClangASTType &return_clang_type) const +{ + ValueObjectSP return_valobj_sp; + + if (!return_clang_type) + return return_valobj_sp; + + ExecutionContext exe_ctx (thread.shared_from_this()); + return_valobj_sp = GetReturnValueObjectSimple(thread, return_clang_type); + if (return_valobj_sp) + return return_valobj_sp; + + RegisterContextSP reg_ctx_sp = thread.GetRegisterContext(); + if (!reg_ctx_sp) + return return_valobj_sp; + + const size_t bit_width = return_clang_type.GetBitSize(); + if (return_clang_type.IsAggregateType()) + { + Target *target = exe_ctx.GetTargetPtr(); + bool is_memory = true; + if (bit_width <= 128) + { + ByteOrder target_byte_order = target->GetArchitecture().GetByteOrder(); + DataBufferSP data_sp (new DataBufferHeap(16, 0)); + DataExtractor return_ext (data_sp, + target_byte_order, + target->GetArchitecture().GetAddressByteSize()); + + const RegisterInfo *r3_info = reg_ctx_sp->GetRegisterInfoByName("r3", 0); + const RegisterInfo *rdx_info = reg_ctx_sp->GetRegisterInfoByName("rdx", 0); + + RegisterValue r3_value, rdx_value; + reg_ctx_sp->ReadRegister (r3_info, r3_value); + reg_ctx_sp->ReadRegister (rdx_info, rdx_value); + + DataExtractor r3_data, rdx_data; + + r3_value.GetData(r3_data); + rdx_value.GetData(rdx_data); + + uint32_t fp_bytes = 0; // Tracks how much of the xmm registers we've consumed so far + uint32_t integer_bytes = 0; // Tracks how much of the r3/rds registers we've consumed so far + + const uint32_t num_children = return_clang_type.GetNumFields (); + + // Since we are in the small struct regime, assume we are not in memory. + is_memory = false; + + for (uint32_t idx = 0; idx < num_children; idx++) + { + std::string name; + uint64_t field_bit_offset = 0; + bool is_signed; + bool is_complex; + uint32_t count; + + ClangASTType field_clang_type = return_clang_type.GetFieldAtIndex (idx, name, &field_bit_offset, NULL, NULL); + const size_t field_bit_width = field_clang_type.GetBitSize(); + + // If there are any unaligned fields, this is stored in memory. + if (field_bit_offset % field_bit_width != 0) + { + is_memory = true; + break; + } + + uint32_t field_byte_width = field_bit_width/8; + uint32_t field_byte_offset = field_bit_offset/8; + + + DataExtractor *copy_from_extractor = NULL; + uint32_t copy_from_offset = 0; + + if (field_clang_type.IsIntegerType (is_signed) || field_clang_type.IsPointerType ()) + { + if (integer_bytes < 8) + { + if (integer_bytes + field_byte_width <= 8) + { + // This is in RAX, copy from register to our result structure: + copy_from_extractor = &r3_data; + copy_from_offset = integer_bytes; + integer_bytes += field_byte_width; + } + else + { + // The next field wouldn't fit in the remaining space, so we pushed it to rdx. + copy_from_extractor = &rdx_data; + copy_from_offset = 0; + integer_bytes = 8 + field_byte_width; + + } + } + else if (integer_bytes + field_byte_width <= 16) + { + copy_from_extractor = &rdx_data; + copy_from_offset = integer_bytes - 8; + integer_bytes += field_byte_width; + } + else + { + // The last field didn't fit. I can't see how that would happen w/o the overall size being + // greater than 16 bytes. For now, return a NULL return value object. + return return_valobj_sp; + } + } + else if (field_clang_type.IsFloatingPointType (count, is_complex)) + { + // Structs with long doubles are always passed in memory. + if (field_bit_width == 128) + { + is_memory = true; + break; + } + else if (field_bit_width == 64) + { + copy_from_offset = 0; + fp_bytes += field_byte_width; + } + else if (field_bit_width == 32) + { + // This one is kind of complicated. If we are in an "eightbyte" with another float, we'll + // be stuffed into an xmm register with it. If we are in an "eightbyte" with one or more ints, + // then we will be stuffed into the appropriate GPR with them. + bool in_gpr; + if (field_byte_offset % 8 == 0) + { + // We are at the beginning of one of the eightbytes, so check the next element (if any) + if (idx == num_children - 1) + in_gpr = false; + else + { + uint64_t next_field_bit_offset = 0; + ClangASTType next_field_clang_type = return_clang_type.GetFieldAtIndex (idx + 1, + name, + &next_field_bit_offset, + NULL, + NULL); + if (next_field_clang_type.IsIntegerType (is_signed)) + in_gpr = true; + else + { + copy_from_offset = 0; + in_gpr = false; + } + } + + } + else if (field_byte_offset % 4 == 0) + { + // We are inside of an eightbyte, so see if the field before us is floating point: + // This could happen if somebody put padding in the structure. + if (idx == 0) + in_gpr = false; + else + { + uint64_t prev_field_bit_offset = 0; + ClangASTType prev_field_clang_type = return_clang_type.GetFieldAtIndex (idx - 1, + name, + &prev_field_bit_offset, + NULL, + NULL); + if (prev_field_clang_type.IsIntegerType (is_signed)) + in_gpr = true; + else + { + copy_from_offset = 4; + in_gpr = false; + } + } + + } + else + { + is_memory = true; + continue; + } + + // Okay, we've figured out whether we are in GPR or XMM, now figure out which one. + if (in_gpr) + { + if (integer_bytes < 8) + { + // This is in RAX, copy from register to our result structure: + copy_from_extractor = &r3_data; + copy_from_offset = integer_bytes; + integer_bytes += field_byte_width; + } + else + { + copy_from_extractor = &rdx_data; + copy_from_offset = integer_bytes - 8; + integer_bytes += field_byte_width; + } + } + else + { + fp_bytes += field_byte_width; + } + } + } + + // These two tests are just sanity checks. If I somehow get the + // type calculation wrong above it is better to just return nothing + // than to assert or crash. + if (!copy_from_extractor) + return return_valobj_sp; + if (copy_from_offset + field_byte_width > copy_from_extractor->GetByteSize()) + return return_valobj_sp; + + copy_from_extractor->CopyByteOrderedData (copy_from_offset, + field_byte_width, + data_sp->GetBytes() + field_byte_offset, + field_byte_width, + target_byte_order); + } + + if (!is_memory) + { + // The result is in our data buffer. Let's make a variable object out of it: + return_valobj_sp = ValueObjectConstResult::Create (&thread, + return_clang_type, + ConstString(""), + return_ext); + } + } + + + // FIXME: This is just taking a guess, r3 may very well no longer hold the return storage location. + // If we are going to do this right, when we make a new frame we should check to see if it uses a memory + // return, and if we are at the first instruction and if so stash away the return location. Then we would + // only return the memory return value if we know it is valid. + + if (is_memory) + { + unsigned r3_id = reg_ctx_sp->GetRegisterInfoByName("r3", 0)->kinds[eRegisterKindLLDB]; + lldb::addr_t storage_addr = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(r3_id, 0); + return_valobj_sp = ValueObjectMemory::Create (&thread, + "", + Address (storage_addr, NULL), + return_clang_type); + } + } + + return return_valobj_sp; +} + +bool +ABISysV_ppc64::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) +{ + unwind_plan.Clear(); + unwind_plan.SetRegisterKind (eRegisterKindDWARF); + + uint32_t lr_reg_num = gcc_dwarf_lr; + uint32_t sp_reg_num = gcc_dwarf_r1; + uint32_t pc_reg_num = gcc_dwarf_pc; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + + // Our Call Frame Address is the stack pointer value + row->SetCFARegister (sp_reg_num); + + // The previous PC is in the LR + row->SetRegisterLocationToRegister(pc_reg_num, lr_reg_num, true); + unwind_plan.AppendRow (row); + + // All other registers are the same. + + unwind_plan.SetSourceName ("ppc64 at-func-entry default"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + + return true; +} + +bool +ABISysV_ppc64::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) +{ + unwind_plan.Clear(); + unwind_plan.SetRegisterKind (eRegisterKindDWARF); + + uint32_t sp_reg_num = gcc_dwarf_r1; + uint32_t pc_reg_num = gcc_dwarf_lr; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + + const int32_t ptr_size = 8; + row->SetCFARegister (sp_reg_num); + row->SetCFAType(lldb_private::UnwindPlan::Row::CFAIsRegisterDereferenced); + + row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, ptr_size * 2, true); + row->SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true); + row->SetRegisterLocationToAtCFAPlusOffset(gcc_dwarf_cr, ptr_size, true); + + unwind_plan.AppendRow (row); + unwind_plan.SetSourceName ("ppc64 default unwind plan"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); + unwind_plan.SetReturnAddressRegister(gcc_dwarf_lr); + return true; +} + +bool +ABISysV_ppc64::RegisterIsVolatile (const RegisterInfo *reg_info) +{ + return !RegisterIsCalleeSaved (reg_info); +} + + + +// See "Register Usage" in the +// "System V Application Binary Interface" +// "64-bit PowerPC ELF Application Binary Interface Supplement" +// current version is 1.9 released 2004 at http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.pdf + +bool +ABISysV_ppc64::RegisterIsCalleeSaved (const RegisterInfo *reg_info) +{ + if (reg_info) + { + // Preserved registers are : + // r1,r2,r13-r31 + // cr2-cr4 (partially preserved) + // f14-f31 (not yet) + // v20-v31 (not yet) + // vrsave (not yet) + + const char *name = reg_info->name; + if (name[0] == 'r') + { + if ((name[1] == '1' || name[1] == '2') && name[2] == '\0') + return true; + if (name[1] == '1' && name[2] > '2') + return true; + if ((name[1] == '2' || name[1] == '3') && name[2] != '\0') + return true; + } + + if (name[0] == 'f' && name[1] >= '0' && name[2] <= '9') + { + if (name[2] == '\0') + return false; + if (name[1] == '1' && name[2] >= '4') + return true; + if ((name[1] == '2' || name[1] == '3') && name[2] != '\0') + return true; + } + + if (name[0] == 's' && name[1] == 'p' && name[2] == '\0') // sp + return true; + if (name[0] == 'f' && name[1] == 'p' && name[2] == '\0') // fp + return true; + if (name[0] == 'p' && name[1] == 'c' && name[2] == '\0') // pc + return true; + } + return false; +} + + + +void +ABISysV_ppc64::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "System V ABI for ppc64 targets", + CreateInstance); +} + +void +ABISysV_ppc64::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +ABISysV_ppc64::GetPluginNameStatic() +{ + static ConstString g_name("sysv-ppc64"); + return g_name; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +lldb_private::ConstString +ABISysV_ppc64::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ABISysV_ppc64::GetPluginVersion() +{ + return 1; +} + diff --git a/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h b/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h new file mode 100644 index 000000000000..d77cb9f1efe3 --- /dev/null +++ b/source/Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h @@ -0,0 +1,143 @@ +//===-- ABISysV_ppc64.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABISysV_ppc64_h_ +#define liblldb_ABISysV_ppc64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" + +class ABISysV_ppc64 : + public lldb_private::ABI +{ +public: + + ~ABISysV_ppc64() + { + } + + virtual size_t + GetRedZoneSize () const; + + virtual bool + PrepareTrivialCall (lldb_private::Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + llvm::ArrayRef<lldb::addr_t> args) const; + + virtual bool + GetArgumentValues (lldb_private::Thread &thread, + lldb_private::ValueList &values) const; + + virtual lldb_private::Error + SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value); + +protected: + lldb::ValueObjectSP + GetReturnValueObjectSimple (lldb_private::Thread &thread, + lldb_private::ClangASTType &ast_type) const; + +public: + virtual lldb::ValueObjectSP + GetReturnValueObjectImpl (lldb_private::Thread &thread, + lldb_private::ClangASTType &type) const; + + virtual bool + CreateFunctionEntryUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + CreateDefaultUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + RegisterIsVolatile (const lldb_private::RegisterInfo *reg_info); + + virtual bool + StackUsesFrames () + { + return true; + } + + // The SysV ppc64 ABI requires that stack frames be 16 byte aligned. + // When there is a trap handler on the stack, e.g. _sigtramp in userland + // code, we've seen that the stack pointer is often not aligned properly + // before the handler is invoked. This means that lldb will stop the unwind + // early -- before the function which caused the trap. + // + // To work around this, we relax that alignment to be just word-size (8-bytes). + // Whitelisting the trap handlers for user space would be easy (_sigtramp) but + // in other environments there can be a large number of different functions + // involved in async traps. + virtual bool + CallFrameAddressIsValid (lldb::addr_t cfa) + { + // Make sure the stack call frame addresses are 8 byte aligned + if (cfa & (8ull - 1ull)) + return false; // Not 8 byte aligned + if (cfa == 0) + return false; // Zero is not a valid stack address + return true; + } + + virtual bool + CodeAddressIsValid (lldb::addr_t pc) + { + // We have a 64 bit address space, so anything is valid as opcodes + // aren't fixed width... + return true; + } + + virtual bool + FunctionCallsChangeCFA () + { + return true; + } + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoArray (uint32_t &count); + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb::ABISP + CreateInstance (const lldb_private::ArchSpec &arch); + + static lldb_private::ConstString + GetPluginNameStatic(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + void + CreateRegisterMapIfNeeded (); + + bool + RegisterIsCalleeSaved (const lldb_private::RegisterInfo *reg_info); + +private: + ABISysV_ppc64() : lldb_private::ABI() { } // Call CreateInstance instead. +}; + +#endif // liblldb_ABI_h_ diff --git a/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp b/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp index b537415bf055..776e7fea67c7 100644 --- a/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp +++ b/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp @@ -649,18 +649,18 @@ ABISysV_x86_64::GetReturnValueObjectSimple (Thread &thread, return return_valobj_sp; const uint32_t type_flags = return_clang_type.GetTypeInfo (); - if (type_flags & ClangASTType::eTypeIsScalar) + if (type_flags & eTypeIsScalar) { value.SetValueType(Value::eValueTypeScalar); bool success = false; - if (type_flags & ClangASTType::eTypeIsInteger) + if (type_flags & eTypeIsInteger) { // Extract the register context so we can read arguments from registers const size_t byte_size = return_clang_type.GetByteSize(); uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg_ctx->GetRegisterInfoByName("rax", 0), 0); - const bool is_signed = (type_flags & ClangASTType::eTypeIsSigned) != 0; + const bool is_signed = (type_flags & eTypeIsSigned) != 0; switch (byte_size) { default: @@ -699,9 +699,9 @@ ABISysV_x86_64::GetReturnValueObjectSimple (Thread &thread, break; } } - else if (type_flags & ClangASTType::eTypeIsFloat) + else if (type_flags & eTypeIsFloat) { - if (type_flags & ClangASTType::eTypeIsComplex) + if (type_flags & eTypeIsComplex) { // Don't handle complex yet. } @@ -744,7 +744,7 @@ ABISysV_x86_64::GetReturnValueObjectSimple (Thread &thread, ConstString("")); } - else if (type_flags & ClangASTType::eTypeIsPointer) + else if (type_flags & eTypeIsPointer) { unsigned rax_id = reg_ctx->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB]; value.GetScalar() = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0); @@ -753,7 +753,7 @@ ABISysV_x86_64::GetReturnValueObjectSimple (Thread &thread, value, ConstString("")); } - else if (type_flags & ClangASTType::eTypeIsVector) + else if (type_flags & eTypeIsVector) { const size_t byte_size = return_clang_type.GetByteSize(); if (byte_size > 0) diff --git a/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h b/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h index 7fd1b6c4b0f3..6fefcc2a9c74 100644 --- a/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h +++ b/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h @@ -61,13 +61,7 @@ public: virtual bool RegisterIsVolatile (const lldb_private::RegisterInfo *reg_info); - - virtual bool - StackUsesFrames () - { - return true; - } - + // The SysV x86_64 ABI requires that stack frames be 16 byte aligned. // When there is a trap handler on the stack, e.g. _sigtramp in userland // code, we've seen that the stack pointer is often not aligned properly @@ -97,12 +91,6 @@ public: return true; } - virtual bool - FunctionCallsChangeCFA () - { - return true; - } - virtual const lldb_private::RegisterInfo * GetRegisterInfoArray (uint32_t &count); //------------------------------------------------------------------ diff --git a/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp b/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp index c14371d0589c..97ddbef0e307 100644 --- a/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp +++ b/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp @@ -21,7 +21,6 @@ #include "llvm/MC/MCRelocationInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/MemoryObject.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/ADT/SmallString.h" @@ -489,41 +488,19 @@ DisassemblerLLVMC::LLVMCDisassembler::~LLVMCDisassembler() { } -namespace { - // This is the memory object we use in GetInstruction. - class LLDBDisasmMemoryObject : public llvm::MemoryObject { - const uint8_t *m_bytes; - uint64_t m_size; - uint64_t m_base_PC; - public: - LLDBDisasmMemoryObject(const uint8_t *bytes, uint64_t size, uint64_t basePC) : - m_bytes(bytes), m_size(size), m_base_PC(basePC) {} - - uint64_t getBase() const { return m_base_PC; } - uint64_t getExtent() const { return m_size; } - - int readByte(uint64_t addr, uint8_t *byte) const { - if (addr - m_base_PC >= m_size) - return -1; - *byte = m_bytes[addr - m_base_PC]; - return 0; - } - }; -} // End Anonymous Namespace - uint64_t DisassemblerLLVMC::LLVMCDisassembler::GetMCInst (const uint8_t *opcode_data, size_t opcode_data_len, lldb::addr_t pc, llvm::MCInst &mc_inst) { - LLDBDisasmMemoryObject memory_object (opcode_data, opcode_data_len, pc); + llvm::ArrayRef<uint8_t> data(opcode_data, opcode_data_len); llvm::MCDisassembler::DecodeStatus status; uint64_t new_inst_size; status = m_disasm_ap->getInstruction(mc_inst, new_inst_size, - memory_object, + data, pc, llvm::nulls(), llvm::nulls()); @@ -832,11 +809,19 @@ const char *DisassemblerLLVMC::SymbolLookup (uint64_t value, value_so_addr.Dump (&ss, target, - Address::DumpStyleResolvedDescriptionNoModule, + Address::DumpStyleResolvedDescriptionNoFunctionArguments, Address::DumpStyleSectionNameOffset); if (!ss.GetString().empty()) { + // If Address::Dump returned a multi-line description, most commonly seen when we + // have multiple levels of inlined functions at an address, only show the first line. + std::string &str(ss.GetString()); + size_t first_eol_char = str.find_first_of ("\r\n"); + if (first_eol_char != std::string::npos) + { + str.erase (first_eol_char); + } m_inst->AppendComment(ss.GetString()); } } diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp b/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp index 0e203fe43a79..d8279e44e14a 100644 --- a/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp @@ -34,6 +34,13 @@ ResolveRendezvousAddress(Process *process) addr_t info_addr; Error error; + if (!process) + { + if (log) + log->Printf ("%s null process provided", __FUNCTION__); + return LLDB_INVALID_ADDRESS; + } + // Try to get it from our process. This might be a remote process and might // grab it via some remote-specific mechanism. info_location = process->GetImageInfoAddress(); @@ -43,7 +50,7 @@ ResolveRendezvousAddress(Process *process) // If the process fails to return an address, fall back to seeing if the local object file can help us find it. if (info_location == LLDB_INVALID_ADDRESS) { - Target *target = process ? &process->GetTarget() : nullptr; + Target *target = &process->GetTarget(); if (target) { ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile(); @@ -70,6 +77,9 @@ ResolveRendezvousAddress(Process *process) return LLDB_INVALID_ADDRESS; } + if (log) + log->Printf ("%s reading pointer (%" PRIu32 " bytes) from 0x%" PRIx64, __FUNCTION__, process->GetAddressByteSize(), info_location); + info_addr = process->ReadPointerFromMemory(info_location, error); if (error.Fail()) { @@ -193,7 +203,7 @@ DYLDRendezvous::UpdateSOEntries() // state and take a snapshot of the currently loaded images. if (m_current.state == eAdd || m_current.state == eDelete) { - assert(m_previous.state == eConsistent); + assert(m_previous.state == eConsistent || (m_previous.state == eAdd && m_current.state == eDelete)); m_soentries.clear(); m_added_soentries.clear(); m_removed_soentries.clear(); @@ -320,23 +330,11 @@ DYLDRendezvous::ReadStringFromMemory(addr_t addr) { std::string str; Error error; - size_t size; - char c; if (addr == LLDB_INVALID_ADDRESS) return std::string(); - for (;;) { - size = m_process->DoReadMemory(addr, &c, 1, error); - if (size != 1 || error.Fail()) - return std::string(); - if (c == 0) - break; - else { - str.push_back(c); - addr++; - } - } + m_process->ReadCStringFromMemory(addr, str, error); return str; } diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp index 549e5f9b5345..6b0b6f5cc8b8 100644 --- a/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -111,27 +111,88 @@ DynamicLoaderPOSIXDYLD::~DynamicLoaderPOSIXDYLD() void DynamicLoaderPOSIXDYLD::DidAttach() { - ModuleSP executable; - addr_t load_offset; + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s() pid %" PRIu64, __FUNCTION__, m_process ? m_process->GetID () : LLDB_INVALID_PROCESS_ID); m_auxv.reset(new AuxVector(m_process)); + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 " reloaded auxv data", __FUNCTION__, m_process ? m_process->GetID () : LLDB_INVALID_PROCESS_ID); - executable = GetTargetExecutable(); - load_offset = ComputeLoadOffset(); + ModuleSP executable_sp = GetTargetExecutable(); + ModuleSpec process_module_spec; + if (GetProcessModuleSpec(process_module_spec)) + { + if (executable_sp == nullptr || !executable_sp->MatchesModuleSpec(process_module_spec)) + { + executable_sp.reset(new Module(process_module_spec)); + assert(m_process != nullptr); + m_process->GetTarget().SetExecutableModule(executable_sp, false); + } + } - if (executable.get() && load_offset != LLDB_INVALID_ADDRESS) + addr_t load_offset = ComputeLoadOffset(); + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 " executable '%s', load_offset 0x%" PRIx64, __FUNCTION__, m_process ? m_process->GetID () : LLDB_INVALID_PROCESS_ID, executable_sp ? executable_sp->GetFileSpec().GetPath().c_str () : "<null executable>", load_offset); + + + if (executable_sp && load_offset != LLDB_INVALID_ADDRESS) { ModuleList module_list; - module_list.Append(executable); - UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_offset); - LoadAllCurrentModules(); + + module_list.Append(executable_sp); + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 " added executable '%s' to module load list", + __FUNCTION__, + m_process ? m_process->GetID () : LLDB_INVALID_PROCESS_ID, + executable_sp->GetFileSpec().GetPath().c_str ()); + + UpdateLoadedSections(executable_sp, LLDB_INVALID_ADDRESS, load_offset); + + // When attaching to a target, there are two possible states: + // (1) We already crossed the entry point and therefore the rendezvous + // structure is ready to be used and we can load the list of modules + // and place the rendezvous breakpoint. + // (2) We didn't cross the entry point yet, so these structures are not + // ready; we should behave as if we just launched the target and + // call ProbeEntry(). This will place a breakpoint on the entry + // point which itself will be hit after the rendezvous structure is + // set up and will perform actions described in (1). + if (m_rendezvous.Resolve()) + { + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s() pid %" PRIu64 " rendezvous could resolve: attach assuming dynamic loader info is available now", __FUNCTION__, m_process ? m_process->GetID () : LLDB_INVALID_PROCESS_ID); + LoadAllCurrentModules(); + SetRendezvousBreakpoint(); + } + else + { + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s() pid %" PRIu64 " rendezvous could not yet resolve: adding breakpoint to catch future rendezvous setup", __FUNCTION__, m_process ? m_process->GetID () : LLDB_INVALID_PROCESS_ID); + ProbeEntry(); + } + m_process->GetTarget().ModulesDidLoad(module_list); + if (log) + { + log->Printf ("DynamicLoaderPOSIXDYLD::%s told the target about the modules that loaded:", __FUNCTION__); + for (auto module_sp : module_list.Modules ()) + { + log->Printf ("-- [module] %s (pid %" PRIu64 ")", + module_sp ? module_sp->GetFileSpec().GetPath().c_str () : "<null>", + m_process ? m_process->GetID () : LLDB_INVALID_PROCESS_ID); + } + } } } void DynamicLoaderPOSIXDYLD::DidLaunch() { + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s()", __FUNCTION__); + ModuleSP executable; addr_t load_offset; @@ -145,7 +206,11 @@ DynamicLoaderPOSIXDYLD::DidLaunch() ModuleList module_list; module_list.Append(executable); UpdateLoadedSections(executable, LLDB_INVALID_ADDRESS, load_offset); + + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s about to call ProbeEntry()", __FUNCTION__); ProbeEntry(); + m_process->GetTarget().ModulesDidLoad(module_list); } } @@ -187,15 +252,28 @@ DynamicLoaderPOSIXDYLD::UnloadSections(const ModuleSP module) void DynamicLoaderPOSIXDYLD::ProbeEntry() { - Breakpoint *entry_break; - addr_t entry; + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); - if ((entry = GetEntryPoint()) == LLDB_INVALID_ADDRESS) + const addr_t entry = GetEntryPoint(); + if (entry == LLDB_INVALID_ADDRESS) + { + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 " GetEntryPoint() returned no address, not setting entry breakpoint", __FUNCTION__, m_process ? m_process->GetID () : LLDB_INVALID_PROCESS_ID); return; - - entry_break = m_process->GetTarget().CreateBreakpoint(entry, true, false).get(); - entry_break->SetCallback(EntryBreakpointHit, this, true); - entry_break->SetBreakpointKind("shared-library-event"); + } + + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 " GetEntryPoint() returned address 0x%" PRIx64 ", setting entry breakpoint", __FUNCTION__, m_process ? m_process->GetID () : LLDB_INVALID_PROCESS_ID, entry); + + if (m_process) + { + Breakpoint *const entry_break = m_process->GetTarget().CreateBreakpoint(entry, true, false).get(); + entry_break->SetCallback(EntryBreakpointHit, this, true); + entry_break->SetBreakpointKind("shared-library-event"); + + // Shoudn't hit this more than once. + entry_break->SetOneShot (true); + } } // The runtime linker has run and initialized the rendezvous structure once the @@ -210,9 +288,40 @@ DynamicLoaderPOSIXDYLD::EntryBreakpointHit(void *baton, user_id_t break_id, user_id_t break_loc_id) { - DynamicLoaderPOSIXDYLD* dyld_instance; + assert(baton && "null baton"); + if (!baton) + return false; + + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + DynamicLoaderPOSIXDYLD *const dyld_instance = static_cast<DynamicLoaderPOSIXDYLD*>(baton); + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s called for pid %" PRIu64, __FUNCTION__, dyld_instance->m_process ? dyld_instance->m_process->GetID () : LLDB_INVALID_PROCESS_ID); + + // Disable the breakpoint --- if a stop happens right after this, which we've seen on occasion, we don't + // want the breakpoint stepping thread-plan logic to show a breakpoint instruction at the disassembled + // entry point to the program. Disabling it prevents it. (One-shot is not enough - one-shot removal logic + // only happens after the breakpoint goes public, which wasn't happening in our scenario). + if (dyld_instance->m_process) + { + BreakpointSP breakpoint_sp = dyld_instance->m_process->GetTarget().GetBreakpointByID (break_id); + if (breakpoint_sp) + { + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 " disabling breakpoint id %" PRIu64, __FUNCTION__, dyld_instance->m_process->GetID (), break_id); + breakpoint_sp->SetEnabled (false); + } + else + { + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 " failed to find breakpoint for breakpoint id %" PRIu64, __FUNCTION__, dyld_instance->m_process->GetID (), break_id); + } + } + else + { + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s breakpoint id %" PRIu64 " no Process instance! Cannot disable breakpoint", __FUNCTION__, break_id); + } - dyld_instance = static_cast<DynamicLoaderPOSIXDYLD*>(baton); dyld_instance->LoadAllCurrentModules(); dyld_instance->SetRendezvousBreakpoint(); return false; // Continue running. @@ -221,16 +330,25 @@ DynamicLoaderPOSIXDYLD::EntryBreakpointHit(void *baton, void DynamicLoaderPOSIXDYLD::SetRendezvousBreakpoint() { + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + addr_t break_addr = m_rendezvous.GetBreakAddress(); Target &target = m_process->GetTarget(); if (m_dyld_bid == LLDB_INVALID_BREAK_ID) { + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 " setting rendezvous break address at 0x%" PRIx64, __FUNCTION__, m_process ? m_process->GetID () : LLDB_INVALID_PROCESS_ID, break_addr); Breakpoint *dyld_break = target.CreateBreakpoint (break_addr, true, false).get(); dyld_break->SetCallback(RendezvousBreakpointHit, this, true); dyld_break->SetBreakpointKind ("shared-library-event"); m_dyld_bid = dyld_break->GetID(); } + else + { + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 " reusing break id %" PRIu32 ", address at 0x%" PRIx64, __FUNCTION__, m_process ? m_process->GetID () : LLDB_INVALID_PROCESS_ID, m_dyld_bid, break_addr); + } // Make sure our breakpoint is at the right address. assert (target.GetBreakpointByID(m_dyld_bid)->FindLocationByAddress(break_addr)->GetBreakpoint().GetID() == m_dyld_bid); @@ -242,13 +360,22 @@ DynamicLoaderPOSIXDYLD::RendezvousBreakpointHit(void *baton, user_id_t break_id, user_id_t break_loc_id) { - DynamicLoaderPOSIXDYLD* dyld_instance; + assert (baton && "null baton"); + if (!baton) + return false; + + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + DynamicLoaderPOSIXDYLD *const dyld_instance = static_cast<DynamicLoaderPOSIXDYLD*>(baton); + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s called for pid %" PRIu64, __FUNCTION__, dyld_instance->m_process ? dyld_instance->m_process->GetID () : LLDB_INVALID_PROCESS_ID); - dyld_instance = static_cast<DynamicLoaderPOSIXDYLD*>(baton); dyld_instance->RefreshModules(); // Return true to stop the target, false to just let the target run. - return dyld_instance->GetStopWhenImagesChange(); + const bool stop_when_images_change = dyld_instance->GetStopWhenImagesChange(); + if (log) + log->Printf ("DynamicLoaderPOSIXDYLD::%s pid %" PRIu64 " stop_when_images_change=%s", __FUNCTION__, dyld_instance->m_process ? dyld_instance->m_process->GetID () : LLDB_INVALID_PROCESS_ID, stop_when_images_change ? "true" : "false"); + return stop_when_images_change; } void @@ -439,6 +566,13 @@ DynamicLoaderPOSIXDYLD::GetEntryPoint() return LLDB_INVALID_ADDRESS; m_entry_point = static_cast<addr_t>(I->value); + + const ArchSpec &arch = m_process->GetTarget().GetArchitecture(); + + // On ppc64, the entry point is actually a descriptor. Dereference it. + if (arch.GetMachine() == llvm::Triple::ppc64) + m_entry_point = ReadUnsignedIntWithSizeInBytes(m_entry_point, 8); + return m_entry_point; } @@ -487,3 +621,18 @@ DynamicLoaderPOSIXDYLD::GetThreadLocalData (const lldb::ModuleSP module, const l return tls_block; } + +bool +DynamicLoaderPOSIXDYLD::GetProcessModuleSpec (ModuleSpec& module_spec) +{ + if (m_process == nullptr) + return false; + + auto& target = m_process->GetTarget (); + ProcessInstanceInfo process_info; + if (!target.GetPlatform ()->GetProcessInfo (m_process->GetID (), process_info)) + return false; + + module_spec = ModuleSpec (process_info.GetExecutableFile (), process_info.GetArchitecture ()); + return true; +} diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h index 9ced5da8a015..747ff3f2be36 100644 --- a/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h @@ -168,6 +168,10 @@ protected: lldb::addr_t GetEntryPoint(); + /// Loads ModuleSpec data from inferior process. + bool + GetProcessModuleSpec(lldb_private::ModuleSpec& module_spec); + private: DISALLOW_COPY_AND_ASSIGN(DynamicLoaderPOSIXDYLD); }; diff --git a/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp b/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp index fa8681ed69fe..bc358a985248 100644 --- a/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp +++ b/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp @@ -9873,12 +9873,14 @@ EmulateInstructionARM::EmulateSTREX (const uint32_t opcode, const ARMEncoding en if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, 0)) return false; } +#if 0 // unreachable because if true else { // R[d] = 1; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, 1)) return false; } +#endif // unreachable because if true } return true; } diff --git a/source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.cpp b/source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.cpp new file mode 100644 index 000000000000..96754ff78787 --- /dev/null +++ b/source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.cpp @@ -0,0 +1,314 @@ +//===-- AddressSanitizerRuntime.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AddressSanitizerRuntime.h" + +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/InstrumentationRuntimeStopInfo.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +lldb::InstrumentationRuntimeSP +AddressSanitizerRuntime::CreateInstance (const lldb::ProcessSP &process_sp) +{ + return InstrumentationRuntimeSP(new AddressSanitizerRuntime(process_sp)); +} + +void +AddressSanitizerRuntime::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "AddressSanitizer instrumentation runtime plugin.", + CreateInstance, + GetTypeStatic); +} + +void +AddressSanitizerRuntime::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + +lldb_private::ConstString +AddressSanitizerRuntime::GetPluginNameStatic() +{ + return ConstString("AddressSanitizer"); +} + +lldb::InstrumentationRuntimeType +AddressSanitizerRuntime::GetTypeStatic() +{ + return eInstrumentationRuntimeTypeAddressSanitizer; +} + +AddressSanitizerRuntime::AddressSanitizerRuntime(const ProcessSP &process_sp) : + m_is_active(false), + m_runtime_module(), + m_process(process_sp), + m_breakpoint_id(0) +{ +} + +AddressSanitizerRuntime::~AddressSanitizerRuntime() +{ + Deactivate(); +} + +bool ModuleContainsASanRuntime(Module * module) +{ + SymbolContextList sc_list; + const bool include_symbols = true; + const bool append = true; + const bool include_inlines = true; + + size_t num_matches = module->FindFunctions(ConstString("__asan_get_alloc_stack"), NULL, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list); + + return num_matches > 0; +} + +void +AddressSanitizerRuntime::ModulesDidLoad(lldb_private::ModuleList &module_list) +{ + if (IsActive()) + return; + + if (m_runtime_module) { + Activate(); + return; + } + + Mutex::Locker modules_locker(module_list.GetMutex()); + const size_t num_modules = module_list.GetSize(); + for (size_t i = 0; i < num_modules; ++i) + { + Module *module_pointer = module_list.GetModulePointerAtIndexUnlocked(i); + const FileSpec & file_spec = module_pointer->GetFileSpec(); + if (! file_spec) + continue; + + static RegularExpression g_asan_runtime_regex("libclang_rt.asan_(.*)_dynamic\\.dylib"); + if (g_asan_runtime_regex.Execute (file_spec.GetFilename().GetCString()) || module_pointer->IsExecutable()) + { + if (ModuleContainsASanRuntime(module_pointer)) + { + m_runtime_module = module_pointer->shared_from_this(); + Activate(); + return; + } + } + } +} + +bool +AddressSanitizerRuntime::IsActive() +{ + return m_is_active; +} + +#define RETRIEVE_REPORT_DATA_FUNCTION_TIMEOUT_USEC 2*1000*1000 + +const char * +address_sanitizer_retrieve_report_data_command = R"( + struct { + int present; + void *pc, *bp, *sp, *address; + int access_type; + size_t access_size; + const char *description; + } t; + + t.present = ((int (*) ())__asan_report_present)(); + t.pc = ((void * (*) ())__asan_get_report_pc)(); + /* commented out because rdar://problem/18533301 + t.bp = ((void * (*) ())__asan_get_report_bp)(); + t.sp = ((void * (*) ())__asan_get_report_sp)(); + */ + t.address = ((void * (*) ())__asan_get_report_address)(); + t.description = ((const char * (*) ())__asan_get_report_description)(); + t.access_type = ((int (*) ())__asan_get_report_access_type)(); + t.access_size = ((size_t (*) ())__asan_get_report_access_size)(); + + t; +)"; + +StructuredData::ObjectSP +AddressSanitizerRuntime::RetrieveReportData() +{ + ThreadSP thread_sp = m_process->GetThreadList().GetSelectedThread(); + StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); + + if (!frame_sp) + return StructuredData::ObjectSP(); + + EvaluateExpressionOptions options; + options.SetUnwindOnError(true); + options.SetTryAllThreads(true); + options.SetStopOthers(true); + options.SetIgnoreBreakpoints(true); + options.SetTimeoutUsec(RETRIEVE_REPORT_DATA_FUNCTION_TIMEOUT_USEC); + + ValueObjectSP return_value_sp; + if (m_process->GetTarget().EvaluateExpression(address_sanitizer_retrieve_report_data_command, frame_sp.get(), return_value_sp, options) != eExpressionCompleted) + return StructuredData::ObjectSP(); + + int present = return_value_sp->GetValueForExpressionPath(".present")->GetValueAsUnsigned(0); + if (present != 1) + return StructuredData::ObjectSP(); + + addr_t pc = return_value_sp->GetValueForExpressionPath(".pc")->GetValueAsUnsigned(0); + addr_t bp = return_value_sp->GetValueForExpressionPath(".bp")->GetValueAsUnsigned(0); + addr_t sp = return_value_sp->GetValueForExpressionPath(".sp")->GetValueAsUnsigned(0); + addr_t address = return_value_sp->GetValueForExpressionPath(".address")->GetValueAsUnsigned(0); + addr_t access_type = return_value_sp->GetValueForExpressionPath(".access_type")->GetValueAsUnsigned(0); + addr_t access_size = return_value_sp->GetValueForExpressionPath(".access_size")->GetValueAsUnsigned(0); + addr_t description_ptr = return_value_sp->GetValueForExpressionPath(".description")->GetValueAsUnsigned(0); + std::string description; + Error error; + m_process->ReadCStringFromMemory(description_ptr, description, error); + + StructuredData::Dictionary *dict = new StructuredData::Dictionary(); + dict->AddStringItem("instrumentation_class", "AddressSanitizer"); + dict->AddStringItem("stop_type", "fatal_error"); + dict->AddIntegerItem("pc", pc); + dict->AddIntegerItem("bp", bp); + dict->AddIntegerItem("sp", sp); + dict->AddIntegerItem("address", address); + dict->AddIntegerItem("access_type", access_type); + dict->AddIntegerItem("access_size", access_size); + dict->AddStringItem("description", description); + + return StructuredData::ObjectSP(dict); +} + +std::string +AddressSanitizerRuntime::FormatDescription(StructuredData::ObjectSP report) +{ + std::string description = report->GetAsDictionary()->GetValueForKey("description")->GetAsString()->GetValue(); + if (description == "heap-use-after-free") { + return "Use of deallocated memory detected"; + } else if (description == "heap-buffer-overflow") { + return "Heap buffer overflow detected"; + } else if (description == "stack-buffer-underflow") { + return "Stack buffer underflow detected"; + } else if (description == "initialization-order-fiasco") { + return "Initialization order problem detected"; + } else if (description == "stack-buffer-overflow") { + return "Stack buffer overflow detected"; + } else if (description == "stack-use-after-return") { + return "Use of returned stack memory detected"; + } else if (description == "use-after-poison") { + return "Use of poisoned memory detected"; + } else if (description == "container-overflow") { + return "Container overflow detected"; + } else if (description == "stack-use-after-scope") { + return "Use of out-of-scope stack memory detected"; + } else if (description == "global-buffer-overflow") { + return "Global buffer overflow detected"; + } else if (description == "unknown-crash") { + return "Invalid memory access detected"; + } + + // for unknown report codes just show the code + return description; +} + +bool +AddressSanitizerRuntime::NotifyBreakpointHit(void *baton, StoppointCallbackContext *context, user_id_t break_id, user_id_t break_loc_id) +{ + assert (baton && "null baton"); + if (!baton) + return false; + + AddressSanitizerRuntime *const instance = static_cast<AddressSanitizerRuntime*>(baton); + + StructuredData::ObjectSP report = instance->RetrieveReportData(); + std::string description; + if (report) { + description = instance->FormatDescription(report); + } + ThreadSP thread = context->exe_ctx_ref.GetThreadSP(); + thread->SetStopInfo(InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(*thread, description.c_str(), report)); + + if (instance->m_process) + { + StreamFileSP stream_sp (instance->m_process->GetTarget().GetDebugger().GetOutputFile()); + if (stream_sp) + { + stream_sp->Printf ("AddressSanitizer report breakpoint hit. Use 'thread info -s' to get extended information about the report.\n"); + } + } + // Return true to stop the target, false to just let the target run. + return true; +} + +void +AddressSanitizerRuntime::Activate() +{ + if (m_is_active) + return; + + ConstString symbol_name ("__asan::AsanDie()"); + const Symbol *symbol = m_runtime_module->FindFirstSymbolWithNameAndType (symbol_name, eSymbolTypeCode); + + if (symbol == NULL) + return; + + if (!symbol->GetAddress().IsValid()) + return; + + Target &target = m_process->GetTarget(); + addr_t symbol_address = symbol->GetAddress().GetOpcodeLoadAddress(&target); + + if (symbol_address == LLDB_INVALID_ADDRESS) + return; + + bool internal = true; + bool hardware = false; + Breakpoint *breakpoint = m_process->GetTarget().CreateBreakpoint(symbol_address, internal, hardware).get(); + breakpoint->SetCallback (AddressSanitizerRuntime::NotifyBreakpointHit, this, true); + breakpoint->SetBreakpointKind ("address-sanitizer-report"); + m_breakpoint_id = breakpoint->GetID(); + + if (m_process) + { + StreamFileSP stream_sp (m_process->GetTarget().GetDebugger().GetOutputFile()); + if (stream_sp) + { + stream_sp->Printf ("AddressSanitizer debugger support is active. Memory error breakpoint has been installed and you can now use the 'memory history' command.\n"); + } + } + + m_is_active = true; +} + +void +AddressSanitizerRuntime::Deactivate() +{ + if (m_breakpoint_id != LLDB_INVALID_BREAK_ID) + { + m_process->GetTarget().RemoveBreakpointByID(m_breakpoint_id); + m_breakpoint_id = LLDB_INVALID_BREAK_ID; + } + m_is_active = false; +} diff --git a/source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.h b/source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.h new file mode 100644 index 000000000000..69c134cbedaf --- /dev/null +++ b/source/Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.h @@ -0,0 +1,86 @@ +//===-- AddressSanitizerRuntime.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AddressSanitizerRuntime_h_ +#define liblldb_AddressSanitizerRuntime_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/InstrumentationRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/StructuredData.h" + +namespace lldb_private { + +class AddressSanitizerRuntime : public lldb_private::InstrumentationRuntime +{ +public: + + static lldb::InstrumentationRuntimeSP + CreateInstance (const lldb::ProcessSP &process_sp); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static lldb::InstrumentationRuntimeType + GetTypeStatic(); + + virtual + ~AddressSanitizerRuntime(); + + virtual lldb_private::ConstString + GetPluginName() { return GetPluginNameStatic(); } + + virtual lldb::InstrumentationRuntimeType + GetType() { return GetTypeStatic(); } + + virtual uint32_t + GetPluginVersion() { return 1; } + + virtual void + ModulesDidLoad(lldb_private::ModuleList &module_list); + + virtual bool + IsActive(); + +private: + + AddressSanitizerRuntime(const lldb::ProcessSP &process_sp); + + void + Activate(); + + void + Deactivate(); + + static bool + NotifyBreakpointHit(void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + + StructuredData::ObjectSP + RetrieveReportData(); + + std::string + FormatDescription(StructuredData::ObjectSP report); + + bool m_is_active; + lldb::ModuleSP m_runtime_module; + lldb::ProcessSP m_process; + lldb::user_id_t m_breakpoint_id; + +}; + +} // namespace lldb_private + +#endif // liblldb_InstrumentationRuntime_h_ diff --git a/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp b/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp index 905984d33410..cd303cb0768f 100644 --- a/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp +++ b/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp @@ -36,21 +36,24 @@ typedef enum JIT_UNREGISTER_FN } jit_actions_t; +#pragma pack(push, 4) +template <typename ptr_t> struct jit_code_entry { - struct jit_code_entry *next_entry; - struct jit_code_entry *prev_entry; - const char *symfile_addr; + ptr_t next_entry; // pointer + ptr_t prev_entry; // pointer + ptr_t symfile_addr; // pointer uint64_t symfile_size; }; - +template <typename ptr_t> struct jit_descriptor { uint32_t version; uint32_t action_flag; // Values are jit_action_t - struct jit_code_entry *relevant_entry; - struct jit_code_entry *first_entry; + ptr_t relevant_entry; // pointer + ptr_t first_entry; // pointer }; +#pragma pack(pop) JITLoaderGDB::JITLoaderGDB (lldb_private::Process *process) : JITLoader(process), @@ -198,6 +201,17 @@ static void updateSectionLoadAddress(const SectionList §ion_list, bool JITLoaderGDB::ReadJITDescriptor(bool all_entries) { + Target &target = m_process->GetTarget(); + if (target.GetArchitecture().GetAddressByteSize() == 8) + return ReadJITDescriptorImpl<uint64_t>(all_entries); + else + return ReadJITDescriptorImpl<uint32_t>(all_entries); +} + +template <typename ptr_t> +bool +JITLoaderGDB::ReadJITDescriptorImpl(bool all_entries) +{ if (m_jit_descriptor_addr == LLDB_INVALID_ADDRESS) return false; @@ -205,7 +219,7 @@ JITLoaderGDB::ReadJITDescriptor(bool all_entries) Target &target = m_process->GetTarget(); ModuleList &module_list = target.GetImages(); - jit_descriptor jit_desc; + jit_descriptor<ptr_t> jit_desc; const size_t jit_desc_size = sizeof(jit_desc); Error error; size_t bytes_read = m_process->DoReadMemory( @@ -228,7 +242,7 @@ JITLoaderGDB::ReadJITDescriptor(bool all_entries) while (jit_relevant_entry != 0) { - jit_code_entry jit_entry; + jit_code_entry<ptr_t> jit_entry; const size_t jit_entry_size = sizeof(jit_entry); bytes_read = m_process->DoReadMemory(jit_relevant_entry, &jit_entry, jit_entry_size, error); if (bytes_read != jit_entry_size || !error.Success()) diff --git a/source/Plugins/JITLoader/GDB/JITLoaderGDB.h b/source/Plugins/JITLoader/GDB/JITLoaderGDB.h index 5fa66b874972..bfa1721d3349 100644 --- a/source/Plugins/JITLoader/GDB/JITLoaderGDB.h +++ b/source/Plugins/JITLoader/GDB/JITLoaderGDB.h @@ -81,6 +81,10 @@ private: bool ReadJITDescriptor(bool all_entries); + template <typename ptr_t> + bool + ReadJITDescriptorImpl(bool all_entries); + static bool JITDebugBreakpointHit(void *baton, lldb_private::StoppointCallbackContext *context, diff --git a/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp b/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp new file mode 100644 index 000000000000..c7c64ed54d87 --- /dev/null +++ b/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp @@ -0,0 +1,183 @@ +//===-- MemoryHistoryASan.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MemoryHistoryASan.h" + +#include "lldb/Target/MemoryHistory.h" + +#include "lldb/lldb-private.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/ThreadList.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Core/Module.h" +#include "Plugins/Process/Utility/HistoryThread.h" +#include "lldb/Core/ValueObject.h" + +using namespace lldb; +using namespace lldb_private; + +MemoryHistorySP +MemoryHistoryASan::CreateInstance (const ProcessSP &process_sp) +{ + if (!process_sp.get()) + return NULL; + + Target & target = process_sp->GetTarget(); + + bool found_asan_runtime = false; + + const ModuleList &target_modules = target.GetImages(); + Mutex::Locker modules_locker(target_modules.GetMutex()); + const size_t num_modules = target_modules.GetSize(); + for (size_t i = 0; i < num_modules; ++i) + { + Module *module_pointer = target_modules.GetModulePointerAtIndexUnlocked(i); + + SymbolContextList sc_list; + const bool include_symbols = true; + const bool append = true; + const bool include_inlines = true; + + size_t num_matches = module_pointer->FindFunctions(ConstString("__asan_get_alloc_stack"), NULL, eFunctionNameTypeAuto, include_symbols, include_inlines, append, sc_list); + + if (num_matches) + { + found_asan_runtime = true; + break; + } + } + + if (! found_asan_runtime) + return MemoryHistorySP(); + + return MemoryHistorySP(new MemoryHistoryASan(process_sp)); +} + +void +MemoryHistoryASan::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + "ASan memory history provider.", + CreateInstance); +} + +void +MemoryHistoryASan::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +ConstString +MemoryHistoryASan::GetPluginNameStatic() +{ + static ConstString g_name("asan"); + return g_name; +} + +MemoryHistoryASan::MemoryHistoryASan(const ProcessSP &process_sp) +{ + this->m_process_sp = process_sp; +} + +const char * +memory_history_asan_command_format = R"( + struct t { + void *alloc_trace[256]; + size_t alloc_count; + int alloc_tid; + + void *free_trace[256]; + size_t free_count; + int free_tid; + } t; + + t.alloc_count = ((size_t (*) (void *, void **, size_t, int *))__asan_get_alloc_stack)((void *)0x%)" PRIx64 R"(, t.alloc_trace, 256, &t.alloc_tid); + t.free_count = ((size_t (*) (void *, void **, size_t, int *))__asan_get_free_stack)((void *)0x%)" PRIx64 R"(, t.free_trace, 256, &t.free_tid); + + t; +)"; + +static void CreateHistoryThreadFromValueObject(ProcessSP process_sp, ValueObjectSP return_value_sp, const char *type, const char *thread_name, HistoryThreads & result) +{ + std::string count_path = "." + std::string(type) + "_count"; + std::string tid_path = "." + std::string(type) + "_tid"; + std::string trace_path = "." + std::string(type) + "_trace"; + + int count = return_value_sp->GetValueForExpressionPath(count_path.c_str())->GetValueAsUnsigned(0); + tid_t tid = return_value_sp->GetValueForExpressionPath(tid_path.c_str())->GetValueAsUnsigned(0); + + if (count <= 0) + return; + + ValueObjectSP trace_sp = return_value_sp->GetValueForExpressionPath(trace_path.c_str()); + + std::vector<lldb::addr_t> pcs; + for (int i = 0; i < count; i++) + { + addr_t pc = trace_sp->GetChildAtIndex(i, true)->GetValueAsUnsigned(0); + if (pc == 0 || pc == 1 || pc == LLDB_INVALID_ADDRESS) + continue; + pcs.push_back(pc); + } + + HistoryThread *history_thread = new HistoryThread(*process_sp, tid, pcs, 0, false); + ThreadSP new_thread_sp(history_thread); + // let's use thread name for the type of history thread, since history threads don't have names anyway + history_thread->SetThreadName(thread_name); + // Save this in the Process' ExtendedThreadList so a strong pointer retains the object + process_sp->GetExtendedThreadList().AddThread (new_thread_sp); + result.push_back(new_thread_sp); +} + +#define GET_STACK_FUNCTION_TIMEOUT_USEC 2*1000*1000 + +HistoryThreads +MemoryHistoryASan::GetHistoryThreads(lldb::addr_t address) +{ + ProcessSP process_sp = m_process_sp; + ThreadSP thread_sp = m_process_sp->GetThreadList().GetSelectedThread(); + StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); + + if (!frame_sp) + { + return HistoryThreads(); + } + + ExecutionContext exe_ctx (frame_sp); + ValueObjectSP return_value_sp; + StreamString expr; + expr.Printf(memory_history_asan_command_format, address, address); + + EvaluateExpressionOptions options; + options.SetUnwindOnError(true); + options.SetTryAllThreads(true); + options.SetStopOthers(true); + options.SetIgnoreBreakpoints(true); + options.SetTimeoutUsec(GET_STACK_FUNCTION_TIMEOUT_USEC); + + if (m_process_sp->GetTarget().EvaluateExpression(expr.GetData(), frame_sp.get(), return_value_sp, options) != eExpressionCompleted) + { + return HistoryThreads(); + } + if (!return_value_sp) + { + return HistoryThreads(); + } + + HistoryThreads result; + + CreateHistoryThreadFromValueObject(process_sp, return_value_sp, "alloc", "Memory allocated at", result); + CreateHistoryThreadFromValueObject(process_sp, return_value_sp, "free", "Memory deallocated at", result); + + return result; +} diff --git a/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.h b/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.h new file mode 100644 index 000000000000..5307e0b34081 --- /dev/null +++ b/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.h @@ -0,0 +1,62 @@ +//===-- MemoryHistoryASan.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_MemoryHistoryASan_h_ +#define liblldb_MemoryHistoryASan_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/MemoryHistory.h" +#include "lldb/Target/Process.h" + +namespace lldb_private { + +class MemoryHistoryASan : public lldb_private::MemoryHistory +{ +public: + + static lldb::MemoryHistorySP + CreateInstance (const lldb::ProcessSP &process_sp); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + virtual + ~MemoryHistoryASan () {} + + virtual lldb_private::ConstString + GetPluginName() { return GetPluginNameStatic(); } + + virtual uint32_t + GetPluginVersion() { return 1; } + + virtual lldb_private::HistoryThreads + GetHistoryThreads(lldb::addr_t address); + +private: + + MemoryHistoryASan(const lldb::ProcessSP &process_sp); + + lldb::ProcessSP m_process_sp; + +}; + +} // namespace lldb_private + +#endif // liblldb_MemoryHistoryASan_h_ diff --git a/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp b/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp index 0263c23ce307..1e2a0c721ff6 100644 --- a/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp +++ b/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp @@ -9,7 +9,7 @@ #include "ObjectContainerBSDArchive.h" -#ifdef _WIN32 +#if defined(_WIN32) || defined(__ANDROID_NDK__) // Defines from ar, missing on Windows #define ARMAG "!<arch>\n" #define SARMAG 8 diff --git a/source/Plugins/ObjectFile/ELF/ELFHeader.cpp b/source/Plugins/ObjectFile/ELF/ELFHeader.cpp index f027294b7c57..12392c24407b 100644 --- a/source/Plugins/ObjectFile/ELF/ELFHeader.cpp +++ b/source/Plugins/ObjectFile/ELF/ELFHeader.cpp @@ -173,6 +173,12 @@ ELFHeader::GetRelocationJumpSlotType() const default: assert(false && "architecture not supported"); break; + case EM_PPC: + slot = R_PPC_JMP_SLOT; + break; + case EM_PPC64: + slot = R_PPC64_JMP_SLOT; + break; case EM_386: case EM_486: slot = R_386_JUMP_SLOT; @@ -189,6 +195,9 @@ ELFHeader::GetRelocationJumpSlotType() const case EM_AARCH64: slot = R_AARCH64_JUMP_SLOT; break; + case EM_MIPS: + slot = R_MIPS_JUMP_SLOT; + break; } return slot; diff --git a/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index d86aee78947f..e7bf20e18008 100644 --- a/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -267,10 +267,14 @@ kalimbaVariantFromElfFlags(const elf::elf_word e_flags) { // TODO(mg11) Support more variants case 10: - kal_arch_variant = 3; + kal_arch_variant = llvm::Triple::KalimbaSubArch_v3; break; case 14: - kal_arch_variant = 4; + kal_arch_variant = llvm::Triple::KalimbaSubArch_v4; + break; + case 17: + case 20: + kal_arch_variant = llvm::Triple::KalimbaSubArch_v5; break; default: break; @@ -287,6 +291,34 @@ subTypeFromElfHeader(const elf::ELFHeader& header) LLDB_INVALID_CPUTYPE; } +//! The kalimba toolchain identifies a code section as being +//! one with the SHT_PROGBITS set in the section sh_type and the top +//! bit in the 32-bit address field set. +static lldb::SectionType +kalimbaSectionType( + const elf::ELFHeader& header, + const elf::ELFSectionHeader& sect_hdr) +{ + if (llvm::ELF::EM_CSR_KALIMBA != header.e_machine) + { + return eSectionTypeOther; + } + + if (llvm::ELF::SHT_NOBITS == sect_hdr.sh_type) + { + return eSectionTypeZeroFill; + } + + if (llvm::ELF::SHT_PROGBITS == sect_hdr.sh_type) + { + const lldb::addr_t KAL_CODE_BIT = 1 << 31; + return KAL_CODE_BIT & sect_hdr.sh_addr ? + eSectionTypeCode : eSectionTypeData; + } + + return eSectionTypeOther; +} + // Arbitrary constant used as UUID prefix for core files. const uint32_t ObjectFileELF::g_core_uuid_magic(0xE210C); @@ -826,6 +858,38 @@ ObjectFileELF::GetAddressByteSize() const return m_data.GetAddressByteSize(); } +// Top 16 bits of the `Symbol` flags are available. +#define ARM_ELF_SYM_IS_THUMB (1 << 16) + +AddressClass +ObjectFileELF::GetAddressClass (addr_t file_addr) +{ + auto res = ObjectFile::GetAddressClass (file_addr); + + if (res != eAddressClassCode) + return res; + + ArchSpec arch_spec; + GetArchitecture(arch_spec); + if (arch_spec.GetMachine() != llvm::Triple::arm) + return res; + + auto symtab = GetSymtab(); + if (symtab == nullptr) + return res; + + auto symbol = symtab->FindSymbolContainingFileAddress(file_addr); + if (symbol == nullptr) + return res; + + // Thumb symbols have the lower bit set in the flags field so we just check + // for that. + if (symbol->GetFlags() & ARM_ELF_SYM_IS_THUMB) + res = eAddressClassCodeAlternateISA; + + return res; +} + size_t ObjectFileELF::SectionIndex(const SectionHeaderCollIter &I) { @@ -1560,6 +1624,20 @@ ObjectFileELF::CreateSections(SectionList &unified_section_list) break; } + if (eSectionTypeOther == sect_type) + { + // the kalimba toolchain assumes that ELF section names are free-form. It does + // supports linkscripts which (can) give rise to various arbitarily named + // sections being "Code" or "Data". + sect_type = kalimbaSectionType(m_header, header); + } + + const uint32_t target_bytes_size = + (eSectionTypeData == sect_type || eSectionTypeZeroFill == sect_type) ? + m_arch_spec.GetDataByteSize() : + eSectionTypeCode == sect_type ? + m_arch_spec.GetCodeByteSize() : 1; + elf::elf_xword log2align = (header.sh_addralign==0) ? 0 : llvm::Log2_64(header.sh_addralign); @@ -1573,7 +1651,8 @@ ObjectFileELF::CreateSections(SectionList &unified_section_list) header.sh_offset, // Offset of this section in the file. file_size, // Size of the section as found in the file. log2align, // Alignment of the section - header.sh_flags)); // Flags for this section. + header.sh_flags, // Flags for this section. + target_bytes_size));// Number of host bytes per target byte if (is_thread_specific) section_sp->SetIsThreadSpecific (is_thread_specific); @@ -1645,6 +1724,7 @@ ObjectFileELF::ParseSymbols (Symtab *symtab, static ConstString rodata1_section_name(".rodata1"); static ConstString data2_section_name(".data1"); static ConstString bss_section_name(".bss"); + static ConstString opd_section_name(".opd"); // For ppc64 //StreamFile strm(stdout, false); unsigned i; @@ -1746,6 +1826,48 @@ ObjectFileELF::ParseSymbols (Symtab *symtab, } } + ArchSpec arch; + int64_t symbol_value_offset = 0; + uint32_t additional_flags = 0; + + if (GetArchitecture(arch) && + arch.GetMachine() == llvm::Triple::arm) + { + // ELF symbol tables may contain some mapping symbols. They provide + // information about the underlying data. There are three of them + // currently defined: + // $a[.<any>]* - marks an ARM instruction sequence + // $t[.<any>]* - marks a THUMB instruction sequence + // $d[.<any>]* - marks a data item sequence (e.g. lit pool) + // These symbols interfere with normal debugger operations and we + // don't need them. We can drop them here. + + static const llvm::StringRef g_armelf_arm_marker("$a"); + static const llvm::StringRef g_armelf_thumb_marker("$t"); + static const llvm::StringRef g_armelf_data_marker("$d"); + llvm::StringRef symbol_name_ref(symbol_name); + + if (symbol_name && + (symbol_name_ref.startswith(g_armelf_arm_marker) || + symbol_name_ref.startswith(g_armelf_thumb_marker) || + symbol_name_ref.startswith(g_armelf_data_marker))) + continue; + + // THUMB functions have the lower bit of their address set. Fixup + // the actual address and mark the symbol as THUMB. + if (symbol_type == eSymbolTypeCode && symbol.st_value & 1) + { + // Substracting 1 from the address effectively unsets + // the low order bit, which results in the address + // actually pointing to the beginning of the symbol. + // This delta will be used below in conjuction with + // symbol.st_value to produce the final symbol_value + // that we store in the symtab. + symbol_value_offset = -1; + additional_flags = ARM_ELF_SYM_IS_THUMB; + } + } + // If the symbol section we've found has no data (SHT_NOBITS), then check the module section // list. This can happen if we're parsing the debug file and it has no .text section, for example. if (symbol_section_sp && (symbol_section_sp->GetFileSize() == 0)) @@ -1766,12 +1888,15 @@ ObjectFileELF::ParseSymbols (Symtab *symtab, } } - uint64_t symbol_value = symbol.st_value; + // symbol_value_offset may contain 0 for ARM symbols or -1 for + // THUMB symbols. See above for more details. + uint64_t symbol_value = symbol.st_value | symbol_value_offset; if (symbol_section_sp && CalculateType() != ObjectFile::Type::eTypeObjectFile) symbol_value -= symbol_section_sp->GetFileAddress(); bool is_global = symbol.getBinding() == STB_GLOBAL; - uint32_t flags = symbol.st_other << 8 | symbol.st_info; + uint32_t flags = symbol.st_other << 8 | symbol.st_info | additional_flags; bool is_mangled = symbol_name ? (symbol_name[0] == '_' && symbol_name[1] == 'Z') : false; + Symbol dc_symbol( i + start_id, // ID is the original symbol table index. symbol_name, // Symbol name. diff --git a/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/source/Plugins/ObjectFile/ELF/ObjectFileELF.h index 6b036af7aeff..b10dfb532cd0 100644 --- a/source/Plugins/ObjectFile/ELF/ObjectFileELF.h +++ b/source/Plugins/ObjectFile/ELF/ObjectFileELF.h @@ -133,6 +133,9 @@ public: virtual uint32_t GetAddressByteSize() const; + virtual lldb::AddressClass + GetAddressClass (lldb::addr_t file_addr); + virtual lldb_private::Symtab * GetSymtab(); diff --git a/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp b/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp index 7aa940a530b4..3b38a5819934 100644 --- a/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp +++ b/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp @@ -32,7 +32,7 @@ using namespace lldb; using namespace lldb_private; -Platform * +PlatformSP PlatformFreeBSD::CreateInstance (bool force, const lldb_private::ArchSpec *arch) { // The only time we create an instance is when we are creating a remote @@ -84,8 +84,8 @@ PlatformFreeBSD::CreateInstance (bool force, const lldb_private::ArchSpec *arch) } } if (create) - return new PlatformFreeBSD (is_host); - return NULL; + return PlatformSP(new PlatformFreeBSD (is_host)); + return PlatformSP(); } @@ -124,7 +124,7 @@ PlatformFreeBSD::Initialize () // Force a host flag to true for the default platform object. PlatformSP default_platform_sp (new PlatformFreeBSD(true)); default_platform_sp->SetSystemArchitecture(HostInfo::GetArchitecture()); - Platform::SetDefaultPlatform (default_platform_sp); + Platform::SetHostPlatform (default_platform_sp); #endif PluginManager::RegisterPlugin(PlatformFreeBSD::GetPluginNameStatic(false), PlatformFreeBSD::GetDescriptionStatic(false), @@ -180,8 +180,7 @@ PlatformFreeBSD::RunShellCommand (const char *command, Error -PlatformFreeBSD::ResolveExecutable (const FileSpec &exe_file, - const ArchSpec &exe_arch, +PlatformFreeBSD::ResolveExecutable (const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp, const FileSpecList *module_search_paths_ptr) { @@ -189,35 +188,33 @@ PlatformFreeBSD::ResolveExecutable (const FileSpec &exe_file, // Nothing special to do here, just use the actual file and architecture char exe_path[PATH_MAX]; - FileSpec resolved_exe_file (exe_file); + ModuleSpec resolved_module_spec(module_spec); if (IsHost()) { - // If we have "ls" as the exe_file, resolve the executable location based on + // If we have "ls" as the module_spec's file, resolve the executable location based on // the current path variables - if (!resolved_exe_file.Exists()) + if (!resolved_module_spec.GetFileSpec().Exists()) { - exe_file.GetPath(exe_path, sizeof(exe_path)); - resolved_exe_file.SetFile(exe_path, true); + module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path)); + resolved_module_spec.GetFileSpec().SetFile(exe_path, true); } - if (!resolved_exe_file.Exists()) - resolved_exe_file.ResolveExecutableLocation (); + if (!resolved_module_spec.GetFileSpec().Exists()) + resolved_module_spec.GetFileSpec().ResolveExecutableLocation (); - if (resolved_exe_file.Exists()) + if (resolved_module_spec.GetFileSpec().Exists()) error.Clear(); else { - exe_file.GetPath(exe_path, sizeof(exe_path)); - error.SetErrorStringWithFormat("unable to find executable for '%s'", exe_path); + error.SetErrorStringWithFormat("unable to find executable for '%s'", resolved_module_spec.GetFileSpec().GetPath().c_str()); } } else { if (m_remote_platform_sp) { - error = m_remote_platform_sp->ResolveExecutable (exe_file, - exe_arch, + error = m_remote_platform_sp->ResolveExecutable (module_spec, exe_module_sp, module_search_paths_ptr); } @@ -226,25 +223,24 @@ PlatformFreeBSD::ResolveExecutable (const FileSpec &exe_file, // We may connect to a process and use the provided executable (Don't use local $PATH). // Resolve any executable within a bundle on MacOSX - Host::ResolveExecutableInBundle (resolved_exe_file); + Host::ResolveExecutableInBundle (resolved_module_spec.GetFileSpec()); - if (resolved_exe_file.Exists()) { + if (resolved_module_spec.GetFileSpec().Exists()) + { error.Clear(); } else { - exe_file.GetPath(exe_path, sizeof(exe_path)); - error.SetErrorStringWithFormat("the platform is not currently connected, and '%s' doesn't exist in the system root.", exe_path); + error.SetErrorStringWithFormat("the platform is not currently connected, and '%s' doesn't exist in the system root.", resolved_module_spec.GetFileSpec().GetPath().c_str()); } } } if (error.Success()) { - ModuleSpec module_spec (resolved_exe_file, exe_arch); - if (module_spec.GetArchitecture().IsValid()) + if (resolved_module_spec.GetArchitecture().IsValid()) { - error = ModuleList::GetSharedModule (module_spec, + error = ModuleList::GetSharedModule (resolved_module_spec, exe_module_sp, module_search_paths_ptr, NULL, @@ -254,8 +250,8 @@ PlatformFreeBSD::ResolveExecutable (const FileSpec &exe_file, { exe_module_sp.reset(); error.SetErrorStringWithFormat ("'%s' doesn't contain the architecture %s", - exe_file.GetPath().c_str(), - exe_arch.GetArchitectureName()); + resolved_module_spec.GetFileSpec().GetPath().c_str(), + resolved_module_spec.GetArchitecture().GetArchitectureName()); } } else @@ -264,10 +260,9 @@ PlatformFreeBSD::ResolveExecutable (const FileSpec &exe_file, // the architectures that we should be using (in the correct order) // and see if we can find a match that way StreamString arch_names; - ArchSpec platform_arch; - for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, platform_arch); ++idx) + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, resolved_module_spec.GetArchitecture()); ++idx) { - error = ModuleList::GetSharedModule (module_spec, + error = ModuleList::GetSharedModule (resolved_module_spec, exe_module_sp, module_search_paths_ptr, NULL, @@ -283,21 +278,21 @@ PlatformFreeBSD::ResolveExecutable (const FileSpec &exe_file, if (idx > 0) arch_names.PutCString (", "); - arch_names.PutCString (platform_arch.GetArchitectureName()); + arch_names.PutCString (resolved_module_spec.GetArchitecture().GetArchitectureName()); } if (error.Fail() || !exe_module_sp) { - if (exe_file.Readable()) + if (resolved_module_spec.GetFileSpec().Readable()) { error.SetErrorStringWithFormat ("'%s' doesn't contain any '%s' platform architectures: %s", - exe_file.GetPath().c_str(), + resolved_module_spec.GetFileSpec().GetPath().c_str(), GetPluginName().GetCString(), arch_names.GetString().c_str()); } else { - error.SetErrorStringWithFormat("'%s' is not readable", exe_file.GetPath().c_str()); + error.SetErrorStringWithFormat("'%s' is not readable", resolved_module_spec.GetFileSpec().GetPath().c_str()); } } } @@ -326,6 +321,13 @@ PlatformFreeBSD::GetSoftwareBreakpointTrapOpcode (Target &target, BreakpointSite trap_opcode_size = sizeof(g_i386_opcode); } break; + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + { + static const uint8_t g_ppc_opcode[] = { 0x7f, 0xe0, 0x00, 0x08 }; + trap_opcode = g_ppc_opcode; + trap_opcode_size = sizeof(g_ppc_opcode); + } } if (bp_site->SetTrapOpcode(trap_opcode, trap_opcode_size)) @@ -404,7 +406,7 @@ PlatformFreeBSD::ConnectRemote (Args& args) else { if (!m_remote_platform_sp) - m_remote_platform_sp = Platform::Create ("remote-gdb-server", error); + m_remote_platform_sp = Platform::Create (ConstString("remote-gdb-server"), error); if (m_remote_platform_sp) { @@ -507,7 +509,6 @@ lldb::ProcessSP PlatformFreeBSD::Attach(ProcessAttachInfo &attach_info, Debugger &debugger, Target *target, - Listener &listener, Error &error) { lldb::ProcessSP process_sp; @@ -535,7 +536,7 @@ PlatformFreeBSD::Attach(ProcessAttachInfo &attach_info, // The freebsd always currently uses the GDB remote debugger plug-in // so even when debugging locally we are debugging remotely! // Just like the darwin plugin. - process_sp = target->CreateProcess (listener, "gdb-remote", NULL); + process_sp = target->CreateProcess (attach_info.GetListenerForProcess(debugger), "gdb-remote", NULL); if (process_sp) error = process_sp->Attach (attach_info); @@ -544,7 +545,7 @@ PlatformFreeBSD::Attach(ProcessAttachInfo &attach_info, else { if (m_remote_platform_sp) - process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, listener, error); + process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, error); else error.SetErrorString ("the platform is not currently connected"); } diff --git a/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h b/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h index 62958a08a9e0..ce3f8cfad976 100644 --- a/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h +++ b/source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h @@ -24,7 +24,7 @@ public: //------------------------------------------------------------ // Class functions //------------------------------------------------------------ - static lldb_private::Platform* + static lldb::PlatformSP CreateInstance (bool force, const lldb_private::ArchSpec *arch); static void @@ -80,8 +80,7 @@ public: uint32_t timeout_sec); virtual lldb_private::Error - ResolveExecutable (const lldb_private::FileSpec &exe_file, - const lldb_private::ArchSpec &arch, + ResolveExecutable (const lldb_private::ModuleSpec &module_spec, lldb::ModuleSP &module_sp, const lldb_private::FileSpecList *module_search_paths_ptr); @@ -135,7 +134,6 @@ public: Attach(lldb_private::ProcessAttachInfo &attach_info, lldb_private::Debugger &debugger, lldb_private::Target *target, - lldb_private::Listener &listener, lldb_private::Error &error); // FreeBSD processes can not be launched by spawning and attaching. diff --git a/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp b/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp index cc4c693e1b43..b1be0f5b1fe1 100644 --- a/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp +++ b/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp @@ -15,7 +15,9 @@ // Project includes #include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Debugger.h" #include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/File.h" #include "lldb/Host/FileCache.h" @@ -330,8 +332,14 @@ PlatformPOSIX::PutFile (const lldb_private::FileSpec& source, error = source_file.Read(buffer_sp->GetBytes(), bytes_read); if (bytes_read) { - WriteFile(dest_file, offset, buffer_sp->GetBytes(), bytes_read, error); - offset += bytes_read; + const uint64_t bytes_written = WriteFile(dest_file, offset, buffer_sp->GetBytes(), bytes_read, error); + offset += bytes_written; + if (bytes_written != bytes_read) + { + // We didn't write the correct numbe of bytes, so adjust + // the file position in the source file we are reading from... + source_file.SeekFromStart(offset); + } } else break; @@ -343,6 +351,18 @@ PlatformPOSIX::PutFile (const lldb_private::FileSpec& source, // std::string dst_path (destination.GetPath()); // if (chown_file(this,dst_path.c_str(),uid,gid) != 0) // return Error("unable to perform chown"); + + + uint64_t src_md5[2]; + uint64_t dst_md5[2]; + + if (FileSystem::CalculateMD5 (source, src_md5[0], src_md5[1]) && CalculateMD5 (destination, dst_md5[0], dst_md5[1])) + { + if (src_md5[0] != dst_md5[0] || src_md5[1] != dst_md5[1]) + { + error.SetErrorString("md5 checksum of installed file doesn't match, installation failed"); + } + } return error; } return Platform::PutFile(source,destination,uid,gid); @@ -616,6 +636,18 @@ PlatformPOSIX::GetRemoteOSBuildString (std::string &s) return false; } +size_t +PlatformPOSIX::GetEnvironment (StringList &env) +{ + if (IsRemote()) + { + if (m_remote_platform_sp) + return m_remote_platform_sp->GetEnvironment(env); + return 0; + } + return Host::GetEnvironment(env); +} + bool PlatformPOSIX::GetRemoteOSKernelDescription (std::string &s) { @@ -681,7 +713,7 @@ PlatformPOSIX::ConnectRemote (Args& args) else { if (!m_remote_platform_sp) - m_remote_platform_sp = Platform::Create ("remote-gdb-server", error); + m_remote_platform_sp = Platform::Create (ConstString("remote-gdb-server"), error); if (m_remote_platform_sp && error.Success()) error = m_remote_platform_sp->ConnectRemote (args); @@ -739,11 +771,97 @@ PlatformPOSIX::DisconnectRemote () return error; } +Error +PlatformPOSIX::LaunchProcess (ProcessLaunchInfo &launch_info) +{ + Error error; + + if (IsHost()) + { + error = Platform::LaunchProcess (launch_info); + } + else + { + if (m_remote_platform_sp) + error = m_remote_platform_sp->LaunchProcess (launch_info); + else + error.SetErrorString ("the platform is not currently connected"); + } + return error; +} + +lldb::ProcessSP +PlatformPOSIX::Attach (ProcessAttachInfo &attach_info, + Debugger &debugger, + Target *target, + Error &error) +{ + lldb::ProcessSP process_sp; + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + + if (IsHost()) + { + if (target == NULL) + { + TargetSP new_target_sp; + + error = debugger.GetTargetList().CreateTarget (debugger, + NULL, + NULL, + false, + NULL, + new_target_sp); + target = new_target_sp.get(); + if (log) + log->Printf ("PlatformPOSIX::%s created new target", __FUNCTION__); + } + else + { + error.Clear(); + if (log) + log->Printf ("PlatformPOSIX::%s target already existed, setting target", __FUNCTION__); + } + + if (target && error.Success()) + { + debugger.GetTargetList().SetSelectedTarget(target); + if (log) + { + ModuleSP exe_module_sp = target->GetExecutableModule (); + log->Printf ("PlatformPOSIX::%s set selected target to %p %s", __FUNCTION__, + target, + exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str () : "<null>" ); + } + + + process_sp = target->CreateProcess (attach_info.GetListenerForProcess(debugger), attach_info.GetProcessPluginName(), NULL); + + if (process_sp) + { + // Set UnixSignals appropriately. + process_sp->SetUnixSignals (Host::GetUnixSignals ()); + + ListenerSP listener_sp (new Listener("lldb.PlatformPOSIX.attach.hijack")); + attach_info.SetHijackListener(listener_sp); + process_sp->HijackProcessEvents(listener_sp.get()); + error = process_sp->Attach (attach_info); + } + } + } + else + { + if (m_remote_platform_sp) + process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, error); + else + error.SetErrorString ("the platform is not currently connected"); + } + return process_sp; +} + lldb::ProcessSP PlatformPOSIX::DebugProcess (ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, // Can be NULL, if NULL create a new target, else use existing one - Listener &listener, Error &error) { ProcessSP process_sp; @@ -754,12 +872,12 @@ PlatformPOSIX::DebugProcess (ProcessLaunchInfo &launch_info, // We still need to reap it from lldb but if we let the monitor thread also set the exit status, we set up a // race between debugserver & us for who will find out about the debugged process's death. launch_info.GetFlags().Set(eLaunchFlagDontSetExitStatus); - process_sp = Platform::DebugProcess (launch_info, debugger, target, listener, error); + process_sp = Platform::DebugProcess (launch_info, debugger, target, error); } else { if (m_remote_platform_sp) - process_sp = m_remote_platform_sp->DebugProcess (launch_info, debugger, target, listener, error); + process_sp = m_remote_platform_sp->DebugProcess (launch_info, debugger, target, error); else error.SetErrorString ("the platform is not currently connected"); } diff --git a/source/Plugins/Platform/POSIX/PlatformPOSIX.h b/source/Plugins/Platform/POSIX/PlatformPOSIX.h index 374e36495d88..aae415e6eefa 100644 --- a/source/Plugins/Platform/POSIX/PlatformPOSIX.h +++ b/source/Plugins/Platform/POSIX/PlatformPOSIX.h @@ -31,8 +31,9 @@ public: //------------------------------------------------------------ // lldb_private::Platform functions //------------------------------------------------------------ - virtual lldb_private::OptionGroupOptions* - GetConnectionOptions (lldb_private::CommandInterpreter& interpreter); + virtual lldb_private::OptionGroupOptions + *GetConnectionOptions( + lldb_private::CommandInterpreter &interpreter) override; const char * GetHostname () override; @@ -47,47 +48,47 @@ public: PutFile (const lldb_private::FileSpec& source, const lldb_private::FileSpec& destination, uint32_t uid = UINT32_MAX, - uint32_t gid = UINT32_MAX); + uint32_t gid = UINT32_MAX) override; virtual lldb::user_id_t OpenFile (const lldb_private::FileSpec& file_spec, uint32_t flags, uint32_t mode, - lldb_private::Error &error); + lldb_private::Error &error) override; virtual bool CloseFile (lldb::user_id_t fd, - lldb_private::Error &error); + lldb_private::Error &error) override; virtual uint64_t ReadFile (lldb::user_id_t fd, uint64_t offset, void *dst, uint64_t dst_len, - lldb_private::Error &error); + lldb_private::Error &error) override; virtual uint64_t WriteFile (lldb::user_id_t fd, uint64_t offset, const void* src, uint64_t src_len, - lldb_private::Error &error); + lldb_private::Error &error) override; virtual lldb::user_id_t - GetFileSize (const lldb_private::FileSpec& file_spec); + GetFileSize (const lldb_private::FileSpec& file_spec) override; virtual lldb_private::Error - CreateSymlink(const char *src, const char *dst); + CreateSymlink(const char *src, const char *dst) override; virtual lldb_private::Error GetFile (const lldb_private::FileSpec& source, - const lldb_private::FileSpec& destination); + const lldb_private::FileSpec& destination) override; virtual lldb_private::ConstString - GetRemoteWorkingDirectory(); + GetRemoteWorkingDirectory() override; virtual bool - SetRemoteWorkingDirectory(const lldb_private::ConstString &path); + SetRemoteWorkingDirectory(const lldb_private::ConstString &path) override; bool GetRemoteOSVersion () override; @@ -101,6 +102,9 @@ public: lldb_private::ArchSpec GetRemoteSystemArchitecture () override; + size_t + GetEnvironment (lldb_private::StringList &environment) override; + bool IsConnected () const override; @@ -110,40 +114,48 @@ public: int *status_ptr, // Pass NULL if you don't want the process exit status int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit std::string *command_output, // Pass NULL if you don't want the command output - uint32_t timeout_sec); // Timeout in seconds to wait for shell program to finish + uint32_t timeout_sec) override;// Timeout in seconds to wait for shell program to finish virtual lldb_private::Error - MakeDirectory (const char *path, uint32_t mode); + MakeDirectory (const char *path, uint32_t mode) override; virtual lldb_private::Error - GetFilePermissions (const char *path, uint32_t &file_permissions); + GetFilePermissions (const char *path, uint32_t &file_permissions) override; virtual lldb_private::Error - SetFilePermissions (const char *path, uint32_t file_permissions); + SetFilePermissions (const char *path, uint32_t file_permissions) override; virtual bool - GetFileExists (const lldb_private::FileSpec& file_spec); + GetFileExists (const lldb_private::FileSpec& file_spec) override; virtual lldb_private::Error - Unlink (const char *path); + Unlink (const char *path) override; + + lldb_private::Error + LaunchProcess (lldb_private::ProcessLaunchInfo &launch_info) override; + + lldb::ProcessSP + Attach (lldb_private::ProcessAttachInfo &attach_info, + lldb_private::Debugger &debugger, + lldb_private::Target *target, // Can be NULL, if NULL create a new target, else use existing one + lldb_private::Error &error) override; lldb::ProcessSP DebugProcess (lldb_private::ProcessLaunchInfo &launch_info, lldb_private::Debugger &debugger, lldb_private::Target *target, // Can be NULL, if NULL create a new target, else use existing one - lldb_private::Listener &listener, lldb_private::Error &error) override; virtual std::string - GetPlatformSpecificConnectionInformation(); + GetPlatformSpecificConnectionInformation() override; virtual bool CalculateMD5 (const lldb_private::FileSpec& file_spec, uint64_t &low, - uint64_t &high); + uint64_t &high) override; virtual void - CalculateTrapHandlerSymbolNames (); + CalculateTrapHandlerSymbolNames () override; lldb_private::Error ConnectRemote (lldb_private::Args& args) override; diff --git a/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp b/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp index 05fbc5101278..43eae4d906ec 100644 --- a/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ b/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -16,14 +16,15 @@ // Other libraries and framework includes // Project includes #include "lldb/Breakpoint/BreakpointLocation.h" -#include "lldb/Core/ConnectionFileDescriptor.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/StreamString.h" +#include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/Host.h" #include "lldb/Target/Process.h" @@ -56,7 +57,7 @@ PlatformRemoteGDBServer::Terminate () } } -Platform* +PlatformSP PlatformRemoteGDBServer::CreateInstance (bool force, const lldb_private::ArchSpec *arch) { bool create = force; @@ -65,8 +66,8 @@ PlatformRemoteGDBServer::CreateInstance (bool force, const lldb_private::ArchSpe create = !arch->TripleVendorWasSpecified() && !arch->TripleOSWasSpecified(); } if (create) - return new PlatformRemoteGDBServer (); - return NULL; + return PlatformSP(new PlatformRemoteGDBServer()); + return PlatformSP(); } @@ -100,14 +101,13 @@ PlatformRemoteGDBServer::GetDescription () } Error -PlatformRemoteGDBServer::ResolveExecutable (const FileSpec &exe_file, - const ArchSpec &exe_arch, +PlatformRemoteGDBServer::ResolveExecutable (const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp, const FileSpecList *module_search_paths_ptr) { Error error; //error.SetErrorString ("PlatformRemoteGDBServer::ResolveExecutable() is unimplemented"); - if (m_gdb_client.GetFileExists(exe_file)) + if (m_gdb_client.GetFileExists(module_spec.GetFileSpec())) return error; // TODO: get the remote end to somehow resolve this file error.SetErrorString("file not found on remote end"); @@ -421,7 +421,6 @@ lldb::ProcessSP PlatformRemoteGDBServer::DebugProcess (lldb_private::ProcessLaunchInfo &launch_info, lldb_private::Debugger &debugger, lldb_private::Target *target, // Can be NULL, if NULL create a new target, else use existing one - lldb_private::Listener &listener, lldb_private::Error &error) { lldb::ProcessSP process_sp; @@ -473,7 +472,7 @@ PlatformRemoteGDBServer::DebugProcess (lldb_private::ProcessLaunchInfo &launch_i // The darwin always currently uses the GDB remote debugger plug-in // so even when debugging locally we are debugging remotely! - process_sp = target->CreateProcess (listener, "gdb-remote", NULL); + process_sp = target->CreateProcess (launch_info.GetListenerForProcess(debugger), "gdb-remote", NULL); if (process_sp) { @@ -488,10 +487,16 @@ PlatformRemoteGDBServer::DebugProcess (lldb_private::ProcessLaunchInfo &launch_i port + port_offset); assert (connect_url_len < (int)sizeof(connect_url)); error = process_sp->ConnectRemote (NULL, connect_url); + // Retry the connect remote one time... + if (error.Fail()) + error = process_sp->ConnectRemote (NULL, connect_url); if (error.Success()) error = process_sp->Launch(launch_info); else if (debugserver_pid != LLDB_INVALID_PROCESS_ID) + { + printf ("error: connect remote failed (%s)\n", error.AsCString()); m_gdb_client.KillSpawnedProcess(debugserver_pid); + } } } } @@ -509,7 +514,6 @@ lldb::ProcessSP PlatformRemoteGDBServer::Attach (lldb_private::ProcessAttachInfo &attach_info, Debugger &debugger, Target *target, // Can be NULL, if NULL create a new target, else use existing one - Listener &listener, Error &error) { lldb::ProcessSP process_sp; @@ -561,7 +565,7 @@ PlatformRemoteGDBServer::Attach (lldb_private::ProcessAttachInfo &attach_info, // The darwin always currently uses the GDB remote debugger plug-in // so even when debugging locally we are debugging remotely! - process_sp = target->CreateProcess (listener, "gdb-remote", NULL); + process_sp = target->CreateProcess (attach_info.GetListenerForProcess(debugger), "gdb-remote", NULL); if (process_sp) { diff --git a/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h b/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h index e236e97c8bb3..90b16b8b8fa9 100644 --- a/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h +++ b/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h @@ -29,7 +29,7 @@ public: static void Terminate (); - static lldb_private::Platform* + static lldb::PlatformSP CreateInstance (bool force, const lldb_private::ArchSpec *arch); static lldb_private::ConstString @@ -64,8 +64,7 @@ public: // lldb_private::Platform functions //------------------------------------------------------------ virtual lldb_private::Error - ResolveExecutable (const lldb_private::FileSpec &exe_file, - const lldb_private::ArchSpec &arch, + ResolveExecutable (const lldb_private::ModuleSpec &module_spec, lldb::ModuleSP &module_sp, const lldb_private::FileSpecList *module_search_paths_ptr); @@ -92,14 +91,12 @@ public: DebugProcess (lldb_private::ProcessLaunchInfo &launch_info, lldb_private::Debugger &debugger, lldb_private::Target *target, // Can be NULL, if NULL create a new target, else use existing one - lldb_private::Listener &listener, lldb_private::Error &error); virtual lldb::ProcessSP Attach (lldb_private::ProcessAttachInfo &attach_info, lldb_private::Debugger &debugger, lldb_private::Target *target, // Can be NULL, if NULL create a new target, else use existing one - lldb_private::Listener &listener, lldb_private::Error &error); virtual bool diff --git a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp index 4b488444de1e..5a0b5ed14194 100644 --- a/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp +++ b/source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp @@ -258,8 +258,7 @@ ProcessFreeBSD::SendMessage(const ProcessMessage &message) case ProcessMessage::eLimboMessage: case ProcessMessage::eExitMessage: - m_exit_status = message.GetExitStatus(); - SetExitStatus(m_exit_status, NULL); + SetExitStatus(message.GetExitStatus(), NULL); break; case ProcessMessage::eSignalMessage: diff --git a/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp b/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp index 63439b155111..84e35ba22644 100644 --- a/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp +++ b/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp @@ -25,6 +25,7 @@ #include "lldb/Core/RegisterValue.h" #include "lldb/Core/Scalar.h" #include "lldb/Host/Host.h" +#include "lldb/Host/ThreadLauncher.h" #include "lldb/Target/Thread.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Utility/PseudoTerminal.h" @@ -112,6 +113,7 @@ PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data, log->Printf("PT_GETREGS: ax=0x%lx", r->r_rax); } #endif +#ifndef __powerpc__ if (req == PT_GETDBREGS || req == PT_SETDBREGS) { struct dbreg *r = (struct dbreg *) addr; char setget = (req == PT_GETDBREGS) ? 'G' : 'S'; @@ -119,6 +121,7 @@ PtraceWrapper(int req, lldb::pid_t pid, void *addr, int data, for (int i = 0; i <= 7; i++) log->Printf("PT_%cETDBREGS: dr[%d]=0x%lx", setget, i, r->dr[i]); } +#endif } return result; @@ -309,9 +312,14 @@ ReadRegOperation::Execute(ProcessMonitor *monitor) if ((rc = PTRACE(PT_GETREGS, m_tid, (caddr_t)®s, 0)) < 0) { m_result = false; } else { - if (m_size == sizeof(uintptr_t)) - m_value = *(uintptr_t *)(((caddr_t)®s) + m_offset); - else + // 'struct reg' contains only 32- or 64-bit register values. Punt on + // others. Also, not all entries may be uintptr_t sized, such as 32-bit + // processes on powerpc64 (probably the same for i386 on amd64) + if (m_size == sizeof(uint32_t)) + m_value = *(uint32_t *)(((caddr_t)®s) + m_offset); + else if (m_size == sizeof(uint64_t)) + m_value = *(uint64_t *)(((caddr_t)®s) + m_offset); + else memcpy(&m_value, (((caddr_t)®s) + m_offset), m_size); m_result = true; } @@ -810,8 +818,6 @@ ProcessMonitor::ProcessMonitor(ProcessPOSIX *process, const lldb_private::ProcessLaunchInfo & /* launch_info */, lldb_private::Error &error) : m_process(static_cast<ProcessFreeBSD *>(process)), - m_operation_thread(LLDB_INVALID_HOST_THREAD), - m_monitor_thread(LLDB_INVALID_HOST_THREAD), m_pid(LLDB_INVALID_PROCESS_ID), m_terminal_fd(-1), m_operation(0) @@ -852,7 +858,7 @@ WAIT_AGAIN: // Finally, start monitoring the child process for change in state. m_monitor_thread = Host::StartMonitoringChildProcess( ProcessMonitor::MonitorCallback, this, GetPID(), true); - if (!IS_VALID_LLDB_HOST_THREAD(m_monitor_thread)) + if (!m_monitor_thread.IsJoinable()) { error.SetErrorToGenericError(); error.SetErrorString("Process launch failed."); @@ -864,8 +870,6 @@ ProcessMonitor::ProcessMonitor(ProcessPOSIX *process, lldb::pid_t pid, lldb_private::Error &error) : m_process(static_cast<ProcessFreeBSD *>(process)), - m_operation_thread(LLDB_INVALID_HOST_THREAD), - m_monitor_thread(LLDB_INVALID_HOST_THREAD), m_pid(pid), m_terminal_fd(-1), m_operation(0) @@ -904,7 +908,7 @@ WAIT_AGAIN: // Finally, start monitoring the child process for change in state. m_monitor_thread = Host::StartMonitoringChildProcess( ProcessMonitor::MonitorCallback, this, GetPID(), true); - if (!IS_VALID_LLDB_HOST_THREAD(m_monitor_thread)) + if (!m_monitor_thread.IsJoinable()) { error.SetErrorToGenericError(); error.SetErrorString("Process attach failed."); @@ -924,11 +928,10 @@ ProcessMonitor::StartLaunchOpThread(LaunchArgs *args, Error &error) { static const char *g_thread_name = "lldb.process.freebsd.operation"; - if (IS_VALID_LLDB_HOST_THREAD(m_operation_thread)) + if (m_operation_thread.IsJoinable()) return; - m_operation_thread = - Host::ThreadCreate(g_thread_name, LaunchOpThread, args, &error); + m_operation_thread = ThreadLauncher::LaunchThread(g_thread_name, LaunchOpThread, args, &error); } void * @@ -1101,11 +1104,10 @@ ProcessMonitor::StartAttachOpThread(AttachArgs *args, lldb_private::Error &error { static const char *g_thread_name = "lldb.process.freebsd.operation"; - if (IS_VALID_LLDB_HOST_THREAD(m_operation_thread)) + if (m_operation_thread.IsJoinable()) return; - m_operation_thread = - Host::ThreadCreate(g_thread_name, AttachOpThread, args, &error); + m_operation_thread = ThreadLauncher::LaunchThread(g_thread_name, AttachOpThread, args, &error); } void * @@ -1113,14 +1115,13 @@ ProcessMonitor::AttachOpThread(void *arg) { AttachArgs *args = static_cast<AttachArgs*>(arg); - if (!Attach(args)) - return NULL; + Attach(args); ServeOperation(args); return NULL; } -bool +void ProcessMonitor::Attach(AttachArgs *args) { lldb::pid_t pid = args->m_pid; @@ -1132,27 +1133,24 @@ ProcessMonitor::Attach(AttachArgs *args) { args->m_error.SetErrorToGenericError(); args->m_error.SetErrorString("Attaching to process 1 is not allowed."); - goto FINISH; + return; } // Attach to the requested process. if (PTRACE(PT_ATTACH, pid, NULL, 0) < 0) { args->m_error.SetErrorToErrno(); - goto FINISH; + return; } int status; if ((status = waitpid(pid, NULL, 0)) < 0) { args->m_error.SetErrorToErrno(); - goto FINISH; + return; } process.SendMessage(ProcessMessage::Attach(pid)); - -FINISH: - return args->m_error.Success(); } size_t @@ -1714,13 +1712,11 @@ ProcessMonitor::DupDescriptor(const char *path, int fd, int flags) void ProcessMonitor::StopMonitoringChildProcess() { - lldb::thread_result_t thread_result; - - if (IS_VALID_LLDB_HOST_THREAD(m_monitor_thread)) + if (m_monitor_thread.IsJoinable()) { - Host::ThreadCancel(m_monitor_thread, NULL); - Host::ThreadJoin(m_monitor_thread, &thread_result, NULL); - m_monitor_thread = LLDB_INVALID_HOST_THREAD; + m_monitor_thread.Cancel(); + m_monitor_thread.Join(nullptr); + m_monitor_thread.Reset(); } } @@ -1764,12 +1760,10 @@ ProcessMonitor::WaitForInitialTIDStop(lldb::tid_t tid) void ProcessMonitor::StopOpThread() { - lldb::thread_result_t result; - - if (!IS_VALID_LLDB_HOST_THREAD(m_operation_thread)) + if (!m_operation_thread.IsJoinable()) return; - Host::ThreadCancel(m_operation_thread, NULL); - Host::ThreadJoin(m_operation_thread, &result, NULL); - m_operation_thread = LLDB_INVALID_HOST_THREAD; + m_operation_thread.Cancel(); + m_operation_thread.Join(nullptr); + m_operation_thread.Reset(); } diff --git a/source/Plugins/Process/FreeBSD/ProcessMonitor.h b/source/Plugins/Process/FreeBSD/ProcessMonitor.h index 314743b00754..935fd85ed37a 100644 --- a/source/Plugins/Process/FreeBSD/ProcessMonitor.h +++ b/source/Plugins/Process/FreeBSD/ProcessMonitor.h @@ -17,6 +17,7 @@ // C++ Includes // Other libraries and framework includes #include "lldb/lldb-types.h" +#include "lldb/Host/HostThread.h" #include "lldb/Host/Mutex.h" namespace lldb_private @@ -212,8 +213,8 @@ public: private: ProcessFreeBSD *m_process; - lldb::thread_t m_operation_thread; - lldb::thread_t m_monitor_thread; + lldb_private::HostThread m_operation_thread; + lldb_private::HostThread m_monitor_thread; lldb::pid_t m_pid; int m_terminal_fd; @@ -289,7 +290,7 @@ private: static void * AttachOpThread(void *args); - static bool + static void Attach(AttachArgs *args); static void diff --git a/source/Plugins/Process/POSIX/POSIXThread.cpp b/source/Plugins/Process/POSIX/POSIXThread.cpp index d48f8f9dd307..1057585e1b2a 100644 --- a/source/Plugins/Process/POSIX/POSIXThread.cpp +++ b/source/Plugins/Process/POSIX/POSIXThread.cpp @@ -20,27 +20,30 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/State.h" #include "lldb/Host/Host.h" +#include "lldb/Host/HostNativeThread.h" #include "lldb/Host/HostInfo.h" #include "lldb/Target/Process.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/ThreadSpec.h" +#include "llvm/ADT/SmallString.h" #include "POSIXStopInfo.h" #include "POSIXThread.h" #include "ProcessPOSIX.h" #include "ProcessPOSIXLog.h" -#include "ProcessMonitor.h" +#include "Plugins/Process/Linux/ProcessMonitor.h" #include "RegisterContextPOSIXProcessMonitor_arm64.h" #include "RegisterContextPOSIXProcessMonitor_mips64.h" +#include "RegisterContextPOSIXProcessMonitor_powerpc.h" #include "RegisterContextPOSIXProcessMonitor_x86.h" -#include "RegisterContextLinux_arm64.h" -#include "RegisterContextLinux_i386.h" -#include "RegisterContextLinux_x86_64.h" -#include "RegisterContextFreeBSD_i386.h" -#include "RegisterContextFreeBSD_mips64.h" -#include "RegisterContextFreeBSD_x86_64.h" - -#include "UnwindLLDB.h" +#include "Plugins/Process/Utility/RegisterContextLinux_arm64.h" +#include "Plugins/Process/Utility/RegisterContextLinux_i386.h" +#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h" +#include "Plugins/Process/Utility/UnwindLLDB.h" using namespace lldb; using namespace lldb_private; @@ -140,7 +143,9 @@ POSIXThread::GetName () { if (!m_thread_name_valid) { - SetName(Host::GetThreadName(GetProcess()->GetID(), GetID()).c_str()); + llvm::SmallString<32> thread_name; + HostNativeThread::GetName(GetID(), thread_name); + m_thread_name = thread_name.c_str(); m_thread_name_valid = true; } @@ -164,6 +169,14 @@ POSIXThread::GetRegisterContext() case llvm::Triple::FreeBSD: switch (target_arch.GetMachine()) { + case llvm::Triple::ppc: +#ifndef __powerpc64__ + reg_interface = new RegisterContextFreeBSD_powerpc32(target_arch); + break; +#endif + case llvm::Triple::ppc64: + reg_interface = new RegisterContextFreeBSD_powerpc64(target_arch); + break; case llvm::Triple::mips64: reg_interface = new RegisterContextFreeBSD_mips64(target_arch); break; @@ -226,6 +239,14 @@ POSIXThread::GetRegisterContext() m_reg_context_sp.reset(reg_ctx); break; } + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + { + RegisterContextPOSIXProcessMonitor_powerpc *reg_ctx = new RegisterContextPOSIXProcessMonitor_powerpc(*this, 0, reg_interface); + m_posix_thread = reg_ctx; + m_reg_context_sp.reset(reg_ctx); + break; + } case llvm::Triple::x86: case llvm::Triple::x86_64: { @@ -621,6 +642,8 @@ POSIXThread::GetRegisterIndexFromOffset(unsigned offset) case llvm::Triple::aarch64: case llvm::Triple::mips64: + case llvm::Triple::ppc: + case llvm::Triple::ppc64: case llvm::Triple::x86: case llvm::Triple::x86_64: { @@ -652,6 +675,8 @@ POSIXThread::GetRegisterName(unsigned reg) case llvm::Triple::aarch64: case llvm::Triple::mips64: + case llvm::Triple::ppc: + case llvm::Triple::ppc64: case llvm::Triple::x86: case llvm::Triple::x86_64: name = GetRegisterContext()->GetRegisterName(reg); diff --git a/source/Plugins/Process/POSIX/POSIXThread.h b/source/Plugins/Process/POSIX/POSIXThread.h index 51d6645f209d..56dcccbfb0f9 100644 --- a/source/Plugins/Process/POSIX/POSIXThread.h +++ b/source/Plugins/Process/POSIX/POSIXThread.h @@ -17,7 +17,7 @@ // Other libraries and framework includes #include "lldb/Target/Thread.h" -#include "RegisterContextPOSIX.h" +#include "Plugins/Process/Utility/RegisterContextPOSIX.h" class ProcessMessage; class ProcessMonitor; diff --git a/source/Plugins/Process/POSIX/ProcessPOSIX.cpp b/source/Plugins/Process/POSIX/ProcessPOSIX.cpp index f340631c7d07..0e5ab5a8d8b1 100644 --- a/source/Plugins/Process/POSIX/ProcessPOSIX.cpp +++ b/source/Plugins/Process/POSIX/ProcessPOSIX.cpp @@ -16,6 +16,7 @@ // Other libraries and framework includes #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Host/FileSpec.h" @@ -28,7 +29,7 @@ #include "ProcessPOSIX.h" #include "ProcessPOSIXLog.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" -#include "ProcessMonitor.h" +#include "Plugins/Process/Linux/ProcessMonitor.h" #include "POSIXThread.h" using namespace lldb; @@ -140,8 +141,8 @@ ProcessPOSIX::DoAttachToProcessWithID(lldb::pid_t pid) // Resolve the executable module ModuleSP exe_module_sp; FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths()); - error = platform_sp->ResolveExecutable(process_info.GetExecutableFile(), - m_target.GetArchitecture(), + ModuleSpec exe_module_spec(process_info.GetExecutableFile(), m_target.GetArchitecture()); + error = platform_sp->ResolveExecutable(exe_module_spec, exe_module_sp, executable_search_paths.GetSize() ? &executable_search_paths : NULL); if (!error.Success()) @@ -176,9 +177,9 @@ ProcessPOSIX::WillLaunch(Module* module) } const char * -ProcessPOSIX::GetFilePath(const lldb_private::FileAction *file_action, const char *default_path) +ProcessPOSIX::GetFilePath(const lldb_private::FileAction *file_action, const char *default_path, + const char *dbg_pts_path) { - const char *pts_name = "/dev/pts/"; const char *path = NULL; if (file_action) @@ -190,11 +191,11 @@ ProcessPOSIX::GetFilePath(const lldb_private::FileAction *file_action, const cha // (/dev/pts). If so, convert to using a different default path // instead to redirect I/O to the debugger console. This should // also handle user overrides to /dev/null or a different file. - if (!path || ::strncmp(path, pts_name, ::strlen(pts_name)) == 0) + if (!path || (dbg_pts_path && + ::strncmp(path, dbg_pts_path, ::strlen(dbg_pts_path)) == 0)) path = default_path; } } - return path; } @@ -224,14 +225,16 @@ ProcessPOSIX::DoLaunch (Module *module, const char *stdout_path = NULL; const char *stderr_path = NULL; + const char * dbg_pts_path = launch_info.GetPTY().GetSlaveName(NULL,0); + file_action = launch_info.GetFileActionForFD (STDIN_FILENO); - stdin_path = GetFilePath(file_action, stdin_path); + stdin_path = GetFilePath(file_action, stdin_path, dbg_pts_path); file_action = launch_info.GetFileActionForFD (STDOUT_FILENO); - stdout_path = GetFilePath(file_action, stdout_path); + stdout_path = GetFilePath(file_action, stdout_path, dbg_pts_path); file_action = launch_info.GetFileActionForFD (STDERR_FILENO); - stderr_path = GetFilePath(file_action, stderr_path); + stderr_path = GetFilePath(file_action, stderr_path, dbg_pts_path); m_monitor = new ProcessMonitor (this, module, @@ -338,6 +341,11 @@ ProcessPOSIX::DoDestroy() { assert(m_monitor); m_exit_now = true; + if (GetID() == LLDB_INVALID_PROCESS_ID) + { + error.SetErrorString("invalid process id"); + return error; + } if (!m_monitor->Kill()) { error.SetErrorToErrno(); @@ -363,11 +371,11 @@ ProcessPOSIX::DoDidExec() ProcessInstanceInfo process_info; platform_sp->GetProcessInfo(GetID(), process_info); ModuleSP exe_module_sp; + ModuleSpec exe_module_spec(process_info.GetExecutableFile(), target->GetArchitecture()); FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths()); - Error error = platform_sp->ResolveExecutable(process_info.GetExecutableFile(), - target->GetArchitecture(), - exe_module_sp, - executable_search_paths.GetSize() ? &executable_search_paths : NULL); + Error error = platform_sp->ResolveExecutable(exe_module_spec, + exe_module_sp, + executable_search_paths.GetSize() ? &executable_search_paths : NULL); if (!error.Success()) return; target->SetExecutableModule(exe_module_sp, true); @@ -432,8 +440,7 @@ ProcessPOSIX::SendMessage(const ProcessMessage &message) // FIXME: I'm not sure we need to do this. if (message.GetTID() == GetID()) { - m_exit_status = message.GetExitStatus(); - SetExitStatus(m_exit_status, NULL); + SetExitStatus(message.GetExitStatus(), NULL); } else if (!IsAThreadRunning()) SetPrivateState(eStateStopped); diff --git a/source/Plugins/Process/POSIX/ProcessPOSIX.h b/source/Plugins/Process/POSIX/ProcessPOSIX.h index 033accf17f29..f152356b3093 100644 --- a/source/Plugins/Process/POSIX/ProcessPOSIX.h +++ b/source/Plugins/Process/POSIX/ProcessPOSIX.h @@ -42,104 +42,104 @@ public: // Process protocol. //------------------------------------------------------------------ virtual void - Finalize(); + Finalize() override; virtual bool - CanDebug(lldb_private::Target &target, bool plugin_specified_by_name); + CanDebug(lldb_private::Target &target, bool plugin_specified_by_name) override; virtual lldb_private::Error - WillLaunch(lldb_private::Module *module); + WillLaunch(lldb_private::Module *module) override; virtual lldb_private::Error - DoAttachToProcessWithID(lldb::pid_t pid); + DoAttachToProcessWithID(lldb::pid_t pid) override; virtual lldb_private::Error - DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info); + DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info) override; virtual lldb_private::Error DoLaunch (lldb_private::Module *exe_module, - lldb_private::ProcessLaunchInfo &launch_info); + lldb_private::ProcessLaunchInfo &launch_info) override; virtual void - DidLaunch(); + DidLaunch() override; virtual lldb_private::Error - DoResume(); + DoResume() override; virtual lldb_private::Error - DoHalt(bool &caused_stop); + DoHalt(bool &caused_stop) override; virtual lldb_private::Error - DoDetach(bool keep_stopped) = 0; + DoDetach(bool keep_stopped) override = 0; virtual lldb_private::Error - DoSignal(int signal); + DoSignal(int signal) override; virtual lldb_private::Error - DoDestroy(); + DoDestroy() override; virtual void - DoDidExec(); + DoDidExec() override; virtual void - RefreshStateAfterStop(); + RefreshStateAfterStop() override; virtual bool - IsAlive(); + IsAlive() override; virtual size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, - lldb_private::Error &error); + lldb_private::Error &error) override; virtual size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, - lldb_private::Error &error); + lldb_private::Error &error) override; virtual lldb::addr_t DoAllocateMemory(size_t size, uint32_t permissions, - lldb_private::Error &error); + lldb_private::Error &error) override; virtual lldb_private::Error - DoDeallocateMemory(lldb::addr_t ptr); + DoDeallocateMemory(lldb::addr_t ptr) override; virtual size_t GetSoftwareBreakpointTrapOpcode(lldb_private::BreakpointSite* bp_site); virtual lldb_private::Error - EnableBreakpointSite(lldb_private::BreakpointSite *bp_site); + EnableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; virtual lldb_private::Error - DisableBreakpointSite(lldb_private::BreakpointSite *bp_site); + DisableBreakpointSite(lldb_private::BreakpointSite *bp_site) override; virtual lldb_private::Error - EnableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true); + EnableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true) override; virtual lldb_private::Error - DisableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true); + DisableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true) override; virtual lldb_private::Error - GetWatchpointSupportInfo(uint32_t &num); + GetWatchpointSupportInfo(uint32_t &num) override; virtual lldb_private::Error - GetWatchpointSupportInfo(uint32_t &num, bool &after); + GetWatchpointSupportInfo(uint32_t &num, bool &after) override; virtual uint32_t UpdateThreadListIfNeeded(); virtual bool UpdateThreadList(lldb_private::ThreadList &old_thread_list, - lldb_private::ThreadList &new_thread_list) = 0; + lldb_private::ThreadList &new_thread_list) override = 0; virtual lldb::ByteOrder GetByteOrder() const; virtual lldb::addr_t - GetImageInfoAddress(); + GetImageInfoAddress() override; virtual size_t - PutSTDIN(const char *buf, size_t len, lldb_private::Error &error); + PutSTDIN(const char *buf, size_t len, lldb_private::Error &error) override; const lldb::DataBufferSP GetAuxvData () override; @@ -154,7 +154,8 @@ public: ProcessMonitor & GetMonitor() { assert(m_monitor); return *m_monitor; } - const char *GetFilePath(const lldb_private::FileAction *file_action, const char *default_path); + const char *GetFilePath(const lldb_private::FileAction *file_action, const char *default_path, + const char *dbg_pts_path); /// Stops all threads in the process. /// The \p stop_tid parameter indicates the thread which initiated the stop. diff --git a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_arm64.cpp b/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_arm64.cpp index 9109cdb000ae..ec34d9e28161 100644 --- a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_arm64.cpp +++ b/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_arm64.cpp @@ -10,10 +10,10 @@ #include "lldb/Target/Thread.h" #include "lldb/Core/RegisterValue.h" -#include "RegisterContextPOSIX_arm64.h" +#include "Plugins/Process/Utility/RegisterContextPOSIX_arm64.h" #include "ProcessPOSIX.h" #include "RegisterContextPOSIXProcessMonitor_arm64.h" -#include "ProcessMonitor.h" +#include "Plugins/Process/Linux/ProcessMonitor.h" #define REG_CONTEXT_SIZE (GetGPRSize()) diff --git a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_mips64.cpp b/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_mips64.cpp index 9bfe236de139..6717d20da056 100644 --- a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_mips64.cpp +++ b/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_mips64.cpp @@ -10,10 +10,10 @@ #include "lldb/Target/Thread.h" #include "lldb/Core/RegisterValue.h" -#include "RegisterContextPOSIX_mips64.h" +#include "Plugins/Process/Utility/RegisterContextPOSIX_mips64.h" #include "ProcessPOSIX.h" #include "RegisterContextPOSIXProcessMonitor_mips64.h" -#include "ProcessMonitor.h" +#include "Plugins/Process/Linux/ProcessMonitor.h" using namespace lldb_private; using namespace lldb; diff --git a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_powerpc.cpp b/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_powerpc.cpp new file mode 100644 index 000000000000..b542db4779db --- /dev/null +++ b/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_powerpc.cpp @@ -0,0 +1,321 @@ +//===-- RegisterContextPOSIXProcessMonitor_powerpc.h ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#include "lldb/Target/Thread.h" +#include "lldb/Core/RegisterValue.h" + +#include "RegisterContextPOSIX_powerpc.h" +#include "ProcessPOSIX.h" +#include "RegisterContextPOSIXProcessMonitor_powerpc.h" +#include "ProcessMonitor.h" + +using namespace lldb_private; +using namespace lldb; + +#define REG_CONTEXT_SIZE (GetGPRSize()) + +RegisterContextPOSIXProcessMonitor_powerpc::RegisterContextPOSIXProcessMonitor_powerpc(Thread &thread, + uint32_t concrete_frame_idx, + lldb_private::RegisterInfoInterface *register_info) + : RegisterContextPOSIX_powerpc(thread, concrete_frame_idx, register_info) +{ +} + +ProcessMonitor & +RegisterContextPOSIXProcessMonitor_powerpc::GetMonitor() +{ + ProcessSP base = CalculateProcess(); + ProcessPOSIX *process = static_cast<ProcessPOSIX*>(base.get()); + return process->GetMonitor(); +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::ReadGPR() +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.ReadGPR(m_thread.GetID(), &m_gpr_powerpc, GetGPRSize()); +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::ReadFPR() +{ + // XXX not yet implemented + return false; +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::WriteGPR() +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.WriteGPR(m_thread.GetID(), &m_gpr_powerpc, GetGPRSize()); +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::WriteFPR() +{ + // XXX not yet implemented + return false; +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::ReadRegister(const unsigned reg, + RegisterValue &value) +{ + ProcessMonitor &monitor = GetMonitor(); + return monitor.ReadRegisterValue(m_thread.GetID(), + GetRegisterOffset(reg), + GetRegisterName(reg), + GetRegisterSize(reg), + value); +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::WriteRegister(const unsigned reg, + const RegisterValue &value) +{ + unsigned reg_to_write = reg; + RegisterValue value_to_write = value; + + // Check if this is a subregister of a full register. + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg); + if (reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM)) + { + RegisterValue full_value; + uint32_t full_reg = reg_info->invalidate_regs[0]; + const RegisterInfo *full_reg_info = GetRegisterInfoAtIndex(full_reg); + + // Read the full register. + if (ReadRegister(full_reg_info, full_value)) + { + Error error; + ByteOrder byte_order = GetByteOrder(); + uint8_t dst[RegisterValue::kMaxRegisterByteSize]; + + // Get the bytes for the full register. + const uint32_t dest_size = full_value.GetAsMemoryData (full_reg_info, + dst, + sizeof(dst), + byte_order, + error); + if (error.Success() && dest_size) + { + uint8_t src[RegisterValue::kMaxRegisterByteSize]; + + // Get the bytes for the source data. + const uint32_t src_size = value.GetAsMemoryData (reg_info, src, sizeof(src), byte_order, error); + if (error.Success() && src_size && (src_size < dest_size)) + { + // Copy the src bytes to the destination. + memcpy (dst + (reg_info->byte_offset & 0x1), src, src_size); + // Set this full register as the value to write. + value_to_write.SetBytes(dst, full_value.GetByteSize(), byte_order); + value_to_write.SetType(full_reg_info); + reg_to_write = full_reg; + } + } + } + } + + ProcessMonitor &monitor = GetMonitor(); + // Account for the fact that 32-bit targets on powerpc64 really use 64-bit + // registers in ptrace, but expose here 32-bit registers with a higher + // offset. + uint64_t offset = GetRegisterOffset(reg_to_write); + offset &= ~(sizeof(uintptr_t) - 1); + return monitor.WriteRegisterValue(m_thread.GetID(), + offset, + GetRegisterName(reg_to_write), + value_to_write); +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value) +{ + if (!reg_info) + return false; + + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + + if (IsFPR(reg)) + { + if (!ReadFPR()) + return false; + } + else + { + uint32_t full_reg = reg; + bool is_subreg = reg_info->invalidate_regs && (reg_info->invalidate_regs[0] != LLDB_INVALID_REGNUM); + + if (is_subreg) + { + // Read the full aligned 64-bit register. + full_reg = reg_info->invalidate_regs[0]; + } + + bool success = ReadRegister(full_reg, value); + + if (success) + { + // If our read was not aligned (for ah,bh,ch,dh), shift our returned value one byte to the right. + if (is_subreg && (reg_info->byte_offset & 0x1)) + value.SetUInt64(value.GetAsUInt64() >> 8); + + // If our return byte size was greater than the return value reg size, then + // use the type specified by reg_info rather than the uint64_t default + if (value.GetByteSize() > reg_info->byte_size) + value.SetType(reg_info); + } + return success; + } + + return false; +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value) +{ + const uint32_t reg = reg_info->kinds[eRegisterKindLLDB]; + + if (IsGPR(reg)) + return WriteRegister(reg, value); + + return false; +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::ReadAllRegisterValues(DataBufferSP &data_sp) +{ + bool success = false; + data_sp.reset (new DataBufferHeap (REG_CONTEXT_SIZE, 0)); + if (data_sp && ReadGPR () && ReadFPR ()) + { + uint8_t *dst = data_sp->GetBytes(); + success = dst != 0; + + if (success) + { + ::memcpy (dst, &m_gpr_powerpc, GetGPRSize()); + dst += GetGPRSize(); + } + } + return success; +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::WriteAllRegisterValues(const DataBufferSP &data_sp) +{ + bool success = false; + if (data_sp && data_sp->GetByteSize() == REG_CONTEXT_SIZE) + { + uint8_t *src = data_sp->GetBytes(); + if (src) + { + ::memcpy (&m_gpr_powerpc, src, GetGPRSize()); + + if (WriteGPR()) + { + src += GetGPRSize(); + } + } + } + return success; +} + +uint32_t +RegisterContextPOSIXProcessMonitor_powerpc::SetHardwareWatchpoint(addr_t addr, size_t size, + bool read, bool write) +{ + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + uint32_t hw_index; + + for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index) + { + if (IsWatchpointVacant(hw_index)) + return SetHardwareWatchpointWithIndex(addr, size, + read, write, + hw_index); + } + + return LLDB_INVALID_INDEX32; +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::ClearHardwareWatchpoint(uint32_t hw_index) +{ + return false; +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::HardwareSingleStep(bool enable) +{ + return false; +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::UpdateAfterBreakpoint() +{ + lldb::addr_t pc; + + if ((pc = GetPC()) == LLDB_INVALID_ADDRESS) + return false; + + return true; +} + +unsigned +RegisterContextPOSIXProcessMonitor_powerpc::GetRegisterIndexFromOffset(unsigned offset) +{ + unsigned reg; + for (reg = 0; reg < k_num_registers_powerpc; reg++) + { + if (GetRegisterInfo()[reg].byte_offset == offset) + break; + } + assert(reg < k_num_registers_powerpc && "Invalid register offset."); + return reg; +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::IsWatchpointHit(uint32_t hw_index) +{ + return false; +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::ClearWatchpointHits() +{ + return false; +} + +addr_t +RegisterContextPOSIXProcessMonitor_powerpc::GetWatchpointAddress(uint32_t hw_index) +{ + return LLDB_INVALID_ADDRESS; +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::IsWatchpointVacant(uint32_t hw_index) +{ + return false; +} + +bool +RegisterContextPOSIXProcessMonitor_powerpc::SetHardwareWatchpointWithIndex(addr_t addr, size_t size, + bool read, bool write, + uint32_t hw_index) +{ + return false; +} + +uint32_t +RegisterContextPOSIXProcessMonitor_powerpc::NumSupportedHardwareWatchpoints() +{ + return 0; +} + diff --git a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_powerpc.h b/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_powerpc.h new file mode 100644 index 000000000000..92a331285515 --- /dev/null +++ b/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_powerpc.h @@ -0,0 +1,95 @@ +//===-- RegisterContextPOSIXProcessMonitor_powerpc.h -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextPOSIXProcessMonitor_powerpc_H_ +#define liblldb_RegisterContextPOSIXProcessMonitor_powerpc_H_ + +#include "Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h" + +class RegisterContextPOSIXProcessMonitor_powerpc: + public RegisterContextPOSIX_powerpc, + public POSIXBreakpointProtocol +{ +public: + RegisterContextPOSIXProcessMonitor_powerpc(lldb_private::Thread &thread, + uint32_t concrete_frame_idx, + lldb_private::RegisterInfoInterface *register_info); + +protected: + bool + ReadGPR(); + + bool + ReadFPR(); + + bool + WriteGPR(); + + bool + WriteFPR(); + + // lldb_private::RegisterContext + bool + ReadRegister(const unsigned reg, lldb_private::RegisterValue &value); + + bool + WriteRegister(const unsigned reg, const lldb_private::RegisterValue &value); + + bool + ReadRegister(const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value); + + bool + WriteRegister(const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value); + + bool + ReadAllRegisterValues(lldb::DataBufferSP &data_sp); + + bool + WriteAllRegisterValues(const lldb::DataBufferSP &data_sp); + + uint32_t + SetHardwareWatchpoint(lldb::addr_t addr, size_t size, bool read, bool write); + + bool + ClearHardwareWatchpoint(uint32_t hw_index); + + bool + HardwareSingleStep(bool enable); + + // POSIXBreakpointProtocol + bool + UpdateAfterBreakpoint(); + + unsigned + GetRegisterIndexFromOffset(unsigned offset); + + bool + IsWatchpointHit(uint32_t hw_index); + + bool + ClearWatchpointHits(); + + lldb::addr_t + GetWatchpointAddress(uint32_t hw_index); + + bool + IsWatchpointVacant(uint32_t hw_index); + + bool + SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, bool read, bool write, uint32_t hw_index); + + uint32_t + NumSupportedHardwareWatchpoints(); + +private: + ProcessMonitor & + GetMonitor(); +}; + +#endif diff --git a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_x86.cpp b/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_x86.cpp index e534f3b4f9d0..1956e4584fa9 100644 --- a/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_x86.cpp +++ b/source/Plugins/Process/POSIX/RegisterContextPOSIXProcessMonitor_x86.cpp @@ -10,9 +10,13 @@ #include "lldb/Target/Thread.h" #include "lldb/Core/RegisterValue.h" -#include "ProcessPOSIX.h" +#include "Plugins/Process/POSIX/ProcessPOSIX.h" #include "RegisterContextPOSIXProcessMonitor_x86.h" -#include "ProcessMonitor.h" +#if defined(__FreeBSD__) +#include "Plugins/Process/FreeBSD/ProcessMonitor.h" +#else +#include "Plugins/Process/Linux/ProcessMonitor.h" +#endif using namespace lldb_private; using namespace lldb; @@ -48,6 +52,7 @@ size_and_rw_bits(size_t size, bool read, bool write) return (0x2 << 2) | rw; default: assert(0 && "invalid size, must be one of 1, 2, 4, or 8"); + return 0; // Unreachable. Just to silence compiler. } } diff --git a/source/Plugins/Process/Utility/ARMDefines.h b/source/Plugins/Process/Utility/ARMDefines.h index 2c8ad3586af1..cfb33beb447a 100644 --- a/source/Plugins/Process/Utility/ARMDefines.h +++ b/source/Plugins/Process/Utility/ARMDefines.h @@ -45,7 +45,8 @@ typedef enum #define COND_AL 0xE // Always (unconditional) Always (unconditional) Any #define COND_UNCOND 0xF -static inline const char *ARMCondCodeToString(uint32_t CC) +static inline const char * +ARMCondCodeToString(uint32_t CC) { switch (CC) { default: assert(0 && "Unknown condition code"); @@ -67,6 +68,37 @@ static inline const char *ARMCondCodeToString(uint32_t CC) } } +static inline bool +ARMConditionPassed(const uint32_t condition, const uint32_t cpsr) +{ + const uint32_t cpsr_n = (cpsr >> 31) & 1u; // Negative condition code flag + const uint32_t cpsr_z = (cpsr >> 30) & 1u; // Zero condition code flag + const uint32_t cpsr_c = (cpsr >> 29) & 1u; // Carry condition code flag + const uint32_t cpsr_v = (cpsr >> 28) & 1u; // Overflow condition code flag + + switch (condition) { + case COND_EQ: return (cpsr_z == 1); + case COND_NE: return (cpsr_z == 0); + case COND_CS: return (cpsr_c == 1); + case COND_CC: return (cpsr_c == 0); + case COND_MI: return (cpsr_n == 1); + case COND_PL: return (cpsr_n == 0); + case COND_VS: return (cpsr_v == 1); + case COND_VC: return (cpsr_v == 0); + case COND_HI: return ((cpsr_c == 1) && (cpsr_z == 0)); + case COND_LS: return ((cpsr_c == 0) || (cpsr_z == 1)); + case COND_GE: return (cpsr_n == cpsr_v); + case COND_LT: return (cpsr_n != cpsr_v); + case COND_GT: return ((cpsr_z == 0) && (cpsr_n == cpsr_v)); + case COND_LE: return ((cpsr_z == 1) || (cpsr_n != cpsr_v)); + case COND_AL: + case COND_UNCOND: + default: + return true; + } + return false; +} + // Bit positions for CPSR #define CPSR_T_POS 5 #define CPSR_F_POS 6 diff --git a/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp b/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp index 3507ccf92065..1088924bfeac 100644 --- a/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp +++ b/source/Plugins/Process/Utility/DynamicRegisterInfo.cpp @@ -81,6 +81,7 @@ DynamicRegisterInfo::SetRegisterInfo (const lldb_private::PythonDictionary &dict else { Clear(); + printf("error: register sets must have valid names\n"); return 0; } } @@ -121,6 +122,8 @@ DynamicRegisterInfo::SetRegisterInfo (const lldb_private::PythonDictionary &dict if (reg_info.name == NULL) { Clear(); + printf("error: registers must have valid names\n"); + reg_info_dict.Dump(); return 0; } @@ -290,6 +293,7 @@ DynamicRegisterInfo::SetRegisterInfo (const lldb_private::PythonDictionary &dict if (!success) { Clear(); + reg_info_dict.Dump(); return 0; } } @@ -297,6 +301,8 @@ DynamicRegisterInfo::SetRegisterInfo (const lldb_private::PythonDictionary &dict if (bitsize == 0) { Clear(); + printf("error: invalid or missing 'bitsize' key/value pair in register dictionary\n"); + reg_info_dict.Dump(); return 0; } @@ -308,6 +314,8 @@ DynamicRegisterInfo::SetRegisterInfo (const lldb_private::PythonDictionary &dict if (Args::StringToFormat(format_cstr, reg_info.format, NULL).Fail()) { Clear(); + printf("error: invalid 'format' value in register dictionary\n"); + reg_info_dict.Dump(); return 0; } } @@ -326,6 +334,8 @@ DynamicRegisterInfo::SetRegisterInfo (const lldb_private::PythonDictionary &dict if (static_cast<size_t>(set) >= m_sets.size()) { Clear(); + printf("error: invalid 'set' value in register dictionary, valid values are 0 - %i\n", (int)set); + reg_info_dict.Dump(); return 0; } @@ -409,6 +419,8 @@ DynamicRegisterInfo::SetRegisterInfo (const lldb_private::PythonDictionary &dict else { Clear(); + printf("error: items in the 'registers' array must be dictionaries\n"); + regs.Dump(); return 0; } } diff --git a/source/Plugins/Process/Utility/HistoryThread.cpp b/source/Plugins/Process/Utility/HistoryThread.cpp index 590bb0162c8a..206b8290c5fd 100644 --- a/source/Plugins/Process/Utility/HistoryThread.cpp +++ b/source/Plugins/Process/Utility/HistoryThread.cpp @@ -39,7 +39,7 @@ HistoryThread::HistoryThread (lldb_private::Process &process, m_originating_unique_thread_id (tid), m_queue_id (LLDB_INVALID_QUEUE_ID) { - m_unwinder_ap.reset (new HistoryUnwind (*this, pcs, stop_id, stop_id_is_valid)); + m_unwinder_ap.reset (new HistoryUnwind (*this, pcs, stop_id_is_valid)); Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); if (log) log->Printf ("%p HistoryThread::HistoryThread", diff --git a/source/Plugins/Process/Utility/HistoryThread.h b/source/Plugins/Process/Utility/HistoryThread.h index f9a431d8340b..51173c626d71 100644 --- a/source/Plugins/Process/Utility/HistoryThread.h +++ b/source/Plugins/Process/Utility/HistoryThread.h @@ -101,6 +101,18 @@ public: { m_thread_name = name; } + + virtual const char * + GetName () + { + return m_thread_name.c_str(); + } + + virtual void + SetName(const char *name) + { + m_thread_name = name; + } protected: virtual lldb::StackFrameListSP diff --git a/source/Plugins/Process/Utility/HistoryUnwind.cpp b/source/Plugins/Process/Utility/HistoryUnwind.cpp index f809ebedcfc8..14afcbee0b49 100644 --- a/source/Plugins/Process/Utility/HistoryUnwind.cpp +++ b/source/Plugins/Process/Utility/HistoryUnwind.cpp @@ -24,11 +24,9 @@ using namespace lldb_private; HistoryUnwind::HistoryUnwind (Thread &thread, std::vector<lldb::addr_t> pcs, - uint32_t stop_id, bool stop_id_is_valid) : Unwind (thread), m_pcs (pcs), - m_stop_id (stop_id), m_stop_id_is_valid (stop_id_is_valid) { } diff --git a/source/Plugins/Process/Utility/HistoryUnwind.h b/source/Plugins/Process/Utility/HistoryUnwind.h index 0661b8028608..733f93e1ff87 100644 --- a/source/Plugins/Process/Utility/HistoryUnwind.h +++ b/source/Plugins/Process/Utility/HistoryUnwind.h @@ -21,7 +21,7 @@ namespace lldb_private { class HistoryUnwind : public lldb_private::Unwind { public: - HistoryUnwind (Thread &thread, std::vector<lldb::addr_t> pcs, uint32_t stop_id, bool stop_id_is_valid); + HistoryUnwind (Thread &thread, std::vector<lldb::addr_t> pcs, bool stop_id_is_valid); virtual ~HistoryUnwind (); @@ -42,7 +42,6 @@ protected: private: std::vector<lldb::addr_t> m_pcs; - uint32_t m_stop_id; bool m_stop_id_is_valid; }; diff --git a/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp b/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp index 4a94457466be..7db83ae5467f 100644 --- a/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp +++ b/source/Plugins/Process/Utility/InferiorCallPOSIX.cpp @@ -98,13 +98,11 @@ lldb_private::InferiorCallMmap (Process *process, ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext(); ClangASTType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); lldb::addr_t args[] = { addr, length, prot_arg, flags_arg, fd, offset }; - ThreadPlanCallFunction *call_function_thread_plan - = new ThreadPlanCallFunction (*thread, - mmap_range.GetBaseAddress(), - clang_void_ptr_type, - args, - options); - lldb::ThreadPlanSP call_plan_sp (call_function_thread_plan); + lldb::ThreadPlanSP call_plan_sp (new ThreadPlanCallFunction (*thread, + mmap_range.GetBaseAddress(), + clang_void_ptr_type, + args, + options)); if (call_plan_sp) { StreamFile error_strm; @@ -241,13 +239,11 @@ lldb_private::InferiorCall (Process *process, ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext(); ClangASTType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType(); - ThreadPlanCallFunction *call_function_thread_plan - = new ThreadPlanCallFunction (*thread, - *address, - clang_void_ptr_type, - llvm::ArrayRef<addr_t>(), - options); - lldb::ThreadPlanSP call_plan_sp (call_function_thread_plan); + lldb::ThreadPlanSP call_plan_sp (new ThreadPlanCallFunction (*thread, + *address, + clang_void_ptr_type, + llvm::ArrayRef<addr_t>(), + options)); if (call_plan_sp) { StreamString error_strm; diff --git a/source/Plugins/Process/Utility/InstructionUtils.h b/source/Plugins/Process/Utility/InstructionUtils.h index 813990095c49..6226fbc04b08 100644 --- a/source/Plugins/Process/Utility/InstructionUtils.h +++ b/source/Plugins/Process/Utility/InstructionUtils.h @@ -20,7 +20,7 @@ static inline uint64_t Bits64 (const uint64_t bits, const uint32_t msbit, const uint32_t lsbit) { assert(msbit < 64 && lsbit <= msbit); - return (bits >> lsbit) & ((1u << (msbit - lsbit + 1)) - 1); + return (bits >> lsbit) & ((1ull << (msbit - lsbit + 1)) - 1); } // Return the bit field(s) from the most significant bit (msbit) to the diff --git a/source/Plugins/Process/Utility/LinuxSignals.cpp b/source/Plugins/Process/Utility/LinuxSignals.cpp index fb49df681cae..11a3eef23529 100644 --- a/source/Plugins/Process/Utility/LinuxSignals.cpp +++ b/source/Plugins/Process/Utility/LinuxSignals.cpp @@ -53,7 +53,7 @@ LinuxSignals::Reset() AddSignal (24, "SIGXCPU", "XCPU", false, true , true , "CPU resource exceeded"); AddSignal (25, "SIGXFSZ", "XFSZ", false, true , true , "file size limit exceeded"); AddSignal (26, "SIGVTALRM", "VTALRM", false, true , true , "virtual time alarm"); - AddSignal (27, "SIGPROF", "PROF", false, true , true , "profiling time alarm"); + AddSignal (27, "SIGPROF", "PROF", false, false, false, "profiling time alarm"); AddSignal (28, "SIGWINCH", "WINCH", false, true , true , "window size changes"); AddSignal (29, "SIGPOLL", "POLL", false, true , true , "pollable event"); AddSignal (29, "SIGIO", "IO", false, true , true , "input/output ready"); diff --git a/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.cpp b/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.cpp new file mode 100644 index 000000000000..5170e6d2accb --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.cpp @@ -0,0 +1,230 @@ +//===-- RegisterContextFreeBSD_powerpc.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#include <vector> +#include "RegisterContextPOSIX_powerpc.h" +#include "RegisterContextFreeBSD_powerpc.h" + +using namespace lldb_private; +using namespace lldb; + +// http://svnweb.freebsd.org/base/head/sys/powerpc/include/reg.h +typedef struct _GPR64 +{ + uint64_t r0; + uint64_t r1; + uint64_t r2; + uint64_t r3; + uint64_t r4; + uint64_t r5; + uint64_t r6; + uint64_t r7; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t r16; + uint64_t r17; + uint64_t r18; + uint64_t r19; + uint64_t r20; + uint64_t r21; + uint64_t r22; + uint64_t r23; + uint64_t r24; + uint64_t r25; + uint64_t r26; + uint64_t r27; + uint64_t r28; + uint64_t r29; + uint64_t r30; + uint64_t r31; + uint64_t lr; + uint64_t cr; + uint64_t xer; + uint64_t ctr; + uint64_t pc; +} GPR64; + +typedef struct _GPR32 +{ + uint32_t r0; + uint32_t r1; + uint32_t r2; + uint32_t r3; + uint32_t r4; + uint32_t r5; + uint32_t r6; + uint32_t r7; + uint32_t r8; + uint32_t r9; + uint32_t r10; + uint32_t r11; + uint32_t r12; + uint32_t r13; + uint32_t r14; + uint32_t r15; + uint32_t r16; + uint32_t r17; + uint32_t r18; + uint32_t r19; + uint32_t r20; + uint32_t r21; + uint32_t r22; + uint32_t r23; + uint32_t r24; + uint32_t r25; + uint32_t r26; + uint32_t r27; + uint32_t r28; + uint32_t r29; + uint32_t r30; + uint32_t r31; + uint32_t lr; + uint32_t cr; + uint32_t xer; + uint32_t ctr; + uint32_t pc; +} GPR32; + +typedef struct _FPR +{ + uint64_t f0; + uint64_t f1; + uint64_t f2; + uint64_t f3; + uint64_t f4; + uint64_t f5; + uint64_t f6; + uint64_t f7; + uint64_t f8; + uint64_t f9; + uint64_t f10; + uint64_t f11; + uint64_t f12; + uint64_t f13; + uint64_t f14; + uint64_t f15; + uint64_t f16; + uint64_t f17; + uint64_t f18; + uint64_t f19; + uint64_t f20; + uint64_t f21; + uint64_t f22; + uint64_t f23; + uint64_t f24; + uint64_t f25; + uint64_t f26; + uint64_t f27; + uint64_t f28; + uint64_t f29; + uint64_t f30; + uint64_t f31; + uint64_t fpscr; +} FPR; + +//--------------------------------------------------------------------------- +// Include RegisterInfos_powerpc to declare our g_register_infos_powerpc structure. +//--------------------------------------------------------------------------- +#define DECLARE_REGISTER_INFOS_POWERPC_STRUCT +#include "RegisterInfos_powerpc.h" +#undef DECLARE_REGISTER_INFOS_POWERPC_STRUCT + +RegisterContextFreeBSD_powerpc::RegisterContextFreeBSD_powerpc(const ArchSpec &target_arch) : + RegisterInfoInterface(target_arch) +{ +} + +RegisterContextFreeBSD_powerpc::~RegisterContextFreeBSD_powerpc() +{ +} + +size_t +RegisterContextFreeBSD_powerpc::GetGPRSize() const +{ + // This is an 'abstract' base, so no GPR struct. + return 0; +} + +const RegisterInfo * +RegisterContextFreeBSD_powerpc::GetRegisterInfo() const +{ + //assert (m_target_arch.GetCore() == ArchSpec::eCore_powerpc); + llvm_unreachable("Abstract class!"); + return NULL; +} + +uint32_t +RegisterContextFreeBSD_powerpc::GetRegisterCount () const +{ + return 0; +} + +RegisterContextFreeBSD_powerpc32::RegisterContextFreeBSD_powerpc32(const ArchSpec &target_arch) : + RegisterContextFreeBSD_powerpc(target_arch) +{ +} + +RegisterContextFreeBSD_powerpc32::~RegisterContextFreeBSD_powerpc32() +{ +} + +size_t +RegisterContextFreeBSD_powerpc32::GetGPRSize() const +{ + return sizeof(GPR32); +} + +const RegisterInfo * +RegisterContextFreeBSD_powerpc32::GetRegisterInfo() const +{ + //assert (m_target_arch.GetCore() == ArchSpec::eCore_powerpc); + return g_register_infos_powerpc32; +} + +uint32_t +RegisterContextFreeBSD_powerpc32::GetRegisterCount () const +{ + return static_cast<uint32_t> (sizeof (g_register_infos_powerpc32) / sizeof (g_register_infos_powerpc32 [0])); +} + +RegisterContextFreeBSD_powerpc64::RegisterContextFreeBSD_powerpc64(const ArchSpec &target_arch) : + RegisterContextFreeBSD_powerpc(target_arch) +{ +} + +RegisterContextFreeBSD_powerpc64::~RegisterContextFreeBSD_powerpc64() +{ +} + +size_t +RegisterContextFreeBSD_powerpc64::GetGPRSize() const +{ + return sizeof(GPR64); +} + +const RegisterInfo * +RegisterContextFreeBSD_powerpc64::GetRegisterInfo() const +{ + //assert (m_target_arch.GetCore() == ArchSpec::eCore_powerpc); + if (m_target_arch.GetMachine() == llvm::Triple::ppc) + return g_register_infos_powerpc64_32; + return g_register_infos_powerpc64; +} + +uint32_t +RegisterContextFreeBSD_powerpc64::GetRegisterCount () const +{ + return static_cast<uint32_t> (sizeof (g_register_infos_powerpc64) / sizeof (g_register_infos_powerpc64 [0])); +} diff --git a/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h b/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h new file mode 100644 index 000000000000..b907fe99b5e0 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h @@ -0,0 +1,66 @@ +//===-- RegisterContextFreeBSD_powerpc.h -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextFreeBSD_powerpc_H_ +#define liblldb_RegisterContextFreeBSD_powerpc_H_ + +#include "RegisterContextPOSIX.h" + +class RegisterContextFreeBSD_powerpc: + public lldb_private::RegisterInfoInterface +{ +public: + RegisterContextFreeBSD_powerpc(const lldb_private::ArchSpec &target_arch); + virtual ~RegisterContextFreeBSD_powerpc(); + + size_t + GetGPRSize() const override; + + const lldb_private::RegisterInfo * + GetRegisterInfo() const override; + + uint32_t + GetRegisterCount() const override; +}; + +class RegisterContextFreeBSD_powerpc32: + public RegisterContextFreeBSD_powerpc +{ +public: + RegisterContextFreeBSD_powerpc32(const lldb_private::ArchSpec &target_arch); + virtual ~RegisterContextFreeBSD_powerpc32(); + + size_t + GetGPRSize() const override; + + const lldb_private::RegisterInfo * + GetRegisterInfo() const override; + + uint32_t + GetRegisterCount() const override; +}; + +class RegisterContextFreeBSD_powerpc64: + public RegisterContextFreeBSD_powerpc +{ +public: + RegisterContextFreeBSD_powerpc64(const lldb_private::ArchSpec &target_arch); + virtual ~RegisterContextFreeBSD_powerpc64(); + + size_t + GetGPRSize() const override; + + const lldb_private::RegisterInfo * + GetRegisterInfo() const override; + + uint32_t + GetRegisterCount() const override; +}; + +#endif diff --git a/source/Plugins/Process/Utility/RegisterContextLLDB.cpp b/source/Plugins/Process/Utility/RegisterContextLLDB.cpp index b58e6bb607ed..f47d687702ec 100644 --- a/source/Plugins/Process/Utility/RegisterContextLLDB.cpp +++ b/source/Plugins/Process/Utility/RegisterContextLLDB.cpp @@ -172,6 +172,21 @@ RegisterContextLLDB::InitializeZerothFrame() m_sym_ctx_valid = true; } + if (m_sym_ctx.symbol) + { + UnwindLogMsg ("with pc value of 0x%" PRIx64 ", symbol name is '%s'", + current_pc, m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.symbol->GetName().AsCString()); + } + else if (m_sym_ctx.function) + { + UnwindLogMsg ("with pc value of 0x%" PRIx64 ", function name is '%s'", + current_pc, m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.function->GetName().AsCString()); + } + else + { + UnwindLogMsg ("with pc value of 0x%" PRIx64 ", no symbol/function name is known.", current_pc); + } + AddressRange addr_range; m_sym_ctx.GetAddressRange (resolve_scope, 0, false, addr_range); @@ -217,7 +232,6 @@ RegisterContextLLDB::InitializeZerothFrame() m_full_unwind_plan_sp = GetFullUnwindPlanForFrame (); UnwindPlan::RowSP active_row; - int cfa_offset = 0; lldb::RegisterKind row_register_kind = eRegisterKindGeneric; if (m_full_unwind_plan_sp && m_full_unwind_plan_sp->PlanValidAtAddress (m_current_pc)) { @@ -239,18 +253,13 @@ RegisterContextLLDB::InitializeZerothFrame() } - addr_t cfa_regval = LLDB_INVALID_ADDRESS; - if (!ReadGPRValue (row_register_kind, active_row->GetCFARegister(), cfa_regval)) + if (!ReadCFAValueForRow (row_register_kind, active_row, m_cfa)) { UnwindLogMsg ("could not read CFA register for this frame."); m_frame_type = eNotAValidFrame; return; } - cfa_offset = active_row->GetCFAOffset (); - m_cfa = cfa_regval + cfa_offset; - - UnwindLogMsg ("cfa_regval = 0x%16.16" PRIx64 " (cfa_regval = 0x%16.16" PRIx64 ", cfa_offset = %i)", m_cfa, cfa_regval, cfa_offset); UnwindLogMsg ("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64 " using %s UnwindPlan", (uint64_t) m_current_pc.GetLoadAddress (exe_ctx.GetTargetPtr()), (uint64_t) m_cfa, @@ -294,20 +303,27 @@ RegisterContextLLDB::InitializeNonZerothFrame() if (log) { - UnwindLogMsg ("pc = 0x%16.16" PRIx64, pc); + UnwindLogMsg ("pc = 0x%" PRIx64, pc); addr_t reg_val; if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP, reg_val)) - UnwindLogMsg ("fp = 0x%16.16" PRIx64, reg_val); + UnwindLogMsg ("fp = 0x%" PRIx64, reg_val); if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, reg_val)) - UnwindLogMsg ("sp = 0x%16.16" PRIx64, reg_val); + UnwindLogMsg ("sp = 0x%" PRIx64, reg_val); } - // A pc of 0x0 means it's the end of the stack crawl - if (pc == 0) + // A pc of 0x0 means it's the end of the stack crawl unless we're above a trap handler function + bool above_trap_handler = false; + if (GetNextFrame().get() && GetNextFrame()->IsValid() && GetNextFrame()->IsTrapHandlerFrame()) + above_trap_handler = true; + + if (pc == 0 || pc == 0x1) { - m_frame_type = eNotAValidFrame; - UnwindLogMsg ("this frame has a pc of 0x0"); - return; + if (above_trap_handler == false) + { + m_frame_type = eNotAValidFrame; + UnwindLogMsg ("this frame has a pc of 0x0"); + return; + } } ExecutionContext exe_ctx(m_thread.shared_from_this()); @@ -363,35 +379,31 @@ RegisterContextLLDB::InitializeNonZerothFrame() m_all_registers_available = false; m_current_offset = -1; m_current_offset_backed_up_one = -1; - addr_t cfa_regval = LLDB_INVALID_ADDRESS; RegisterKind row_register_kind = m_full_unwind_plan_sp->GetRegisterKind (); UnwindPlan::RowSP row = m_full_unwind_plan_sp->GetRowForFunctionOffset(0); if (row.get()) { - uint32_t cfa_regnum = row->GetCFARegister(); - int cfa_offset = row->GetCFAOffset(); - if (!ReadGPRValue (row_register_kind, cfa_regnum, cfa_regval)) + if (!ReadCFAValueForRow (row_register_kind, row, m_cfa)) { UnwindLogMsg ("failed to get cfa value"); if (m_frame_type != eSkipFrame) // don't override eSkipFrame { - m_frame_type = eNormalFrame; + m_frame_type = eNotAValidFrame; } return; } - m_cfa = cfa_regval + cfa_offset; // A couple of sanity checks.. - if (cfa_regval == LLDB_INVALID_ADDRESS || cfa_regval == 0 || cfa_regval == 1) + if (m_cfa == LLDB_INVALID_ADDRESS || m_cfa == 0 || m_cfa == 1) { UnwindLogMsg ("could not find a valid cfa address"); m_frame_type = eNotAValidFrame; return; } - // cfa_regval should point into the stack memory; if we can query memory region permissions, + // m_cfa should point into the stack memory; if we can query memory region permissions, // see if the memory is allocated & readable. - if (process->GetLoadAddressPermissions(cfa_regval, permissions) + if (process->GetLoadAddressPermissions(m_cfa, permissions) && (permissions & ePermissionsReadable) == 0) { m_frame_type = eNotAValidFrame; @@ -406,6 +418,17 @@ RegisterContextLLDB::InitializeNonZerothFrame() return; } + if (CheckIfLoopingStack ()) + { + TryFallbackUnwindPlan(); + if (CheckIfLoopingStack ()) + { + UnwindLogMsg ("same CFA address as next frame, assuming the unwind is looping - stopping"); + m_frame_type = eNotAValidFrame; + return; + } + } + UnwindLogMsg ("initialized frame cfa is 0x%" PRIx64, (uint64_t) m_cfa); return; } @@ -431,6 +454,21 @@ RegisterContextLLDB::InitializeNonZerothFrame() m_sym_ctx_valid = true; } + if (m_sym_ctx.symbol) + { + UnwindLogMsg ("with pc value of 0x%" PRIx64 ", symbol name is '%s'", + pc, m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.symbol->GetName().AsCString()); + } + else if (m_sym_ctx.function) + { + UnwindLogMsg ("with pc value of 0x%" PRIx64 ", function name is '%s'", + pc, m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.function->GetName().AsCString()); + } + else + { + UnwindLogMsg ("with pc value of 0x%" PRIx64 ", no symbol/function name is known.", pc); + } + AddressRange addr_range; if (!m_sym_ctx.GetAddressRange (resolve_scope, 0, false, addr_range)) { @@ -461,17 +499,22 @@ RegisterContextLLDB::InitializeNonZerothFrame() // to the ABI plugin and consult that. if (decr_pc_and_recompute_addr_range) { - Address temporary_pc(m_current_pc); - temporary_pc.SetOffset(m_current_pc.GetOffset() - 1); - m_sym_ctx.Clear(false); + UnwindLogMsg ("Backing up the pc value of 0x%" PRIx64 " by 1 and re-doing symbol lookup; old symbol was %s", + pc, m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.symbol->GetName().AsCString()); + Address temporary_pc; + temporary_pc.SetLoadAddress (pc - 1, &process->GetTarget()); + m_sym_ctx.Clear (false); m_sym_ctx_valid = false; uint32_t resolve_scope = eSymbolContextFunction | eSymbolContextSymbol; - if (pc_module_sp->ResolveSymbolContextForAddress (temporary_pc, resolve_scope, m_sym_ctx) & resolve_scope) + ModuleSP temporary_module_sp = temporary_pc.GetModule(); + if (temporary_module_sp && + temporary_module_sp->ResolveSymbolContextForAddress (temporary_pc, resolve_scope, m_sym_ctx) & resolve_scope) { if (m_sym_ctx.GetAddressRange (resolve_scope, 0, false, addr_range)) m_sym_ctx_valid = true; } + UnwindLogMsg ("Symbol is now %s", m_sym_ctx.symbol == NULL ? "" : m_sym_ctx.symbol->GetName().AsCString()); } // If we were able to find a symbol/function, set addr_range_ptr to the bounds of that symbol/function. @@ -479,13 +522,15 @@ RegisterContextLLDB::InitializeNonZerothFrame() if (addr_range.GetBaseAddress().IsValid()) { m_start_pc = addr_range.GetBaseAddress(); - m_current_offset = m_current_pc.GetOffset() - m_start_pc.GetOffset(); + m_current_offset = pc - m_start_pc.GetLoadAddress (&process->GetTarget()); m_current_offset_backed_up_one = m_current_offset; if (decr_pc_and_recompute_addr_range && m_current_offset_backed_up_one > 0) { m_current_offset_backed_up_one--; if (m_sym_ctx_valid) - m_current_pc.SetOffset(m_current_pc.GetOffset() - 1); + { + m_current_pc.SetLoadAddress (pc - 1, &process->GetTarget()); + } } } else @@ -512,7 +557,6 @@ RegisterContextLLDB::InitializeNonZerothFrame() m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame (); UnwindPlan::RowSP active_row; - int cfa_offset = 0; RegisterKind row_register_kind = eRegisterKindGeneric; // Try to get by with just the fast UnwindPlan if possible - the full UnwindPlan may be expensive to get @@ -553,27 +597,33 @@ RegisterContextLLDB::InitializeNonZerothFrame() return; } - addr_t cfa_regval = LLDB_INVALID_ADDRESS; - if (!ReadGPRValue (row_register_kind, active_row->GetCFARegister(), cfa_regval)) + if (!ReadCFAValueForRow (row_register_kind, active_row, m_cfa)) { UnwindLogMsg ("failed to get cfa reg %d/%d", row_register_kind, active_row->GetCFARegister()); m_frame_type = eNotAValidFrame; return; } - cfa_offset = active_row->GetCFAOffset (); - m_cfa = cfa_regval + cfa_offset; + UnwindLogMsg ("m_cfa = 0x%" PRIx64, m_cfa); - UnwindLogMsg ("cfa_regval = 0x%16.16" PRIx64 " (cfa_regval = 0x%16.16" PRIx64 ", cfa_offset = %i)", m_cfa, cfa_regval, cfa_offset); - - // A couple of sanity checks.. - if (cfa_regval == LLDB_INVALID_ADDRESS || cfa_regval == 0 || cfa_regval == 1) + if (CheckIfLoopingStack ()) { - UnwindLogMsg ("could not find a valid cfa address"); - m_frame_type = eNotAValidFrame; - return; + TryFallbackUnwindPlan(); + if (CheckIfLoopingStack ()) + { + UnwindLogMsg ("same CFA address as next frame, assuming the unwind is looping - stopping"); + m_frame_type = eNotAValidFrame; + return; + } } + UnwindLogMsg ("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64, + (uint64_t) m_current_pc.GetLoadAddress (exe_ctx.GetTargetPtr()), (uint64_t) m_cfa); +} + +bool +RegisterContextLLDB::CheckIfLoopingStack () +{ // If we have a bad stack setup, we can get the same CFA value multiple times -- or even // more devious, we can actually oscillate between two CFA values. Detect that here and // break out to avoid a possible infinite loop in lldb trying to unwind the stack. @@ -581,29 +631,19 @@ RegisterContextLLDB::InitializeNonZerothFrame() addr_t next_next_frame_cfa = LLDB_INVALID_ADDRESS; if (GetNextFrame().get() && GetNextFrame()->GetCFA(next_frame_cfa)) { - bool repeating_frames = false; if (next_frame_cfa == m_cfa) { - repeating_frames = true; - } - else - { - if (GetNextFrame()->GetNextFrame() && GetNextFrame()->GetNextFrame()->GetCFA(next_next_frame_cfa) - && next_next_frame_cfa == m_cfa) - { - repeating_frames = true; - } + // We have a loop in the stack unwind + return true; } - if (repeating_frames && abi && abi->FunctionCallsChangeCFA()) + if (GetNextFrame()->GetNextFrame().get() && GetNextFrame()->GetNextFrame()->GetCFA(next_next_frame_cfa) + && next_next_frame_cfa == m_cfa) { - UnwindLogMsg ("same CFA address as next frame, assuming the unwind is looping - stopping"); - m_frame_type = eNotAValidFrame; - return; + // We have a loop in the stack unwind + return true; } } - - UnwindLogMsg ("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64, - (uint64_t) m_current_pc.GetLoadAddress (exe_ctx.GetTargetPtr()), (uint64_t) m_cfa); + return false; } @@ -715,13 +755,17 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame () uint32_t permissions; addr_t current_pc_addr = m_current_pc.GetLoadAddress (exe_ctx.GetTargetPtr()); if (current_pc_addr == 0 - || (process->GetLoadAddressPermissions (current_pc_addr, permissions) + || (process && + process->GetLoadAddressPermissions (current_pc_addr, permissions) && (permissions & ePermissionsExecutable) == 0)) { - unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); - abi->CreateFunctionEntryUnwindPlan(*unwind_plan_sp); - m_frame_type = eNormalFrame; - return unwind_plan_sp; + if (abi) + { + unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + abi->CreateFunctionEntryUnwindPlan(*unwind_plan_sp); + m_frame_type = eNormalFrame; + return unwind_plan_sp; + } } } @@ -764,10 +808,10 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame () // is properly encoded in the eh_frame section, so prefer that if available. // On other platforms we may need to provide a platform-specific UnwindPlan which encodes the details of // how to unwind out of sigtramp. - if (m_frame_type == eTrapHandlerFrame) + if (m_frame_type == eTrapHandlerFrame && process) { m_fast_unwind_plan_sp.reset(); - unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (m_current_offset_backed_up_one); + unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (process->GetTarget(), m_current_offset_backed_up_one); if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc) && unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes) { return unwind_plan_sp; @@ -782,7 +826,7 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame () // But there is not. if (process && process->GetDynamicLoader() && process->GetDynamicLoader()->AlwaysRelyOnEHUnwindInfo (m_sym_ctx)) { - unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (m_current_offset_backed_up_one); + unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (process->GetTarget(), m_current_offset_backed_up_one); if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc)) { UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan because the DynamicLoader suggested we prefer it", @@ -792,7 +836,7 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame () } // Typically the NonCallSite UnwindPlan is the unwind created by inspecting the assembly language instructions - if (behaves_like_zeroth_frame) + if (behaves_like_zeroth_frame && process) { 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)) @@ -812,7 +856,10 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame () } // Typically this is unwind info from an eh_frame section intended for exception handling; only valid at call sites - unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (m_current_offset_backed_up_one); + if (process) + { + unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (process->GetTarget(), m_current_offset_backed_up_one); + } int valid_offset = -1; if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp, valid_offset)) { @@ -822,7 +869,10 @@ 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 (process->GetTarget(), m_thread, m_current_offset_backed_up_one); + if (process) + { + 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 @@ -908,6 +958,16 @@ RegisterContextLLDB::ReadRegisterValueFromRegisterLocation (lldb_private::Unwind switch (regloc.type) { + case UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext: + { + const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number); + + if (!other_reg_info) + return false; + + success = m_thread.GetRegisterContext()->ReadRegister (other_reg_info, value); + } + break; case UnwindLLDB::RegisterLocation::eRegisterInRegister: { const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number); @@ -962,6 +1022,12 @@ RegisterContextLLDB::WriteRegisterValueToRegisterLocation (lldb_private::UnwindL switch (regloc.type) { + case UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext: + { + const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number); + success = m_thread.GetRegisterContext()->WriteRegister (other_reg_info, value); + } + break; case UnwindLLDB::RegisterLocation::eRegisterInRegister: { const RegisterInfo *other_reg_info = GetRegisterInfoAtIndex(regloc.location.register_number); @@ -1004,6 +1070,11 @@ RegisterContextLLDB::IsValid () const return m_frame_type != eNotAValidFrame; } +// After the final stack frame in a stack walk we'll get one invalid (eNotAValidFrame) stack frame -- +// one past the end of the stack walk. But higher-level code will need to tell the differnece between +// "the unwind plan below this frame failed" versus "we successfully completed the stack walk" so +// this method helps to disambiguate that. + bool RegisterContextLLDB::IsTrapHandlerFrame () const { @@ -1056,57 +1127,42 @@ RegisterContextLLDB::IsTrapHandlerSymbol (lldb_private::Process *process, const enum UnwindLLDB::RegisterSearchResult RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc) { + RegisterNumber regnum (m_thread, eRegisterKindLLDB, lldb_regnum); + // Have we already found this register location? if (!m_registers.empty()) { std::map<uint32_t, lldb_private::UnwindLLDB::RegisterLocation>::const_iterator iterator; - iterator = m_registers.find (lldb_regnum); + iterator = m_registers.find (regnum.GetAsKind (eRegisterKindLLDB)); if (iterator != m_registers.end()) { regloc = iterator->second; - UnwindLogMsg ("supplying caller's saved reg %d's location, cached", lldb_regnum); + UnwindLogMsg ("supplying caller's saved %s (%d)'s location, cached", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } } - uint32_t sp_regnum = LLDB_INVALID_REGNUM; - uint32_t pc_regnum = LLDB_INVALID_REGNUM; - m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, eRegisterKindLLDB, sp_regnum); - m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, eRegisterKindLLDB, pc_regnum); - - // Are we looking for the CALLER's stack pointer? The stack pointer is defined to be the same as THIS frame's - // CFA so just return the CFA value. This is true on x86-32/x86-64 at least. - if (sp_regnum != LLDB_INVALID_REGNUM && sp_regnum == lldb_regnum) - { - // make sure we won't lose precision copying an addr_t (m_cfa) into a uint64_t (.inferred_value) - assert (sizeof (addr_t) <= sizeof (uint64_t)); - regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred; - regloc.location.inferred_value = m_cfa; - m_registers[lldb_regnum] = regloc; - UnwindLogMsg ("supplying caller's stack pointer (%d) value, computed from CFA", lldb_regnum); - return UnwindLLDB::RegisterSearchResult::eRegisterFound; - } - // Look through the available UnwindPlans for the register location. UnwindPlan::Row::RegisterLocation unwindplan_regloc; bool have_unwindplan_regloc = false; - RegisterKind unwindplan_registerkind = (RegisterKind)-1; + RegisterKind unwindplan_registerkind = kNumRegisterKinds; if (m_fast_unwind_plan_sp) { UnwindPlan::RowSP active_row = m_fast_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset); unwindplan_registerkind = m_fast_unwind_plan_sp->GetRegisterKind (); - uint32_t row_regnum; - if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindLLDB, lldb_regnum, unwindplan_registerkind, row_regnum)) + if (regnum.GetAsKind (unwindplan_registerkind) == LLDB_INVALID_REGNUM) { - UnwindLogMsg ("could not convert lldb regnum %d into %d RegisterKind reg numbering scheme", - lldb_regnum, (int) unwindplan_registerkind); + UnwindLogMsg ("could not convert lldb regnum %s (%d) into %d RegisterKind reg numbering scheme", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB), (int) unwindplan_registerkind); return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } - if (active_row->GetRegisterInfo (row_regnum, unwindplan_regloc)) + if (active_row->GetRegisterInfo (regnum.GetAsKind (unwindplan_registerkind), unwindplan_regloc)) { - UnwindLogMsg ("supplying caller's saved reg %d's location using FastUnwindPlan", lldb_regnum); + UnwindLogMsg ("supplying caller's saved %s (%d)'s location using FastUnwindPlan", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); have_unwindplan_regloc = true; } } @@ -1119,37 +1175,49 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat if (m_full_unwind_plan_sp) { + RegisterNumber pc_regnum (m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + UnwindPlan::RowSP active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset); unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind (); - uint32_t row_regnum; - bool row_register_rewritten_to_return_address_reg = false; + + RegisterNumber return_address_reg; // If we're fetching the saved pc and this UnwindPlan defines a ReturnAddress register (e.g. lr on arm), // look for the return address register number in the UnwindPlan's row. - if (lldb_regnum == pc_regnum && m_full_unwind_plan_sp->GetReturnAddressRegister() != LLDB_INVALID_REGNUM) + if (pc_regnum.IsValid() + && pc_regnum == regnum + && m_full_unwind_plan_sp->GetReturnAddressRegister() != LLDB_INVALID_REGNUM) { - row_regnum = m_full_unwind_plan_sp->GetReturnAddressRegister(); - row_register_rewritten_to_return_address_reg = true; - UnwindLogMsg ("requested caller's saved PC but this UnwindPlan uses a RA reg; getting reg %d instead", - row_regnum); + + return_address_reg.init (m_thread, m_full_unwind_plan_sp->GetRegisterKind(), m_full_unwind_plan_sp->GetReturnAddressRegister()); + regnum = return_address_reg; + UnwindLogMsg ("requested caller's saved PC but this UnwindPlan uses a RA reg; getting %s (%d) instead", + return_address_reg.GetName(), return_address_reg.GetAsKind (eRegisterKindLLDB)); } else { - if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindLLDB, lldb_regnum, unwindplan_registerkind, row_regnum)) + if (regnum.GetAsKind (unwindplan_registerkind) == LLDB_INVALID_REGNUM) { if (unwindplan_registerkind == eRegisterKindGeneric) - UnwindLogMsg ("could not convert lldb regnum %d into eRegisterKindGeneric reg numbering scheme", lldb_regnum); + { + UnwindLogMsg ("could not convert lldb regnum %s (%d) into eRegisterKindGeneric reg numbering scheme", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); + } else - UnwindLogMsg ("could not convert lldb regnum %d into %d RegisterKind reg numbering scheme", - lldb_regnum, (int) unwindplan_registerkind); + { + UnwindLogMsg ("could not convert lldb regnum %s (%d) into %d RegisterKind reg numbering scheme", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB), (int) unwindplan_registerkind); + } return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } } - if (active_row->GetRegisterInfo (row_regnum, unwindplan_regloc)) + if (regnum.IsValid() + && active_row->GetRegisterInfo (regnum.GetAsKind (unwindplan_registerkind), unwindplan_regloc)) { have_unwindplan_regloc = true; - UnwindLogMsg ("supplying caller's saved reg %d's location using %s UnwindPlan", lldb_regnum, + UnwindLogMsg ("supplying caller's saved %s (%d)'s location using %s UnwindPlan", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB), m_full_unwind_plan_sp->GetSourceName().GetCString()); } @@ -1158,19 +1226,19 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat // Handle this specially. if (have_unwindplan_regloc == false - && row_register_rewritten_to_return_address_reg == true - && IsFrameZero() - && row_regnum != LLDB_INVALID_REGNUM) + && return_address_reg.IsValid() + && IsFrameZero()) { - uint32_t ra_regnum_in_lldb_reg_numbering; - if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (unwindplan_registerkind, row_regnum, eRegisterKindLLDB, ra_regnum_in_lldb_reg_numbering)) + if (return_address_reg.GetAsKind (eRegisterKindLLDB) != LLDB_INVALID_REGNUM) { lldb_private::UnwindLLDB::RegisterLocation new_regloc; - new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister; - new_regloc.location.register_number = ra_regnum_in_lldb_reg_numbering; - m_registers[lldb_regnum] = new_regloc; + new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext; + new_regloc.location.register_number = return_address_reg.GetAsKind (eRegisterKindLLDB); + m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = new_regloc; regloc = new_regloc; - UnwindLogMsg ("supplying caller's register %d from the live RegisterContext at frame 0, saved in %d", lldb_regnum, ra_regnum_in_lldb_reg_numbering); + UnwindLogMsg ("supplying caller's register %s (%d) from the live RegisterContext at frame 0, saved in %d", + return_address_reg.GetName(), return_address_reg.GetAsKind (eRegisterKindLLDB), + return_address_reg.GetAsKind (eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } } @@ -1187,13 +1255,12 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat // when we're at a call site location. // arch_default_ra_regnum is the return address register # in the Full UnwindPlan register numbering - uint32_t arch_default_ra_regnum = LLDB_INVALID_REGNUM; - if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, unwindplan_registerkind, arch_default_ra_regnum) - && arch_default_ra_regnum != LLDB_INVALID_REGNUM - && pc_regnum != LLDB_INVALID_REGNUM - && pc_regnum == lldb_regnum + RegisterNumber arch_default_ra_regnum (m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA); + + if (arch_default_ra_regnum.GetAsKind (unwindplan_registerkind) != LLDB_INVALID_REGNUM + && pc_regnum == regnum && unwindplan_regloc.IsInOtherRegister() - && unwindplan_regloc.GetRegisterNumber() == arch_default_ra_regnum + && unwindplan_regloc.GetRegisterNumber() == arch_default_ra_regnum.GetAsKind (unwindplan_registerkind) && m_full_unwind_plan_sp->GetSourcedFromCompiler() != eLazyBoolYes && !m_all_registers_available) { @@ -1201,15 +1268,32 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat m_full_unwind_plan_sp->GetSourceName().GetCString()); // Throw away the full unwindplan; install the arch default unwindplan - if (TryFallbackUnwindPlan()) + if (ForceSwitchToFallbackUnwindPlan()) { - // Now re-fetch the pc value we're searching for - uint32_t arch_default_pc_reg = LLDB_INVALID_REGNUM; + // Update for the possibly new unwind plan + unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind (); UnwindPlan::RowSP active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset); - if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, m_full_unwind_plan_sp->GetRegisterKind(), arch_default_pc_reg) - && arch_default_pc_reg != LLDB_INVALID_REGNUM - && active_row - && active_row->GetRegisterInfo (arch_default_pc_reg, unwindplan_regloc)) + + // Sanity check: Verify that we can fetch a pc value and CFA value with this unwind plan + + RegisterNumber arch_default_pc_reg (m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + bool can_fetch_pc_value = false; + bool can_fetch_cfa = false; + addr_t cfa_value; + if (active_row) + { + if (arch_default_pc_reg.GetAsKind (unwindplan_registerkind) != LLDB_INVALID_REGNUM + && active_row->GetRegisterInfo (arch_default_pc_reg.GetAsKind (unwindplan_registerkind), unwindplan_regloc)) + { + can_fetch_pc_value = true; + } + if (ReadCFAValueForRow (unwindplan_registerkind, active_row, cfa_value)) + { + can_fetch_cfa = true; + } + } + + if (can_fetch_pc_value && can_fetch_cfa) { have_unwindplan_regloc = true; } @@ -1218,10 +1302,35 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat have_unwindplan_regloc = false; } } + else + { + // We were unable to fall back to another unwind plan + have_unwindplan_regloc = false; + } } } } + if (have_unwindplan_regloc == false) + { + // Did the UnwindPlan fail to give us the caller's stack pointer? + // The stack pointer is defined to be the same as THIS frame's CFA, so return the CFA value as + // the caller's stack pointer. This is true on x86-32/x86-64 at least. + + RegisterNumber sp_regnum (m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + if (sp_regnum.GetAsKind (eRegisterKindLLDB) != LLDB_INVALID_REGNUM + && sp_regnum.GetAsKind (eRegisterKindLLDB) == regnum.GetAsKind (eRegisterKindLLDB)) + { + // make sure we won't lose precision copying an addr_t (m_cfa) into a uint64_t (.inferred_value) + assert (sizeof (addr_t) <= sizeof (uint64_t)); + regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred; + regloc.location.inferred_value = m_cfa; + m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = regloc; + UnwindLogMsg ("supplying caller's stack pointer %s (%d) value, computed from CFA", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); + return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } + } ExecutionContext exe_ctx(m_thread.shared_from_this()); Process *process = exe_ctx.GetProcessPtr(); @@ -1232,11 +1341,11 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat ABI *abi = process ? process->GetABI().get() : NULL; if (abi) { - const RegisterInfo *reg_info = GetRegisterInfoAtIndex(lldb_regnum); + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(regnum.GetAsKind (eRegisterKindLLDB)); if (reg_info && abi->RegisterIsVolatile (reg_info)) { - UnwindLogMsg ("did not supply reg location for %d (%s) because it is volatile", - lldb_regnum, reg_info->name ? reg_info->name : "??"); + UnwindLogMsg ("did not supply reg location for %s (%d) because it is volatile", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterIsVolatile; } } @@ -1245,15 +1354,27 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat { // This is frame 0 - we should return the actual live register context value lldb_private::UnwindLLDB::RegisterLocation new_regloc; - new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister; - new_regloc.location.register_number = lldb_regnum; - m_registers[lldb_regnum] = new_regloc; + new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext; + new_regloc.location.register_number = regnum.GetAsKind (eRegisterKindLLDB); + m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = new_regloc; regloc = new_regloc; - UnwindLogMsg ("supplying caller's register %d from the live RegisterContext at frame 0", lldb_regnum); + UnwindLogMsg ("supplying caller's register %s (%d) from the live RegisterContext at frame 0", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } else - UnwindLogMsg ("could not supply caller's reg %d location", lldb_regnum); + { + std::string unwindplan_name (""); + if (m_full_unwind_plan_sp) + { + unwindplan_name += "via '"; + unwindplan_name += m_full_unwind_plan_sp->GetSourceName().AsCString(); + unwindplan_name += "'"; + } + UnwindLogMsg ("no save location for %s (%d) %s", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB), + unwindplan_name.c_str()); + } return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } @@ -1262,8 +1383,9 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat { lldb_private::UnwindLLDB::RegisterLocation new_regloc; new_regloc.type = UnwindLLDB::RegisterLocation::eRegisterNotSaved; - m_registers[lldb_regnum] = new_regloc; - UnwindLogMsg ("could not supply caller's reg %d location", lldb_regnum); + m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = new_regloc; + UnwindLogMsg ("save location for %s (%d) is unspecified, continue searching", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } @@ -1271,7 +1393,8 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat { if (IsFrameZero ()) { - UnwindLogMsg ("could not supply caller's reg %d location", lldb_regnum); + UnwindLogMsg ("could not supply caller's %s (%d) location, IsSame", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } else @@ -1285,8 +1408,10 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat int offset = unwindplan_regloc.GetOffset(); regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred; regloc.location.inferred_value = m_cfa + offset; - m_registers[lldb_regnum] = regloc; - UnwindLogMsg ("supplying caller's register %d, value is CFA plus offset %d", lldb_regnum, offset); + m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = regloc; + UnwindLogMsg ("supplying caller's register %s (%d), value is CFA plus offset %d [value is 0x%" PRIx64 "]", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB), + offset, regloc.location.inferred_value); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } @@ -1295,24 +1420,29 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat int offset = unwindplan_regloc.GetOffset(); regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation; regloc.location.target_memory_location = m_cfa + offset; - m_registers[lldb_regnum] = regloc; - UnwindLogMsg ("supplying caller's register %d from the stack, saved at CFA plus offset %d", lldb_regnum, offset); + m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = regloc; + UnwindLogMsg ("supplying caller's register %s (%d) from the stack, saved at CFA plus offset %d [saved at 0x%" PRIx64 "]", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB), + offset, regloc.location.target_memory_location); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } if (unwindplan_regloc.IsInOtherRegister()) { uint32_t unwindplan_regnum = unwindplan_regloc.GetRegisterNumber(); - uint32_t row_regnum_in_lldb; - if (!m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (unwindplan_registerkind, unwindplan_regnum, eRegisterKindLLDB, row_regnum_in_lldb)) + RegisterNumber row_regnum (m_thread, unwindplan_registerkind, unwindplan_regnum); + if (row_regnum.GetAsKind (eRegisterKindLLDB) == LLDB_INVALID_REGNUM) { - UnwindLogMsg ("could not supply caller's reg %d location", lldb_regnum); + UnwindLogMsg ("could not supply caller's %s (%d) location - was saved in another reg but couldn't convert that regnum", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } regloc.type = UnwindLLDB::RegisterLocation::eRegisterInRegister; - regloc.location.register_number = row_regnum_in_lldb; - m_registers[lldb_regnum] = regloc; - UnwindLogMsg ("supplying caller's register %d, saved in register %d", lldb_regnum, row_regnum_in_lldb); + regloc.location.register_number = row_regnum.GetAsKind (eRegisterKindLLDB); + m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = regloc; + UnwindLogMsg ("supplying caller's register %s (%d), saved in register %s (%d)", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB), + row_regnum.GetName(), row_regnum.GetAsKind (eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } @@ -1334,75 +1464,277 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat { regloc.type = UnwindLLDB::RegisterLocation::eRegisterValueInferred; regloc.location.inferred_value = val; - m_registers[lldb_regnum] = regloc; - UnwindLogMsg ("supplying caller's register %d via DWARF expression (IsDWARFExpression)", lldb_regnum); + m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = regloc; + UnwindLogMsg ("supplying caller's register %s (%d) via DWARF expression (IsDWARFExpression)", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } else { regloc.type = UnwindLLDB::RegisterLocation::eRegisterSavedAtMemoryLocation; regloc.location.target_memory_location = val; - m_registers[lldb_regnum] = regloc; - UnwindLogMsg ("supplying caller's register %d via DWARF expression (IsAtDWARFExpression)", lldb_regnum); + m_registers[regnum.GetAsKind (eRegisterKindLLDB)] = regloc; + UnwindLogMsg ("supplying caller's register %s (%d) via DWARF expression (IsAtDWARFExpression)", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterFound; } } - UnwindLogMsg ("tried to use IsDWARFExpression or IsAtDWARFExpression for reg %d but failed", lldb_regnum); + UnwindLogMsg ("tried to use IsDWARFExpression or IsAtDWARFExpression for %s (%d) but failed", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } - UnwindLogMsg ("could not supply caller's reg %d location", lldb_regnum); + UnwindLogMsg ("no save location for %s (%d) in this stack frame", + regnum.GetName(), regnum.GetAsKind (eRegisterKindLLDB)); // FIXME UnwindPlan::Row types atDWARFExpression and isDWARFExpression are unsupported. return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } -// If the Full unwindplan has been determined to be incorrect, this method will -// replace it with the architecture's default unwindplan, if one is defined. -// It will also find the FuncUnwinders object for this function and replace the -// Full unwind method for the function there so we don't use the errant Full unwindplan -// again in the future of this debug session. -// We're most likely doing this because the Full unwindplan was generated by assembly -// instruction profiling and the profiler got something wrong. +// TryFallbackUnwindPlan() -- this method is a little tricky. +// +// When this is called, the frame above -- the caller frame, the "previous" frame -- +// is invalid or bad. +// +// Instead of stopping the stack walk here, we'll try a different UnwindPlan and see +// if we can get a valid frame above us. +// +// This most often happens when an unwind plan based on assembly instruction inspection +// is not correct -- mostly with hand-written assembly functions or functions where the +// stack frame is set up "out of band", e.g. the kernel saved the register context and +// then called an asynchronous trap handler like _sigtramp. +// +// Often in these cases, if we just do a dumb stack walk we'll get past this tricky +// frame and our usual techniques can continue to be used. bool RegisterContextLLDB::TryFallbackUnwindPlan () { - UnwindPlan::Row::RegisterLocation unwindplan_regloc; - if (m_fallback_unwind_plan_sp.get() == NULL) + if (m_fallback_unwind_plan_sp.get() == nullptr) + return false; + + if (m_full_unwind_plan_sp.get() == nullptr) return false; + if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() + || m_full_unwind_plan_sp->GetSourceName() == m_fallback_unwind_plan_sp->GetSourceName()) + { + return false; + } + + // If a compiler generated unwind plan failed, trying the arch default unwindplan + // isn't going to do any better. + if (m_full_unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes) + return false; + + + // Get the caller's pc value and our own CFA value. + // Swap in the fallback unwind plan, re-fetch the caller's pc value and CFA value. + // If they're the same, then the fallback unwind plan provides no benefit. + + RegisterNumber pc_regnum (m_thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + + addr_t old_caller_pc_value = LLDB_INVALID_ADDRESS; + addr_t new_caller_pc_value = LLDB_INVALID_ADDRESS; + addr_t old_this_frame_cfa_value = m_cfa; + UnwindLLDB::RegisterLocation regloc; + if (SavedLocationForRegister (pc_regnum.GetAsKind (eRegisterKindLLDB), regloc) == UnwindLLDB::RegisterSearchResult::eRegisterFound) + { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(pc_regnum.GetAsKind (eRegisterKindLLDB)); + if (reg_info) + { + RegisterValue reg_value; + if (ReadRegisterValueFromRegisterLocation (regloc, reg_info, reg_value)) + { + old_caller_pc_value = reg_value.GetAsUInt64(); + } + } + } + + // This is a tricky wrinkle! If SavedLocationForRegister() detects a really impossible + // register location for the full unwind plan, it may call ForceSwitchToFallbackUnwindPlan() + // which in turn replaces the full unwindplan with the fallback... in short, we're done, + // we're using the fallback UnwindPlan. + // We checked if m_fallback_unwind_plan_sp was nullptr at the top -- the only way it + // became nullptr since then is via SavedLocationForRegister(). + if (m_fallback_unwind_plan_sp.get() == nullptr) + return true; + + + // Switch the full UnwindPlan to be the fallback UnwindPlan. If we decide this isn't + // working, we need to restore. + // We'll also need to save & restore the value of the m_cfa ivar. Save is down below a bit in 'old_cfa'. UnwindPlanSP original_full_unwind_plan_sp = m_full_unwind_plan_sp; + addr_t old_cfa = m_cfa; + + m_registers.clear(); + + m_full_unwind_plan_sp = m_fallback_unwind_plan_sp; + UnwindPlan::RowSP active_row = m_fallback_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset); if (active_row && active_row->GetCFARegister() != LLDB_INVALID_REGNUM) { - FuncUnwindersSP func_unwinders_sp; - if (m_sym_ctx_valid && m_current_pc.IsValid() && m_current_pc.GetModule()) + addr_t new_cfa; + if (!ReadCFAValueForRow (m_fallback_unwind_plan_sp->GetRegisterKind(), active_row, new_cfa) + || new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) + { + UnwindLogMsg ("failed to get cfa with fallback unwindplan"); + m_fallback_unwind_plan_sp.reset(); + m_full_unwind_plan_sp = original_full_unwind_plan_sp; + m_cfa = old_cfa; + return false; + } + m_cfa = new_cfa; + + if (SavedLocationForRegister (pc_regnum.GetAsKind (eRegisterKindLLDB), regloc) == UnwindLLDB::RegisterSearchResult::eRegisterFound) { - func_unwinders_sp = m_current_pc.GetModule()->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx); - if (func_unwinders_sp) + const RegisterInfo *reg_info = GetRegisterInfoAtIndex(pc_regnum.GetAsKind (eRegisterKindLLDB)); + if (reg_info) { - func_unwinders_sp->InvalidateNonCallSiteUnwindPlan (m_thread); + RegisterValue reg_value; + if (ReadRegisterValueFromRegisterLocation (regloc, reg_info, reg_value)) + { + new_caller_pc_value = reg_value.GetAsUInt64(); + } } } - m_registers.clear(); - m_full_unwind_plan_sp = m_fallback_unwind_plan_sp; - addr_t cfa_regval = LLDB_INVALID_ADDRESS; - if (ReadGPRValue (m_fallback_unwind_plan_sp->GetRegisterKind(), active_row->GetCFARegister(), cfa_regval)) + + + if (new_caller_pc_value == LLDB_INVALID_ADDRESS) { - m_cfa = cfa_regval + active_row->GetCFAOffset (); + UnwindLogMsg ("failed to get a pc value for the caller frame with the fallback unwind plan"); + m_fallback_unwind_plan_sp.reset(); + m_full_unwind_plan_sp = original_full_unwind_plan_sp; + m_cfa = old_cfa; + return false; + } + + if (old_caller_pc_value != LLDB_INVALID_ADDRESS) + { + if (old_caller_pc_value == new_caller_pc_value && new_cfa == old_this_frame_cfa_value) + { + UnwindLogMsg ("fallback unwind plan got the same values for this frame CFA and caller frame pc, not using"); + m_fallback_unwind_plan_sp.reset(); + m_full_unwind_plan_sp = original_full_unwind_plan_sp; + m_cfa = old_cfa; + return false; + } } - UnwindLogMsg ("full unwind plan '%s' has been replaced by architecture default unwind plan '%s' for this function from now on.", - original_full_unwind_plan_sp->GetSourceName().GetCString(), m_fallback_unwind_plan_sp->GetSourceName().GetCString()); + UnwindLogMsg ("trying to unwind from this function with the UnwindPlan '%s' because UnwindPlan '%s' failed.", + m_fallback_unwind_plan_sp->GetSourceName().GetCString(), + original_full_unwind_plan_sp->GetSourceName().GetCString()); + + // We've copied the fallback unwind plan into the full - now clear the fallback. m_fallback_unwind_plan_sp.reset(); } return true; } +bool +RegisterContextLLDB::ForceSwitchToFallbackUnwindPlan () +{ + if (m_fallback_unwind_plan_sp.get() == NULL) + return false; + + if (m_full_unwind_plan_sp.get() == NULL) + return false; + + if (m_full_unwind_plan_sp.get() == m_fallback_unwind_plan_sp.get() + || m_full_unwind_plan_sp->GetSourceName() == m_fallback_unwind_plan_sp->GetSourceName()) + { + return false; + } + + UnwindPlan::RowSP active_row = m_fallback_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset); + + if (active_row && active_row->GetCFARegister() != LLDB_INVALID_REGNUM) + { + addr_t new_cfa; + if (!ReadCFAValueForRow (m_fallback_unwind_plan_sp->GetRegisterKind(), active_row, new_cfa) + || new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) + { + UnwindLogMsg ("failed to get cfa with fallback unwindplan"); + m_fallback_unwind_plan_sp.reset(); + return false; + } + + m_full_unwind_plan_sp = m_fallback_unwind_plan_sp; + m_fallback_unwind_plan_sp.reset(); + + m_registers.clear(); + + m_cfa = new_cfa; + + UnwindLogMsg ("switched unconditionally to the fallback unwindplan %s", m_full_unwind_plan_sp->GetSourceName().GetCString()); + return true; + } + return false; +} + +bool +RegisterContextLLDB::ReadCFAValueForRow (lldb::RegisterKind row_register_kind, + const UnwindPlan::RowSP &row, + addr_t &cfa_value) +{ + RegisterNumber cfa_reg (m_thread, row_register_kind, row->GetCFARegister()); + RegisterValue reg_value; + + cfa_value = LLDB_INVALID_ADDRESS; + addr_t cfa_reg_contents; + + if (ReadGPRValue (cfa_reg, cfa_reg_contents)) + { + if (row->GetCFAType() == UnwindPlan::Row::CFAIsRegisterDereferenced) + { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (cfa_reg.GetAsKind (eRegisterKindLLDB)); + RegisterValue reg_value; + if (reg_info) + { + Error error = ReadRegisterValueFromMemory(reg_info, + cfa_reg_contents, + reg_info->byte_size, + reg_value); + if (error.Success ()) + { + cfa_value = reg_value.GetAsUInt64(); + UnwindLogMsg ("CFA value via dereferencing reg %s (%d): reg has val 0x%" PRIx64 ", CFA value is 0x%" PRIx64, + cfa_reg.GetName(), cfa_reg.GetAsKind (eRegisterKindLLDB), + cfa_reg_contents, cfa_value); + return true; + } + else + { + UnwindLogMsg ("Tried to deref reg %s (%d) [0x%" PRIx64 "] but memory read failed.", + cfa_reg.GetName(), cfa_reg.GetAsKind (eRegisterKindLLDB), + cfa_reg_contents); + } + } + } + else + { + if (cfa_reg_contents == LLDB_INVALID_ADDRESS || cfa_reg_contents == 0 || cfa_reg_contents == 1) + { + UnwindLogMsg ("Got an invalid CFA register value - reg %s (%d), value 0x%" PRIx64, + cfa_reg.GetName(), cfa_reg.GetAsKind (eRegisterKindLLDB), + cfa_reg_contents); + cfa_reg_contents = LLDB_INVALID_ADDRESS; + return false; + } + cfa_value = cfa_reg_contents + row->GetCFAOffset (); + UnwindLogMsg ("CFA is 0x%" PRIx64 ": Register %s (%d) contents are 0x%" PRIx64 ", offset is %d", + cfa_value, + cfa_reg.GetName(), cfa_reg.GetAsKind (eRegisterKindLLDB), + cfa_reg_contents, row->GetCFAOffset ()); + return true; + } + } + return false; +} + // Retrieve a general purpose register value for THIS frame, as saved by the NEXT frame, i.e. the frame that // this frame called. e.g. // @@ -1471,6 +1803,12 @@ RegisterContextLLDB::ReadGPRValue (lldb::RegisterKind register_kind, uint32_t re return false; } +bool +RegisterContextLLDB::ReadGPRValue (const RegisterNumber ®num, addr_t &value) +{ + return ReadGPRValue (regnum.GetRegisterKind(), regnum.GetRegisterNumber(), value); +} + // Find the value of a register in THIS frame bool @@ -1593,6 +1931,10 @@ RegisterContextLLDB::ReadPC (addr_t& pc) if (!IsValid()) return false; + bool above_trap_handler = false; + if (GetNextFrame().get() && GetNextFrame()->IsValid() && GetNextFrame()->IsTrapHandlerFrame()) + above_trap_handler = true; + if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) { // A pc value of 0 or 1 is impossible in the middle of the stack -- it indicates the end of a stack walk. @@ -1601,6 +1943,7 @@ RegisterContextLLDB::ReadPC (addr_t& pc) // find the bug. if (m_all_registers_available == false + && above_trap_handler == false && (pc == 0 || pc == 1)) { return false; diff --git a/source/Plugins/Process/Utility/RegisterContextLLDB.h b/source/Plugins/Process/Utility/RegisterContextLLDB.h index d6ecfeb68caa..5f94a977448d 100644 --- a/source/Plugins/Process/Utility/RegisterContextLLDB.h +++ b/source/Plugins/Process/Utility/RegisterContextLLDB.h @@ -16,6 +16,7 @@ #include "lldb/Target/RegisterContext.h" #include "lldb/Symbol/UnwindPlan.h" #include "lldb/Symbol/SymbolContext.h" +#include "lldb/Utility/RegisterNumber.h" #include "UnwindLLDB.h" namespace lldb_private { @@ -98,6 +99,12 @@ private: // UnwindLLDB needs to pass around references to RegisterLocations friend class UnwindLLDB; + + // Returns true if we have an unwind loop -- the same stack frame unwinding + // multiple times. + bool + CheckIfLoopingStack (); + // Indicates whether this frame is frame zero -- the currently // executing frame -- or not. bool @@ -175,11 +182,30 @@ private: bool TryFallbackUnwindPlan (); + //------------------------------------------------------------------ + /// Switch to the fallback unwind plan unconditionally without any safety + /// checks that it is providing better results than the normal unwind plan. + /// + /// The only time it is valid to call this method is if the full unwindplan is + /// found to be fundamentally incorrect/impossible. + /// + /// Returns true if it was able to install the fallback unwind plan. + //------------------------------------------------------------------ + bool + ForceSwitchToFallbackUnwindPlan (); + // Get the contents of a general purpose (address-size) register for this frame // (usually retrieved from the next frame) bool ReadGPRValue (lldb::RegisterKind register_kind, uint32_t regnum, lldb::addr_t &value); + bool + ReadGPRValue (const RegisterNumber ®_num, lldb::addr_t &value); + + // Get the CFA register for a given frame. + bool + ReadCFAValueForRow (lldb::RegisterKind register_kind, const UnwindPlan::RowSP &row, lldb::addr_t &value); + lldb::UnwindPlanSP GetFastUnwindPlanForFrame (); diff --git a/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp b/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp index e246e715de86..a2ab67438b75 100644 --- a/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp +++ b/source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp @@ -149,7 +149,8 @@ RegisterContextMacOSXFrameBackchain::ReadRegister (const RegisterInfo *reg_info, // TOOD: need a better way to detect when "long double" types are // the same bytes size as "double" -#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) && !defined(_MSC_VER) && !defined(__mips__) +#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) && !defined(_MSC_VER) && \ + !defined(__mips__) && !defined(__powerpc__) && !defined(__ANDROID_NDK__) case sizeof (long double): if (sizeof (long double) == sizeof(uint32_t)) { diff --git a/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp b/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp new file mode 100644 index 000000000000..a9477d583517 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.cpp @@ -0,0 +1,273 @@ +//===-- RegisterContextPOSIX_powerpc.cpp -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <cstring> +#include <errno.h> +#include <stdint.h> + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Host/Endian.h" +#include "llvm/Support/Compiler.h" + +#include "RegisterContextPOSIX_powerpc.h" +#include "Plugins/Process/elf-core/ProcessElfCore.h" + +using namespace lldb_private; +using namespace lldb; + +static const +uint32_t g_gpr_regnums[] = +{ + gpr_r0_powerpc, + gpr_r1_powerpc, + gpr_r2_powerpc, + gpr_r3_powerpc, + gpr_r4_powerpc, + gpr_r5_powerpc, + gpr_r6_powerpc, + gpr_r7_powerpc, + gpr_r8_powerpc, + gpr_r9_powerpc, + gpr_r10_powerpc, + gpr_r11_powerpc, + gpr_r12_powerpc, + gpr_r13_powerpc, + gpr_r14_powerpc, + gpr_r15_powerpc, + gpr_r16_powerpc, + gpr_r17_powerpc, + gpr_r18_powerpc, + gpr_r19_powerpc, + gpr_r20_powerpc, + gpr_r21_powerpc, + gpr_r22_powerpc, + gpr_r23_powerpc, + gpr_r24_powerpc, + gpr_r25_powerpc, + gpr_r26_powerpc, + gpr_r27_powerpc, + gpr_r28_powerpc, + gpr_r29_powerpc, + gpr_r30_powerpc, + gpr_r31_powerpc, + gpr_lr_powerpc, + gpr_cr_powerpc, + gpr_xer_powerpc, + gpr_ctr_powerpc, + gpr_pc_powerpc, +}; + +static const +uint32_t g_fpr_regnums[] = +{ + fpr_f0_powerpc, + fpr_f1_powerpc, + fpr_f2_powerpc, + fpr_f3_powerpc, + fpr_f4_powerpc, + fpr_f5_powerpc, + fpr_f6_powerpc, + fpr_f7_powerpc, + fpr_f8_powerpc, + fpr_f9_powerpc, + fpr_f10_powerpc, + fpr_f11_powerpc, + fpr_f12_powerpc, + fpr_f13_powerpc, + fpr_f14_powerpc, + fpr_f15_powerpc, + fpr_f16_powerpc, + fpr_f17_powerpc, + fpr_f18_powerpc, + fpr_f19_powerpc, + fpr_f20_powerpc, + fpr_f21_powerpc, + fpr_f22_powerpc, + fpr_f23_powerpc, + fpr_f24_powerpc, + fpr_f25_powerpc, + fpr_f26_powerpc, + fpr_f27_powerpc, + fpr_f28_powerpc, + fpr_f29_powerpc, + fpr_f30_powerpc, + fpr_f31_powerpc, + fpr_fpscr_powerpc, +}; + +// Number of register sets provided by this context. +enum +{ + k_num_register_sets = 2 +}; + +static const RegisterSet +g_reg_sets_powerpc[k_num_register_sets] = +{ + { "General Purpose Registers", "gpr", k_num_gpr_registers_powerpc, g_gpr_regnums }, + { "Floating Point Registers", "fpr", k_num_fpr_registers_powerpc, g_fpr_regnums }, +}; + +bool RegisterContextPOSIX_powerpc::IsGPR(unsigned reg) +{ + return reg <= k_num_gpr_registers_powerpc; // GPR's come first. +} + +bool +RegisterContextPOSIX_powerpc::IsFPR(unsigned reg) +{ + // XXX + return (reg >= k_first_fpr) && (reg <= k_last_fpr); +} + +RegisterContextPOSIX_powerpc::RegisterContextPOSIX_powerpc(Thread &thread, + uint32_t concrete_frame_idx, + RegisterInfoInterface *register_info) + : RegisterContext(thread, concrete_frame_idx) +{ + m_register_info_ap.reset(register_info); + + // elf-core yet to support ReadFPR() + ProcessSP base = CalculateProcess(); + if (base.get()->GetPluginName() == ProcessElfCore::GetPluginNameStatic()) + return; +} + +RegisterContextPOSIX_powerpc::~RegisterContextPOSIX_powerpc() +{ +} + +void +RegisterContextPOSIX_powerpc::Invalidate() +{ +} + +void +RegisterContextPOSIX_powerpc::InvalidateAllRegisters() +{ +} + +unsigned +RegisterContextPOSIX_powerpc::GetRegisterOffset(unsigned reg) +{ + assert(reg < k_num_registers_powerpc && "Invalid register number."); + return GetRegisterInfo()[reg].byte_offset; +} + +unsigned +RegisterContextPOSIX_powerpc::GetRegisterSize(unsigned reg) +{ + assert(reg < k_num_registers_powerpc && "Invalid register number."); + return GetRegisterInfo()[reg].byte_size; +} + +size_t +RegisterContextPOSIX_powerpc::GetRegisterCount() +{ + size_t num_registers = k_num_registers_powerpc; + return num_registers; +} + +size_t +RegisterContextPOSIX_powerpc::GetGPRSize() +{ + return m_register_info_ap->GetGPRSize(); +} + +const RegisterInfo * +RegisterContextPOSIX_powerpc::GetRegisterInfo() +{ + // Commonly, this method is overridden and g_register_infos is copied and specialized. + // So, use GetRegisterInfo() rather than g_register_infos in this scope. + return m_register_info_ap->GetRegisterInfo (); +} + +const RegisterInfo * +RegisterContextPOSIX_powerpc::GetRegisterInfoAtIndex(size_t reg) +{ + if (reg < k_num_registers_powerpc) + return &GetRegisterInfo()[reg]; + else + return NULL; +} + +size_t +RegisterContextPOSIX_powerpc::GetRegisterSetCount() +{ + size_t sets = 0; + for (size_t set = 0; set < k_num_register_sets; ++set) + { + if (IsRegisterSetAvailable(set)) + ++sets; + } + + return sets; +} + +const RegisterSet * +RegisterContextPOSIX_powerpc::GetRegisterSet(size_t set) +{ + if (IsRegisterSetAvailable(set)) + return &g_reg_sets_powerpc[set]; + else + return NULL; +} + +const char * +RegisterContextPOSIX_powerpc::GetRegisterName(unsigned reg) +{ + assert(reg < k_num_registers_powerpc && "Invalid register offset."); + return GetRegisterInfo()[reg].name; +} + +lldb::ByteOrder +RegisterContextPOSIX_powerpc::GetByteOrder() +{ + // Get the target process whose privileged thread was used for the register read. + lldb::ByteOrder byte_order = eByteOrderInvalid; + Process *process = CalculateProcess().get(); + + if (process) + byte_order = process->GetByteOrder(); + return byte_order; +} + +bool +RegisterContextPOSIX_powerpc::IsRegisterSetAvailable(size_t set_index) +{ + size_t num_sets = k_num_register_sets; + + return (set_index < num_sets); +} + +// Used when parsing DWARF and EH frame information and any other +// object file sections that contain register numbers in them. +uint32_t +RegisterContextPOSIX_powerpc::ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) +{ + const uint32_t num_regs = GetRegisterCount(); + + assert (kind < kNumRegisterKinds); + for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) + { + const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg_idx); + + if (reg_info->kinds[kind] == num) + return reg_idx; + } + + return LLDB_INVALID_REGNUM; +} + diff --git a/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h b/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h new file mode 100644 index 000000000000..3194c3968c98 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h @@ -0,0 +1,173 @@ +//===-- RegisterContextPOSIX_powerpc.h ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextPOSIX_powerpc_H_ +#define liblldb_RegisterContextPOSIX_powerpc_H_ + +#include "lldb/Core/Log.h" +#include "RegisterContextPOSIX.h" +#include "RegisterContext_powerpc.h" + +class ProcessMonitor; + +// --------------------------------------------------------------------------- +// Internal codes for all powerpc registers. +// --------------------------------------------------------------------------- +enum +{ + k_first_gpr_powerpc, + gpr_r0_powerpc = k_first_gpr_powerpc, + gpr_r1_powerpc, + gpr_r2_powerpc, + gpr_r3_powerpc, + gpr_r4_powerpc, + gpr_r5_powerpc, + gpr_r6_powerpc, + gpr_r7_powerpc, + gpr_r8_powerpc, + gpr_r9_powerpc, + gpr_r10_powerpc, + gpr_r11_powerpc, + gpr_r12_powerpc, + gpr_r13_powerpc, + gpr_r14_powerpc, + gpr_r15_powerpc, + gpr_r16_powerpc, + gpr_r17_powerpc, + gpr_r18_powerpc, + gpr_r19_powerpc, + gpr_r20_powerpc, + gpr_r21_powerpc, + gpr_r22_powerpc, + gpr_r23_powerpc, + gpr_r24_powerpc, + gpr_r25_powerpc, + gpr_r26_powerpc, + gpr_r27_powerpc, + gpr_r28_powerpc, + gpr_r29_powerpc, + gpr_r30_powerpc, + gpr_r31_powerpc, + gpr_lr_powerpc, + gpr_cr_powerpc, + gpr_xer_powerpc, + gpr_ctr_powerpc, + gpr_pc_powerpc, + k_last_gpr_powerpc = gpr_pc_powerpc, + + k_first_fpr, + fpr_f0_powerpc = k_first_fpr, + fpr_f1_powerpc, + fpr_f2_powerpc, + fpr_f3_powerpc, + fpr_f4_powerpc, + fpr_f5_powerpc, + fpr_f6_powerpc, + fpr_f7_powerpc, + fpr_f8_powerpc, + fpr_f9_powerpc, + fpr_f10_powerpc, + fpr_f11_powerpc, + fpr_f12_powerpc, + fpr_f13_powerpc, + fpr_f14_powerpc, + fpr_f15_powerpc, + fpr_f16_powerpc, + fpr_f17_powerpc, + fpr_f18_powerpc, + fpr_f19_powerpc, + fpr_f20_powerpc, + fpr_f21_powerpc, + fpr_f22_powerpc, + fpr_f23_powerpc, + fpr_f24_powerpc, + fpr_f25_powerpc, + fpr_f26_powerpc, + fpr_f27_powerpc, + fpr_f28_powerpc, + fpr_f29_powerpc, + fpr_f30_powerpc, + fpr_f31_powerpc, + fpr_fpscr_powerpc, + k_last_fpr = fpr_fpscr_powerpc, + + k_num_registers_powerpc, + k_num_gpr_registers_powerpc = k_last_gpr_powerpc - k_first_gpr_powerpc + 1, + k_num_fpr_registers_powerpc = k_last_fpr - k_first_fpr + 1, +}; + +class RegisterContextPOSIX_powerpc + : public lldb_private::RegisterContext +{ +public: + RegisterContextPOSIX_powerpc (lldb_private::Thread &thread, + uint32_t concrete_frame_idx, + lldb_private::RegisterInfoInterface *register_info); + + ~RegisterContextPOSIX_powerpc(); + + void + Invalidate(); + + void + InvalidateAllRegisters(); + + size_t + GetRegisterCount(); + + virtual size_t + GetGPRSize(); + + virtual unsigned + GetRegisterSize(unsigned reg); + + virtual unsigned + GetRegisterOffset(unsigned reg); + + const lldb_private::RegisterInfo * + GetRegisterInfoAtIndex(size_t reg); + + size_t + GetRegisterSetCount(); + + const lldb_private::RegisterSet * + GetRegisterSet(size_t set); + + const char * + GetRegisterName(unsigned reg); + + uint32_t + ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num); + +protected: + uint64_t m_gpr_powerpc[k_num_gpr_registers_powerpc]; // general purpose registers. + std::unique_ptr<lldb_private::RegisterInfoInterface> m_register_info_ap; // Register Info Interface (FreeBSD or Linux) + + // Determines if an extended register set is supported on the processor running the inferior process. + virtual bool + IsRegisterSetAvailable(size_t set_index); + + virtual const lldb_private::RegisterInfo * + GetRegisterInfo(); + + bool + IsGPR(unsigned reg); + + bool + IsFPR(unsigned reg); + + lldb::ByteOrder GetByteOrder(); + + virtual bool ReadGPR() = 0; + virtual bool ReadFPR() = 0; + virtual bool WriteGPR() = 0; + virtual bool WriteFPR() = 0; +}; + +#endif // #ifndef liblldb_RegisterContextPOSIX_powerpc_H_ diff --git a/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.cpp b/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.cpp index 2925a33a1690..77b6385da8bd 100644 --- a/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.cpp +++ b/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.cpp @@ -30,96 +30,96 @@ using namespace lldb; const uint32_t g_gpr_regnums_i386[] = { - gpr_eax_i386, - gpr_ebx_i386, - gpr_ecx_i386, - gpr_edx_i386, - gpr_edi_i386, - gpr_esi_i386, - gpr_ebp_i386, - gpr_esp_i386, - gpr_eip_i386, - gpr_eflags_i386, - gpr_cs_i386, - gpr_fs_i386, - gpr_gs_i386, - gpr_ss_i386, - gpr_ds_i386, - gpr_es_i386, - gpr_ax_i386, - gpr_bx_i386, - gpr_cx_i386, - gpr_dx_i386, - gpr_di_i386, - gpr_si_i386, - gpr_bp_i386, - gpr_sp_i386, - gpr_ah_i386, - gpr_bh_i386, - gpr_ch_i386, - gpr_dh_i386, - gpr_al_i386, - gpr_bl_i386, - gpr_cl_i386, - gpr_dl_i386, + lldb_eax_i386, + lldb_ebx_i386, + lldb_ecx_i386, + lldb_edx_i386, + lldb_edi_i386, + lldb_esi_i386, + lldb_ebp_i386, + lldb_esp_i386, + lldb_eip_i386, + lldb_eflags_i386, + lldb_cs_i386, + lldb_fs_i386, + lldb_gs_i386, + lldb_ss_i386, + lldb_ds_i386, + lldb_es_i386, + lldb_ax_i386, + lldb_bx_i386, + lldb_cx_i386, + lldb_dx_i386, + lldb_di_i386, + lldb_si_i386, + lldb_bp_i386, + lldb_sp_i386, + lldb_ah_i386, + lldb_bh_i386, + lldb_ch_i386, + lldb_dh_i386, + lldb_al_i386, + lldb_bl_i386, + lldb_cl_i386, + lldb_dl_i386, LLDB_INVALID_REGNUM, // Register sets must be terminated with LLDB_INVALID_REGNUM. }; static_assert((sizeof(g_gpr_regnums_i386) / sizeof(g_gpr_regnums_i386[0])) - 1 == k_num_gpr_registers_i386, "g_gpr_regnums_i386 has wrong number of register infos"); const uint32_t -g_fpu_regnums_i386[] = -{ - fpu_fctrl_i386, - fpu_fstat_i386, - fpu_ftag_i386, - fpu_fop_i386, - fpu_fiseg_i386, - fpu_fioff_i386, - fpu_foseg_i386, - fpu_fooff_i386, - fpu_mxcsr_i386, - fpu_mxcsrmask_i386, - fpu_st0_i386, - fpu_st1_i386, - fpu_st2_i386, - fpu_st3_i386, - fpu_st4_i386, - fpu_st5_i386, - fpu_st6_i386, - fpu_st7_i386, - fpu_mm0_i386, - fpu_mm1_i386, - fpu_mm2_i386, - fpu_mm3_i386, - fpu_mm4_i386, - fpu_mm5_i386, - fpu_mm6_i386, - fpu_mm7_i386, - fpu_xmm0_i386, - fpu_xmm1_i386, - fpu_xmm2_i386, - fpu_xmm3_i386, - fpu_xmm4_i386, - fpu_xmm5_i386, - fpu_xmm6_i386, - fpu_xmm7_i386, +g_lldb_regnums_i386[] = +{ + lldb_fctrl_i386, + lldb_fstat_i386, + lldb_ftag_i386, + lldb_fop_i386, + lldb_fiseg_i386, + lldb_fioff_i386, + lldb_foseg_i386, + lldb_fooff_i386, + lldb_mxcsr_i386, + lldb_mxcsrmask_i386, + lldb_st0_i386, + lldb_st1_i386, + lldb_st2_i386, + lldb_st3_i386, + lldb_st4_i386, + lldb_st5_i386, + lldb_st6_i386, + lldb_st7_i386, + lldb_mm0_i386, + lldb_mm1_i386, + lldb_mm2_i386, + lldb_mm3_i386, + lldb_mm4_i386, + lldb_mm5_i386, + lldb_mm6_i386, + lldb_mm7_i386, + lldb_xmm0_i386, + lldb_xmm1_i386, + lldb_xmm2_i386, + lldb_xmm3_i386, + lldb_xmm4_i386, + lldb_xmm5_i386, + lldb_xmm6_i386, + lldb_xmm7_i386, LLDB_INVALID_REGNUM // Register sets must be terminated with LLDB_INVALID_REGNUM. }; -static_assert((sizeof(g_fpu_regnums_i386) / sizeof(g_fpu_regnums_i386[0])) - 1 == k_num_fpr_registers_i386, - "g_fpu_regnums_i386 has wrong number of register infos"); +static_assert((sizeof(g_lldb_regnums_i386) / sizeof(g_lldb_regnums_i386[0])) - 1 == k_num_fpr_registers_i386, + "g_lldb_regnums_i386 has wrong number of register infos"); const uint32_t g_avx_regnums_i386[] = { - fpu_ymm0_i386, - fpu_ymm1_i386, - fpu_ymm2_i386, - fpu_ymm3_i386, - fpu_ymm4_i386, - fpu_ymm5_i386, - fpu_ymm6_i386, - fpu_ymm7_i386, + lldb_ymm0_i386, + lldb_ymm1_i386, + lldb_ymm2_i386, + lldb_ymm3_i386, + lldb_ymm4_i386, + lldb_ymm5_i386, + lldb_ymm6_i386, + lldb_ymm7_i386, LLDB_INVALID_REGNUM // Register sets must be terminated with LLDB_INVALID_REGNUM. }; static_assert((sizeof(g_avx_regnums_i386) / sizeof(g_avx_regnums_i386[0])) - 1 == k_num_avx_registers_i386, @@ -128,212 +128,212 @@ static_assert((sizeof(g_avx_regnums_i386) / sizeof(g_avx_regnums_i386[0])) - 1 = static const uint32_t g_gpr_regnums_x86_64[] = { - gpr_rax_x86_64, - gpr_rbx_x86_64, - gpr_rcx_x86_64, - gpr_rdx_x86_64, - gpr_rdi_x86_64, - gpr_rsi_x86_64, - gpr_rbp_x86_64, - gpr_rsp_x86_64, - gpr_r8_x86_64, - gpr_r9_x86_64, - gpr_r10_x86_64, - gpr_r11_x86_64, - gpr_r12_x86_64, - gpr_r13_x86_64, - gpr_r14_x86_64, - gpr_r15_x86_64, - gpr_rip_x86_64, - gpr_rflags_x86_64, - gpr_cs_x86_64, - gpr_fs_x86_64, - gpr_gs_x86_64, - gpr_ss_x86_64, - gpr_ds_x86_64, - gpr_es_x86_64, - gpr_eax_x86_64, - gpr_ebx_x86_64, - gpr_ecx_x86_64, - gpr_edx_x86_64, - gpr_edi_x86_64, - gpr_esi_x86_64, - gpr_ebp_x86_64, - gpr_esp_x86_64, - gpr_r8d_x86_64, // Low 32 bits or r8 - gpr_r9d_x86_64, // Low 32 bits or r9 - gpr_r10d_x86_64, // Low 32 bits or r10 - gpr_r11d_x86_64, // Low 32 bits or r11 - gpr_r12d_x86_64, // Low 32 bits or r12 - gpr_r13d_x86_64, // Low 32 bits or r13 - gpr_r14d_x86_64, // Low 32 bits or r14 - gpr_r15d_x86_64, // Low 32 bits or r15 - gpr_ax_x86_64, - gpr_bx_x86_64, - gpr_cx_x86_64, - gpr_dx_x86_64, - gpr_di_x86_64, - gpr_si_x86_64, - gpr_bp_x86_64, - gpr_sp_x86_64, - gpr_r8w_x86_64, // Low 16 bits or r8 - gpr_r9w_x86_64, // Low 16 bits or r9 - gpr_r10w_x86_64, // Low 16 bits or r10 - gpr_r11w_x86_64, // Low 16 bits or r11 - gpr_r12w_x86_64, // Low 16 bits or r12 - gpr_r13w_x86_64, // Low 16 bits or r13 - gpr_r14w_x86_64, // Low 16 bits or r14 - gpr_r15w_x86_64, // Low 16 bits or r15 - gpr_ah_x86_64, - gpr_bh_x86_64, - gpr_ch_x86_64, - gpr_dh_x86_64, - gpr_al_x86_64, - gpr_bl_x86_64, - gpr_cl_x86_64, - gpr_dl_x86_64, - gpr_dil_x86_64, - gpr_sil_x86_64, - gpr_bpl_x86_64, - gpr_spl_x86_64, - gpr_r8l_x86_64, // Low 8 bits or r8 - gpr_r9l_x86_64, // Low 8 bits or r9 - gpr_r10l_x86_64, // Low 8 bits or r10 - gpr_r11l_x86_64, // Low 8 bits or r11 - gpr_r12l_x86_64, // Low 8 bits or r12 - gpr_r13l_x86_64, // Low 8 bits or r13 - gpr_r14l_x86_64, // Low 8 bits or r14 - gpr_r15l_x86_64, // Low 8 bits or r15 + lldb_rax_x86_64, + lldb_rbx_x86_64, + lldb_rcx_x86_64, + lldb_rdx_x86_64, + lldb_rdi_x86_64, + lldb_rsi_x86_64, + lldb_rbp_x86_64, + lldb_rsp_x86_64, + lldb_r8_x86_64, + lldb_r9_x86_64, + lldb_r10_x86_64, + lldb_r11_x86_64, + lldb_r12_x86_64, + lldb_r13_x86_64, + lldb_r14_x86_64, + lldb_r15_x86_64, + lldb_rip_x86_64, + lldb_rflags_x86_64, + lldb_cs_x86_64, + lldb_fs_x86_64, + lldb_gs_x86_64, + lldb_ss_x86_64, + lldb_ds_x86_64, + lldb_es_x86_64, + lldb_eax_x86_64, + lldb_ebx_x86_64, + lldb_ecx_x86_64, + lldb_edx_x86_64, + lldb_edi_x86_64, + lldb_esi_x86_64, + lldb_ebp_x86_64, + lldb_esp_x86_64, + lldb_r8d_x86_64, // Low 32 bits or r8 + lldb_r9d_x86_64, // Low 32 bits or r9 + lldb_r10d_x86_64, // Low 32 bits or r10 + lldb_r11d_x86_64, // Low 32 bits or r11 + lldb_r12d_x86_64, // Low 32 bits or r12 + lldb_r13d_x86_64, // Low 32 bits or r13 + lldb_r14d_x86_64, // Low 32 bits or r14 + lldb_r15d_x86_64, // Low 32 bits or r15 + lldb_ax_x86_64, + lldb_bx_x86_64, + lldb_cx_x86_64, + lldb_dx_x86_64, + lldb_di_x86_64, + lldb_si_x86_64, + lldb_bp_x86_64, + lldb_sp_x86_64, + lldb_r8w_x86_64, // Low 16 bits or r8 + lldb_r9w_x86_64, // Low 16 bits or r9 + lldb_r10w_x86_64, // Low 16 bits or r10 + lldb_r11w_x86_64, // Low 16 bits or r11 + lldb_r12w_x86_64, // Low 16 bits or r12 + lldb_r13w_x86_64, // Low 16 bits or r13 + lldb_r14w_x86_64, // Low 16 bits or r14 + lldb_r15w_x86_64, // Low 16 bits or r15 + lldb_ah_x86_64, + lldb_bh_x86_64, + lldb_ch_x86_64, + lldb_dh_x86_64, + lldb_al_x86_64, + lldb_bl_x86_64, + lldb_cl_x86_64, + lldb_dl_x86_64, + lldb_dil_x86_64, + lldb_sil_x86_64, + lldb_bpl_x86_64, + lldb_spl_x86_64, + lldb_r8l_x86_64, // Low 8 bits or r8 + lldb_r9l_x86_64, // Low 8 bits or r9 + lldb_r10l_x86_64, // Low 8 bits or r10 + lldb_r11l_x86_64, // Low 8 bits or r11 + lldb_r12l_x86_64, // Low 8 bits or r12 + lldb_r13l_x86_64, // Low 8 bits or r13 + lldb_r14l_x86_64, // Low 8 bits or r14 + lldb_r15l_x86_64, // Low 8 bits or r15 LLDB_INVALID_REGNUM // Register sets must be terminated with LLDB_INVALID_REGNUM. }; static_assert((sizeof(g_gpr_regnums_x86_64) / sizeof(g_gpr_regnums_x86_64[0])) - 1 == k_num_gpr_registers_x86_64, "g_gpr_regnums_x86_64 has wrong number of register infos"); static const uint32_t -g_fpu_regnums_x86_64[] = -{ - fpu_fctrl_x86_64, - fpu_fstat_x86_64, - fpu_ftag_x86_64, - fpu_fop_x86_64, - fpu_fiseg_x86_64, - fpu_fioff_x86_64, - fpu_foseg_x86_64, - fpu_fooff_x86_64, - fpu_mxcsr_x86_64, - fpu_mxcsrmask_x86_64, - fpu_st0_x86_64, - fpu_st1_x86_64, - fpu_st2_x86_64, - fpu_st3_x86_64, - fpu_st4_x86_64, - fpu_st5_x86_64, - fpu_st6_x86_64, - fpu_st7_x86_64, - fpu_mm0_x86_64, - fpu_mm1_x86_64, - fpu_mm2_x86_64, - fpu_mm3_x86_64, - fpu_mm4_x86_64, - fpu_mm5_x86_64, - fpu_mm6_x86_64, - fpu_mm7_x86_64, - fpu_xmm0_x86_64, - fpu_xmm1_x86_64, - fpu_xmm2_x86_64, - fpu_xmm3_x86_64, - fpu_xmm4_x86_64, - fpu_xmm5_x86_64, - fpu_xmm6_x86_64, - fpu_xmm7_x86_64, - fpu_xmm8_x86_64, - fpu_xmm9_x86_64, - fpu_xmm10_x86_64, - fpu_xmm11_x86_64, - fpu_xmm12_x86_64, - fpu_xmm13_x86_64, - fpu_xmm14_x86_64, - fpu_xmm15_x86_64, +g_lldb_regnums_x86_64[] = +{ + lldb_fctrl_x86_64, + lldb_fstat_x86_64, + lldb_ftag_x86_64, + lldb_fop_x86_64, + lldb_fiseg_x86_64, + lldb_fioff_x86_64, + lldb_foseg_x86_64, + lldb_fooff_x86_64, + lldb_mxcsr_x86_64, + lldb_mxcsrmask_x86_64, + lldb_st0_x86_64, + lldb_st1_x86_64, + lldb_st2_x86_64, + lldb_st3_x86_64, + lldb_st4_x86_64, + lldb_st5_x86_64, + lldb_st6_x86_64, + lldb_st7_x86_64, + lldb_mm0_x86_64, + lldb_mm1_x86_64, + lldb_mm2_x86_64, + lldb_mm3_x86_64, + lldb_mm4_x86_64, + lldb_mm5_x86_64, + lldb_mm6_x86_64, + lldb_mm7_x86_64, + lldb_xmm0_x86_64, + lldb_xmm1_x86_64, + lldb_xmm2_x86_64, + lldb_xmm3_x86_64, + lldb_xmm4_x86_64, + lldb_xmm5_x86_64, + lldb_xmm6_x86_64, + lldb_xmm7_x86_64, + lldb_xmm8_x86_64, + lldb_xmm9_x86_64, + lldb_xmm10_x86_64, + lldb_xmm11_x86_64, + lldb_xmm12_x86_64, + lldb_xmm13_x86_64, + lldb_xmm14_x86_64, + lldb_xmm15_x86_64, LLDB_INVALID_REGNUM // Register sets must be terminated with LLDB_INVALID_REGNUM. }; -static_assert((sizeof(g_fpu_regnums_x86_64) / sizeof(g_fpu_regnums_x86_64[0])) - 1 == k_num_fpr_registers_x86_64, - "g_fpu_regnums_x86_64 has wrong number of register infos"); +static_assert((sizeof(g_lldb_regnums_x86_64) / sizeof(g_lldb_regnums_x86_64[0])) - 1 == k_num_fpr_registers_x86_64, + "g_lldb_regnums_x86_64 has wrong number of register infos"); static const uint32_t g_avx_regnums_x86_64[] = { - fpu_ymm0_x86_64, - fpu_ymm1_x86_64, - fpu_ymm2_x86_64, - fpu_ymm3_x86_64, - fpu_ymm4_x86_64, - fpu_ymm5_x86_64, - fpu_ymm6_x86_64, - fpu_ymm7_x86_64, - fpu_ymm8_x86_64, - fpu_ymm9_x86_64, - fpu_ymm10_x86_64, - fpu_ymm11_x86_64, - fpu_ymm12_x86_64, - fpu_ymm13_x86_64, - fpu_ymm14_x86_64, - fpu_ymm15_x86_64, + lldb_ymm0_x86_64, + lldb_ymm1_x86_64, + lldb_ymm2_x86_64, + lldb_ymm3_x86_64, + lldb_ymm4_x86_64, + lldb_ymm5_x86_64, + lldb_ymm6_x86_64, + lldb_ymm7_x86_64, + lldb_ymm8_x86_64, + lldb_ymm9_x86_64, + lldb_ymm10_x86_64, + lldb_ymm11_x86_64, + lldb_ymm12_x86_64, + lldb_ymm13_x86_64, + lldb_ymm14_x86_64, + lldb_ymm15_x86_64, LLDB_INVALID_REGNUM // Register sets must be terminated with LLDB_INVALID_REGNUM. }; static_assert((sizeof(g_avx_regnums_x86_64) / sizeof(g_avx_regnums_x86_64[0])) - 1 == k_num_avx_registers_x86_64, "g_avx_regnums_x86_64 has wrong number of register infos"); -uint32_t RegisterContextPOSIX_x86::g_contained_eax[] = { gpr_eax_i386, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_ebx[] = { gpr_ebx_i386, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_ecx[] = { gpr_ecx_i386, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_edx[] = { gpr_edx_i386, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_edi[] = { gpr_edi_i386, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_esi[] = { gpr_esi_i386, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_ebp[] = { gpr_ebp_i386, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_esp[] = { gpr_esp_i386, LLDB_INVALID_REGNUM }; - -uint32_t RegisterContextPOSIX_x86::g_invalidate_eax[] = { gpr_eax_i386, gpr_ax_i386, gpr_ah_i386, gpr_al_i386, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_ebx[] = { gpr_ebx_i386, gpr_bx_i386, gpr_bh_i386, gpr_bl_i386, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_ecx[] = { gpr_ecx_i386, gpr_cx_i386, gpr_ch_i386, gpr_cl_i386, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_edx[] = { gpr_edx_i386, gpr_dx_i386, gpr_dh_i386, gpr_dl_i386, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_edi[] = { gpr_edi_i386, gpr_di_i386, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_esi[] = { gpr_esi_i386, gpr_si_i386, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_ebp[] = { gpr_ebp_i386, gpr_bp_i386, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_esp[] = { gpr_esp_i386, gpr_sp_i386, LLDB_INVALID_REGNUM }; - -uint32_t RegisterContextPOSIX_x86::g_contained_rax[] = { gpr_rax_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_rbx[] = { gpr_rbx_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_rcx[] = { gpr_rcx_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_rdx[] = { gpr_rdx_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_rdi[] = { gpr_rdi_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_rsi[] = { gpr_rsi_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_rbp[] = { gpr_rbp_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_rsp[] = { gpr_rsp_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_r8[] = { gpr_r8_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_r9[] = { gpr_r9_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_r10[] = { gpr_r10_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_r11[] = { gpr_r11_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_r12[] = { gpr_r12_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_r13[] = { gpr_r13_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_r14[] = { gpr_r14_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_contained_r15[] = { gpr_r15_x86_64, LLDB_INVALID_REGNUM }; - -uint32_t RegisterContextPOSIX_x86::g_invalidate_rax[] = { gpr_rax_x86_64, gpr_eax_x86_64, gpr_ax_x86_64, gpr_ah_x86_64, gpr_al_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_rbx[] = { gpr_rbx_x86_64, gpr_ebx_x86_64, gpr_bx_x86_64, gpr_bh_x86_64, gpr_bl_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_rcx[] = { gpr_rcx_x86_64, gpr_ecx_x86_64, gpr_cx_x86_64, gpr_ch_x86_64, gpr_cl_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_rdx[] = { gpr_rdx_x86_64, gpr_edx_x86_64, gpr_dx_x86_64, gpr_dh_x86_64, gpr_dl_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_rdi[] = { gpr_rdi_x86_64, gpr_edi_x86_64, gpr_di_x86_64, gpr_dil_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_rsi[] = { gpr_rsi_x86_64, gpr_esi_x86_64, gpr_si_x86_64, gpr_sil_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_rbp[] = { gpr_rbp_x86_64, gpr_ebp_x86_64, gpr_bp_x86_64, gpr_bpl_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_rsp[] = { gpr_rsp_x86_64, gpr_esp_x86_64, gpr_sp_x86_64, gpr_spl_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_r8[] = { gpr_r8_x86_64, gpr_r8d_x86_64, gpr_r8w_x86_64, gpr_r8l_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_r9[] = { gpr_r9_x86_64, gpr_r9d_x86_64, gpr_r9w_x86_64, gpr_r9l_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_r10[] = { gpr_r10_x86_64, gpr_r10d_x86_64, gpr_r10w_x86_64, gpr_r10l_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_r11[] = { gpr_r11_x86_64, gpr_r11d_x86_64, gpr_r11w_x86_64, gpr_r11l_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_r12[] = { gpr_r12_x86_64, gpr_r12d_x86_64, gpr_r12w_x86_64, gpr_r12l_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_r13[] = { gpr_r13_x86_64, gpr_r13d_x86_64, gpr_r13w_x86_64, gpr_r13l_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_r14[] = { gpr_r14_x86_64, gpr_r14d_x86_64, gpr_r14w_x86_64, gpr_r14l_x86_64, LLDB_INVALID_REGNUM }; -uint32_t RegisterContextPOSIX_x86::g_invalidate_r15[] = { gpr_r15_x86_64, gpr_r15d_x86_64, gpr_r15w_x86_64, gpr_r15l_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_eax[] = { lldb_eax_i386, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_ebx[] = { lldb_ebx_i386, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_ecx[] = { lldb_ecx_i386, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_edx[] = { lldb_edx_i386, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_edi[] = { lldb_edi_i386, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_esi[] = { lldb_esi_i386, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_ebp[] = { lldb_ebp_i386, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_esp[] = { lldb_esp_i386, LLDB_INVALID_REGNUM }; + +uint32_t RegisterContextPOSIX_x86::g_invalidate_eax[] = { lldb_eax_i386, lldb_ax_i386, lldb_ah_i386, lldb_al_i386, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_ebx[] = { lldb_ebx_i386, lldb_bx_i386, lldb_bh_i386, lldb_bl_i386, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_ecx[] = { lldb_ecx_i386, lldb_cx_i386, lldb_ch_i386, lldb_cl_i386, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_edx[] = { lldb_edx_i386, lldb_dx_i386, lldb_dh_i386, lldb_dl_i386, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_edi[] = { lldb_edi_i386, lldb_di_i386, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_esi[] = { lldb_esi_i386, lldb_si_i386, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_ebp[] = { lldb_ebp_i386, lldb_bp_i386, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_esp[] = { lldb_esp_i386, lldb_sp_i386, LLDB_INVALID_REGNUM }; + +uint32_t RegisterContextPOSIX_x86::g_contained_rax[] = { lldb_rax_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_rbx[] = { lldb_rbx_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_rcx[] = { lldb_rcx_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_rdx[] = { lldb_rdx_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_rdi[] = { lldb_rdi_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_rsi[] = { lldb_rsi_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_rbp[] = { lldb_rbp_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_rsp[] = { lldb_rsp_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_r8[] = { lldb_r8_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_r9[] = { lldb_r9_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_r10[] = { lldb_r10_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_r11[] = { lldb_r11_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_r12[] = { lldb_r12_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_r13[] = { lldb_r13_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_r14[] = { lldb_r14_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_contained_r15[] = { lldb_r15_x86_64, LLDB_INVALID_REGNUM }; + +uint32_t RegisterContextPOSIX_x86::g_invalidate_rax[] = { lldb_rax_x86_64, lldb_eax_x86_64, lldb_ax_x86_64, lldb_ah_x86_64, lldb_al_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_rbx[] = { lldb_rbx_x86_64, lldb_ebx_x86_64, lldb_bx_x86_64, lldb_bh_x86_64, lldb_bl_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_rcx[] = { lldb_rcx_x86_64, lldb_ecx_x86_64, lldb_cx_x86_64, lldb_ch_x86_64, lldb_cl_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_rdx[] = { lldb_rdx_x86_64, lldb_edx_x86_64, lldb_dx_x86_64, lldb_dh_x86_64, lldb_dl_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_rdi[] = { lldb_rdi_x86_64, lldb_edi_x86_64, lldb_di_x86_64, lldb_dil_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_rsi[] = { lldb_rsi_x86_64, lldb_esi_x86_64, lldb_si_x86_64, lldb_sil_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_rbp[] = { lldb_rbp_x86_64, lldb_ebp_x86_64, lldb_bp_x86_64, lldb_bpl_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_rsp[] = { lldb_rsp_x86_64, lldb_esp_x86_64, lldb_sp_x86_64, lldb_spl_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r8[] = { lldb_r8_x86_64, lldb_r8d_x86_64, lldb_r8w_x86_64, lldb_r8l_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r9[] = { lldb_r9_x86_64, lldb_r9d_x86_64, lldb_r9w_x86_64, lldb_r9l_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r10[] = { lldb_r10_x86_64, lldb_r10d_x86_64, lldb_r10w_x86_64, lldb_r10l_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r11[] = { lldb_r11_x86_64, lldb_r11d_x86_64, lldb_r11w_x86_64, lldb_r11l_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r12[] = { lldb_r12_x86_64, lldb_r12d_x86_64, lldb_r12w_x86_64, lldb_r12l_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r13[] = { lldb_r13_x86_64, lldb_r13d_x86_64, lldb_r13w_x86_64, lldb_r13l_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r14[] = { lldb_r14_x86_64, lldb_r14d_x86_64, lldb_r14w_x86_64, lldb_r14l_x86_64, LLDB_INVALID_REGNUM }; +uint32_t RegisterContextPOSIX_x86::g_invalidate_r15[] = { lldb_r15_x86_64, lldb_r15d_x86_64, lldb_r15w_x86_64, lldb_r15l_x86_64, LLDB_INVALID_REGNUM }; // Number of register sets provided by this context. enum @@ -346,7 +346,7 @@ static const RegisterSet g_reg_sets_i386[k_num_register_sets] = { { "General Purpose Registers", "gpr", k_num_gpr_registers_i386, g_gpr_regnums_i386 }, - { "Floating Point Registers", "fpu", k_num_fpr_registers_i386, g_fpu_regnums_i386 }, + { "Floating Point Registers", "fpu", k_num_fpr_registers_i386, g_lldb_regnums_i386 }, { "Advanced Vector Extensions", "avx", k_num_avx_registers_i386, g_avx_regnums_i386 } }; @@ -354,7 +354,7 @@ static const RegisterSet g_reg_sets_x86_64[k_num_register_sets] = { { "General Purpose Registers", "gpr", k_num_gpr_registers_x86_64, g_gpr_regnums_x86_64 }, - { "Floating Point Registers", "fpu", k_num_fpr_registers_x86_64, g_fpu_regnums_x86_64 }, + { "Floating Point Registers", "fpu", k_num_fpr_registers_x86_64, g_lldb_regnums_x86_64 }, { "Advanced Vector Extensions", "avx", k_num_avx_registers_x86_64, g_avx_regnums_x86_64 } }; @@ -399,16 +399,16 @@ RegisterContextPOSIX_x86::RegisterContextPOSIX_x86(Thread &thread, m_reg_info.last_gpr = k_last_gpr_i386; m_reg_info.first_fpr = k_first_fpr_i386; m_reg_info.last_fpr = k_last_fpr_i386; - m_reg_info.first_st = fpu_st0_i386; - m_reg_info.last_st = fpu_st7_i386; - m_reg_info.first_mm = fpu_mm0_i386; - m_reg_info.last_mm = fpu_mm7_i386; - m_reg_info.first_xmm = fpu_xmm0_i386; - m_reg_info.last_xmm = fpu_xmm7_i386; - m_reg_info.first_ymm = fpu_ymm0_i386; - m_reg_info.last_ymm = fpu_ymm7_i386; - m_reg_info.first_dr = dr0_i386; - m_reg_info.gpr_flags = gpr_eflags_i386; + m_reg_info.first_st = lldb_st0_i386; + m_reg_info.last_st = lldb_st7_i386; + m_reg_info.first_mm = lldb_mm0_i386; + m_reg_info.last_mm = lldb_mm7_i386; + m_reg_info.first_xmm = lldb_xmm0_i386; + m_reg_info.last_xmm = lldb_xmm7_i386; + m_reg_info.first_ymm = lldb_ymm0_i386; + m_reg_info.last_ymm = lldb_ymm7_i386; + m_reg_info.first_dr = lldb_dr0_i386; + m_reg_info.gpr_flags = lldb_eflags_i386; break; case llvm::Triple::x86_64: m_reg_info.num_registers = k_num_registers_x86_64; @@ -418,16 +418,16 @@ RegisterContextPOSIX_x86::RegisterContextPOSIX_x86(Thread &thread, m_reg_info.last_gpr = k_last_gpr_x86_64; m_reg_info.first_fpr = k_first_fpr_x86_64; m_reg_info.last_fpr = k_last_fpr_x86_64; - m_reg_info.first_st = fpu_st0_x86_64; - m_reg_info.last_st = fpu_st7_x86_64; - m_reg_info.first_mm = fpu_mm0_x86_64; - m_reg_info.last_mm = fpu_mm7_x86_64; - m_reg_info.first_xmm = fpu_xmm0_x86_64; - m_reg_info.last_xmm = fpu_xmm15_x86_64; - m_reg_info.first_ymm = fpu_ymm0_x86_64; - m_reg_info.last_ymm = fpu_ymm15_x86_64; - m_reg_info.first_dr = dr0_x86_64; - m_reg_info.gpr_flags = gpr_rflags_x86_64; + m_reg_info.first_st = lldb_st0_x86_64; + m_reg_info.last_st = lldb_st7_x86_64; + m_reg_info.first_mm = lldb_mm0_x86_64; + m_reg_info.last_mm = lldb_mm7_x86_64; + m_reg_info.first_xmm = lldb_xmm0_x86_64; + m_reg_info.last_xmm = lldb_xmm15_x86_64; + m_reg_info.first_ymm = lldb_ymm0_x86_64; + m_reg_info.last_ymm = lldb_ymm15_x86_64; + m_reg_info.first_dr = lldb_dr0_x86_64; + m_reg_info.gpr_flags = lldb_rflags_x86_64; break; default: assert(false && "Unhandled target architecture."); diff --git a/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.h b/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.h index 4db7802e1b44..0eec1d909c1a 100644 --- a/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.h +++ b/source/Plugins/Process/Utility/RegisterContextPOSIX_x86.h @@ -11,285 +11,12 @@ #define liblldb_RegisterContextPOSIX_x86_H_ #include "lldb/Core/Log.h" +#include "lldb-x86-register-enums.h" #include "RegisterContextPOSIX.h" #include "RegisterContext_x86.h" class ProcessMonitor; -//--------------------------------------------------------------------------- -// Internal codes for all i386 registers. -//--------------------------------------------------------------------------- -enum -{ - k_first_gpr_i386, - gpr_eax_i386 = k_first_gpr_i386, - gpr_ebx_i386, - gpr_ecx_i386, - gpr_edx_i386, - gpr_edi_i386, - gpr_esi_i386, - gpr_ebp_i386, - gpr_esp_i386, - gpr_eip_i386, - gpr_eflags_i386, - gpr_cs_i386, - gpr_fs_i386, - gpr_gs_i386, - gpr_ss_i386, - gpr_ds_i386, - gpr_es_i386, - - k_first_alias_i386, - gpr_ax_i386 = k_first_alias_i386, - gpr_bx_i386, - gpr_cx_i386, - gpr_dx_i386, - gpr_di_i386, - gpr_si_i386, - gpr_bp_i386, - gpr_sp_i386, - gpr_ah_i386, - gpr_bh_i386, - gpr_ch_i386, - gpr_dh_i386, - gpr_al_i386, - gpr_bl_i386, - gpr_cl_i386, - gpr_dl_i386, - k_last_alias_i386 = gpr_dl_i386, - - k_last_gpr_i386 = k_last_alias_i386, - - k_first_fpr_i386, - fpu_fctrl_i386 = k_first_fpr_i386, - fpu_fstat_i386, - fpu_ftag_i386, - fpu_fop_i386, - fpu_fiseg_i386, - fpu_fioff_i386, - fpu_foseg_i386, - fpu_fooff_i386, - fpu_mxcsr_i386, - fpu_mxcsrmask_i386, - fpu_st0_i386, - fpu_st1_i386, - fpu_st2_i386, - fpu_st3_i386, - fpu_st4_i386, - fpu_st5_i386, - fpu_st6_i386, - fpu_st7_i386, - fpu_mm0_i386, - fpu_mm1_i386, - fpu_mm2_i386, - fpu_mm3_i386, - fpu_mm4_i386, - fpu_mm5_i386, - fpu_mm6_i386, - fpu_mm7_i386, - fpu_xmm0_i386, - fpu_xmm1_i386, - fpu_xmm2_i386, - fpu_xmm3_i386, - fpu_xmm4_i386, - fpu_xmm5_i386, - fpu_xmm6_i386, - fpu_xmm7_i386, - k_last_fpr_i386 = fpu_xmm7_i386, - - k_first_avx_i386, - fpu_ymm0_i386 = k_first_avx_i386, - fpu_ymm1_i386, - fpu_ymm2_i386, - fpu_ymm3_i386, - fpu_ymm4_i386, - fpu_ymm5_i386, - fpu_ymm6_i386, - fpu_ymm7_i386, - k_last_avx_i386 = fpu_ymm7_i386, - - dr0_i386, - dr1_i386, - dr2_i386, - dr3_i386, - dr4_i386, - dr5_i386, - dr6_i386, - dr7_i386, - - k_num_registers_i386, - k_num_gpr_registers_i386 = k_last_gpr_i386 - k_first_gpr_i386 + 1, - k_num_fpr_registers_i386 = k_last_fpr_i386 - k_first_fpr_i386 + 1, - k_num_avx_registers_i386 = k_last_avx_i386 - k_first_avx_i386 + 1 -}; - -//--------------------------------------------------------------------------- -// Internal codes for all x86_64 registers. -//--------------------------------------------------------------------------- -enum -{ - k_first_gpr_x86_64, - gpr_rax_x86_64 = k_first_gpr_x86_64, - gpr_rbx_x86_64, - gpr_rcx_x86_64, - gpr_rdx_x86_64, - gpr_rdi_x86_64, - gpr_rsi_x86_64, - gpr_rbp_x86_64, - gpr_rsp_x86_64, - gpr_r8_x86_64, - gpr_r9_x86_64, - gpr_r10_x86_64, - gpr_r11_x86_64, - gpr_r12_x86_64, - gpr_r13_x86_64, - gpr_r14_x86_64, - gpr_r15_x86_64, - gpr_rip_x86_64, - gpr_rflags_x86_64, - gpr_cs_x86_64, - gpr_fs_x86_64, - gpr_gs_x86_64, - gpr_ss_x86_64, - gpr_ds_x86_64, - gpr_es_x86_64, - - k_first_alias_x86_64, - gpr_eax_x86_64 = k_first_alias_x86_64, - gpr_ebx_x86_64, - gpr_ecx_x86_64, - gpr_edx_x86_64, - gpr_edi_x86_64, - gpr_esi_x86_64, - gpr_ebp_x86_64, - gpr_esp_x86_64, - gpr_r8d_x86_64, // Low 32 bits of r8 - gpr_r9d_x86_64, // Low 32 bits of r9 - gpr_r10d_x86_64, // Low 32 bits of r10 - gpr_r11d_x86_64, // Low 32 bits of r11 - gpr_r12d_x86_64, // Low 32 bits of r12 - gpr_r13d_x86_64, // Low 32 bits of r13 - gpr_r14d_x86_64, // Low 32 bits of r14 - gpr_r15d_x86_64, // Low 32 bits of r15 - gpr_ax_x86_64, - gpr_bx_x86_64, - gpr_cx_x86_64, - gpr_dx_x86_64, - gpr_di_x86_64, - gpr_si_x86_64, - gpr_bp_x86_64, - gpr_sp_x86_64, - gpr_r8w_x86_64, // Low 16 bits of r8 - gpr_r9w_x86_64, // Low 16 bits of r9 - gpr_r10w_x86_64, // Low 16 bits of r10 - gpr_r11w_x86_64, // Low 16 bits of r11 - gpr_r12w_x86_64, // Low 16 bits of r12 - gpr_r13w_x86_64, // Low 16 bits of r13 - gpr_r14w_x86_64, // Low 16 bits of r14 - gpr_r15w_x86_64, // Low 16 bits of r15 - gpr_ah_x86_64, - gpr_bh_x86_64, - gpr_ch_x86_64, - gpr_dh_x86_64, - gpr_al_x86_64, - gpr_bl_x86_64, - gpr_cl_x86_64, - gpr_dl_x86_64, - gpr_dil_x86_64, - gpr_sil_x86_64, - gpr_bpl_x86_64, - gpr_spl_x86_64, - gpr_r8l_x86_64, // Low 8 bits of r8 - gpr_r9l_x86_64, // Low 8 bits of r9 - gpr_r10l_x86_64, // Low 8 bits of r10 - gpr_r11l_x86_64, // Low 8 bits of r11 - gpr_r12l_x86_64, // Low 8 bits of r12 - gpr_r13l_x86_64, // Low 8 bits of r13 - gpr_r14l_x86_64, // Low 8 bits of r14 - gpr_r15l_x86_64, // Low 8 bits of r15 - k_last_alias_x86_64 = gpr_r15l_x86_64, - - k_last_gpr_x86_64 = k_last_alias_x86_64, - - k_first_fpr_x86_64, - fpu_fctrl_x86_64 = k_first_fpr_x86_64, - fpu_fstat_x86_64, - fpu_ftag_x86_64, - fpu_fop_x86_64, - fpu_fiseg_x86_64, - fpu_fioff_x86_64, - fpu_foseg_x86_64, - fpu_fooff_x86_64, - fpu_mxcsr_x86_64, - fpu_mxcsrmask_x86_64, - fpu_st0_x86_64, - fpu_st1_x86_64, - fpu_st2_x86_64, - fpu_st3_x86_64, - fpu_st4_x86_64, - fpu_st5_x86_64, - fpu_st6_x86_64, - fpu_st7_x86_64, - fpu_mm0_x86_64, - fpu_mm1_x86_64, - fpu_mm2_x86_64, - fpu_mm3_x86_64, - fpu_mm4_x86_64, - fpu_mm5_x86_64, - fpu_mm6_x86_64, - fpu_mm7_x86_64, - fpu_xmm0_x86_64, - fpu_xmm1_x86_64, - fpu_xmm2_x86_64, - fpu_xmm3_x86_64, - fpu_xmm4_x86_64, - fpu_xmm5_x86_64, - fpu_xmm6_x86_64, - fpu_xmm7_x86_64, - fpu_xmm8_x86_64, - fpu_xmm9_x86_64, - fpu_xmm10_x86_64, - fpu_xmm11_x86_64, - fpu_xmm12_x86_64, - fpu_xmm13_x86_64, - fpu_xmm14_x86_64, - fpu_xmm15_x86_64, - k_last_fpr_x86_64 = fpu_xmm15_x86_64, - - k_first_avx_x86_64, - fpu_ymm0_x86_64 = k_first_avx_x86_64, - fpu_ymm1_x86_64, - fpu_ymm2_x86_64, - fpu_ymm3_x86_64, - fpu_ymm4_x86_64, - fpu_ymm5_x86_64, - fpu_ymm6_x86_64, - fpu_ymm7_x86_64, - fpu_ymm8_x86_64, - fpu_ymm9_x86_64, - fpu_ymm10_x86_64, - fpu_ymm11_x86_64, - fpu_ymm12_x86_64, - fpu_ymm13_x86_64, - fpu_ymm14_x86_64, - fpu_ymm15_x86_64, - k_last_avx_x86_64 = fpu_ymm15_x86_64, - - dr0_x86_64, - dr1_x86_64, - dr2_x86_64, - dr3_x86_64, - dr4_x86_64, - dr5_x86_64, - dr6_x86_64, - dr7_x86_64, - - k_num_registers_x86_64, - k_num_gpr_registers_x86_64 = k_last_gpr_x86_64 - k_first_gpr_x86_64 + 1, - k_num_fpr_registers_x86_64 = k_last_fpr_x86_64 - k_first_fpr_x86_64 + 1, - k_num_avx_registers_x86_64 = k_last_avx_x86_64 - k_first_avx_x86_64 + 1 -}; - class RegisterContextPOSIX_x86 : public lldb_private::RegisterContext { @@ -422,7 +149,7 @@ protected: uint32_t gpr_flags; }; - uint64_t m_gpr_x86_64[k_num_gpr_registers_x86_64]; // 64-bit general purpose registers. + uint64_t m_gpr_x86_64[lldb_private::k_num_gpr_registers_x86_64]; // 64-bit general purpose registers. RegInfo m_reg_info; FPRType m_fpr_type; // determines the type of data stored by union FPR, if any. FPR m_fpr; // floating-point registers including extended register sets. diff --git a/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp b/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp index 46dafa11d8f7..200ef4d3d651 100644 --- a/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp +++ b/source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp @@ -59,7 +59,7 @@ RegisterContextThreadMemory::UpdateRegisterContext () { OperatingSystem *os = process_sp->GetOperatingSystem (); if (os->IsOperatingSystemPluginThread (thread_sp)) - m_reg_ctx_sp = os->CreateRegisterContextForThread (thread_sp.get(), LLDB_INVALID_ADDRESS); + m_reg_ctx_sp = os->CreateRegisterContextForThread (thread_sp.get(), m_register_data_addr); } } } diff --git a/source/Plugins/Process/Utility/RegisterContext_powerpc.h b/source/Plugins/Process/Utility/RegisterContext_powerpc.h new file mode 100644 index 000000000000..cf54cc0c2145 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterContext_powerpc.h @@ -0,0 +1,163 @@ +//===-- RegisterContext_powerpc.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContext_powerpc_H_ +#define liblldb_RegisterContext_powerpc_H_ + +// GCC and DWARF Register numbers (eRegisterKindGCC & eRegisterKindDWARF) +enum +{ + gcc_dwarf_r0_powerpc = 0, + gcc_dwarf_r1_powerpc, + gcc_dwarf_r2_powerpc, + gcc_dwarf_r3_powerpc, + gcc_dwarf_r4_powerpc, + gcc_dwarf_r5_powerpc, + gcc_dwarf_r6_powerpc, + gcc_dwarf_r7_powerpc, + gcc_dwarf_r8_powerpc, + gcc_dwarf_r9_powerpc, + gcc_dwarf_r10_powerpc, + gcc_dwarf_r11_powerpc, + gcc_dwarf_r12_powerpc, + gcc_dwarf_r13_powerpc, + gcc_dwarf_r14_powerpc, + gcc_dwarf_r15_powerpc, + gcc_dwarf_r16_powerpc, + gcc_dwarf_r17_powerpc, + gcc_dwarf_r18_powerpc, + gcc_dwarf_r19_powerpc, + gcc_dwarf_r20_powerpc, + gcc_dwarf_r21_powerpc, + gcc_dwarf_r22_powerpc, + gcc_dwarf_r23_powerpc, + gcc_dwarf_r24_powerpc, + gcc_dwarf_r25_powerpc, + gcc_dwarf_r26_powerpc, + gcc_dwarf_r27_powerpc, + gcc_dwarf_r28_powerpc, + gcc_dwarf_r29_powerpc, + gcc_dwarf_r30_powerpc, + gcc_dwarf_r31_powerpc, + gcc_dwarf_f0_powerpc, + gcc_dwarf_f1_powerpc, + gcc_dwarf_f2_powerpc, + gcc_dwarf_f3_powerpc, + gcc_dwarf_f4_powerpc, + gcc_dwarf_f5_powerpc, + gcc_dwarf_f6_powerpc, + gcc_dwarf_f7_powerpc, + gcc_dwarf_f8_powerpc, + gcc_dwarf_f9_powerpc, + gcc_dwarf_f10_powerpc, + gcc_dwarf_f11_powerpc, + gcc_dwarf_f12_powerpc, + gcc_dwarf_f13_powerpc, + gcc_dwarf_f14_powerpc, + gcc_dwarf_f15_powerpc, + gcc_dwarf_f16_powerpc, + gcc_dwarf_f17_powerpc, + gcc_dwarf_f18_powerpc, + gcc_dwarf_f19_powerpc, + gcc_dwarf_f20_powerpc, + gcc_dwarf_f21_powerpc, + gcc_dwarf_f22_powerpc, + gcc_dwarf_f23_powerpc, + gcc_dwarf_f24_powerpc, + gcc_dwarf_f25_powerpc, + gcc_dwarf_f26_powerpc, + gcc_dwarf_f27_powerpc, + gcc_dwarf_f28_powerpc, + gcc_dwarf_f29_powerpc, + gcc_dwarf_f30_powerpc, + gcc_dwarf_f31_powerpc, + gcc_dwarf_cr_powerpc, + gcc_dwarf_fpscr_powerpc, + gcc_dwarf_xer_powerpc = 101, + gcc_dwarf_lr_powerpc = 108, + gcc_dwarf_ctr_powerpc, + gcc_dwarf_pc_powerpc, +}; + +// GDB Register numbers (eRegisterKindGDB) +enum +{ + gdb_r0_powerpc = 0, + gdb_r1_powerpc, + gdb_r2_powerpc, + gdb_r3_powerpc, + gdb_r4_powerpc, + gdb_r5_powerpc, + gdb_r6_powerpc, + gdb_r7_powerpc, + gdb_r8_powerpc, + gdb_r9_powerpc, + gdb_r10_powerpc, + gdb_r11_powerpc, + gdb_r12_powerpc, + gdb_r13_powerpc, + gdb_r14_powerpc, + gdb_r15_powerpc, + gdb_r16_powerpc, + gdb_r17_powerpc, + gdb_r18_powerpc, + gdb_r19_powerpc, + gdb_r20_powerpc, + gdb_r21_powerpc, + gdb_r22_powerpc, + gdb_r23_powerpc, + gdb_r24_powerpc, + gdb_r25_powerpc, + gdb_r26_powerpc, + gdb_r27_powerpc, + gdb_r28_powerpc, + gdb_r29_powerpc, + gdb_r30_powerpc, + gdb_r31_powerpc, + gdb_f0_powerpc, + gdb_f1_powerpc, + gdb_f2_powerpc, + gdb_f3_powerpc, + gdb_f4_powerpc, + gdb_f5_powerpc, + gdb_f6_powerpc, + gdb_f7_powerpc, + gdb_f8_powerpc, + gdb_f9_powerpc, + gdb_f10_powerpc, + gdb_f11_powerpc, + gdb_f12_powerpc, + gdb_f13_powerpc, + gdb_f14_powerpc, + gdb_f15_powerpc, + gdb_f16_powerpc, + gdb_f17_powerpc, + gdb_f18_powerpc, + gdb_f19_powerpc, + gdb_f20_powerpc, + gdb_f21_powerpc, + gdb_f22_powerpc, + gdb_f23_powerpc, + gdb_f24_powerpc, + gdb_f25_powerpc, + gdb_f26_powerpc, + gdb_f27_powerpc, + gdb_f28_powerpc, + gdb_f29_powerpc, + gdb_f30_powerpc, + gdb_f31_powerpc, + gdb_cr_powerpc, + gdb_fpscr_powerpc, + gdb_xer_powerpc = 101, + gdb_lr_powerpc = 108, + gdb_ctr_powerpc, + gdb_pc_powerpc, +}; + +#endif // liblldb_RegisterContext_powerpc_H_ diff --git a/source/Plugins/Process/Utility/RegisterInfos_arm64.h b/source/Plugins/Process/Utility/RegisterInfos_arm64.h index 1bb4e89c8f78..b687423622a4 100644 --- a/source/Plugins/Process/Utility/RegisterInfos_arm64.h +++ b/source/Plugins/Process/Utility/RegisterInfos_arm64.h @@ -15,8 +15,8 @@ #include "lldb/lldb-defines.h" #include "lldb/lldb-enumerations.h" -#include "ARM64_GCC_Registers.h" -#include "ARM64_DWARF_Registers.h" +#include "Utility/ARM64_GCC_Registers.h" +#include "Utility/ARM64_DWARF_Registers.h" #ifndef GPR_OFFSET #error GPR_OFFSET must be defined before including this header file diff --git a/source/Plugins/Process/Utility/RegisterInfos_i386.h b/source/Plugins/Process/Utility/RegisterInfos_i386.h index fa152b4147ce..fc94b8b2a738 100644 --- a/source/Plugins/Process/Utility/RegisterInfos_i386.h +++ b/source/Plugins/Process/Utility/RegisterInfos_i386.h @@ -42,37 +42,37 @@ // Note that the size and offset will be updated by platform-specific classes. #define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ { #reg, alt, sizeof(((GPR*)NULL)->reg), GPR_OFFSET(reg), eEncodingUint, \ - eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg##_i386 }, NULL, NULL } + eFormatHex, { kind1, kind2, kind3, kind4, lldb_##reg##_i386 }, NULL, NULL } #define DEFINE_FPR(name, reg, kind1, kind2, kind3, kind4) \ { #name, NULL, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, \ - eFormatHex, { kind1, kind2, kind3, kind4, fpu_##name##_i386 }, NULL, NULL } + eFormatHex, { kind1, kind2, kind3, kind4, lldb_##name##_i386 }, NULL, NULL } // RegisterKind: GCC, DWARF, Generic, GDB, LLDB #define DEFINE_FP_ST(reg, i) \ { #reg#i, NULL, FP_SIZE, LLVM_EXTENSION FPR_OFFSET(stmm[i]), \ eEncodingVector, eFormatVectorOfUInt8, \ - { gcc_st##i##_i386, dwarf_st##i##_i386, LLDB_INVALID_REGNUM, gdb_st##i##_i386, fpu_st##i##_i386 }, \ + { gcc_st##i##_i386, dwarf_st##i##_i386, LLDB_INVALID_REGNUM, gdb_st##i##_i386, lldb_st##i##_i386 }, \ NULL, NULL } #define DEFINE_FP_MM(reg, i) \ { #reg#i, NULL, sizeof(uint64_t), LLVM_EXTENSION FPR_OFFSET(stmm[i]), \ eEncodingUint, eFormatHex, \ - { gcc_mm##i##_i386, dwarf_mm##i##_i386, LLDB_INVALID_REGNUM, gdb_mm##i##_i386, fpu_mm##i##_i386 }, \ + { gcc_mm##i##_i386, dwarf_mm##i##_i386, LLDB_INVALID_REGNUM, gdb_mm##i##_i386, lldb_mm##i##_i386 }, \ NULL, NULL } #define DEFINE_XMM(reg, i) \ { #reg#i, NULL, XMM_SIZE, LLVM_EXTENSION FPR_OFFSET(reg[i]), \ eEncodingVector, eFormatVectorOfUInt8, \ - { gcc_##reg##i##_i386, dwarf_##reg##i##_i386, LLDB_INVALID_REGNUM, gdb_##reg##i##_i386, fpu_##reg##i##_i386}, \ + { gcc_##reg##i##_i386, dwarf_##reg##i##_i386, LLDB_INVALID_REGNUM, gdb_##reg##i##_i386, lldb_##reg##i##_i386}, \ NULL, NULL } // I believe the YMM registers use dwarf_xmm_%_i386 register numbers and then differentiate based on register size. #define DEFINE_YMM(reg, i) \ { #reg#i, NULL, YMM_SIZE, LLVM_EXTENSION YMM_OFFSET(reg[i]), \ eEncodingVector, eFormatVectorOfUInt8, \ - { LLDB_INVALID_REGNUM, dwarf_xmm##i##_i386, LLDB_INVALID_REGNUM, gdb_##reg##i##h_i386, fpu_##reg##i##_i386 }, \ + { LLDB_INVALID_REGNUM, dwarf_xmm##i##_i386, LLDB_INVALID_REGNUM, gdb_##reg##i##h_i386, lldb_##reg##i##_i386 }, \ NULL, NULL } #define DEFINE_DR(reg, i) \ @@ -82,13 +82,13 @@ #define DEFINE_GPR_PSEUDO_16(reg16, reg32) \ { #reg16, NULL, 2, GPR_OFFSET(reg32), eEncodingUint, \ - eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_##reg16##_i386 }, RegisterContextPOSIX_x86::g_contained_##reg32, RegisterContextPOSIX_x86::g_invalidate_##reg32 } + eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg16##_i386 }, RegisterContextPOSIX_x86::g_contained_##reg32, RegisterContextPOSIX_x86::g_invalidate_##reg32 } #define DEFINE_GPR_PSEUDO_8H(reg8, reg32) \ { #reg8, NULL, 1, GPR_OFFSET(reg32)+1, eEncodingUint, \ - eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_##reg8##_i386 }, RegisterContextPOSIX_x86::g_contained_##reg32, RegisterContextPOSIX_x86::g_invalidate_##reg32 } + eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg8##_i386 }, RegisterContextPOSIX_x86::g_contained_##reg32, RegisterContextPOSIX_x86::g_invalidate_##reg32 } #define DEFINE_GPR_PSEUDO_8L(reg8, reg32) \ { #reg8, NULL, 1, GPR_OFFSET(reg32), eEncodingUint, \ - eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_##reg8##_i386 }, RegisterContextPOSIX_x86::g_contained_##reg32, RegisterContextPOSIX_x86::g_invalidate_##reg32 } + eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg8##_i386 }, RegisterContextPOSIX_x86::g_contained_##reg32, RegisterContextPOSIX_x86::g_invalidate_##reg32 } static RegisterInfo g_register_infos_i386[] = diff --git a/source/Plugins/Process/Utility/RegisterInfos_powerpc.h b/source/Plugins/Process/Utility/RegisterInfos_powerpc.h new file mode 100644 index 000000000000..045426648105 --- /dev/null +++ b/source/Plugins/Process/Utility/RegisterInfos_powerpc.h @@ -0,0 +1,144 @@ +//===-- RegisterInfos_powerpc.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#include <stddef.h> + +// Computes the offset of the given GPR in the user data area. +#define GPR_OFFSET(regname) \ + (offsetof(GPR, regname)) +#define FPR_OFFSET(regname) \ + (offsetof(FPR, regname)) +#define GPR_SIZE(regname) \ + (sizeof(((GPR*)NULL)->regname)) + +#ifdef DECLARE_REGISTER_INFOS_POWERPC_STRUCT + +// Note that the size and offset will be updated by platform-specific classes. +#define DEFINE_GPR(reg, alt, lldb_kind) \ + { #reg, alt, GPR_SIZE(reg), GPR_OFFSET(reg), eEncodingUint, \ + eFormatHex, { gcc_dwarf_##reg##_powerpc, gcc_dwarf_##reg##_powerpc, lldb_kind, gdb_##reg##_powerpc, gpr_##reg##_powerpc }, NULL, NULL } +#define DEFINE_FPR(reg, lldb_kind) \ + { #reg, NULL, 8, FPR_OFFSET(reg), eEncodingIEEE754, \ + eFormatFloat, { gcc_dwarf_##reg##_powerpc, gcc_dwarf_##reg##_powerpc, lldb_kind, gdb_##reg##_powerpc, fpr_##reg##_powerpc }, NULL, NULL } + + // General purpose registers. GCC, DWARF, Generic, GDB +#define POWERPC_REGS \ + DEFINE_GPR(r0, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r1, "sp", LLDB_REGNUM_GENERIC_SP), \ + DEFINE_GPR(r2, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r3, "arg1",LLDB_REGNUM_GENERIC_ARG1), \ + DEFINE_GPR(r4, "arg2",LLDB_REGNUM_GENERIC_ARG2), \ + DEFINE_GPR(r5, "arg3",LLDB_REGNUM_GENERIC_ARG3), \ + DEFINE_GPR(r6, "arg4",LLDB_REGNUM_GENERIC_ARG4), \ + DEFINE_GPR(r7, "arg5",LLDB_REGNUM_GENERIC_ARG5), \ + DEFINE_GPR(r8, "arg6",LLDB_REGNUM_GENERIC_ARG6), \ + DEFINE_GPR(r9, "arg7",LLDB_REGNUM_GENERIC_ARG7), \ + DEFINE_GPR(r10, "arg8",LLDB_REGNUM_GENERIC_ARG8), \ + DEFINE_GPR(r11, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r12, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r13, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r14, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r15, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r16, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r17, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r18, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r19, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r20, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r21, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r22, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r23, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r24, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r25, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r26, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r27, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r28, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r29, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r30, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(r31, NULL, LLDB_INVALID_REGNUM), \ + DEFINE_GPR(lr, "lr", LLDB_REGNUM_GENERIC_RA), \ + DEFINE_GPR(cr, "cr", LLDB_REGNUM_GENERIC_FLAGS), \ + DEFINE_GPR(xer, "xer", LLDB_INVALID_REGNUM), \ + DEFINE_GPR(ctr, "ctr", LLDB_INVALID_REGNUM), \ + DEFINE_GPR(pc, "pc", LLDB_REGNUM_GENERIC_PC), \ + DEFINE_FPR(f0, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f1, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f2, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f3, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f4, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f5, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f6, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f7, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f8, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f9, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f10, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f11, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f12, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f13, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f14, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f15, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f16, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f17, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f18, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f19, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f20, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f21, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f22, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f23, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f24, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f25, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f26, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f27, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f28, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f29, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f30, LLDB_INVALID_REGNUM), \ + DEFINE_FPR(f31, LLDB_INVALID_REGNUM), \ + { "fpscr", NULL, 8, FPR_OFFSET(fpscr), eEncodingUint, eFormatHex, { gcc_dwarf_fpscr_powerpc, gcc_dwarf_fpscr_powerpc, LLDB_INVALID_REGNUM, gdb_fpscr_powerpc, fpr_fpscr_powerpc }, NULL, NULL }, +static RegisterInfo +g_register_infos_powerpc64[] = +{ +#define GPR GPR64 + POWERPC_REGS +#undef GPR +}; + +static RegisterInfo +g_register_infos_powerpc32[] = +{ +#define GPR GPR32 + POWERPC_REGS +#undef GPR +}; + +static RegisterInfo +g_register_infos_powerpc64_32[] = +{ +#define GPR GPR64 +#undef GPR_SIZE +#define GPR_SIZE(reg) (sizeof(uint32_t)) +#undef GPR_OFFSET +#define GPR_OFFSET(regname) \ + (offsetof(GPR, regname) + (sizeof(((GPR *)NULL)->regname) - GPR_SIZE(reg))) + POWERPC_REGS +#undef GPR +}; + +static_assert((sizeof(g_register_infos_powerpc32) / sizeof(g_register_infos_powerpc32[0])) == k_num_registers_powerpc, + "g_register_infos_powerpc32 has wrong number of register infos"); +static_assert((sizeof(g_register_infos_powerpc64) / sizeof(g_register_infos_powerpc64[0])) == k_num_registers_powerpc, + "g_register_infos_powerpc64 has wrong number of register infos"); +static_assert(sizeof(g_register_infos_powerpc64_32) == sizeof(g_register_infos_powerpc64), + "g_register_infos_powerpc64_32 doesn't match size of g_register_infos_powerpc64"); + +#undef DEFINE_FPR +#undef DEFINE_GPR + +#endif // DECLARE_REGISTER_INFOS_POWERPC_STRUCT + +#undef GPR_OFFSET + diff --git a/source/Plugins/Process/Utility/RegisterInfos_x86_64.h b/source/Plugins/Process/Utility/RegisterInfos_x86_64.h index c4dc6041ce43..c1bcd27053c6 100644 --- a/source/Plugins/Process/Utility/RegisterInfos_x86_64.h +++ b/source/Plugins/Process/Utility/RegisterInfos_x86_64.h @@ -42,34 +42,34 @@ // Note that the size and offset will be updated by platform-specific classes. #define DEFINE_GPR(reg, alt, kind1, kind2, kind3, kind4) \ { #reg, alt, sizeof(((GPR*)NULL)->reg), GPR_OFFSET(reg), eEncodingUint, \ - eFormatHex, { kind1, kind2, kind3, kind4, gpr_##reg##_x86_64 }, NULL, NULL } + eFormatHex, { kind1, kind2, kind3, kind4, lldb_##reg##_x86_64 }, NULL, NULL } #define DEFINE_FPR(name, reg, kind1, kind2, kind3, kind4) \ { #name, NULL, FPR_SIZE(reg), FPR_OFFSET(reg), eEncodingUint, \ - eFormatHex, { kind1, kind2, kind3, kind4, fpu_##name##_x86_64 }, NULL, NULL } + eFormatHex, { kind1, kind2, kind3, kind4, lldb_##name##_x86_64 }, NULL, NULL } #define DEFINE_FP_ST(reg, i) \ { #reg#i, NULL, FP_SIZE, LLVM_EXTENSION FPR_OFFSET(stmm[i]), \ eEncodingVector, eFormatVectorOfUInt8, \ - { gcc_dwarf_st##i##_x86_64, gcc_dwarf_st##i##_x86_64, LLDB_INVALID_REGNUM, gdb_st##i##_x86_64, fpu_st##i##_x86_64 }, \ + { gcc_dwarf_st##i##_x86_64, gcc_dwarf_st##i##_x86_64, LLDB_INVALID_REGNUM, gdb_st##i##_x86_64, lldb_st##i##_x86_64 }, \ NULL, NULL } #define DEFINE_FP_MM(reg, i) \ { #reg#i, NULL, sizeof(uint64_t), LLVM_EXTENSION FPR_OFFSET(stmm[i]), \ eEncodingUint, eFormatHex, \ - { gcc_dwarf_mm##i##_x86_64, gcc_dwarf_mm##i##_x86_64, LLDB_INVALID_REGNUM, gdb_st##i##_x86_64, fpu_mm##i##_x86_64 }, \ + { gcc_dwarf_mm##i##_x86_64, gcc_dwarf_mm##i##_x86_64, LLDB_INVALID_REGNUM, gdb_st##i##_x86_64, lldb_mm##i##_x86_64 }, \ NULL, NULL } #define DEFINE_XMM(reg, i) \ { #reg#i, NULL, XMM_SIZE, LLVM_EXTENSION FPR_OFFSET(reg[i]), \ eEncodingVector, eFormatVectorOfUInt8, \ - { gcc_dwarf_##reg##i##_x86_64, gcc_dwarf_##reg##i##_x86_64, LLDB_INVALID_REGNUM, gdb_##reg##i##_x86_64, fpu_##reg##i##_x86_64}, \ + { gcc_dwarf_##reg##i##_x86_64, gcc_dwarf_##reg##i##_x86_64, LLDB_INVALID_REGNUM, gdb_##reg##i##_x86_64, lldb_##reg##i##_x86_64}, \ NULL, NULL } #define DEFINE_YMM(reg, i) \ { #reg#i, NULL, YMM_SIZE, LLVM_EXTENSION YMM_OFFSET(reg[i]), \ eEncodingVector, eFormatVectorOfUInt8, \ - { gcc_dwarf_##reg##i##h_x86_64, gcc_dwarf_##reg##i##h_x86_64, LLDB_INVALID_REGNUM, gdb_##reg##i##h_x86_64, fpu_##reg##i##_x86_64 }, \ + { gcc_dwarf_##reg##i##h_x86_64, gcc_dwarf_##reg##i##h_x86_64, LLDB_INVALID_REGNUM, gdb_##reg##i##h_x86_64, lldb_##reg##i##_x86_64 }, \ NULL, NULL } #define DEFINE_DR(reg, i) \ @@ -79,16 +79,16 @@ #define DEFINE_GPR_PSEUDO_32(reg32, reg64) \ { #reg32, NULL, 4, GPR_OFFSET(reg64), eEncodingUint, \ - eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_##reg32##_x86_64 }, RegisterContextPOSIX_x86::g_contained_##reg64, RegisterContextPOSIX_x86::g_invalidate_##reg64 } + eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg32##_x86_64 }, RegisterContextPOSIX_x86::g_contained_##reg64, RegisterContextPOSIX_x86::g_invalidate_##reg64 } #define DEFINE_GPR_PSEUDO_16(reg16, reg64) \ { #reg16, NULL, 2, GPR_OFFSET(reg64), eEncodingUint, \ - eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_##reg16##_x86_64 }, RegisterContextPOSIX_x86::g_contained_##reg64, RegisterContextPOSIX_x86::g_invalidate_##reg64 } + eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg16##_x86_64 }, RegisterContextPOSIX_x86::g_contained_##reg64, RegisterContextPOSIX_x86::g_invalidate_##reg64 } #define DEFINE_GPR_PSEUDO_8H(reg8, reg64) \ { #reg8, NULL, 1, GPR_OFFSET(reg64)+1, eEncodingUint, \ - eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_##reg8##_x86_64 }, RegisterContextPOSIX_x86::g_contained_##reg64, RegisterContextPOSIX_x86::g_invalidate_##reg64 } + eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg8##_x86_64 }, RegisterContextPOSIX_x86::g_contained_##reg64, RegisterContextPOSIX_x86::g_invalidate_##reg64 } #define DEFINE_GPR_PSEUDO_8L(reg8, reg64) \ { #reg8, NULL, 1, GPR_OFFSET(reg64), eEncodingUint, \ - eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, gpr_##reg8##_x86_64 }, RegisterContextPOSIX_x86::g_contained_##reg64, RegisterContextPOSIX_x86::g_invalidate_##reg64 } + eFormatHex, { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, lldb_##reg8##_x86_64 }, RegisterContextPOSIX_x86::g_contained_##reg64, RegisterContextPOSIX_x86::g_invalidate_##reg64 } static RegisterInfo g_register_infos_x86_64[] = @@ -273,37 +273,37 @@ static_assert((sizeof(g_register_infos_x86_64) / sizeof(g_register_infos_x86_64[ #define UPDATE_GPR_INFO(reg, reg64) \ do { \ - g_register_infos[gpr_##reg##_i386].byte_offset = GPR_OFFSET(reg64); \ + g_register_infos[lldb_##reg##_i386].byte_offset = GPR_OFFSET(reg64); \ } while(false); #define UPDATE_GPR_INFO_8H(reg, reg64) \ do { \ - g_register_infos[gpr_##reg##_i386].byte_offset = GPR_OFFSET(reg64) + 1; \ + g_register_infos[lldb_##reg##_i386].byte_offset = GPR_OFFSET(reg64) + 1; \ } while(false); #define UPDATE_FPR_INFO(reg, reg64) \ do { \ - g_register_infos[fpu_##reg##_i386].byte_offset = FPR_OFFSET(reg64); \ + g_register_infos[lldb_##reg##_i386].byte_offset = FPR_OFFSET(reg64); \ } while(false); #define UPDATE_FP_INFO(reg, i) \ do { \ - g_register_infos[fpu_##reg##i##_i386].byte_offset = FPR_OFFSET(stmm[i]); \ + g_register_infos[lldb_##reg##i##_i386].byte_offset = FPR_OFFSET(stmm[i]); \ } while(false); #define UPDATE_XMM_INFO(reg, i) \ do { \ - g_register_infos[fpu_##reg##i##_i386].byte_offset = FPR_OFFSET(reg[i]); \ + g_register_infos[lldb_##reg##i##_i386].byte_offset = FPR_OFFSET(reg[i]); \ } while(false); #define UPDATE_YMM_INFO(reg, i) \ do { \ - g_register_infos[fpu_##reg##i##_i386].byte_offset = YMM_OFFSET(reg[i]); \ + g_register_infos[lldb_##reg##i##_i386].byte_offset = YMM_OFFSET(reg[i]); \ } while(false); #define UPDATE_DR_INFO(reg_index) \ do { \ - g_register_infos[dr##reg_index##_i386].byte_offset = DR_OFFSET(reg_index); \ + g_register_infos[lldb_dr##reg_index##_i386].byte_offset = DR_OFFSET(reg_index); \ } while(false); // Update the register offsets diff --git a/source/Plugins/Process/Utility/StopInfoMachException.cpp b/source/Plugins/Process/Utility/StopInfoMachException.cpp index 0e3e559aef5c..a69b38b6c93e 100644 --- a/source/Plugins/Process/Utility/StopInfoMachException.cpp +++ b/source/Plugins/Process/Utility/StopInfoMachException.cpp @@ -423,9 +423,11 @@ StopInfoMachException::CreateStopReasonWithMachException wp_sp->SetHardwareIndex((uint32_t)exc_sub_sub_code); return StopInfo::CreateStopReasonWithWatchpointID(thread, wp_sp->GetID()); } - // EXC_ARM_DA_DEBUG seems to be reused for EXC_BREAKPOINT as well as EXC_BAD_ACCESS - if (thread.GetTemporaryResumeState() == eStateStepping) - return StopInfo::CreateStopReasonToTrace(thread); + else + { + is_actual_breakpoint = true; + is_trace_if_actual_breakpoint_missing = true; + } } else if (exc_code == 1) // EXC_ARM_BREAKPOINT { diff --git a/source/Plugins/Process/Utility/UnwindLLDB.cpp b/source/Plugins/Process/Utility/UnwindLLDB.cpp index 37fd4f489552..fc592e60d86d 100644 --- a/source/Plugins/Process/Utility/UnwindLLDB.cpp +++ b/source/Plugins/Process/Utility/UnwindLLDB.cpp @@ -172,8 +172,9 @@ UnwindLLDB::AddOneMoreFrame (ABI *abi) if (!reg_ctx_sp->IsValid()) { - // If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to that and return - // true. Subsequent calls to TryFallbackUnwindPlan() will return false. + // We failed to get a valid RegisterContext. + // See if the regctx below this on the stack has a fallback unwind plan it can use. + // Subsequent calls to TryFallbackUnwindPlan() will return false. if (m_frames[cur_idx - 1]->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) { return AddOneMoreFrame (abi); @@ -207,18 +208,35 @@ UnwindLLDB::AddOneMoreFrame (ABI *abi) // these. if (reg_ctx_sp->IsTrapHandlerFrame() == false) { - // If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to that and return - // true. Subsequent calls to TryFallbackUnwindPlan() will return false. - if (m_frames[cur_idx - 1]->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) + // See if we can find a fallback unwind plan for THIS frame. It may be + // that the UnwindPlan we're using for THIS frame was bad and gave us a + // bad CFA. + // If that's not it, then see if we can change the UnwindPlan for the frame + // below us ("NEXT") -- see if using that other UnwindPlan gets us a better + // unwind state. + if (reg_ctx_sp->TryFallbackUnwindPlan() == false + || reg_ctx_sp->GetCFA (cursor_sp->cfa) == false + || abi->CallFrameAddressIsValid(cursor_sp->cfa) == false) { - return AddOneMoreFrame (abi); + if (m_frames[cur_idx - 1]->reg_ctx_lldb_sp->TryFallbackUnwindPlan()) + { + return AddOneMoreFrame (abi); + } + if (log) + { + log->Printf("%*sFrame %d did not get a valid CFA for this frame, stopping stack walk", + cur_idx < 100 ? cur_idx : 100, "", cur_idx); + } + goto unwind_done; } - if (log) + else { - log->Printf("%*sFrame %d did not get a valid CFA for this frame, stopping stack walk", - cur_idx < 100 ? cur_idx : 100, "", cur_idx); + if (log) + { + log->Printf("%*sFrame %d had a bad CFA value but we switched the UnwindPlan being used and got one that looks more realistic.", + cur_idx < 100 ? cur_idx : 100, "", cur_idx); + } } - goto unwind_done; } } if (!reg_ctx_sp->ReadPC (cursor_sp->start_pc)) @@ -366,6 +384,14 @@ UnwindLLDB::SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_privat UnwindLLDB::RegisterSearchResult result; result = m_frames[frame_num]->reg_ctx_lldb_sp->SavedLocationForRegister (lldb_regnum, regloc); + // We descended down to the live register context aka stack frame 0 and are reading the value + // out of a live register. + if (result == UnwindLLDB::RegisterSearchResult::eRegisterFound + && regloc.type == UnwindLLDB::RegisterLocation::eRegisterInLiveRegisterContext) + { + return true; + } + // If we have unwind instructions saying that register N is saved in register M in the middle of // the stack (and N can equal M here, meaning the register was not used in this function), then // change the register number we're looking for to M and keep looking for a concrete location diff --git a/source/Plugins/Process/Utility/UnwindLLDB.h b/source/Plugins/Process/Utility/UnwindLLDB.h index eb5400389df3..35d85e2e3d26 100644 --- a/source/Plugins/Process/Utility/UnwindLLDB.h +++ b/source/Plugins/Process/Utility/UnwindLLDB.h @@ -48,7 +48,8 @@ protected: eRegisterSavedAtMemoryLocation, // register is saved at a specific word of target mem (target_memory_location) eRegisterInRegister, // register is available in a (possible other) register (register_number) eRegisterSavedAtHostMemoryLocation, // register is saved at a word in lldb's address space - eRegisterValueInferred // register val was computed (and is in inferred_value) + eRegisterValueInferred, // register val was computed (and is in inferred_value) + eRegisterInLiveRegisterContext // register value is in a live (stack frame #0) register }; int type; union diff --git a/source/Plugins/Process/Utility/lldb-x86-register-enums.h b/source/Plugins/Process/Utility/lldb-x86-register-enums.h index c4706d567b70..99fca3005820 100644 --- a/source/Plugins/Process/Utility/lldb-x86-register-enums.h +++ b/source/Plugins/Process/Utility/lldb-x86-register-enums.h @@ -12,6 +12,7 @@ namespace lldb_private { + // LLDB register codes (e.g. RegisterKind == eRegisterKindLLDB) //--------------------------------------------------------------------------- // Internal codes for all i386 registers. @@ -19,100 +20,100 @@ namespace lldb_private enum { k_first_gpr_i386, - gpr_eax_i386 = k_first_gpr_i386, - gpr_ebx_i386, - gpr_ecx_i386, - gpr_edx_i386, - gpr_edi_i386, - gpr_esi_i386, - gpr_ebp_i386, - gpr_esp_i386, - gpr_eip_i386, - gpr_eflags_i386, - gpr_cs_i386, - gpr_fs_i386, - gpr_gs_i386, - gpr_ss_i386, - gpr_ds_i386, - gpr_es_i386, + lldb_eax_i386 = k_first_gpr_i386, + lldb_ebx_i386, + lldb_ecx_i386, + lldb_edx_i386, + lldb_edi_i386, + lldb_esi_i386, + lldb_ebp_i386, + lldb_esp_i386, + lldb_eip_i386, + lldb_eflags_i386, + lldb_cs_i386, + lldb_fs_i386, + lldb_gs_i386, + lldb_ss_i386, + lldb_ds_i386, + lldb_es_i386, k_first_alias_i386, - gpr_ax_i386 = k_first_alias_i386, - gpr_bx_i386, - gpr_cx_i386, - gpr_dx_i386, - gpr_di_i386, - gpr_si_i386, - gpr_bp_i386, - gpr_sp_i386, - gpr_ah_i386, - gpr_bh_i386, - gpr_ch_i386, - gpr_dh_i386, - gpr_al_i386, - gpr_bl_i386, - gpr_cl_i386, - gpr_dl_i386, - k_last_alias_i386 = gpr_dl_i386, + lldb_ax_i386 = k_first_alias_i386, + lldb_bx_i386, + lldb_cx_i386, + lldb_dx_i386, + lldb_di_i386, + lldb_si_i386, + lldb_bp_i386, + lldb_sp_i386, + lldb_ah_i386, + lldb_bh_i386, + lldb_ch_i386, + lldb_dh_i386, + lldb_al_i386, + lldb_bl_i386, + lldb_cl_i386, + lldb_dl_i386, + k_last_alias_i386 = lldb_dl_i386, k_last_gpr_i386 = k_last_alias_i386, k_first_fpr_i386, - fpu_fctrl_i386 = k_first_fpr_i386, - fpu_fstat_i386, - fpu_ftag_i386, - fpu_fop_i386, - fpu_fiseg_i386, - fpu_fioff_i386, - fpu_foseg_i386, - fpu_fooff_i386, - fpu_mxcsr_i386, - fpu_mxcsrmask_i386, - fpu_st0_i386, - fpu_st1_i386, - fpu_st2_i386, - fpu_st3_i386, - fpu_st4_i386, - fpu_st5_i386, - fpu_st6_i386, - fpu_st7_i386, - fpu_mm0_i386, - fpu_mm1_i386, - fpu_mm2_i386, - fpu_mm3_i386, - fpu_mm4_i386, - fpu_mm5_i386, - fpu_mm6_i386, - fpu_mm7_i386, - fpu_xmm0_i386, - fpu_xmm1_i386, - fpu_xmm2_i386, - fpu_xmm3_i386, - fpu_xmm4_i386, - fpu_xmm5_i386, - fpu_xmm6_i386, - fpu_xmm7_i386, - k_last_fpr_i386 = fpu_xmm7_i386, + lldb_fctrl_i386 = k_first_fpr_i386, + lldb_fstat_i386, + lldb_ftag_i386, + lldb_fop_i386, + lldb_fiseg_i386, + lldb_fioff_i386, + lldb_foseg_i386, + lldb_fooff_i386, + lldb_mxcsr_i386, + lldb_mxcsrmask_i386, + lldb_st0_i386, + lldb_st1_i386, + lldb_st2_i386, + lldb_st3_i386, + lldb_st4_i386, + lldb_st5_i386, + lldb_st6_i386, + lldb_st7_i386, + lldb_mm0_i386, + lldb_mm1_i386, + lldb_mm2_i386, + lldb_mm3_i386, + lldb_mm4_i386, + lldb_mm5_i386, + lldb_mm6_i386, + lldb_mm7_i386, + lldb_xmm0_i386, + lldb_xmm1_i386, + lldb_xmm2_i386, + lldb_xmm3_i386, + lldb_xmm4_i386, + lldb_xmm5_i386, + lldb_xmm6_i386, + lldb_xmm7_i386, + k_last_fpr_i386 = lldb_xmm7_i386, k_first_avx_i386, - fpu_ymm0_i386 = k_first_avx_i386, - fpu_ymm1_i386, - fpu_ymm2_i386, - fpu_ymm3_i386, - fpu_ymm4_i386, - fpu_ymm5_i386, - fpu_ymm6_i386, - fpu_ymm7_i386, - k_last_avx_i386 = fpu_ymm7_i386, + lldb_ymm0_i386 = k_first_avx_i386, + lldb_ymm1_i386, + lldb_ymm2_i386, + lldb_ymm3_i386, + lldb_ymm4_i386, + lldb_ymm5_i386, + lldb_ymm6_i386, + lldb_ymm7_i386, + k_last_avx_i386 = lldb_ymm7_i386, - dr0_i386, - dr1_i386, - dr2_i386, - dr3_i386, - dr4_i386, - dr5_i386, - dr6_i386, - dr7_i386, + lldb_dr0_i386, + lldb_dr1_i386, + lldb_dr2_i386, + lldb_dr3_i386, + lldb_dr4_i386, + lldb_dr5_i386, + lldb_dr6_i386, + lldb_dr7_i386, k_num_registers_i386, k_num_gpr_registers_i386 = k_last_gpr_i386 - k_first_gpr_i386 + 1, @@ -126,160 +127,160 @@ namespace lldb_private enum { k_first_gpr_x86_64, - gpr_rax_x86_64 = k_first_gpr_x86_64, - gpr_rbx_x86_64, - gpr_rcx_x86_64, - gpr_rdx_x86_64, - gpr_rdi_x86_64, - gpr_rsi_x86_64, - gpr_rbp_x86_64, - gpr_rsp_x86_64, - gpr_r8_x86_64, - gpr_r9_x86_64, - gpr_r10_x86_64, - gpr_r11_x86_64, - gpr_r12_x86_64, - gpr_r13_x86_64, - gpr_r14_x86_64, - gpr_r15_x86_64, - gpr_rip_x86_64, - gpr_rflags_x86_64, - gpr_cs_x86_64, - gpr_fs_x86_64, - gpr_gs_x86_64, - gpr_ss_x86_64, - gpr_ds_x86_64, - gpr_es_x86_64, + lldb_rax_x86_64 = k_first_gpr_x86_64, + lldb_rbx_x86_64, + lldb_rcx_x86_64, + lldb_rdx_x86_64, + lldb_rdi_x86_64, + lldb_rsi_x86_64, + lldb_rbp_x86_64, + lldb_rsp_x86_64, + lldb_r8_x86_64, + lldb_r9_x86_64, + lldb_r10_x86_64, + lldb_r11_x86_64, + lldb_r12_x86_64, + lldb_r13_x86_64, + lldb_r14_x86_64, + lldb_r15_x86_64, + lldb_rip_x86_64, + lldb_rflags_x86_64, + lldb_cs_x86_64, + lldb_fs_x86_64, + lldb_gs_x86_64, + lldb_ss_x86_64, + lldb_ds_x86_64, + lldb_es_x86_64, k_first_alias_x86_64, - gpr_eax_x86_64 = k_first_alias_x86_64, - gpr_ebx_x86_64, - gpr_ecx_x86_64, - gpr_edx_x86_64, - gpr_edi_x86_64, - gpr_esi_x86_64, - gpr_ebp_x86_64, - gpr_esp_x86_64, - gpr_r8d_x86_64, // Low 32 bits of r8 - gpr_r9d_x86_64, // Low 32 bits of r9 - gpr_r10d_x86_64, // Low 32 bits of r10 - gpr_r11d_x86_64, // Low 32 bits of r11 - gpr_r12d_x86_64, // Low 32 bits of r12 - gpr_r13d_x86_64, // Low 32 bits of r13 - gpr_r14d_x86_64, // Low 32 bits of r14 - gpr_r15d_x86_64, // Low 32 bits of r15 - gpr_ax_x86_64, - gpr_bx_x86_64, - gpr_cx_x86_64, - gpr_dx_x86_64, - gpr_di_x86_64, - gpr_si_x86_64, - gpr_bp_x86_64, - gpr_sp_x86_64, - gpr_r8w_x86_64, // Low 16 bits of r8 - gpr_r9w_x86_64, // Low 16 bits of r9 - gpr_r10w_x86_64, // Low 16 bits of r10 - gpr_r11w_x86_64, // Low 16 bits of r11 - gpr_r12w_x86_64, // Low 16 bits of r12 - gpr_r13w_x86_64, // Low 16 bits of r13 - gpr_r14w_x86_64, // Low 16 bits of r14 - gpr_r15w_x86_64, // Low 16 bits of r15 - gpr_ah_x86_64, - gpr_bh_x86_64, - gpr_ch_x86_64, - gpr_dh_x86_64, - gpr_al_x86_64, - gpr_bl_x86_64, - gpr_cl_x86_64, - gpr_dl_x86_64, - gpr_dil_x86_64, - gpr_sil_x86_64, - gpr_bpl_x86_64, - gpr_spl_x86_64, - gpr_r8l_x86_64, // Low 8 bits of r8 - gpr_r9l_x86_64, // Low 8 bits of r9 - gpr_r10l_x86_64, // Low 8 bits of r10 - gpr_r11l_x86_64, // Low 8 bits of r11 - gpr_r12l_x86_64, // Low 8 bits of r12 - gpr_r13l_x86_64, // Low 8 bits of r13 - gpr_r14l_x86_64, // Low 8 bits of r14 - gpr_r15l_x86_64, // Low 8 bits of r15 - k_last_alias_x86_64 = gpr_r15l_x86_64, + lldb_eax_x86_64 = k_first_alias_x86_64, + lldb_ebx_x86_64, + lldb_ecx_x86_64, + lldb_edx_x86_64, + lldb_edi_x86_64, + lldb_esi_x86_64, + lldb_ebp_x86_64, + lldb_esp_x86_64, + lldb_r8d_x86_64, // Low 32 bits of r8 + lldb_r9d_x86_64, // Low 32 bits of r9 + lldb_r10d_x86_64, // Low 32 bits of r10 + lldb_r11d_x86_64, // Low 32 bits of r11 + lldb_r12d_x86_64, // Low 32 bits of r12 + lldb_r13d_x86_64, // Low 32 bits of r13 + lldb_r14d_x86_64, // Low 32 bits of r14 + lldb_r15d_x86_64, // Low 32 bits of r15 + lldb_ax_x86_64, + lldb_bx_x86_64, + lldb_cx_x86_64, + lldb_dx_x86_64, + lldb_di_x86_64, + lldb_si_x86_64, + lldb_bp_x86_64, + lldb_sp_x86_64, + lldb_r8w_x86_64, // Low 16 bits of r8 + lldb_r9w_x86_64, // Low 16 bits of r9 + lldb_r10w_x86_64, // Low 16 bits of r10 + lldb_r11w_x86_64, // Low 16 bits of r11 + lldb_r12w_x86_64, // Low 16 bits of r12 + lldb_r13w_x86_64, // Low 16 bits of r13 + lldb_r14w_x86_64, // Low 16 bits of r14 + lldb_r15w_x86_64, // Low 16 bits of r15 + lldb_ah_x86_64, + lldb_bh_x86_64, + lldb_ch_x86_64, + lldb_dh_x86_64, + lldb_al_x86_64, + lldb_bl_x86_64, + lldb_cl_x86_64, + lldb_dl_x86_64, + lldb_dil_x86_64, + lldb_sil_x86_64, + lldb_bpl_x86_64, + lldb_spl_x86_64, + lldb_r8l_x86_64, // Low 8 bits of r8 + lldb_r9l_x86_64, // Low 8 bits of r9 + lldb_r10l_x86_64, // Low 8 bits of r10 + lldb_r11l_x86_64, // Low 8 bits of r11 + lldb_r12l_x86_64, // Low 8 bits of r12 + lldb_r13l_x86_64, // Low 8 bits of r13 + lldb_r14l_x86_64, // Low 8 bits of r14 + lldb_r15l_x86_64, // Low 8 bits of r15 + k_last_alias_x86_64 = lldb_r15l_x86_64, k_last_gpr_x86_64 = k_last_alias_x86_64, k_first_fpr_x86_64, - fpu_fctrl_x86_64 = k_first_fpr_x86_64, - fpu_fstat_x86_64, - fpu_ftag_x86_64, - fpu_fop_x86_64, - fpu_fiseg_x86_64, - fpu_fioff_x86_64, - fpu_foseg_x86_64, - fpu_fooff_x86_64, - fpu_mxcsr_x86_64, - fpu_mxcsrmask_x86_64, - fpu_st0_x86_64, - fpu_st1_x86_64, - fpu_st2_x86_64, - fpu_st3_x86_64, - fpu_st4_x86_64, - fpu_st5_x86_64, - fpu_st6_x86_64, - fpu_st7_x86_64, - fpu_mm0_x86_64, - fpu_mm1_x86_64, - fpu_mm2_x86_64, - fpu_mm3_x86_64, - fpu_mm4_x86_64, - fpu_mm5_x86_64, - fpu_mm6_x86_64, - fpu_mm7_x86_64, - fpu_xmm0_x86_64, - fpu_xmm1_x86_64, - fpu_xmm2_x86_64, - fpu_xmm3_x86_64, - fpu_xmm4_x86_64, - fpu_xmm5_x86_64, - fpu_xmm6_x86_64, - fpu_xmm7_x86_64, - fpu_xmm8_x86_64, - fpu_xmm9_x86_64, - fpu_xmm10_x86_64, - fpu_xmm11_x86_64, - fpu_xmm12_x86_64, - fpu_xmm13_x86_64, - fpu_xmm14_x86_64, - fpu_xmm15_x86_64, - k_last_fpr_x86_64 = fpu_xmm15_x86_64, + lldb_fctrl_x86_64 = k_first_fpr_x86_64, + lldb_fstat_x86_64, + lldb_ftag_x86_64, + lldb_fop_x86_64, + lldb_fiseg_x86_64, + lldb_fioff_x86_64, + lldb_foseg_x86_64, + lldb_fooff_x86_64, + lldb_mxcsr_x86_64, + lldb_mxcsrmask_x86_64, + lldb_st0_x86_64, + lldb_st1_x86_64, + lldb_st2_x86_64, + lldb_st3_x86_64, + lldb_st4_x86_64, + lldb_st5_x86_64, + lldb_st6_x86_64, + lldb_st7_x86_64, + lldb_mm0_x86_64, + lldb_mm1_x86_64, + lldb_mm2_x86_64, + lldb_mm3_x86_64, + lldb_mm4_x86_64, + lldb_mm5_x86_64, + lldb_mm6_x86_64, + lldb_mm7_x86_64, + lldb_xmm0_x86_64, + lldb_xmm1_x86_64, + lldb_xmm2_x86_64, + lldb_xmm3_x86_64, + lldb_xmm4_x86_64, + lldb_xmm5_x86_64, + lldb_xmm6_x86_64, + lldb_xmm7_x86_64, + lldb_xmm8_x86_64, + lldb_xmm9_x86_64, + lldb_xmm10_x86_64, + lldb_xmm11_x86_64, + lldb_xmm12_x86_64, + lldb_xmm13_x86_64, + lldb_xmm14_x86_64, + lldb_xmm15_x86_64, + k_last_fpr_x86_64 = lldb_xmm15_x86_64, k_first_avx_x86_64, - fpu_ymm0_x86_64 = k_first_avx_x86_64, - fpu_ymm1_x86_64, - fpu_ymm2_x86_64, - fpu_ymm3_x86_64, - fpu_ymm4_x86_64, - fpu_ymm5_x86_64, - fpu_ymm6_x86_64, - fpu_ymm7_x86_64, - fpu_ymm8_x86_64, - fpu_ymm9_x86_64, - fpu_ymm10_x86_64, - fpu_ymm11_x86_64, - fpu_ymm12_x86_64, - fpu_ymm13_x86_64, - fpu_ymm14_x86_64, - fpu_ymm15_x86_64, - k_last_avx_x86_64 = fpu_ymm15_x86_64, + lldb_ymm0_x86_64 = k_first_avx_x86_64, + lldb_ymm1_x86_64, + lldb_ymm2_x86_64, + lldb_ymm3_x86_64, + lldb_ymm4_x86_64, + lldb_ymm5_x86_64, + lldb_ymm6_x86_64, + lldb_ymm7_x86_64, + lldb_ymm8_x86_64, + lldb_ymm9_x86_64, + lldb_ymm10_x86_64, + lldb_ymm11_x86_64, + lldb_ymm12_x86_64, + lldb_ymm13_x86_64, + lldb_ymm14_x86_64, + lldb_ymm15_x86_64, + k_last_avx_x86_64 = lldb_ymm15_x86_64, - dr0_x86_64, - dr1_x86_64, - dr2_x86_64, - dr3_x86_64, - dr4_x86_64, - dr5_x86_64, - dr6_x86_64, - dr7_x86_64, + lldb_dr0_x86_64, + lldb_dr1_x86_64, + lldb_dr2_x86_64, + lldb_dr3_x86_64, + lldb_dr4_x86_64, + lldb_dr5_x86_64, + lldb_dr6_x86_64, + lldb_dr7_x86_64, k_num_registers_x86_64, k_num_gpr_registers_x86_64 = k_last_gpr_x86_64 - k_first_gpr_x86_64 + 1, diff --git a/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/source/Plugins/Process/elf-core/ProcessElfCore.cpp index 566816783c7e..fb39d7318a5a 100644 --- a/source/Plugins/Process/elf-core/ProcessElfCore.cpp +++ b/source/Plugins/Process/elf-core/ProcessElfCore.cpp @@ -405,14 +405,18 @@ enum { NT_AUXV }; +namespace FREEBSD { + enum { - NT_FREEBSD_PRSTATUS = 1, - NT_FREEBSD_FPREGSET, - NT_FREEBSD_PRPSINFO, - NT_FREEBSD_THRMISC = 7, - NT_FREEBSD_PROCSTAT_AUXV = 16 + NT_PRSTATUS = 1, + NT_FPREGSET, + NT_PRPSINFO, + NT_THRMISC = 7, + NT_PROCSTAT_AUXV = 16 }; +} + // Parse a FreeBSD NT_PRSTATUS note - see FreeBSD sys/procfs.h for details. static void ParseFreeBSDPrStatus(ThreadData &thread_data, DataExtractor &data, @@ -420,6 +424,7 @@ ParseFreeBSDPrStatus(ThreadData &thread_data, DataExtractor &data, { lldb::offset_t offset = 0; bool lp64 = (arch.GetMachine() == llvm::Triple::mips64 || + arch.GetMachine() == llvm::Triple::ppc64 || arch.GetMachine() == llvm::Triple::x86_64); int pr_version = data.GetU32(&offset); @@ -516,20 +521,20 @@ ProcessElfCore::ParseThreadContextsFromNoteSegment(const elf::ELFProgramHeader * m_os = llvm::Triple::FreeBSD; switch (note.n_type) { - case NT_FREEBSD_PRSTATUS: + case FREEBSD::NT_PRSTATUS: have_prstatus = true; ParseFreeBSDPrStatus(*thread_data, note_data, arch); break; - case NT_FREEBSD_FPREGSET: + case FREEBSD::NT_FPREGSET: thread_data->fpregset = note_data; break; - case NT_FREEBSD_PRPSINFO: + case FREEBSD::NT_PRPSINFO: have_prpsinfo = true; break; - case NT_FREEBSD_THRMISC: + case FREEBSD::NT_THRMISC: ParseFreeBSDThrMisc(*thread_data, note_data); break; - case NT_FREEBSD_PROCSTAT_AUXV: + case FREEBSD::NT_PROCSTAT_AUXV: // FIXME: FreeBSD sticks an int at the beginning of the note m_auxv = DataExtractor(segment_data, note_start + 4, note_size - 4); break; diff --git a/source/Plugins/Process/elf-core/ProcessElfCore.h b/source/Plugins/Process/elf-core/ProcessElfCore.h index 2fc2e4ee7949..7988bee5ae53 100644 --- a/source/Plugins/Process/elf-core/ProcessElfCore.h +++ b/source/Plugins/Process/elf-core/ProcessElfCore.h @@ -68,52 +68,52 @@ public: //------------------------------------------------------------------ virtual bool CanDebug (lldb_private::Target &target, - bool plugin_specified_by_name); + bool plugin_specified_by_name) override; //------------------------------------------------------------------ // Creating a new process, or attaching to an existing one //------------------------------------------------------------------ virtual lldb_private::Error - DoLoadCore (); + DoLoadCore () override; virtual lldb_private::DynamicLoader * - GetDynamicLoader (); + GetDynamicLoader () override; //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ virtual lldb_private::ConstString - GetPluginName(); + GetPluginName() override; virtual uint32_t - GetPluginVersion(); + GetPluginVersion() override; //------------------------------------------------------------------ // Process Control //------------------------------------------------------------------ virtual lldb_private::Error - DoDestroy (); + DoDestroy () override; virtual void - RefreshStateAfterStop(); + RefreshStateAfterStop() override; //------------------------------------------------------------------ // Process Queries //------------------------------------------------------------------ virtual bool - IsAlive (); + IsAlive () override; //------------------------------------------------------------------ // Process Memory //------------------------------------------------------------------ virtual size_t - ReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error); + ReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; virtual size_t - DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error); + DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; virtual lldb::addr_t - GetImageInfoAddress (); + GetImageInfoAddress () override; lldb_private::ArchSpec GetArchitecture(); @@ -128,7 +128,7 @@ protected: virtual bool UpdateThreadList (lldb_private::ThreadList &old_thread_list, - lldb_private::ThreadList &new_thread_list); + lldb_private::ThreadList &new_thread_list) override; private: //------------------------------------------------------------------ diff --git a/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.cpp b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.cpp index fbf397b933cc..f0750a0cee18 100644 --- a/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.cpp +++ b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_mips64.cpp @@ -10,7 +10,7 @@ #include "lldb/Core/DataExtractor.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Target/Thread.h" -#include "RegisterContextPOSIX.h" +#include "Plugins/Process/Utility/RegisterContextPOSIX.h" #include "RegisterContextPOSIXCore_mips64.h" using namespace lldb_private; diff --git a/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.cpp b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.cpp new file mode 100644 index 000000000000..15b1b44182d7 --- /dev/null +++ b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.cpp @@ -0,0 +1,109 @@ +//===-- RegisterContextCorePOSIX_powerpc.cpp ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Target/Thread.h" +#include "RegisterContextPOSIX.h" +#include "RegisterContextPOSIXCore_powerpc.h" + +using namespace lldb_private; + +RegisterContextCorePOSIX_powerpc::RegisterContextCorePOSIX_powerpc(Thread &thread, + RegisterInfoInterface *register_info, + const DataExtractor &gpregset, + const DataExtractor &fpregset) + : RegisterContextPOSIX_powerpc(thread, 0, register_info) +{ + m_gpr_buffer.reset(new DataBufferHeap(gpregset.GetDataStart(), gpregset.GetByteSize())); + m_gpr.SetData(m_gpr_buffer); + m_gpr.SetByteOrder(gpregset.GetByteOrder()); + m_fpr_buffer.reset(new DataBufferHeap(fpregset.GetDataStart(), fpregset.GetByteSize())); + m_fpr.SetData(m_fpr_buffer); + m_fpr.SetByteOrder(fpregset.GetByteOrder()); +} + +RegisterContextCorePOSIX_powerpc::~RegisterContextCorePOSIX_powerpc() +{ +} + +bool +RegisterContextCorePOSIX_powerpc::ReadGPR() +{ + return true; +} + +bool +RegisterContextCorePOSIX_powerpc::ReadFPR() +{ + return true; +} + +bool +RegisterContextCorePOSIX_powerpc::WriteGPR() +{ + assert(0); + return false; +} + +bool +RegisterContextCorePOSIX_powerpc::WriteFPR() +{ + assert(0); + return false; +} + +bool +RegisterContextCorePOSIX_powerpc::ReadRegister(const RegisterInfo *reg_info, RegisterValue &value) +{ + lldb::offset_t offset = reg_info->byte_offset; + if (reg_info->name[0] == 'f') { + uint64_t v = m_fpr.GetMaxU64(&offset, reg_info->byte_size); + if (offset == reg_info->byte_offset + reg_info->byte_size) + { + value = v; + return true; + } + } else { + uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size); + if (offset == reg_info->byte_offset + reg_info->byte_size) + { + if (reg_info->byte_size < sizeof(v)) + value = (uint32_t)v; + else + value = v; + return true; + } + } + return false; +} + +bool +RegisterContextCorePOSIX_powerpc::ReadAllRegisterValues(lldb::DataBufferSP &data_sp) +{ + return false; +} + +bool +RegisterContextCorePOSIX_powerpc::WriteRegister(const RegisterInfo *reg_info, const RegisterValue &value) +{ + return false; +} + +bool +RegisterContextCorePOSIX_powerpc::WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) +{ + return false; +} + +bool +RegisterContextCorePOSIX_powerpc::HardwareSingleStep(bool enable) +{ + return false; +} diff --git a/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h new file mode 100644 index 000000000000..e6575581b360 --- /dev/null +++ b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_powerpc.h @@ -0,0 +1,62 @@ +//===-- RegisterContextCorePOSIX_powerpc.h ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContextCorePOSIX_powerpc_H_ +#define liblldb_RegisterContextCorePOSIX_powerpc_H_ + +#include "lldb/Core/DataBufferHeap.h" +#include "Plugins/Process/Utility/RegisterContextPOSIX_powerpc.h" + +class RegisterContextCorePOSIX_powerpc : + public RegisterContextPOSIX_powerpc +{ +public: + RegisterContextCorePOSIX_powerpc (lldb_private::Thread &thread, + lldb_private::RegisterInfoInterface *register_info, + const lldb_private::DataExtractor &gpregset, + const lldb_private::DataExtractor &fpregset); + + ~RegisterContextCorePOSIX_powerpc(); + + virtual bool + ReadRegister(const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value); + + virtual bool + WriteRegister(const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value); + + bool + ReadAllRegisterValues(lldb::DataBufferSP &data_sp); + + bool + WriteAllRegisterValues(const lldb::DataBufferSP &data_sp); + + bool + HardwareSingleStep(bool enable); + +protected: + bool + ReadGPR(); + + bool + ReadFPR(); + + bool + WriteGPR(); + + bool + WriteFPR(); + +private: + lldb::DataBufferSP m_gpr_buffer; + lldb::DataBufferSP m_fpr_buffer; + lldb_private::DataExtractor m_gpr; + lldb_private::DataExtractor m_fpr; +}; + +#endif // #ifndef liblldb_RegisterContextCorePOSIX_powerpc_H_ diff --git a/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.cpp b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.cpp index 3e09c7bc2032..412c7ade8295 100644 --- a/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.cpp +++ b/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_x86_64.cpp @@ -10,7 +10,7 @@ #include "lldb/Core/DataExtractor.h" #include "lldb/Core/RegisterValue.h" #include "lldb/Target/Thread.h" -#include "RegisterContextPOSIX.h" +#include "Plugins/Process/Utility/RegisterContextPOSIX.h" #include "RegisterContextPOSIXCore_x86_64.h" using namespace lldb_private; diff --git a/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/source/Plugins/Process/elf-core/ThreadElfCore.cpp index d9f6cc04a343..d62bcfcff600 100644 --- a/source/Plugins/Process/elf-core/ThreadElfCore.cpp +++ b/source/Plugins/Process/elf-core/ThreadElfCore.cpp @@ -16,11 +16,13 @@ #include "ThreadElfCore.h" #include "ProcessElfCore.h" -#include "RegisterContextLinux_x86_64.h" -#include "RegisterContextFreeBSD_i386.h" -#include "RegisterContextFreeBSD_mips64.h" -#include "RegisterContextFreeBSD_x86_64.h" +#include "Plugins/Process/Utility/RegisterContextLinux_x86_64.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_i386.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_mips64.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_powerpc.h" +#include "Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.h" #include "RegisterContextPOSIXCore_mips64.h" +#include "RegisterContextPOSIXCore_powerpc.h" #include "RegisterContextPOSIXCore_x86_64.h" using namespace lldb; @@ -94,6 +96,12 @@ ThreadElfCore::CreateRegisterContextForFrame (StackFrame *frame) { switch (arch.GetMachine()) { + case llvm::Triple::ppc: + reg_interface = new RegisterContextFreeBSD_powerpc32(arch); + break; + case llvm::Triple::ppc64: + reg_interface = new RegisterContextFreeBSD_powerpc64(arch); + break; case llvm::Triple::mips64: reg_interface = new RegisterContextFreeBSD_mips64(arch); break; @@ -138,6 +146,10 @@ ThreadElfCore::CreateRegisterContextForFrame (StackFrame *frame) case llvm::Triple::mips64: m_thread_reg_ctx_sp.reset(new RegisterContextCorePOSIX_mips64 (*this, reg_interface, m_gpregset_data, m_fpregset_data)); break; + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + m_thread_reg_ctx_sp.reset(new RegisterContextCorePOSIX_powerpc (*this, reg_interface, m_gpregset_data, m_fpregset_data)); + break; case llvm::Triple::x86: case llvm::Triple::x86_64: m_thread_reg_ctx_sp.reset(new RegisterContextCorePOSIX_x86_64 (*this, reg_interface, m_gpregset_data, m_fpregset_data)); diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp index 1f4dd93976ec..919fa5405117 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -17,15 +17,16 @@ // C++ Includes // Other libraries and framework includes -#include "lldb/Core/ConnectionFileDescriptor.h" #include "lldb/Core/Log.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/StreamString.h" +#include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/FileSpec.h" -#include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" +#include "lldb/Host/Pipe.h" #include "lldb/Host/Socket.h" +#include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/TimeValue.h" #include "lldb/Target/Process.h" @@ -153,7 +154,6 @@ GDBRemoteCommunication::GDBRemoteCommunication(const char *comm_name, m_history (512), m_send_acks (true), m_is_platform (is_platform), - m_listen_thread (LLDB_INVALID_HOST_THREAD), m_listen_url () { } @@ -227,9 +227,23 @@ GDBRemoteCommunication::SendPacketNoLock (const char *payload, size_t payload_le Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS)); ConnectionStatus status = eConnectionStatusSuccess; - size_t bytes_written = Write (packet.GetData(), packet.GetSize(), status, NULL); + const char *packet_data = packet.GetData(); + const size_t packet_length = packet.GetSize(); + size_t bytes_written = Write (packet_data, packet_length, status, NULL); if (log) { + size_t binary_start_offset = 0; + if (strncmp(packet_data, "$vFile:pwrite:", strlen("$vFile:pwrite:")) == 0) + { + const char *first_comma = strchr(packet_data, ','); + if (first_comma) + { + const char *second_comma = strchr(first_comma + 1, ','); + if (second_comma) + binary_start_offset = second_comma - packet_data + 1; + } + } + // If logging was just enabled and we have history, then dump out what // we have to the log so we get the historical context. The Dump() call that // logs all of the packet will set a boolean so that we don't dump this more @@ -237,13 +251,27 @@ GDBRemoteCommunication::SendPacketNoLock (const char *payload, size_t payload_le if (!m_history.DidDumpToLog ()) m_history.Dump (log); - log->Printf("<%4" PRIu64 "> send packet: %.*s", (uint64_t)bytes_written, (int)packet.GetSize(), packet.GetData()); + if (binary_start_offset) + { + StreamString strm; + // Print non binary data header + strm.Printf("<%4" PRIu64 "> send packet: %.*s", (uint64_t)bytes_written, (int)binary_start_offset, packet_data); + const uint8_t *p; + // Print binary data exactly as sent + for (p = (uint8_t*)packet_data + binary_start_offset; *p != '#'; ++p) + strm.Printf("\\x%2.2x", *p); + // Print the checksum + strm.Printf("%*s", (int)3, p); + log->PutCString(strm.GetString().c_str()); + } + else + log->Printf("<%4" PRIu64 "> send packet: %.*s", (uint64_t)bytes_written, (int)packet_length, packet_data); } - m_history.AddPacket (packet.GetString(), packet.GetSize(), History::ePacketTypeSend, bytes_written); + m_history.AddPacket (packet.GetString(), packet_length, History::ePacketTypeSend, bytes_written); - if (bytes_written == packet.GetSize()) + if (bytes_written == packet_length) { if (GetSendAcks ()) return GetAck (); @@ -253,7 +281,7 @@ GDBRemoteCommunication::SendPacketNoLock (const char *payload, size_t payload_le else { if (log) - log->Printf ("error: failed to send packet: %.*s", (int)packet.GetSize(), packet.GetData()); + log->Printf ("error: failed to send packet: %.*s", (int)packet_length, packet_data); } } return PacketResult::ErrorSendFailed; @@ -447,8 +475,8 @@ GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, Stri } if (log) log->Printf ("GDBRemoteCommunication::%s tossing %u junk bytes: '%.*s'", - __FUNCTION__, idx, idx, m_bytes.c_str()); - m_bytes.erase(0, idx); + __FUNCTION__, idx - 1, idx - 1, m_bytes.c_str()); + m_bytes.erase(0, idx - 1); } break; } @@ -606,7 +634,7 @@ Error GDBRemoteCommunication::StartListenThread (const char *hostname, uint16_t port) { Error error; - if (IS_VALID_LLDB_HOST_THREAD(m_listen_thread)) + if (m_listen_thread.IsJoinable()) { error.SetErrorString("listen thread already running"); } @@ -619,7 +647,7 @@ GDBRemoteCommunication::StartListenThread (const char *hostname, uint16_t port) snprintf(listen_url, sizeof(listen_url), "listen://%i", port); m_listen_url = listen_url; SetConnection(new ConnectionFileDescriptor()); - m_listen_thread = Host::ThreadCreate (listen_url, GDBRemoteCommunication::ListenThread, this, &error); + m_listen_thread = ThreadLauncher::LaunchThread(listen_url, GDBRemoteCommunication::ListenThread, this, &error); } return error; } @@ -627,11 +655,8 @@ GDBRemoteCommunication::StartListenThread (const char *hostname, uint16_t port) bool GDBRemoteCommunication::JoinListenThread () { - if (IS_VALID_LLDB_HOST_THREAD(m_listen_thread)) - { - Host::ThreadJoin(m_listen_thread, NULL, NULL); - m_listen_thread = LLDB_INVALID_HOST_THREAD; - } + if (m_listen_thread.IsJoinable()) + m_listen_thread.Join(nullptr); return true; } @@ -738,6 +763,7 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, char named_pipe_path[PATH_MAX]; named_pipe_path[0] = '\0'; + Pipe port_named_pipe; bool listen = false; if (host_and_port[0]) @@ -763,15 +789,11 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, if (::mktemp (named_pipe_path)) { -#if defined(_WIN32) - if ( false ) -#else - if (::mkfifo(named_pipe_path, 0600) == 0) -#endif - { - debugserver_args.AppendArgument("--named-pipe"); - debugserver_args.AppendArgument(named_pipe_path); - } + error = port_named_pipe.CreateNew(named_pipe_path, false); + if (error.Fail()) + return error; + debugserver_args.AppendArgument("--named-pipe"); + debugserver_args.AppendArgument(named_pipe_path); } } else @@ -838,11 +860,15 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, } } while (has_env_var); - // Close STDIN, STDOUT and STDERR. We might need to redirect them - // to "/dev/null" if we run into any problems. + // Close STDIN, STDOUT and STDERR. launch_info.AppendCloseFileAction (STDIN_FILENO); launch_info.AppendCloseFileAction (STDOUT_FILENO); launch_info.AppendCloseFileAction (STDERR_FILENO); + + // Redirect STDIN, STDOUT and STDERR to "/dev/null". + launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false); + launch_info.AppendSuppressFileAction (STDOUT_FILENO, false, true); + launch_info.AppendSuppressFileAction (STDERR_FILENO, false, true); error = Host::LaunchProcess(launch_info); @@ -850,20 +876,40 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *hostname, { if (named_pipe_path[0]) { - File name_pipe_file; - error = name_pipe_file.Open(named_pipe_path, File::eOpenOptionRead); + error = port_named_pipe.OpenAsReader(named_pipe_path, false); if (error.Success()) { char port_cstr[256]; port_cstr[0] = '\0'; size_t num_bytes = sizeof(port_cstr); - error = name_pipe_file.Read(port_cstr, num_bytes); - assert (error.Success()); - assert (num_bytes > 0 && port_cstr[num_bytes-1] == '\0'); - out_port = Args::StringToUInt32(port_cstr, 0); - name_pipe_file.Close(); + // Read port from pipe with 10 second timeout. + error = port_named_pipe.ReadWithTimeout(port_cstr, num_bytes, std::chrono::microseconds(10 * 1000000), num_bytes); + if (error.Success()) + { + assert (num_bytes > 0 && port_cstr[num_bytes-1] == '\0'); + out_port = Args::StringToUInt32(port_cstr, 0); + if (log) + log->Printf("GDBRemoteCommunication::%s() debugserver listens %u port", __FUNCTION__, out_port); + } + else + { + if (log) + log->Printf("GDBRemoteCommunication::%s() failed to read a port value from named pipe %s: %s", __FUNCTION__, named_pipe_path, error.AsCString()); + + } + port_named_pipe.Close(); + } + else + { + if (log) + log->Printf("GDBRemoteCommunication::%s() failed to open named pipe %s for reading: %s", __FUNCTION__, named_pipe_path, error.AsCString()); + } + const auto err = port_named_pipe.Delete(named_pipe_path); + if (err.Fail()) + { + if (log) + log->Printf ("GDBRemoteCommunication::%s failed to delete pipe %s: %s", __FUNCTION__, named_pipe_path, err.AsCString()); } - FileSystem::Unlink(named_pipe_path); } else if (listen) { diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h index b11d38563207..ac203a62788a 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -20,6 +20,7 @@ #include "lldb/lldb-public.h" #include "lldb/Core/Communication.h" #include "lldb/Core/Listener.h" +#include "lldb/Host/HostThread.h" #include "lldb/Host/Mutex.h" #include "lldb/Host/Predicate.h" #include "lldb/Host/TimeValue.h" @@ -281,8 +282,7 @@ protected: ListenThread (lldb::thread_arg_t arg); private: - - lldb::thread_t m_listen_thread; + lldb_private::HostThread m_listen_thread; std::string m_listen_url; diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 5e4ed7648f95..52750de5a25f 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -20,11 +20,11 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" #include "lldb/Interpreter/Args.h" -#include "lldb/Core/ConnectionFileDescriptor.h" #include "lldb/Core/Log.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamGDBRemote.h" #include "lldb/Core/StreamString.h" +#include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Endian.h" #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" @@ -1643,7 +1643,9 @@ GDBRemoteCommunicationClient::GetHostInfo (bool force) } else if (name.compare("triple") == 0) { - triple.swap(value); + extractor.GetStringRef ().swap (value); + extractor.SetFilePos(0); + extractor.GetHexByteString (triple); ++num_keys_decoded; } else if (name.compare ("distribution_id") == 0) @@ -2331,6 +2333,10 @@ GDBRemoteCommunicationClient::DecodeProcessInfoResponse (StringExtractorGDBRemot } else if (name.compare("triple") == 0) { + StringExtractor extractor; + extractor.GetStringRef().swap(value); + extractor.SetFilePos(0); + extractor.GetHexByteString (value); process_info.GetArchitecture ().SetTriple (value.c_str()); } else if (name.compare("name") == 0) @@ -2404,6 +2410,8 @@ GDBRemoteCommunicationClient::GetProcessInfo (lldb::pid_t pid, ProcessInstanceIn bool GDBRemoteCommunicationClient::GetCurrentProcessInfo () { + Log *log (ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet (GDBR_LOG_PROCESS | GDBR_LOG_PACKETS)); + if (m_qProcessInfo_is_valid == eLazyBoolYes) return true; if (m_qProcessInfo_is_valid == eLazyBoolNo) @@ -2426,6 +2434,7 @@ GDBRemoteCommunicationClient::GetCurrentProcessInfo () std::string triple; uint32_t pointer_byte_size = 0; StringExtractor extractor; + ByteOrder byte_order = eByteOrderInvalid; uint32_t num_keys_decoded = 0; lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; while (response.GetNameColonValue(name, value)) @@ -2444,7 +2453,10 @@ GDBRemoteCommunicationClient::GetCurrentProcessInfo () } else if (name.compare("triple") == 0) { - triple = value; + StringExtractor extractor; + extractor.GetStringRef().swap(value); + extractor.SetFilePos(0); + extractor.GetHexByteString (triple); ++num_keys_decoded; } else if (name.compare("ostype") == 0) @@ -2459,10 +2471,15 @@ GDBRemoteCommunicationClient::GetCurrentProcessInfo () } else if (name.compare("endian") == 0) { - if (value.compare("little") == 0 || - value.compare("big") == 0 || - value.compare("pdp") == 0) - ++num_keys_decoded; + ++num_keys_decoded; + if (value.compare("little") == 0) + byte_order = eByteOrderLittle; + else if (value.compare("big") == 0) + byte_order = eByteOrderBig; + else if (value.compare("pdp") == 0) + byte_order = eByteOrderPDP; + else + --num_keys_decoded; } else if (name.compare("ptrsize") == 0) { @@ -2496,11 +2513,34 @@ GDBRemoteCommunicationClient::GetCurrentProcessInfo () } else if (cpu != LLDB_INVALID_CPUTYPE && !os_name.empty() && !vendor_name.empty()) { - m_process_arch.SetArchitecture (eArchTypeMachO, cpu, sub); + llvm::Triple triple(llvm::Twine("-") + vendor_name + "-" + os_name); + + assert(triple.getObjectFormat() != llvm::Triple::UnknownObjectFormat); + switch (triple.getObjectFormat()) { + case llvm::Triple::MachO: + m_process_arch.SetArchitecture (eArchTypeMachO, cpu, sub); + break; + case llvm::Triple::ELF: + m_process_arch.SetArchitecture (eArchTypeELF, cpu, sub); + break; + case llvm::Triple::COFF: + m_process_arch.SetArchitecture (eArchTypeCOFF, cpu, sub); + break; + case llvm::Triple::UnknownObjectFormat: + if (log) + log->Printf("error: failed to determine target architecture"); + return false; + } + if (pointer_byte_size) { assert (pointer_byte_size == m_process_arch.GetAddressByteSize()); } + if (byte_order != eByteOrderInvalid) + { + assert (byte_order == m_process_arch.GetByteOrder()); + } + m_process_arch.GetTriple().setVendorName (llvm::StringRef (vendor_name)); m_process_arch.GetTriple().setOSName(llvm::StringRef (os_name)); m_host_arch.GetTriple().setVendorName (llvm::StringRef (vendor_name)); m_host_arch.GetTriple().setOSName (llvm::StringRef (os_name)); @@ -2931,6 +2971,11 @@ GDBRemoteCommunicationClient::GetThreadStopInfo (lldb::tid_t tid, StringExtracto uint8_t GDBRemoteCommunicationClient::SendGDBStoppointTypePacket (GDBStoppointType type, bool insert, addr_t addr, uint32_t length) { + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("GDBRemoteCommunicationClient::%s() %s at addr = 0x%" PRIx64, + __FUNCTION__, insert ? "add" : "remove", addr); + // Check if the stub is known not to support this breakpoint type if (!SupportsGDBStoppointPacket(type)) return UINT8_MAX; diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp index ffcdd169eb9f..a7149505e869 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp @@ -23,11 +23,11 @@ // Other libraries and framework includes #include "llvm/ADT/Triple.h" #include "lldb/Interpreter/Args.h" -#include "lldb/Core/ConnectionFileDescriptor.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Log.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamString.h" +#include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Debug.h" #include "lldb/Host/Endian.h" #include "lldb/Host/File.h" @@ -71,7 +71,7 @@ namespace //---------------------------------------------------------------------- GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(bool is_platform) : GDBRemoteCommunication ("gdb-remote.server", "gdb-remote.server.rx_packet", is_platform), - m_platform_sp (Platform::GetDefaultPlatform ()), + m_platform_sp (Platform::GetHostPlatform ()), m_async_thread (LLDB_INVALID_HOST_THREAD), m_process_launch_info (), m_process_launch_error (), @@ -429,6 +429,14 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec, case StringExtractorGDBRemote::eServerPacketType_vAttach: packet_result = Handle_vAttach (packet); break; + + case StringExtractorGDBRemote::eServerPacketType_D: + packet_result = Handle_D (packet); + break; + + case StringExtractorGDBRemote::eServerPacketType_qThreadStopInfo: + packet_result = Handle_qThreadStopInfo (packet); + break; } } else @@ -474,13 +482,34 @@ GDBRemoteCommunicationServer::LaunchProcess () // FIXME This looks an awful lot like we could override this in // derived classes, one for lldb-platform, the other for lldb-gdbserver. if (IsGdbServer ()) - return LaunchDebugServerProcess (); + return LaunchProcessForDebugging (); else return LaunchPlatformProcess (); } +bool +GDBRemoteCommunicationServer::ShouldRedirectInferiorOutputOverGdbRemote (const lldb_private::ProcessLaunchInfo &launch_info) const +{ + // Retrieve the file actions specified for stdout and stderr. + auto stdout_file_action = launch_info.GetFileActionForFD (STDOUT_FILENO); + auto stderr_file_action = launch_info.GetFileActionForFD (STDERR_FILENO); + + // If neither stdout and stderr file actions are specified, we're not doing anything special, so + // assume we want to redirect stdout/stderr over gdb-remote $O messages. + if ((stdout_file_action == nullptr) && (stderr_file_action == nullptr)) + { + // Send stdout/stderr over the gdb-remote protocol. + return true; + } + + // Any other setting for either stdout or stderr implies we are either suppressing + // it (with /dev/null) or we've got it set to a PTY. Either way, we don't want the + // output over gdb-remote. + return false; +} + lldb_private::Error -GDBRemoteCommunicationServer::LaunchDebugServerProcess () +GDBRemoteCommunicationServer::LaunchProcessForDebugging () { Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); @@ -503,20 +532,34 @@ GDBRemoteCommunicationServer::LaunchDebugServerProcess () return error; } - // Setup stdout/stderr mapping from inferior. - auto terminal_fd = m_debugged_process_sp->GetTerminalFileDescriptor (); - if (terminal_fd >= 0) + // Handle mirroring of inferior stdout/stderr over the gdb-remote protocol as needed. + // llgs local-process debugging may specify PTYs, which will eliminate the need to reflect inferior + // stdout/stderr over the gdb-remote protocol. + if (ShouldRedirectInferiorOutputOverGdbRemote (m_process_launch_info)) { if (log) - log->Printf ("ProcessGDBRemoteCommunicationServer::%s setting inferior STDIO fd to %d", __FUNCTION__, terminal_fd); - error = SetSTDIOFileDescriptor (terminal_fd); - if (error.Fail ()) - return error; + log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " setting up stdout/stderr redirection via $O gdb-remote commands", __FUNCTION__, m_debugged_process_sp->GetID ()); + + // Setup stdout/stderr mapping from inferior to $O + auto terminal_fd = m_debugged_process_sp->GetTerminalFileDescriptor (); + if (terminal_fd >= 0) + { + if (log) + log->Printf ("ProcessGDBRemoteCommunicationServer::%s setting inferior STDIO fd to %d", __FUNCTION__, terminal_fd); + error = SetSTDIOFileDescriptor (terminal_fd); + if (error.Fail ()) + return error; + } + else + { + if (log) + log->Printf ("ProcessGDBRemoteCommunicationServer::%s ignoring inferior STDIO since terminal fd reported as %d", __FUNCTION__, terminal_fd); + } } else { if (log) - log->Printf ("ProcessGDBRemoteCommunicationServer::%s ignoring inferior STDIO since terminal fd reported as %d", __FUNCTION__, terminal_fd); + log->Printf ("GDBRemoteCommunicationServer::%s pid %" PRIu64 " skipping stdout/stderr redirection via $O: inferior will communicate over client-provided file descriptors", __FUNCTION__, m_debugged_process_sp->GetID ()); } printf ("Launched '%s' as process %" PRIu64 "...\n", m_process_launch_info.GetArguments ().GetArgumentAtIndex (0), m_process_launch_info.GetProcessID ()); @@ -526,33 +569,10 @@ GDBRemoteCommunicationServer::LaunchDebugServerProcess () if ((pid = m_process_launch_info.GetProcessID ()) != LLDB_INVALID_PROCESS_ID) { // add to spawned pids - { - Mutex::Locker locker (m_spawned_pids_mutex); - // On an lldb-gdbserver, we would expect there to be only one. - assert (m_spawned_pids.empty () && "lldb-gdbserver adding tracked process but one already existed"); - m_spawned_pids.insert (pid); - } - } - - if (error.Success ()) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s beginning check to wait for launched application to hit first stop", __FUNCTION__); - - int iteration = 0; - // Wait for the process to hit its first stop state. - while (!StateIsStoppedState (m_debugged_process_sp->GetState (), false)) - { - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s waiting for launched process to hit first stop (%d)...", __FUNCTION__, iteration++); - - // FIXME use a finer granularity. - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - - if (log) - log->Printf ("GDBRemoteCommunicationServer::%s launched application has hit first stop", __FUNCTION__); - + Mutex::Locker locker (m_spawned_pids_mutex); + // On an lldb-gdbserver, we would expect there to be only one. + assert (m_spawned_pids.empty () && "lldb-gdbserver adding tracked process but one already existed"); + m_spawned_pids.insert (pid); } return error; @@ -699,12 +719,18 @@ GDBRemoteCommunicationServer::SendWResponse (lldb_private::NativeProcessProtocol char return_type_code; switch (exit_type) { - case ExitType::eExitTypeExit: return_type_code = 'W'; break; - case ExitType::eExitTypeSignal: return_type_code = 'X'; break; - case ExitType::eExitTypeStop: return_type_code = 'S'; break; - + case ExitType::eExitTypeExit: + return_type_code = 'W'; + break; + case ExitType::eExitTypeSignal: + return_type_code = 'X'; + break; + case ExitType::eExitTypeStop: + return_type_code = 'S'; + break; case ExitType::eExitTypeInvalid: - default: return_type_code = 'E'; break; + return_type_code = 'E'; + break; } response.PutChar (return_type_code); @@ -861,21 +887,21 @@ GDBRemoteCommunicationServer::SendStopReplyPacketForThread (lldb::tid_t tid) response.Printf ("thread:%" PRIx64 ";", tid); // Include the thread name if there is one. - const char *thread_name = thread_sp->GetName (); - if (thread_name && thread_name[0]) + const std::string thread_name = thread_sp->GetName (); + if (!thread_name.empty ()) { - size_t thread_name_len = strlen(thread_name); + size_t thread_name_len = thread_name.length (); - if (::strcspn (thread_name, "$#+-;:") == thread_name_len) + if (::strcspn (thread_name.c_str (), "$#+-;:") == thread_name_len) { response.PutCString ("name:"); - response.PutCString (thread_name); + response.PutCString (thread_name.c_str ()); } else { // The thread name contains special chars, send as hex bytes. response.PutCString ("hexname:"); - response.PutCStringAsRawHex8 (thread_name); + response.PutCStringAsRawHex8 (thread_name.c_str ()); } response.PutChar (';'); } @@ -1197,7 +1223,7 @@ GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet ArchSpec host_arch(HostInfo::GetArchitecture()); const llvm::Triple &host_triple = host_arch.GetTriple(); response.PutCString("triple:"); - response.PutCString(host_triple.getTriple().c_str()); + response.PutCStringAsRawHex8(host_triple.getTriple().c_str()); response.Printf (";ptrsize:%u;",host_arch.GetAddressByteSize()); const char* distribution_id = host_arch.GetDistributionId ().AsCString (); @@ -1252,7 +1278,6 @@ GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet } std::string s; -#if !defined(__linux__) if (HostInfo::GetOSBuildString(s)) { response.PutCString ("os_build:"); @@ -1265,7 +1290,6 @@ GDBRemoteCommunicationServer::Handle_qHostInfo (StringExtractorGDBRemote &packet response.PutCStringAsRawHex8(s.c_str()); response.PutChar(';'); } -#endif #if defined(__APPLE__) @@ -1315,7 +1339,7 @@ CreateProcessInfoResponse (const ProcessInstanceInfo &proc_info, StreamString &r { const llvm::Triple &proc_triple = proc_arch.GetTriple(); response.PutCString("triple:"); - response.PutCString(proc_triple.getTriple().c_str()); + response.PutCStringAsRawHex8(proc_triple.getTriple().c_str()); response.PutChar(';'); } } @@ -1351,7 +1375,10 @@ CreateProcessInfoResponse_DebugServerStyle (const ProcessInstanceInfo &proc_info response.Printf ("vendor:%s;", vendor.c_str ()); #else // We'll send the triple. - response.Printf ("triple:%s;", proc_triple.getTriple().c_str ()); + response.PutCString("triple:"); + response.PutCStringAsRawHex8(proc_triple.getTriple().c_str()); + response.PutChar(';'); + #endif std::string ostype = proc_triple.getOSName (); // Adjust so ostype reports ios for Apple/ARM and Apple/ARM64. @@ -2372,8 +2399,6 @@ GDBRemoteCommunicationServer::Handle_vCont_actions (StringExtractorGDBRemote &pa return SendUnimplementedResponse (packet.GetStringRef().c_str()); } - // We handle $vCont messages for c. - // TODO add C, s and S. StreamString response; response.Printf("vCont;c;C;s;S"); @@ -3388,10 +3413,8 @@ GDBRemoteCommunicationServer::Handle_interrupt (StringExtractorGDBRemote &packet return SendErrorResponse (0x15); } - // Build the ResumeActionList - stop everything. - lldb_private::ResumeActionList actions (StateType::eStateStopped, 0); - - Error error = m_debugged_process_sp->Resume (actions); + // Interrupt the process. + Error error = m_debugged_process_sp->Interrupt (); if (error.Fail ()) { if (log) @@ -3771,7 +3794,7 @@ GDBRemoteCommunicationServer::Handle_z (StringExtractorGDBRemote &packet) } // Parse out software or hardware breakpoint requested. - packet.SetFilePos (strlen("Z")); + packet.SetFilePos (strlen("z")); if (packet.GetBytesLeft() < 1) return SendIllFormedResponse(packet, "Too short z packet, missing software/hardware specifier"); @@ -3811,7 +3834,7 @@ GDBRemoteCommunicationServer::Handle_z (StringExtractorGDBRemote &packet) if (want_breakpoint) { - // Try to set the breakpoint. + // Try to clear the breakpoint. const Error error = m_debugged_process_sp->RemoveBreakpoint (breakpoint_addr); if (error.Success ()) return SendOKResponse (); @@ -4171,8 +4194,93 @@ GDBRemoteCommunicationServer::Handle_vAttach (StringExtractorGDBRemote &packet) // Notify we attached by sending a stop packet. return SendStopReasonForState (m_debugged_process_sp->GetState (), true); +} - return PacketResult::Success; +GDBRemoteCommunicationServer::PacketResult +GDBRemoteCommunicationServer::Handle_D (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS)); + + // We don't support if we're not llgs. + if (!IsGdbServer()) + return SendUnimplementedResponse ("only supported for lldb-gdbserver"); + + // Scope for mutex locker. + Mutex::Locker locker (m_spawned_pids_mutex); + + // Fail if we don't have a current process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + { + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s failed, no process available", __FUNCTION__); + return SendErrorResponse (0x15); + } + + if (m_spawned_pids.find(m_debugged_process_sp->GetID ()) == m_spawned_pids.end()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s failed to find PID %" PRIu64 " in spawned pids list", + __FUNCTION__, m_debugged_process_sp->GetID ()); + return SendErrorResponse (0x1); + } + + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + + // Consume the ';' after D. + packet.SetFilePos (1); + if (packet.GetBytesLeft ()) + { + if (packet.GetChar () != ';') + return SendIllFormedResponse (packet, "D missing expected ';'"); + + // Grab the PID from which we will detach (assume hex encoding). + pid = packet.GetU32 (LLDB_INVALID_PROCESS_ID, 16); + if (pid == LLDB_INVALID_PROCESS_ID) + return SendIllFormedResponse (packet, "D failed to parse the process id"); + } + + if (pid != LLDB_INVALID_PROCESS_ID && + m_debugged_process_sp->GetID () != pid) + { + return SendIllFormedResponse (packet, "Invalid pid"); + } + + if (m_stdio_communication.IsConnected ()) + { + m_stdio_communication.StopReadThread (); + } + + const Error error = m_debugged_process_sp->Detach (); + if (error.Fail ()) + { + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s failed to detach from pid %" PRIu64 ": %s\n", + __FUNCTION__, m_debugged_process_sp->GetID (), error.AsCString ()); + return SendErrorResponse (0x01); + } + + m_spawned_pids.erase (m_debugged_process_sp->GetID ()); + return SendOKResponse (); +} + +GDBRemoteCommunicationServer::PacketResult +GDBRemoteCommunicationServer::Handle_qThreadStopInfo (StringExtractorGDBRemote &packet) +{ + Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); + + // We don't support if we're not llgs. + if (!IsGdbServer()) + return SendUnimplementedResponse ("only supported for lldb-gdbserver"); + + packet.SetFilePos (strlen("qThreadStopInfo")); + const lldb::tid_t tid = packet.GetHexMaxU32 (false, LLDB_INVALID_THREAD_ID); + if (tid == LLDB_INVALID_THREAD_ID) + { + if (log) + log->Printf ("GDBRemoteCommunicationServer::%s failed, could not parse thread id from request \"%s\"", __FUNCTION__, packet.GetStringRef ().c_str ()); + return SendErrorResponse (0x15); + } + return SendStopReplyPacketForThread (tid); } void diff --git a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h index 13c037c0287b..07ce98e29ac6 100644 --- a/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h +++ b/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h @@ -58,7 +58,7 @@ public: bool &quit); virtual bool - GetThreadSuffixSupported () + GetThreadSuffixSupported () override { return true; } @@ -152,8 +152,6 @@ public: //------------------------------------------------------------------ /// Specify the program to launch and its arguments. /// - /// The LaunchProcess () command can be executed to do the lauching. - /// /// @param[in] args /// The command line to launch. /// @@ -170,8 +168,6 @@ public: //------------------------------------------------------------------ /// Specify the launch flags for the process. /// - /// The LaunchProcess () command can be executed to do the lauching. - /// /// @param[in] launch_flags /// The launch flags to use when launching this process. /// @@ -463,6 +459,12 @@ protected: PacketResult Handle_vAttach (StringExtractorGDBRemote &packet); + PacketResult + Handle_D (StringExtractorGDBRemote &packet); + + PacketResult + Handle_qThreadStopInfo (StringExtractorGDBRemote &packet); + void SetCurrentThreadID (lldb::tid_t tid); @@ -511,9 +513,9 @@ private: return !m_is_platform; } - /// Launch a process from lldb-gdbserver + /// Launch an inferior process from lldb-gdbserver lldb_private::Error - LaunchDebugServerProcess (); + LaunchProcessForDebugging (); /// Launch a process from lldb-platform lldb_private::Error @@ -540,6 +542,9 @@ private: void ClearProcessSpecificData (); + bool + ShouldRedirectInferiorOutputOverGdbRemote (const lldb_private::ProcessLaunchInfo &launch_info) const; + //------------------------------------------------------------------ // For GDBRemoteCommunicationServer only //------------------------------------------------------------------ diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index f35d954caa7b..fe99706969c8 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -14,7 +14,6 @@ #include <errno.h> #include <stdlib.h> #ifndef LLDB_DISABLE_POSIX -#include <spawn.h> #include <netinet/in.h> #include <sys/mman.h> // for mmap #endif @@ -32,7 +31,7 @@ #include "lldb/Interpreter/Args.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Debugger.h" -#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/FileSpec.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" @@ -42,7 +41,9 @@ #include "lldb/Core/StreamString.h" #include "lldb/Core/Timer.h" #include "lldb/Core/Value.h" +#include "lldb/Host/HostThread.h" #include "lldb/Host/Symbols.h" +#include "lldb/Host/ThreadLauncher.h" #include "lldb/Host/TimeValue.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" @@ -272,8 +273,6 @@ ProcessGDBRemote::ProcessGDBRemote(Target& target, Listener &listener) : m_last_stop_packet_mutex (Mutex::eMutexTypeNormal), m_register_info (), m_async_broadcaster (NULL, "lldb.process.gdb-remote.async-broadcaster"), - m_async_thread (LLDB_INVALID_HOST_THREAD), - m_async_thread_state(eAsyncThreadNotStarted), m_async_thread_state_mutex(Mutex::eMutexTypeRecursive), m_thread_ids (), m_continue_c_tids (), @@ -749,8 +748,12 @@ ProcessGDBRemote::WillLaunchOrAttach () Error ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) { + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); Error error; + if (log) + log->Printf ("ProcessGDBRemote::%s() entered", __FUNCTION__); + uint32_t launch_flags = launch_info.GetFlags().Get(); const char *stdin_path = NULL; const char *stdout_path = NULL; @@ -777,10 +780,21 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) stderr_path = file_action->GetPath(); } + if (log) + { + if (stdin_path || stdout_path || stderr_path) + log->Printf ("ProcessGDBRemote::%s provided with STDIO paths via launch_info: stdin=%s, stdout=%s, stdout=%s", + __FUNCTION__, + stdin_path ? stdin_path : "<null>", + stdout_path ? stdout_path : "<null>", + stderr_path ? stderr_path : "<null>"); + else + log->Printf ("ProcessGDBRemote::%s no STDIO paths given via launch_info", __FUNCTION__); + } + // ::LogSetBitMask (GDBR_LOG_DEFAULT); // ::LogSetOptions (LLDB_LOG_OPTION_THREADSAFE | LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD); // ::LogSetLogFile ("/dev/stdout"); - Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); ObjectFile * object_file = exe_module->GetObjectFile(); if (object_file) @@ -817,6 +831,13 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) if (stderr_path == NULL) stderr_path = slave_name; + + if (log) + log->Printf ("ProcessGDBRemote::%s adjusted STDIO paths for local platform (IsHost() is true) using slave: stdin=%s, stdout=%s, stdout=%s", + __FUNCTION__, + stdin_path ? stdin_path : "<null>", + stdout_path ? stdout_path : "<null>", + stderr_path ? stderr_path : "<null>"); } // Set STDIN to /dev/null if we want STDIO disabled or if either @@ -834,7 +855,14 @@ ProcessGDBRemote::DoLaunch (Module *exe_module, ProcessLaunchInfo &launch_info) if (disable_stdio || (stderr_path == NULL && (stdin_path || stdout_path))) stderr_path = "/dev/null"; - if (stdin_path) + if (log) + log->Printf ("ProcessGDBRemote::%s final STDIO paths after all adjustments: stdin=%s, stdout=%s, stdout=%s", + __FUNCTION__, + stdin_path ? stdin_path : "<null>", + stdout_path ? stdout_path : "<null>", + stderr_path ? stderr_path : "<null>"); + + if (stdin_path) m_gdb_comm.SetSTDIN (stdin_path); if (stdout_path) m_gdb_comm.SetSTDOUT (stdout_path); @@ -1026,18 +1054,38 @@ ProcessGDBRemote::DidLaunchOrAttach (ArchSpec& process_arch) // prefer that over the Host information as it will be more specific // to our process. - if (m_gdb_comm.GetProcessArchitecture().IsValid()) - process_arch = m_gdb_comm.GetProcessArchitecture(); + const ArchSpec &remote_process_arch = m_gdb_comm.GetProcessArchitecture(); + if (remote_process_arch.IsValid()) + { + process_arch = remote_process_arch; + if (log) + log->Printf ("ProcessGDBRemote::%s gdb-remote had process architecture, using %s %s", + __FUNCTION__, + process_arch.GetArchitectureName () ? process_arch.GetArchitectureName () : "<null>", + process_arch.GetTriple().getTriple ().c_str() ? process_arch.GetTriple().getTriple ().c_str() : "<null>"); + } else + { process_arch = m_gdb_comm.GetHostArchitecture(); + if (log) + log->Printf ("ProcessGDBRemote::%s gdb-remote did not have process architecture, using gdb-remote host architecture %s %s", + __FUNCTION__, + process_arch.GetArchitectureName () ? process_arch.GetArchitectureName () : "<null>", + process_arch.GetTriple().getTriple ().c_str() ? process_arch.GetTriple().getTriple ().c_str() : "<null>"); + } if (process_arch.IsValid()) { ArchSpec &target_arch = GetTarget().GetArchitecture(); - if (target_arch.IsValid()) { - // If the remote host is ARM and we have apple as the vendor, then + if (log) + log->Printf ("ProcessGDBRemote::%s analyzing target arch, currently %s %s", + __FUNCTION__, + target_arch.GetArchitectureName () ? target_arch.GetArchitectureName () : "<null>", + target_arch.GetTriple().getTriple ().c_str() ? target_arch.GetTriple().getTriple ().c_str() : "<null>"); + + // If the remote host is ARM and we have apple as the vendor, then // ARM executables and shared libraries can have mixed ARM architectures. // You can have an armv6 executable, and if the host is armv7, then the // system will load the best possible architecture for all shared libraries @@ -1048,6 +1096,11 @@ ProcessGDBRemote::DidLaunchOrAttach (ArchSpec& process_arch) process_arch.GetTriple().getVendor() == llvm::Triple::Apple) { GetTarget().SetArchitecture (process_arch); + if (log) + log->Printf ("ProcessGDBRemote::%s remote process is ARM/Apple, setting target arch to %s %s", + __FUNCTION__, + process_arch.GetArchitectureName () ? process_arch.GetArchitectureName () : "<null>", + process_arch.GetTriple().getTriple ().c_str() ? process_arch.GetTriple().getTriple ().c_str() : "<null>"); } else { @@ -1066,7 +1119,14 @@ ProcessGDBRemote::DidLaunchOrAttach (ArchSpec& process_arch) target_triple.setEnvironment (remote_triple.getEnvironment()); } } + } + + if (log) + log->Printf ("ProcessGDBRemote::%s final target arch after adjustments for remote architecture: %s %s", + __FUNCTION__, + target_arch.GetArchitectureName () ? target_arch.GetArchitectureName () : "<null>", + target_arch.GetTriple().getTriple ().c_str() ? target_arch.GetTriple().getTriple ().c_str() : "<null>"); } else { @@ -1095,7 +1155,12 @@ ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid) Error ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid, const ProcessAttachInfo &attach_info) { + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); Error error; + + if (log) + log->Printf ("ProcessGDBRemote::%s()", __FUNCTION__); + // Clear out and clean up from any current state Clear(); if (attach_pid != LLDB_INVALID_PROCESS_ID) @@ -1118,13 +1183,14 @@ ProcessGDBRemote::DoAttachToProcessWithID (lldb::pid_t attach_pid, const Process if (error.Success()) { m_gdb_comm.SetDetachOnError(attach_info.GetDetachOnError()); - + char packet[64]; const int packet_len = ::snprintf (packet, sizeof(packet), "vAttach;%" PRIx64, attach_pid); SetID (attach_pid); m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncContinue, new EventDataBytes (packet, packet_len)); } } + return error; } @@ -1432,7 +1498,7 @@ ProcessGDBRemote::DoResume () TimeValue timeout; timeout = TimeValue::Now(); timeout.OffsetWithSeconds (5); - if (!IS_VALID_LLDB_HOST_THREAD(m_async_thread)) + if (!m_async_thread.IsJoinable()) { error.SetErrorString ("Trying to resume but the async thread is dead."); if (log) @@ -2891,30 +2957,17 @@ ProcessGDBRemote::StartAsyncThread () log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); Mutex::Locker start_locker(m_async_thread_state_mutex); - if (m_async_thread_state == eAsyncThreadNotStarted) + if (!m_async_thread.IsJoinable()) { // Create a thread that watches our internal state and controls which // events make it to clients (into the DCProcess event queue). - m_async_thread = Host::ThreadCreate ("<lldb.process.gdb-remote.async>", ProcessGDBRemote::AsyncThread, this, NULL); - if (IS_VALID_LLDB_HOST_THREAD(m_async_thread)) - { - m_async_thread_state = eAsyncThreadRunning; - return true; - } - else - return false; - } - else - { - // Somebody tried to start the async thread while it was either being started or stopped. If the former, and - // it started up successfully, then say all's well. Otherwise it is an error, since we aren't going to restart it. - if (log) - log->Printf ("ProcessGDBRemote::%s () - Called when Async thread was in state: %d.", __FUNCTION__, m_async_thread_state); - if (m_async_thread_state == eAsyncThreadRunning) - return true; - else - return false; + + m_async_thread = ThreadLauncher::LaunchThread("<lldb.process.gdb-remote.async>", ProcessGDBRemote::AsyncThread, this, NULL); } + else if (log) + log->Printf("ProcessGDBRemote::%s () - Called when Async thread was already running.", __FUNCTION__); + + return m_async_thread.IsJoinable(); } void @@ -2926,7 +2979,7 @@ ProcessGDBRemote::StopAsyncThread () log->Printf ("ProcessGDBRemote::%s ()", __FUNCTION__); Mutex::Locker start_locker(m_async_thread_state_mutex); - if (m_async_thread_state == eAsyncThreadRunning) + if (m_async_thread.IsJoinable()) { m_async_broadcaster.BroadcastEvent (eBroadcastBitAsyncThreadShouldExit); @@ -2934,17 +2987,10 @@ ProcessGDBRemote::StopAsyncThread () m_gdb_comm.Disconnect(); // Disconnect from the debug server. // Stop the stdio thread - if (IS_VALID_LLDB_HOST_THREAD(m_async_thread)) - { - Host::ThreadJoin (m_async_thread, NULL, NULL); - } - m_async_thread_state = eAsyncThreadDone; - } - else - { - if (log) - log->Printf ("ProcessGDBRemote::%s () - Called when Async thread was in state: %d.", __FUNCTION__, m_async_thread_state); + m_async_thread.Join(nullptr); } + else if (log) + log->Printf("ProcessGDBRemote::%s () - Called when Async thread was not running.", __FUNCTION__); } @@ -3086,7 +3132,7 @@ ProcessGDBRemote::AsyncThread (void *arg) if (log) log->Printf ("ProcessGDBRemote::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", __FUNCTION__, arg, process->GetID()); - process->m_async_thread = LLDB_INVALID_HOST_THREAD; + process->m_async_thread.Reset(); return NULL; } diff --git a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 942b31c84dde..e0c460a202d6 100644 --- a/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -25,6 +25,7 @@ #include "lldb/Core/StringList.h" #include "lldb/Core/StructuredData.h" #include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Host/HostThread.h" #include "lldb/lldb-private-forward.h" #include "lldb/Target/Process.h" #include "lldb/Target/Thread.h" @@ -74,148 +75,148 @@ public: //------------------------------------------------------------------ virtual bool CanDebug (lldb_private::Target &target, - bool plugin_specified_by_name); + bool plugin_specified_by_name) override; virtual lldb_private::CommandObject * - GetPluginCommandObject(); + GetPluginCommandObject() override; //------------------------------------------------------------------ // Creating a new process, or attaching to an existing one //------------------------------------------------------------------ virtual lldb_private::Error - WillLaunch (lldb_private::Module* module); + WillLaunch (lldb_private::Module* module) override; virtual lldb_private::Error DoLaunch (lldb_private::Module *exe_module, - lldb_private::ProcessLaunchInfo &launch_info); + lldb_private::ProcessLaunchInfo &launch_info) override; virtual void - DidLaunch (); + DidLaunch () override; virtual lldb_private::Error - WillAttachToProcessWithID (lldb::pid_t pid); + WillAttachToProcessWithID (lldb::pid_t pid) override; virtual lldb_private::Error - WillAttachToProcessWithName (const char *process_name, bool wait_for_launch); + WillAttachToProcessWithName (const char *process_name, bool wait_for_launch) override; virtual lldb_private::Error - DoConnectRemote (lldb_private::Stream *strm, const char *remote_url); + DoConnectRemote (lldb_private::Stream *strm, const char *remote_url) override; lldb_private::Error WillLaunchOrAttach (); virtual lldb_private::Error - DoAttachToProcessWithID (lldb::pid_t pid); + DoAttachToProcessWithID (lldb::pid_t pid) override; virtual lldb_private::Error - DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info); + DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info) override; virtual lldb_private::Error DoAttachToProcessWithName (const char *process_name, - const lldb_private::ProcessAttachInfo &attach_info); + const lldb_private::ProcessAttachInfo &attach_info) override; virtual void - DidAttach (lldb_private::ArchSpec &process_arch); + DidAttach (lldb_private::ArchSpec &process_arch) override; //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ virtual lldb_private::ConstString - GetPluginName(); + GetPluginName() override; virtual uint32_t - GetPluginVersion(); + GetPluginVersion() override; //------------------------------------------------------------------ // Process Control //------------------------------------------------------------------ virtual lldb_private::Error - WillResume (); + WillResume () override; virtual lldb_private::Error - DoResume (); + DoResume () override; virtual lldb_private::Error - DoHalt (bool &caused_stop); + DoHalt (bool &caused_stop) override; virtual lldb_private::Error - DoDetach (bool keep_stopped); + DoDetach (bool keep_stopped) override; virtual bool - DetachRequiresHalt() { return true; } + DetachRequiresHalt() override { return true; } virtual lldb_private::Error - DoSignal (int signal); + DoSignal (int signal) override; virtual lldb_private::Error - DoDestroy (); + DoDestroy () override; virtual void - RefreshStateAfterStop(); + RefreshStateAfterStop() override; //------------------------------------------------------------------ // Process Queries //------------------------------------------------------------------ virtual bool - IsAlive (); + IsAlive () override; virtual lldb::addr_t - GetImageInfoAddress(); + GetImageInfoAddress() override; //------------------------------------------------------------------ // Process Memory //------------------------------------------------------------------ virtual size_t - DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error); + DoReadMemory (lldb::addr_t addr, void *buf, size_t size, lldb_private::Error &error) override; virtual size_t - DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error &error); + DoWriteMemory (lldb::addr_t addr, const void *buf, size_t size, lldb_private::Error &error) override; virtual lldb::addr_t - DoAllocateMemory (size_t size, uint32_t permissions, lldb_private::Error &error); + DoAllocateMemory (size_t size, uint32_t permissions, lldb_private::Error &error) override; virtual lldb_private::Error GetMemoryRegionInfo (lldb::addr_t load_addr, - lldb_private::MemoryRegionInfo ®ion_info); + lldb_private::MemoryRegionInfo ®ion_info) override; virtual lldb_private::Error - DoDeallocateMemory (lldb::addr_t ptr); + DoDeallocateMemory (lldb::addr_t ptr) override; //------------------------------------------------------------------ // Process STDIO //------------------------------------------------------------------ virtual size_t - PutSTDIN (const char *buf, size_t buf_size, lldb_private::Error &error); + PutSTDIN (const char *buf, size_t buf_size, lldb_private::Error &error) override; //---------------------------------------------------------------------- // Process Breakpoints //---------------------------------------------------------------------- virtual lldb_private::Error - EnableBreakpointSite (lldb_private::BreakpointSite *bp_site); + EnableBreakpointSite (lldb_private::BreakpointSite *bp_site) override; virtual lldb_private::Error - DisableBreakpointSite (lldb_private::BreakpointSite *bp_site); + DisableBreakpointSite (lldb_private::BreakpointSite *bp_site) override; //---------------------------------------------------------------------- // Process Watchpoints //---------------------------------------------------------------------- virtual lldb_private::Error - EnableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true); + EnableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true) override; virtual lldb_private::Error - DisableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true); + DisableWatchpoint (lldb_private::Watchpoint *wp, bool notify = true) override; virtual lldb_private::Error - GetWatchpointSupportInfo (uint32_t &num); + GetWatchpointSupportInfo (uint32_t &num) override; virtual lldb_private::Error - GetWatchpointSupportInfo (uint32_t &num, bool& after); + GetWatchpointSupportInfo (uint32_t &num, bool& after) override; virtual bool - StartNoticingNewThreads(); + StartNoticingNewThreads() override; virtual bool - StopNoticingNewThreads(); + StopNoticingNewThreads() override; GDBRemoteCommunicationClient & GetGDBRemote() @@ -224,13 +225,13 @@ public: } virtual lldb_private::Error - SendEventData(const char *data); + SendEventData(const char *data) override; //---------------------------------------------------------------------- // Override SetExitStatus so we can disconnect from the remote GDB server //---------------------------------------------------------------------- virtual bool - SetExitStatus (int exit_status, const char *cstr); + SetExitStatus (int exit_status, const char *cstr) override; void SetUserSpecifiedMaxMemoryTransferSize (uint64_t user_specified_max); @@ -286,7 +287,7 @@ protected: virtual bool UpdateThreadList (lldb_private::ThreadList &old_thread_list, - lldb_private::ThreadList &new_thread_list); + lldb_private::ThreadList &new_thread_list) override; lldb_private::Error LaunchAndConnectToDebugserver (const lldb_private::ProcessInfo &process_info); @@ -324,23 +325,15 @@ protected: eBroadcastBitAsyncThreadShouldExit = (1 << 1), eBroadcastBitAsyncThreadDidExit = (1 << 2) }; - - typedef enum AsyncThreadState - { - eAsyncThreadNotStarted, - eAsyncThreadRunning, - eAsyncThreadDone - } AsyncThreadState; lldb_private::Flags m_flags; // Process specific flags (see eFlags enums) GDBRemoteCommunicationClient m_gdb_comm; - lldb::pid_t m_debugserver_pid; + std::atomic<lldb::pid_t> m_debugserver_pid; StringExtractorGDBRemote m_last_stop_packet; lldb_private::Mutex m_last_stop_packet_mutex; GDBRemoteDynamicRegisterInfo m_register_info; lldb_private::Broadcaster m_async_broadcaster; - lldb::thread_t m_async_thread; - AsyncThreadState m_async_thread_state; + lldb_private::HostThread m_async_thread; lldb_private::Mutex m_async_thread_state_mutex; typedef std::vector<lldb::tid_t> tid_collection; typedef std::vector< std::pair<lldb::tid_t,int> > tid_sig_collection; @@ -395,7 +388,7 @@ protected: std::string &dispatch_queue_name); lldb_private::DynamicLoader * - GetDynamicLoader (); + GetDynamicLoader () override; private: //------------------------------------------------------------------ diff --git a/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp b/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp index bc2e7a62b76e..14fc2cea2329 100644 --- a/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp +++ b/source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp @@ -73,14 +73,14 @@ DWARFAbbreviationDeclaration::Extract(const DWARFDataExtractor& data, lldb::offs void DWARFAbbreviationDeclaration::Dump(Stream *s) const { -// *ostrm_ptr << std::setfill(' ') << std::dec << '[' << std::setw(3) << std::right << m_code << ']' << ' ' << std::setw(30) << std::left << DW_TAG_value_to_name(m_tag) << DW_CHILDREN_value_to_name(m_has_children) << std::endl; -// -// DWARFAttribute::const_iterator pos; -// -// for (pos = m_attributes.begin(); pos != m_attributes.end(); ++pos) -// *ostrm_ptr << " " << std::setw(29) << std::left << DW_AT_value_to_name(pos->attr()) << ' ' << DW_FORM_value_to_name(pos->form()) << std::endl; -// -// *ostrm_ptr << std::endl; + s->Printf("Debug Abbreviation Declaration: code = 0x%4.4x, tag = %s, has_children = %s\n", m_code, DW_TAG_value_to_name(m_tag), DW_CHILDREN_value_to_name(m_has_children)); + + DWARFAttribute::const_iterator pos; + + for (pos = m_attributes.begin(); pos != m_attributes.end(); ++pos) + s->Printf(" attr = %s, form = %s\n", DW_AT_value_to_name(pos->get_attr()), DW_FORM_value_to_name(pos->get_form())); + + s->Printf("\n"); } diff --git a/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp b/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp index b2e64ad9f8c8..067449a289be 100644 --- a/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp +++ b/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp @@ -49,7 +49,8 @@ DWARFCompileUnit::DWARFCompileUnit(SymbolFileDWARF* dwarf2Data) : m_producer (eProducerInvalid), m_producer_version_major (0), m_producer_version_minor (0), - m_producer_version_update (0) + m_producer_version_update (0), + m_is_dwarf64 (false) { } @@ -66,6 +67,7 @@ DWARFCompileUnit::Clear() m_func_aranges_ap.reset(); m_user_data = NULL; m_producer = eProducerInvalid; + m_is_dwarf64 = false; } bool @@ -79,9 +81,10 @@ DWARFCompileUnit::Extract(const DWARFDataExtractor &debug_info, lldb::offset_t * { dw_offset_t abbr_offset; const DWARFDebugAbbrev *abbr = m_dwarf2Data->DebugAbbrev(); - m_length = debug_info.GetU32(offset_ptr); + m_length = debug_info.GetDWARFInitialLength(offset_ptr); + m_is_dwarf64 = debug_info.IsDWARF64(); m_version = debug_info.GetU16(offset_ptr); - abbr_offset = debug_info.GetU32(offset_ptr); + abbr_offset = debug_info.GetDWARFOffset(offset_ptr); m_addr_size = debug_info.GetU8 (offset_ptr); bool length_OK = debug_info.ValidOffset(GetNextCompileUnitOffset()-1); @@ -168,7 +171,7 @@ DWARFCompileUnit::ExtractDIEsIfNeeded (bool cu_die_only) die_index_stack.reserve(32); die_index_stack.push_back(0); bool prev_die_had_children = false; - const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (GetAddressByteSize()); + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (GetAddressByteSize(), m_is_dwarf64); while (offset < next_cu_offset && die.FastExtract (debug_info_data, this, fixed_form_sizes, &offset)) { @@ -347,6 +350,14 @@ DWARFCompileUnit::GetAddressByteSize(const DWARFCompileUnit* cu) return DWARFCompileUnit::GetDefaultAddressSize(); } +bool +DWARFCompileUnit::IsDWARF64(const DWARFCompileUnit* cu) +{ + if (cu) + return cu->IsDWARF64(); + return false; +} + uint8_t DWARFCompileUnit::GetDefaultAddressSize() { @@ -619,7 +630,7 @@ DWARFCompileUnit::Index (const uint32_t cu_idx, { const DWARFDataExtractor* debug_str = &m_dwarf2Data->get_debug_str_data(); - const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (GetAddressByteSize()); + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (GetAddressByteSize(), m_is_dwarf64); Log *log (LogChannelDWARF::GetLogIfAll (DWARF_LOG_LOOKUPS)); @@ -761,7 +772,7 @@ DWARFCompileUnit::Index (const uint32_t cu_idx, case DW_AT_specification: if (attributes.ExtractFormValueAtIndex(m_dwarf2Data, i, form_value)) - specification_die_offset = form_value.Reference(this); + specification_die_offset = form_value.Reference(); break; } } @@ -1030,3 +1041,9 @@ DWARFCompileUnit::GetProducerVersionUpdate() return m_producer_version_update; } +bool +DWARFCompileUnit::IsDWARF64() const +{ + return m_is_dwarf64; +} + diff --git a/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h b/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h index ed1894b8a9f6..3e904892dbd5 100644 --- a/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h +++ b/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h @@ -41,11 +41,11 @@ public: bool Verify(lldb_private::Stream *s) const; void Dump(lldb_private::Stream *s) const; dw_offset_t GetOffset() const { return m_offset; } - uint32_t Size() const { return 11; /* Size in bytes of the compile unit header */ } + uint32_t Size() const { return m_is_dwarf64 ? 23 : 11; /* Size in bytes of the compile unit header */ } bool ContainsDIEOffset(dw_offset_t die_offset) const { return die_offset >= GetFirstDIEOffset() && die_offset < GetNextCompileUnitOffset(); } dw_offset_t GetFirstDIEOffset() const { return m_offset + Size(); } - dw_offset_t GetNextCompileUnitOffset() const { return m_offset + m_length + 4; } - size_t GetDebugInfoSize() const { return m_length + 4 - Size(); /* Size in bytes of the .debug_info data associated with this compile unit. */ } + dw_offset_t GetNextCompileUnitOffset() const { return m_offset + m_length + (m_is_dwarf64 ? 12 : 4); } + size_t GetDebugInfoSize() const { return m_length + (m_is_dwarf64 ? 12 : 4) - Size(); /* Size in bytes of the .debug_info data associated with this compile unit. */ } uint32_t GetLength() const { return m_length; } uint16_t GetVersion() const { return m_version; } const DWARFAbbreviationDeclarationSet* GetAbbreviations() const { return m_abbrevs; } @@ -118,6 +118,9 @@ public: static uint8_t GetAddressByteSize(const DWARFCompileUnit* cu); + static bool + IsDWARF64(const DWARFCompileUnit* cu); + static uint8_t GetDefaultAddressSize(); @@ -183,6 +186,9 @@ public: uint32_t GetProducerVersionUpdate(); + bool + IsDWARF64() const; + protected: SymbolFileDWARF* m_dwarf2Data; const DWARFAbbreviationDeclarationSet *m_abbrevs; @@ -191,13 +197,14 @@ protected: std::unique_ptr<DWARFDebugAranges> m_func_aranges_ap; // A table similar to the .debug_aranges table, but this one points to the exact DW_TAG_subprogram DIEs dw_addr_t m_base_addr; dw_offset_t m_offset; - uint32_t m_length; + dw_offset_t m_length; uint16_t m_version; uint8_t m_addr_size; Producer m_producer; uint32_t m_producer_version_major; uint32_t m_producer_version_minor; uint32_t m_producer_version_update; + bool m_is_dwarf64; void ParseProducerInfo (); diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h b/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h index 6e5b974a71ff..ba2e8ad08acc 100644 --- a/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h +++ b/source/Plugins/SymbolFile/DWARF/DWARFDataExtractor.h @@ -33,6 +33,9 @@ public: size_t GetDWARFSizeofInitialLength() const { return m_is_dwarf64 ? 12 : 4; } + bool + IsDWARF64() const { return m_is_dwarf64; } + protected: mutable bool m_is_dwarf64; }; diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp index a8c553e2d2ca..393434800c01 100644 --- a/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp @@ -555,8 +555,15 @@ static dw_offset_t DumpCallback if (dumpInfo->die_offset == DW_INVALID_OFFSET) { // We are dumping everything - cu->Dump(s); - return cu->GetFirstDIEOffset(); // Return true to parse all DIEs in this Compile Unit + if (cu) + { + cu->Dump(s); + return cu->GetFirstDIEOffset(); // Return true to parse all DIEs in this Compile Unit + } + else + { + return DW_INVALID_OFFSET; + } } else { @@ -568,7 +575,7 @@ static dw_offset_t DumpCallback // We are dumping only a single DIE possibly with it's children and // we must find it's compile unit before we can dump it properly - if (dumpInfo->die_offset < cu->GetFirstDIEOffset()) + if (cu && dumpInfo->die_offset < cu->GetFirstDIEOffset()) { // Not found, maybe the DIE offset provided wasn't correct? // *ostrm_ptr << "DIE at offset " << HEX32 << dumpInfo->die_offset << " was not found." << endl; @@ -577,7 +584,7 @@ static dw_offset_t DumpCallback else { // See if the DIE is in this compile unit? - if (dumpInfo->die_offset < cu->GetNextCompileUnitOffset()) + if (cu && dumpInfo->die_offset < cu->GetNextCompileUnitOffset()) { // This DIE is in this compile unit! if (s->GetVerbose()) @@ -590,7 +597,14 @@ static dw_offset_t DumpCallback else { // Skip to the next compile unit as the DIE isn't in the current one! - return cu->GetNextCompileUnitOffset(); + if (cu) + { + return cu->GetNextCompileUnitOffset(); + } + else + { + return DW_INVALID_OFFSET; + } } } } diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp index 856a7fa7a2ff..10b51ffe0a8a 100644 --- a/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp @@ -88,9 +88,10 @@ DWARFDebugInfoEntry::Attributes::RemoveAttribute(dw_attr_t attr) bool DWARFDebugInfoEntry::Attributes::ExtractFormValueAtIndex (SymbolFileDWARF* dwarf2Data, uint32_t i, DWARFFormValue &form_value) const { + form_value.SetCompileUnit(CompileUnitAtIndex(i)); form_value.SetForm(FormAtIndex(i)); lldb::offset_t offset = DIEOffsetAtIndex(i); - return form_value.ExtractValue(dwarf2Data->get_debug_info_data(), &offset, CompileUnitAtIndex(i)); + return form_value.ExtractValue(dwarf2Data->get_debug_info_data(), &offset); } uint64_t @@ -107,7 +108,7 @@ DWARFDebugInfoEntry::Attributes::FormValueAsUnsignedAtIndex(SymbolFileDWARF* dwa { DWARFFormValue form_value; if (ExtractFormValueAtIndex(dwarf2Data, i, form_value)) - return form_value.Reference(CompileUnitAtIndex(i)); + return form_value.Reference(); return fail_value; } @@ -190,7 +191,7 @@ DWARFDebugInfoEntry::FastExtract if (cu->GetVersion() <= 2) form_size = cu->GetAddressByteSize(); else - form_size = 4; // 4 bytes for DWARF 32, 8 bytes for DWARF 64, but we don't support DWARF64 yet + form_size = cu->IsDWARF64() ? 8 : 4; break; // 0 sized form @@ -212,7 +213,6 @@ DWARFDebugInfoEntry::FastExtract break; // 4 byte values - case DW_FORM_strp : case DW_FORM_data4 : case DW_FORM_ref4 : form_size = 4; @@ -237,11 +237,12 @@ DWARFDebugInfoEntry::FastExtract form = debug_info_data.GetULEB128 (&offset); break; + case DW_FORM_strp : case DW_FORM_sec_offset : - if (cu->GetAddressByteSize () == 4) - debug_info_data.GetU32 (offset_ptr); - else + if (cu->IsDWARF64 ()) debug_info_data.GetU64 (offset_ptr); + else + debug_info_data.GetU32 (offset_ptr); break; default: @@ -284,7 +285,6 @@ DWARFDebugInfoEntry::Extract const DWARFDataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); // const DWARFDataExtractor& debug_str_data = dwarf2Data->get_debug_str_data(); const uint32_t cu_end_offset = cu->GetNextCompileUnitOffset(); - const uint8_t cu_addr_size = cu->GetAddressByteSize(); lldb::offset_t offset = *offset_ptr; // if (offset >= cu_end_offset) // Log::Error("DIE at offset 0x%8.8x is beyond the end of the current compile unit (0x%8.8x)", m_offset, cu_end_offset); @@ -319,8 +319,8 @@ DWARFDebugInfoEntry::Extract if (isCompileUnitTag && ((attr == DW_AT_entry_pc) || (attr == DW_AT_low_pc))) { - DWARFFormValue form_value(form); - if (form_value.ExtractValue(debug_info_data, &offset, cu)) + DWARFFormValue form_value(cu, form); + if (form_value.ExtractValue(debug_info_data, &offset)) { if (attr == DW_AT_low_pc || attr == DW_AT_entry_pc) ((DWARFCompileUnit*)cu)->SetBaseAddress(form_value.Unsigned()); @@ -348,13 +348,13 @@ DWARFDebugInfoEntry::Extract // Compile unit address sized values case DW_FORM_addr : - form_size = cu_addr_size; + form_size = cu->GetAddressByteSize(); break; case DW_FORM_ref_addr : if (cu->GetVersion() <= 2) - form_size = cu_addr_size; + form_size = cu->GetAddressByteSize(); else - form_size = 4; // 4 bytes for DWARF 32, 8 bytes for DWARF 64, but we don't support DWARF64 yet + form_size = cu->IsDWARF64() ? 8 : 4; break; // 0 sized form @@ -376,10 +376,6 @@ DWARFDebugInfoEntry::Extract break; // 4 byte values - case DW_FORM_strp : - form_size = 4; - break; - case DW_FORM_data4 : case DW_FORM_ref4 : form_size = 4; @@ -404,11 +400,12 @@ DWARFDebugInfoEntry::Extract form_is_indirect = true; break; + case DW_FORM_strp : case DW_FORM_sec_offset : - if (cu->GetAddressByteSize () == 4) - debug_info_data.GetU32 (offset_ptr); - else + if (cu->IsDWARF64 ()) debug_info_data.GetU64 (offset_ptr); + else + debug_info_data.GetU32 (offset_ptr); break; default: @@ -781,8 +778,8 @@ DWARFDebugInfoEntry::GetDIENamesAndRanges for (i=0; i<numAttributes; ++i) { abbrevDecl->GetAttrAndFormByIndexUnchecked(i, attr, form); - DWARFFormValue form_value(form); - if (form_value.ExtractValue(debug_info_data, &offset, cu)) + DWARFFormValue form_value(cu, form); + if (form_value.ExtractValue(debug_info_data, &offset)) { switch (attr) { @@ -832,11 +829,11 @@ DWARFDebugInfoEntry::GetDIENamesAndRanges break; case DW_AT_abstract_origin: - die_offsets.push_back(form_value.Reference(cu)); + die_offsets.push_back(form_value.Reference()); break; case DW_AT_specification: - die_offsets.push_back(form_value.Reference(cu)); + die_offsets.push_back(form_value.Reference()); break; case DW_AT_decl_file: @@ -1033,7 +1030,7 @@ DWARFDebugInfoEntry::DumpLocation const char *obj_file_name = NULL; ObjectFile *obj_file = dwarf2Data->GetObjectFile(); if (obj_file) - obj_file_name = obj_file->GetFileSpec().GetFilename().AsCString(); + obj_file_name = obj_file->GetFileSpec().GetFilename().AsCString("<Unknown>"); const char *die_name = GetName (dwarf2Data, cu); s.Printf ("0x%8.8x/0x%8.8x: %-30s (from %s in %s)", cu->GetOffset(), @@ -1077,9 +1074,9 @@ DWARFDebugInfoEntry::DumpAttribute s.Printf( "[%s", DW_FORM_value_to_name(form)); } - DWARFFormValue form_value(form); + DWARFFormValue form_value(cu, form); - if (!form_value.ExtractValue(debug_info_data, offset_ptr, cu)) + if (!form_value.ExtractValue(debug_info_data, offset_ptr)) return; if (show_form) @@ -1097,7 +1094,7 @@ DWARFDebugInfoEntry::DumpAttribute // Always dump form value if verbose is enabled if (verbose) { - form_value.Dump(s, debug_str_data, cu); + form_value.Dump(s, debug_str_data); } @@ -1130,7 +1127,7 @@ DWARFDebugInfoEntry::DumpAttribute if (blockData) { if (!verbose) - form_value.Dump(s, debug_str_data, cu); + form_value.Dump(s, debug_str_data); // Location description is inlined in data in the form value DWARFDataExtractor locationData(debug_info_data, (*offset_ptr) - form_value.Unsigned(), form_value.Unsigned()); @@ -1147,13 +1144,13 @@ DWARFDebugInfoEntry::DumpAttribute if (dwarf2Data) { if ( !verbose ) - form_value.Dump(s, debug_str_data, cu); + form_value.Dump(s, debug_str_data); DWARFLocationList::Dump(s, cu, dwarf2Data->get_debug_loc_data(), debug_loc_offset); } else { if ( !verbose ) - form_value.Dump(s, NULL, cu); + form_value.Dump(s, NULL); } } } @@ -1162,8 +1159,8 @@ DWARFDebugInfoEntry::DumpAttribute case DW_AT_abstract_origin: case DW_AT_specification: { - uint64_t abstract_die_offset = form_value.Reference(cu); - form_value.Dump(s, debug_str_data, cu); + uint64_t abstract_die_offset = form_value.Reference(); + form_value.Dump(s, debug_str_data); // *ostrm_ptr << HEX32 << abstract_die_offset << " ( "; if ( verbose ) s.PutCString(" ( "); GetName(dwarf2Data, cu, abstract_die_offset, s); @@ -1173,9 +1170,9 @@ DWARFDebugInfoEntry::DumpAttribute case DW_AT_type: { - uint64_t type_die_offset = form_value.Reference(cu); + uint64_t type_die_offset = form_value.Reference(); if (!verbose) - form_value.Dump(s, debug_str_data, cu); + form_value.Dump(s, debug_str_data); s.PutCString(" ( "); AppendTypeName(dwarf2Data, cu, type_die_offset, s); s.PutCString(" )"); @@ -1185,7 +1182,7 @@ DWARFDebugInfoEntry::DumpAttribute case DW_AT_ranges: { if ( !verbose ) - form_value.Dump(s, debug_str_data, cu); + form_value.Dump(s, debug_str_data); lldb::offset_t ranges_offset = form_value.Unsigned(); dw_addr_t base_addr = cu ? cu->GetBaseAddress() : 0; if (dwarf2Data) @@ -1195,7 +1192,7 @@ DWARFDebugInfoEntry::DumpAttribute default: if ( !verbose ) - form_value.Dump(s, debug_str_data, cu); + form_value.Dump(s, debug_str_data); break; } @@ -1226,13 +1223,12 @@ DWARFDebugInfoEntry::GetAttributes const DWARFDataExtractor& debug_info_data = dwarf2Data->get_debug_info_data(); if (fixed_form_sizes == NULL) - fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize(cu->GetAddressByteSize()); + fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize(cu->GetAddressByteSize(), cu->IsDWARF64()); const uint32_t num_attributes = abbrevDecl->NumAttributes(); uint32_t i; dw_attr_t attr; dw_form_t form; - DWARFFormValue form_value; for (i=0; i<num_attributes; ++i) { abbrevDecl->GetAttrAndFormByIndexUnchecked (i, attr, form); @@ -1259,11 +1255,11 @@ DWARFDebugInfoEntry::GetAttributes if ((attr == DW_AT_specification) || (attr == DW_AT_abstract_origin)) { - form_value.SetForm(form); - if (form_value.ExtractValue(debug_info_data, &offset, cu)) + DWARFFormValue form_value (cu, form); + if (form_value.ExtractValue(debug_info_data, &offset)) { const DWARFDebugInfoEntry* die = NULL; - dw_offset_t die_offset = form_value.Reference(cu); + dw_offset_t die_offset = form_value.Reference(); if (cu->ContainsDIEOffset(die_offset)) { die = const_cast<DWARFCompileUnit*>(cu)->GetDIEPtr(die_offset); @@ -1331,8 +1327,9 @@ DWARFDebugInfoEntry::GetAttributeValue DWARFFormValue::SkipValue(abbrevDecl->GetFormByIndex(idx++), debug_info_data, &offset, cu); const dw_offset_t attr_offset = offset; + form_value.SetCompileUnit(cu); form_value.SetForm(abbrevDecl->GetFormByIndex(idx)); - if (form_value.ExtractValue(debug_info_data, &offset, cu)) + if (form_value.ExtractValue(debug_info_data, &offset)) { if (end_attr_offset_ptr) *end_attr_offset_ptr = offset; @@ -1423,7 +1420,7 @@ DWARFDebugInfoEntry::GetAttributeValueAsReference { DWARFFormValue form_value; if (GetAttributeValue(dwarf2Data, cu, attr, form_value)) - return form_value.Reference(cu); + return form_value.Reference(); return fail_value; } @@ -1590,7 +1587,7 @@ DWARFDebugInfoEntry::GetName if (GetAttributeValue(dwarf2Data, cu, DW_AT_specification, form_value)) { DWARFCompileUnitSP cu_sp_ptr; - const DWARFDebugInfoEntry* die = const_cast<SymbolFileDWARF*>(dwarf2Data)->DebugInfo()->GetDIEPtr(form_value.Reference(cu), &cu_sp_ptr); + const DWARFDebugInfoEntry* die = const_cast<SymbolFileDWARF*>(dwarf2Data)->DebugInfo()->GetDIEPtr(form_value.Reference(), &cu_sp_ptr); if (die) return die->GetName(dwarf2Data, cu_sp_ptr.get()); } @@ -1661,7 +1658,7 @@ DWARFDebugInfoEntry::GetPubname // The specification DIE may be in another compile unit so we need // to get a die and its compile unit. DWARFCompileUnitSP cu_sp_ptr; - const DWARFDebugInfoEntry* die = const_cast<SymbolFileDWARF*>(dwarf2Data)->DebugInfo()->GetDIEPtr(form_value.Reference(cu), &cu_sp_ptr); + const DWARFDebugInfoEntry* die = const_cast<SymbolFileDWARF*>(dwarf2Data)->DebugInfo()->GetDIEPtr(form_value.Reference(), &cu_sp_ptr); if (die) return die->GetPubname(dwarf2Data, cu_sp_ptr.get()); } @@ -1798,7 +1795,7 @@ DWARFDebugInfoEntry::AppendTypeName DWARFFormValue form_value; if (die.GetAttributeValue(dwarf2Data, cu, DW_AT_type, form_value)) { - uint64_t next_die_offset = form_value.Reference(cu); + uint64_t next_die_offset = form_value.Reference(); result = AppendTypeName(dwarf2Data, cu, next_die_offset, s); } diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp index 6a2649463b54..11589aede708 100644 --- a/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp @@ -418,12 +418,16 @@ DWARFDebugLine::ParsePrologue(const DWARFDataExtractor& debug_line_data, lldb::o const char * s; prologue->total_length = debug_line_data.GetDWARFInitialLength(offset_ptr); prologue->version = debug_line_data.GetU16(offset_ptr); - if (prologue->version < 2 || prologue->version > 3) + if (prologue->version < 2 || prologue->version > 4) return false; prologue->prologue_length = debug_line_data.GetDWARFOffset(offset_ptr); const lldb::offset_t end_prologue_offset = prologue->prologue_length + *offset_ptr; prologue->min_inst_length = debug_line_data.GetU8(offset_ptr); + if (prologue->version >= 4) + prologue->maximum_operations_per_instruction = debug_line_data.GetU8(offset_ptr); + else + prologue->maximum_operations_per_instruction = 1; prologue->default_is_stmt = debug_line_data.GetU8(offset_ptr); prologue->line_base = debug_line_data.GetU8(offset_ptr); prologue->line_range = debug_line_data.GetU8(offset_ptr); @@ -486,13 +490,16 @@ DWARFDebugLine::ParseSupportFiles (const lldb::ModuleSP &module_sp, (void)debug_line_data.GetDWARFInitialLength(&offset); const char * s; uint32_t version = debug_line_data.GetU16(&offset); - if (version < 2 || version > 3) + if (version < 2 || version > 4) return false; const dw_offset_t end_prologue_offset = debug_line_data.GetDWARFOffset(&offset) + offset; - // Skip instruction length, default is stmt, line base, line range and - // opcode base, and all opcode lengths + // Skip instruction length, default is stmt, line base, line range offset += 4; + // For DWARF4, skip maximum operations per instruction + if (version >= 4) + offset += 1; + // Skip opcode base, and all opcode lengths const uint8_t opcode_base = debug_line_data.GetU8(&offset); offset += opcode_base - 1; std::vector<std::string> include_directories; @@ -650,7 +657,10 @@ DWARFDebugLine::ParseStatementTable // relocatable address. All of the other statement program opcodes // that affect the address register add a delta to it. This instruction // stores a relocatable value into it instead. - state.address = debug_line_data.GetAddress(offset_ptr); + if (arg_size == 4) + state.address = debug_line_data.GetU32(offset_ptr); + else // arg_size == 8 + state.address = debug_line_data.GetU64(offset_ptr); break; case DW_LNE_define_file: diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h b/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h index 0caaf596add5..7da0e76a22be 100644 --- a/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h @@ -75,6 +75,7 @@ public: uint16_t version; // Version identifier for the statement information format. uint32_t prologue_length;// The number of bytes following the prologue_length field to the beginning of the first byte of the statement program itself. uint8_t min_inst_length;// The size in bytes of the smallest target machine instruction. Statement program opcodes that alter the address register first multiply their operands by this value. + uint8_t maximum_operations_per_instruction; // New in DWARF4. The maximum number of individual operations that may be encoded in an instruction. uint8_t default_is_stmt;// The initial value of theis_stmtregister. int8_t line_base; // This parameter affects the meaning of the special opcodes. See below. uint8_t line_range; // This parameter affects the meaning of the special opcodes. See below. diff --git a/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.cpp b/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.cpp index 488e9820b6fa..8469b78d0dd5 100644 --- a/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.cpp +++ b/source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.cpp @@ -87,7 +87,7 @@ DWARFDebugPubnames::GeneratePubnames(SymbolFileDWARF* dwarf2Data) DWARFCompileUnit* cu = debug_info->GetCompileUnitAtIndex(cu_idx); - const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (cu->GetAddressByteSize()); + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (cu->GetAddressByteSize(), cu->IsDWARF64()); bool clear_dies = cu->ExtractDIEsIfNeeded (false) > 1; diff --git a/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp b/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp index ab8e68ab5516..a8c550e9176f 100644 --- a/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp +++ b/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp @@ -96,36 +96,92 @@ g_form_sizes_addr8[] = 8, // 0x20 DW_FORM_ref_sig8 }; +// Difference with g_form_sizes_addr8: +// DW_FORM_strp and DW_FORM_sec_offset are 8 instead of 4 +static uint8_t +g_form_sizes_addr8_dwarf64[] = +{ + 0, // 0x00 unused + 8, // 0x01 DW_FORM_addr + 0, // 0x02 unused + 0, // 0x03 DW_FORM_block2 + 0, // 0x04 DW_FORM_block4 + 2, // 0x05 DW_FORM_data2 + 4, // 0x06 DW_FORM_data4 + 8, // 0x07 DW_FORM_data8 + 0, // 0x08 DW_FORM_string + 0, // 0x09 DW_FORM_block + 0, // 0x0a DW_FORM_block1 + 1, // 0x0b DW_FORM_data1 + 1, // 0x0c DW_FORM_flag + 0, // 0x0d DW_FORM_sdata + 8, // 0x0e DW_FORM_strp + 0, // 0x0f DW_FORM_udata + 0, // 0x10 DW_FORM_ref_addr (addr size for DWARF2 and earlier, 4 bytes for DWARF32, 8 bytes for DWARF32 in DWARF 3 and later + 1, // 0x11 DW_FORM_ref1 + 2, // 0x12 DW_FORM_ref2 + 4, // 0x13 DW_FORM_ref4 + 8, // 0x14 DW_FORM_ref8 + 0, // 0x15 DW_FORM_ref_udata + 0, // 0x16 DW_FORM_indirect + 8, // 0x17 DW_FORM_sec_offset + 0, // 0x18 DW_FORM_exprloc + 0, // 0x19 DW_FORM_flag_present + 0, // 0x1a + 0, // 0x1b + 0, // 0x1c + 0, // 0x1d + 0, // 0x1e + 0, // 0x1f + 8, // 0x20 DW_FORM_ref_sig8 +}; + const uint8_t * -DWARFFormValue::GetFixedFormSizesForAddressSize (uint8_t addr_size) +DWARFFormValue::GetFixedFormSizesForAddressSize (uint8_t addr_size, bool is_dwarf64) { - switch (addr_size) - { - case 4: return g_form_sizes_addr4; - case 8: return g_form_sizes_addr8; + if (!is_dwarf64) { + switch (addr_size) + { + case 4: return g_form_sizes_addr4; + case 8: return g_form_sizes_addr8; + } + } else { + if (addr_size == 8) + return g_form_sizes_addr8_dwarf64; + // is_dwarf64 && addr_size == 4 : no provider does this. } return NULL; } -DWARFFormValue::DWARFFormValue(dw_form_t form) : +DWARFFormValue::DWARFFormValue() : + m_cu (NULL), + m_form(0), + m_value() +{ +} + +DWARFFormValue::DWARFFormValue(const DWARFCompileUnit* cu, dw_form_t form) : + m_cu (cu), m_form(form), m_value() { } bool -DWARFFormValue::ExtractValue(const DWARFDataExtractor& data, lldb::offset_t* offset_ptr, const DWARFCompileUnit* cu) +DWARFFormValue::ExtractValue(const DWARFDataExtractor& data, lldb::offset_t* offset_ptr) { bool indirect = false; bool is_block = false; m_value.data = NULL; + uint8_t ref_addr_size; // Read the value for the form into value and follow and DW_FORM_indirect instances we run into do { indirect = false; switch (m_form) { - case DW_FORM_addr: m_value.value.uval = data.GetMaxU64(offset_ptr, DWARFCompileUnit::GetAddressByteSize(cu)); break; + case DW_FORM_addr: assert(m_cu); + m_value.value.uval = data.GetMaxU64(offset_ptr, DWARFCompileUnit::GetAddressByteSize(m_cu)); break; case DW_FORM_block2: m_value.value.uval = data.GetU16(offset_ptr); is_block = true; break; case DW_FORM_block4: m_value.value.uval = data.GetU32(offset_ptr); is_block = true; break; case DW_FORM_data2: m_value.value.uval = data.GetU16(offset_ptr); break; @@ -142,15 +198,17 @@ DWARFFormValue::ExtractValue(const DWARFDataExtractor& data, lldb::offset_t* off case DW_FORM_data1: m_value.value.uval = data.GetU8(offset_ptr); break; case DW_FORM_flag: m_value.value.uval = data.GetU8(offset_ptr); break; case DW_FORM_sdata: m_value.value.sval = data.GetSLEB128(offset_ptr); break; - case DW_FORM_strp: m_value.value.uval = data.GetU32(offset_ptr); break; + case DW_FORM_strp: assert(m_cu); + m_value.value.uval = data.GetMaxU64(offset_ptr, DWARFCompileUnit::IsDWARF64(m_cu) ? 8 : 4); break; // case DW_FORM_APPLE_db_str: case DW_FORM_udata: m_value.value.uval = data.GetULEB128(offset_ptr); break; - case DW_FORM_ref_addr: - if (cu->GetVersion() <= 2) - m_value.value.uval = data.GetMaxU64(offset_ptr, DWARFCompileUnit::GetAddressByteSize(cu)); - else - m_value.value.uval = data.GetU32(offset_ptr); // 4 for DWARF32, 8 for DWARF64, but we don't support DWARF64 yet - break; + case DW_FORM_ref_addr: assert(m_cu); + ref_addr_size = 4; + if (m_cu->GetVersion() <= 2) + ref_addr_size = m_cu->GetAddressByteSize(); + else + ref_addr_size = m_cu->IsDWARF64() ? 8 : 4; + m_value.value.uval = data.GetMaxU64(offset_ptr, ref_addr_size); break; case DW_FORM_ref1: m_value.value.uval = data.GetU8(offset_ptr); break; case DW_FORM_ref2: m_value.value.uval = data.GetU16(offset_ptr); break; case DW_FORM_ref4: m_value.value.uval = data.GetU32(offset_ptr); break; @@ -161,7 +219,8 @@ DWARFFormValue::ExtractValue(const DWARFDataExtractor& data, lldb::offset_t* off indirect = true; break; - case DW_FORM_sec_offset: m_value.value.uval = data.GetU32(offset_ptr); break; + case DW_FORM_sec_offset: assert(m_cu); + m_value.value.uval = data.GetMaxU64(offset_ptr, DWARFCompileUnit::IsDWARF64(m_cu) ? 8 : 4); break; case DW_FORM_flag_present: m_value.value.uval = 1; break; case DW_FORM_ref_sig8: m_value.value.uval = data.GetU64(offset_ptr); break; default: @@ -183,14 +242,15 @@ DWARFFormValue::ExtractValue(const DWARFDataExtractor& data, lldb::offset_t* off } bool -DWARFFormValue::SkipValue(const DWARFDataExtractor& debug_info_data, lldb::offset_t *offset_ptr, const DWARFCompileUnit* cu) const +DWARFFormValue::SkipValue(const DWARFDataExtractor& debug_info_data, lldb::offset_t *offset_ptr) const { - return DWARFFormValue::SkipValue(m_form, debug_info_data, offset_ptr, cu); + return DWARFFormValue::SkipValue(m_form, debug_info_data, offset_ptr, m_cu); } bool DWARFFormValue::SkipValue(dw_form_t form, const DWARFDataExtractor& debug_info_data, lldb::offset_t *offset_ptr, const DWARFCompileUnit* cu) { + uint8_t ref_addr_size; switch (form) { // Blocks if inlined data that have a length field and the data bytes @@ -212,10 +272,13 @@ DWARFFormValue::SkipValue(dw_form_t form, const DWARFDataExtractor& debug_info_d return true; case DW_FORM_ref_addr: + ref_addr_size = 4; + assert (cu); // CU must be valid for DW_FORM_ref_addr objects or we will get this wrong if (cu->GetVersion() <= 2) - *offset_ptr += DWARFCompileUnit::GetAddressByteSize(cu); + ref_addr_size = cu->GetAddressByteSize(); else - *offset_ptr += 4;// 4 for DWARF32, 8 for DWARF64, but we don't support DWARF64 yet + ref_addr_size = cu->IsDWARF64() ? 8 : 4; + *offset_ptr += ref_addr_size; return true; // 0 bytes values (implied from DW_FORM) @@ -237,11 +300,12 @@ DWARFFormValue::SkipValue(dw_form_t form, const DWARFDataExtractor& debug_info_d // 32 bit for DWARF 32, 64 for DWARF 64 case DW_FORM_sec_offset: - *offset_ptr += 4; + case DW_FORM_strp: + assert(cu); + *offset_ptr += (cu->IsDWARF64() ? 8 : 4); return true; // 4 byte values - case DW_FORM_strp: case DW_FORM_data4: case DW_FORM_ref4: *offset_ptr += 4; @@ -278,7 +342,7 @@ DWARFFormValue::SkipValue(dw_form_t form, const DWARFDataExtractor& debug_info_d void -DWARFFormValue::Dump(Stream &s, const DWARFDataExtractor* debug_str_data, const DWARFCompileUnit* cu) const +DWARFFormValue::Dump(Stream &s, const DWARFDataExtractor* debug_str_data) const { uint64_t uvalue = Unsigned(); bool cu_relative_offset = false; @@ -348,7 +412,8 @@ DWARFFormValue::Dump(Stream &s, const DWARFDataExtractor* debug_str_data, const case DW_FORM_ref_addr: { - if (cu->GetVersion() <= 2) + assert (m_cu); // CU must be valid for DW_FORM_ref_addr objects or we will get this wrong + if (m_cu->GetVersion() <= 2) s.Address(uvalue, sizeof (uint64_t) * 2); else s.Address(uvalue, 4 * 2);// 4 for DWARF32, 8 for DWARF64, but we don't support DWARF64 yet @@ -370,10 +435,11 @@ DWARFFormValue::Dump(Stream &s, const DWARFDataExtractor* debug_str_data, const if (cu_relative_offset) { + assert (m_cu); // CU must be valid for DW_FORM_ref forms that are compile unit relative or we will get this wrong if (verbose) s.PutCString(" => "); - s.Printf("{0x%8.8" PRIx64 "}", (uvalue + (cu ? cu->GetOffset() : 0))); + s.Printf("{0x%8.8" PRIx64 "}", uvalue + m_cu->GetOffset()); } } @@ -388,7 +454,7 @@ DWARFFormValue::AsCString(const DWARFDataExtractor* debug_str_data_ptr) const } uint64_t -DWARFFormValue::Reference(const DWARFCompileUnit* cu) const +DWARFFormValue::Reference() const { uint64_t die_offset = m_value.value.uval; switch (m_form) @@ -398,7 +464,8 @@ DWARFFormValue::Reference(const DWARFCompileUnit* cu) const case DW_FORM_ref4: case DW_FORM_ref8: case DW_FORM_ref_udata: - die_offset += (cu ? cu->GetOffset() : 0); + assert (m_cu); // CU must be valid for DW_FORM_ref forms that are compile unit relative or we will get this wrong + die_offset += m_cu->GetOffset(); break; default: @@ -429,32 +496,6 @@ DWARFFormValue::Reference (dw_offset_t base_offset) const return die_offset; } -//---------------------------------------------------------------------- -// Resolve any compile unit specific references so that we don't need -// the compile unit at a later time in order to work with the form -// value. -//---------------------------------------------------------------------- -bool -DWARFFormValue::ResolveCompileUnitReferences(const DWARFCompileUnit* cu) -{ - switch (m_form) - { - case DW_FORM_ref1: - case DW_FORM_ref2: - case DW_FORM_ref4: - case DW_FORM_ref8: - case DW_FORM_ref_udata: - m_value.value.uval += cu->GetOffset(); - m_form = DW_FORM_ref_addr; - return true; - break; - - default: - break; - } - - return false; -} const uint8_t* DWARFFormValue::BlockData() const @@ -496,7 +537,7 @@ DWARFFormValue::IsDataForm(const dw_form_t form) } int -DWARFFormValue::Compare (const DWARFFormValue& a_value, const DWARFFormValue& b_value, const DWARFCompileUnit* a_cu, const DWARFCompileUnit* b_cu, const DWARFDataExtractor* debug_str_data_ptr) +DWARFFormValue::Compare (const DWARFFormValue& a_value, const DWARFFormValue& b_value, const DWARFDataExtractor* debug_str_data_ptr) { dw_form_t a_form = a_value.Form(); dw_form_t b_form = b_value.Form(); @@ -577,8 +618,8 @@ DWARFFormValue::Compare (const DWARFFormValue& a_value, const DWARFFormValue& b_ case DW_FORM_ref8: case DW_FORM_ref_udata: { - uint64_t a = a_value.Reference(a_cu); - uint64_t b = b_value.Reference(b_cu); + uint64_t a = a_value.Reference(); + uint64_t b = b_value.Reference(); if (a < b) return -1; if (a > b) diff --git a/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h b/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h index d6c580c7ab1b..392df26a088e 100644 --- a/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h +++ b/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h @@ -45,35 +45,34 @@ public: eValueTypeBlock }; - DWARFFormValue(dw_form_t form = 0); + DWARFFormValue(); + DWARFFormValue(const DWARFCompileUnit* cu, dw_form_t form); + const DWARFCompileUnit* GetCompileUnit () const { return m_cu; } + void SetCompileUnit (const DWARFCompileUnit* cu) { m_cu = cu; } dw_form_t Form() const { return m_form; } void SetForm(dw_form_t form) { m_form = form; } const ValueType& Value() const { return m_value; } - void Dump(lldb_private::Stream &s, const lldb_private::DWARFDataExtractor* debug_str_data, const DWARFCompileUnit* cu) const; + void Dump(lldb_private::Stream &s, const lldb_private::DWARFDataExtractor* debug_str_data) const; bool ExtractValue(const lldb_private::DWARFDataExtractor& data, - lldb::offset_t* offset_ptr, - const DWARFCompileUnit* cu); + lldb::offset_t* offset_ptr); bool IsInlinedCStr() const { return (m_value.data != NULL) && m_value.data == (const uint8_t*)m_value.value.cstr; } const uint8_t* BlockData() const; - uint64_t Reference(const DWARFCompileUnit* cu) const; + uint64_t Reference() const; uint64_t Reference (dw_offset_t offset) const; - bool ResolveCompileUnitReferences(const DWARFCompileUnit* cu); bool Boolean() const { return m_value.value.uval != 0; } uint64_t Unsigned() const { return m_value.value.uval; } void SetUnsigned(uint64_t uval) { m_value.value.uval = uval; } int64_t Signed() const { return m_value.value.sval; } void SetSigned(int64_t sval) { m_value.value.sval = sval; } const char* AsCString(const lldb_private::DWARFDataExtractor* debug_str_data_ptr) const; - bool SkipValue(const lldb_private::DWARFDataExtractor& debug_info_data, lldb::offset_t *offset_ptr, const DWARFCompileUnit* cu) const; + bool SkipValue(const lldb_private::DWARFDataExtractor& debug_info_data, lldb::offset_t *offset_ptr) const; static bool SkipValue(const dw_form_t form, const lldb_private::DWARFDataExtractor& debug_info_data, lldb::offset_t *offset_ptr, const DWARFCompileUnit* cu); -// static bool TransferValue(dw_form_t form, const lldb_private::DWARFDataExtractor& debug_info_data, lldb::offset_t *offset_ptr, const DWARFCompileUnit* cu, BinaryStreamBuf& out_buff); -// static bool TransferValue(const DWARFFormValue& formValue, const DWARFCompileUnit* cu, BinaryStreamBuf& out_buff); -// static bool PutUnsigned(dw_form_t form, dw_offset_t offset, uint64_t value, BinaryStreamBuf& out_buff, const DWARFCompileUnit* cu, bool fixup_cu_relative_refs); static bool IsBlockForm(const dw_form_t form); static bool IsDataForm(const dw_form_t form); - static const uint8_t * GetFixedFormSizesForAddressSize (uint8_t addr_size); - static int Compare (const DWARFFormValue& a, const DWARFFormValue& b, const DWARFCompileUnit* a_cu, const DWARFCompileUnit* b_cu, const lldb_private::DWARFDataExtractor* debug_str_data_ptr); + static const uint8_t * GetFixedFormSizesForAddressSize (uint8_t addr_size, bool is_dwarf64); + static int Compare (const DWARFFormValue& a, const DWARFFormValue& b, const lldb_private::DWARFDataExtractor* debug_str_data_ptr); protected: + const DWARFCompileUnit* m_cu; // Compile unit for this form dw_form_t m_form; // Form for this value ValueType m_value; // Contains all data for the form }; diff --git a/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h b/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h index ab0c37beeac9..f8a8cc60467d 100644 --- a/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h +++ b/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h @@ -442,9 +442,9 @@ struct DWARFMappedHash for (size_t i=0; i<num_atoms; ++i) { - DWARFFormValue form_value (header_data.atoms[i].form); + DWARFFormValue form_value (NULL, header_data.atoms[i].form); - if (!form_value.ExtractValue(data, offset_ptr, NULL)) + if (!form_value.ExtractValue(data, offset_ptr)) return false; switch (header_data.atoms[i].type) @@ -481,7 +481,7 @@ struct DWARFMappedHash if (i > 0) strm.PutCString (", "); - DWARFFormValue form_value (header_data.atoms[i].form); + DWARFFormValue form_value (NULL, header_data.atoms[i].form); switch (header_data.atoms[i].type) { case eAtomTypeDIEOffset: // DIE offset, check form for encoding diff --git a/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 842260dbc3ba..b3a5476227f4 100644 --- a/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -1457,7 +1457,7 @@ SymbolFileDWARF::ParseTemplateDIE (DWARFCompileUnit* dwarf_cu, case DW_TAG_template_type_parameter: case DW_TAG_template_value_parameter: { - const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize(), dwarf_cu->IsDWARF64()); DWARFDebugInfoEntry::Attributes attributes; const size_t num_attributes = die->GetAttributes (this, @@ -1486,7 +1486,7 @@ SymbolFileDWARF::ParseTemplateDIE (DWARFCompileUnit* dwarf_cu, case DW_AT_type: if (attributes.ExtractFormValueAtIndex(this, i, form_value)) { - const dw_offset_t type_die_offset = form_value.Reference(dwarf_cu); + const dw_offset_t type_die_offset = form_value.Reference(); lldb_type = ResolveTypeUID(type_die_offset); if (lldb_type) clang_type = lldb_type->GetClangForwardType(); @@ -1752,7 +1752,7 @@ SymbolFileDWARF::ParseChildMembers size_t count = 0; const DWARFDebugInfoEntry *die; - const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize(), dwarf_cu->IsDWARF64()); uint32_t member_idx = 0; BitfieldInfo last_field_info; ModuleSP module = GetObjectFile()->GetModule(); @@ -1803,7 +1803,7 @@ SymbolFileDWARF::ParseChildMembers case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: name = form_value.AsCString(&get_debug_str_data()); break; - case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + case DW_AT_type: encoding_uid = form_value.Reference(); break; case DW_AT_bit_offset: bit_offset = form_value.Unsigned(); break; case DW_AT_bit_size: bit_size = form_value.Unsigned(); break; case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; @@ -2201,7 +2201,7 @@ SymbolFileDWARF::ParseChildMembers case DW_AT_decl_file: decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(form_value.Unsigned())); break; case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; - case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + case DW_AT_type: encoding_uid = form_value.Reference(); break; case DW_AT_data_member_location: if (form_value.BlockData()) { @@ -2990,7 +2990,7 @@ SymbolFileDWARF::ResolveSymbolContext(const FileSpec& file_spec, uint32_t line, if (block_die != NULL) sc.block = block.FindBlockByID (MakeUserID(block_die->GetOffset())); - else + else if (function_die != NULL) sc.block = block.FindBlockByID (MakeUserID(function_die->GetOffset())); } } @@ -3033,7 +3033,7 @@ SymbolFileDWARF::Index () m_indexed = true; Timer scoped_timer (__PRETTY_FUNCTION__, "SymbolFileDWARF::Index (%s)", - GetObjectFile()->GetFileSpec().GetFilename().AsCString()); + GetObjectFile()->GetFileSpec().GetFilename().AsCString("<Unknown>")); DWARFDebugInfo* debug_info = DebugInfo(); if (debug_info) @@ -3197,13 +3197,13 @@ SymbolFileDWARF::FindGlobalVariables (const ConstString &name, const lldb_privat if (m_apple_names_ap.get()) { const char *name_cstr = name.GetCString(); - const char *base_name_start; - const char *base_name_end = NULL; + llvm::StringRef basename; + llvm::StringRef context; - if (!CPPLanguageRuntime::StripNamespacesFromVariableName(name_cstr, base_name_start, base_name_end)) - base_name_start = name_cstr; + if (!CPPLanguageRuntime::ExtractContextAndIdentifier(name_cstr, context, basename)) + basename = name_cstr; - m_apple_names_ap->FindByName (base_name_start, die_offsets); + m_apple_names_ap->FindByName (basename.data(), die_offsets); } } else @@ -3402,7 +3402,7 @@ SymbolFileDWARF::ResolveFunction (DWARFCompileUnit *cu, break; } } - assert (die->Tag() == DW_TAG_subprogram); + assert (die && die->Tag() == DW_TAG_subprogram); if (GetFunction (cu, die, sc)) { Address addr; @@ -4231,7 +4231,7 @@ SymbolFileDWARF::ParseChildParameters (const SymbolContext& sc, if (parent_die == NULL) return 0; - const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize(), dwarf_cu->IsDWARF64()); size_t arg_idx = 0; const DWARFDebugInfoEntry *die; @@ -4266,7 +4266,7 @@ SymbolFileDWARF::ParseChildParameters (const SymbolContext& sc, case DW_AT_decl_line: decl.SetLine(form_value.Unsigned()); break; case DW_AT_decl_column: decl.SetColumn(form_value.Unsigned()); break; case DW_AT_name: name = form_value.AsCString(&get_debug_str_data()); break; - case DW_AT_type: param_type_die_offset = form_value.Reference(dwarf_cu); break; + case DW_AT_type: param_type_die_offset = form_value.Reference(); break; case DW_AT_artificial: is_artificial = form_value.Boolean(); break; case DW_AT_location: // if (form_value.BlockData()) @@ -4409,7 +4409,7 @@ SymbolFileDWARF::ParseChildEnumerators size_t enumerators_added = 0; const DWARFDebugInfoEntry *die; - const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize(), dwarf_cu->IsDWARF64()); for (die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) { @@ -4488,7 +4488,7 @@ SymbolFileDWARF::ParseChildArrayInfo return; const DWARFDebugInfoEntry *die; - const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize(), dwarf_cu->IsDWARF64()); for (die = parent_die->GetFirstChild(); die != NULL; die = die->GetSibling()) { const dw_tag_t tag = die->Tag(); @@ -4929,7 +4929,7 @@ SymbolFileDWARF::FindCompleteObjCDefinitionTypeForDIE (const DWARFDebugInfoEntry { DEBUG_PRINTF ("resolved 0x%8.8" PRIx64 " from %s to 0x%8.8" PRIx64 " (cu 0x%8.8" PRIx64 ")\n", MakeUserID(die->GetOffset()), - m_obj_file->GetFileSpec().GetFilename().AsCString(), + m_obj_file->GetFileSpec().GetFilename().AsCString("<Unknown>"), MakeUserID(type_die->GetOffset()), MakeUserID(type_cu->GetOffset())); @@ -5060,189 +5060,6 @@ SymbolFileDWARF::DIEDeclContextsMatch (DWARFCompileUnit* cu1, const DWARFDebugIn return true; } -// This function can be used when a DIE is found that is a forward declaration -// DIE and we want to try and find a type that has the complete definition. -// "cu" and "die" must be from this SymbolFileDWARF -TypeSP -SymbolFileDWARF::FindDefinitionTypeForDIE (DWARFCompileUnit* cu, - const DWARFDebugInfoEntry *die, - const ConstString &type_name) -{ - TypeSP type_sp; - -#if defined (LLDB_CONFIGURATION_DEBUG) - // You can't and shouldn't call this function with a compile unit from - // another SymbolFileDWARF instance. - assert (DebugInfo()->ContainsCompileUnit (cu)); -#endif - - if (cu == NULL || die == NULL || !type_name) - return type_sp; - - std::string qualified_name; - - Log *log (LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION|DWARF_LOG_LOOKUPS)); - if (log) - { - die->GetQualifiedName(this, cu, qualified_name); - GetObjectFile()->GetModule()->LogMessage (log, - "SymbolFileDWARF::FindDefinitionTypeForDIE(die=0x%8.8x (%s), name='%s')", - die->GetOffset(), - qualified_name.c_str(), - type_name.GetCString()); - } - - DIEArray die_offsets; - - if (m_using_apple_tables) - { - if (m_apple_types_ap.get()) - { - const bool has_tag = m_apple_types_ap->GetHeader().header_data.ContainsAtom (DWARFMappedHash::eAtomTypeTag); - const bool has_qualified_name_hash = m_apple_types_ap->GetHeader().header_data.ContainsAtom (DWARFMappedHash::eAtomTypeQualNameHash); - if (has_tag && has_qualified_name_hash) - { - if (qualified_name.empty()) - die->GetQualifiedName(this, cu, qualified_name); - - const uint32_t qualified_name_hash = MappedHash::HashStringUsingDJB (qualified_name.c_str()); - if (log) - GetObjectFile()->GetModule()->LogMessage (log,"FindByNameAndTagAndQualifiedNameHash()"); - m_apple_types_ap->FindByNameAndTagAndQualifiedNameHash (type_name.GetCString(), die->Tag(), qualified_name_hash, die_offsets); - } - else if (has_tag) - { - if (log) - GetObjectFile()->GetModule()->LogMessage (log,"FindByNameAndTag()"); - m_apple_types_ap->FindByNameAndTag (type_name.GetCString(), die->Tag(), die_offsets); - } - else - { - m_apple_types_ap->FindByName (type_name.GetCString(), die_offsets); - } - } - } - else - { - if (!m_indexed) - Index (); - - m_type_index.Find (type_name, die_offsets); - } - - const size_t num_matches = die_offsets.size(); - - const dw_tag_t die_tag = die->Tag(); - - DWARFCompileUnit* type_cu = NULL; - const DWARFDebugInfoEntry* type_die = NULL; - if (num_matches) - { - DWARFDebugInfo* debug_info = DebugInfo(); - for (size_t i=0; i<num_matches; ++i) - { - const dw_offset_t die_offset = die_offsets[i]; - type_die = debug_info->GetDIEPtrWithCompileUnitHint (die_offset, &type_cu); - - if (type_die) - { - bool try_resolving_type = false; - - // Don't try and resolve the DIE we are looking for with the DIE itself! - if (type_die != die) - { - const dw_tag_t type_die_tag = type_die->Tag(); - // Make sure the tags match - if (type_die_tag == die_tag) - { - // The tags match, lets try resolving this type - try_resolving_type = true; - } - else - { - // The tags don't match, but we need to watch our for a - // forward declaration for a struct and ("struct foo") - // ends up being a class ("class foo { ... };") or - // vice versa. - switch (type_die_tag) - { - case DW_TAG_class_type: - // We had a "class foo", see if we ended up with a "struct foo { ... };" - try_resolving_type = (die_tag == DW_TAG_structure_type); - break; - case DW_TAG_structure_type: - // We had a "struct foo", see if we ended up with a "class foo { ... };" - try_resolving_type = (die_tag == DW_TAG_class_type); - break; - default: - // Tags don't match, don't event try to resolve - // using this type whose name matches.... - break; - } - } - } - - if (try_resolving_type) - { - if (log) - { - std::string qualified_name; - type_die->GetQualifiedName(this, cu, qualified_name); - GetObjectFile()->GetModule()->LogMessage (log, - "SymbolFileDWARF::FindDefinitionTypeForDIE(die=0x%8.8x, name='%s') trying die=0x%8.8x (%s)", - die->GetOffset(), - type_name.GetCString(), - type_die->GetOffset(), - qualified_name.c_str()); - } - - // Make sure the decl contexts match all the way up - if (DIEDeclContextsMatch(cu, die, type_cu, type_die)) - { - Type *resolved_type = ResolveType (type_cu, type_die, false); - if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) - { - DEBUG_PRINTF ("resolved 0x%8.8" PRIx64 " (cu 0x%8.8" PRIx64 ") from %s to 0x%8.8" PRIx64 " (cu 0x%8.8" PRIx64 ")\n", - MakeUserID(die->GetOffset()), - MakeUserID(cu->GetOffset()), - m_obj_file->GetFileSpec().GetFilename().AsCString(), - MakeUserID(type_die->GetOffset()), - MakeUserID(type_cu->GetOffset())); - - m_die_to_type[die] = resolved_type; - type_sp = resolved_type->shared_from_this(); - break; - } - } - } - else - { - if (log) - { - std::string qualified_name; - type_die->GetQualifiedName(this, cu, qualified_name); - GetObjectFile()->GetModule()->LogMessage (log, - "SymbolFileDWARF::FindDefinitionTypeForDIE(die=0x%8.8x, name='%s') ignoring die=0x%8.8x (%s)", - die->GetOffset(), - type_name.GetCString(), - type_die->GetOffset(), - qualified_name.c_str()); - } - } - } - else - { - if (m_using_apple_tables) - { - GetObjectFile()->GetModule()->ReportErrorIfModifyDetected ("the DWARF debug information has been modified (.apple_types accelerator table had bad die 0x%8.8x for '%s')\n", - die_offset, type_name.GetCString()); - } - } - - } - } - return type_sp; -} TypeSP SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext (const DWARFDeclContext &dwarf_decl_ctx) @@ -5828,7 +5645,7 @@ SymbolFileDWARF::ParseType (const SymbolContext& sc, DWARFCompileUnit* dwarf_cu, break; case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; case DW_AT_encoding: encoding = form_value.Unsigned(); break; - case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + case DW_AT_type: encoding_uid = form_value.Reference(); break; default: case DW_AT_sibling: break; @@ -6403,7 +6220,7 @@ SymbolFileDWARF::ParseType (const SymbolContext& sc, DWARFCompileUnit* dwarf_cu, type_name_cstr = form_value.AsCString(&get_debug_str_data()); type_name_const_str.SetCString(type_name_cstr); break; - case DW_AT_type: encoding_uid = form_value.Reference(dwarf_cu); break; + case DW_AT_type: encoding_uid = form_value.Reference(); break; case DW_AT_byte_size: byte_size = form_value.Unsigned(); break; case DW_AT_accessibility: break; //accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_declaration: break; //is_forward_declaration = form_value.Boolean(); break; @@ -6521,7 +6338,7 @@ SymbolFileDWARF::ParseType (const SymbolContext& sc, DWARFCompileUnit* dwarf_cu, case DW_AT_linkage_name: case DW_AT_MIPS_linkage_name: break; // mangled = form_value.AsCString(&get_debug_str_data()); break; - case DW_AT_type: type_die_offset = form_value.Reference(dwarf_cu); break; + case DW_AT_type: type_die_offset = form_value.Reference(); break; case DW_AT_accessibility: accessibility = DW_ACCESS_to_AccessType(form_value.Unsigned()); break; case DW_AT_declaration: break; // is_forward_declaration = form_value.Boolean(); break; case DW_AT_inline: is_inline = form_value.Boolean(); break; @@ -6541,15 +6358,15 @@ SymbolFileDWARF::ParseType (const SymbolContext& sc, DWARFCompileUnit* dwarf_cu, break; case DW_AT_specification: - specification_die_offset = form_value.Reference(dwarf_cu); + specification_die_offset = form_value.Reference(); break; case DW_AT_abstract_origin: - abstract_origin_die_offset = form_value.Reference(dwarf_cu); + abstract_origin_die_offset = form_value.Reference(); break; case DW_AT_object_pointer: - object_pointer_die_offset = form_value.Reference(dwarf_cu); + object_pointer_die_offset = form_value.Reference(); break; case DW_AT_allocated: @@ -6985,7 +6802,7 @@ SymbolFileDWARF::ParseType (const SymbolContext& sc, DWARFCompileUnit* dwarf_cu, type_name_const_str.SetCString(type_name_cstr); break; - case DW_AT_type: type_die_offset = form_value.Reference(dwarf_cu); break; + case DW_AT_type: type_die_offset = form_value.Reference(); break; case DW_AT_byte_size: break; // byte_size = form_value.Unsigned(); break; case DW_AT_byte_stride: byte_stride = form_value.Unsigned(); break; case DW_AT_bit_stride: bit_stride = form_value.Unsigned(); break; @@ -7019,17 +6836,26 @@ SymbolFileDWARF::ParseType (const SymbolContext& sc, DWARFCompileUnit* dwarf_cu, byte_stride = element_type->GetByteSize(); ClangASTType array_element_type = element_type->GetClangForwardType(); uint64_t array_element_bit_stride = byte_stride * 8 + bit_stride; - uint64_t num_elements = 0; - std::vector<uint64_t>::const_reverse_iterator pos; - std::vector<uint64_t>::const_reverse_iterator end = element_orders.rend(); - for (pos = element_orders.rbegin(); pos != end; ++pos) + if (element_orders.size() > 0) + { + uint64_t num_elements = 0; + std::vector<uint64_t>::const_reverse_iterator pos; + std::vector<uint64_t>::const_reverse_iterator end = element_orders.rend(); + for (pos = element_orders.rbegin(); pos != end; ++pos) + { + num_elements = *pos; + clang_type = ast.CreateArrayType (array_element_type, + num_elements, + is_vector); + array_element_type = clang_type; + array_element_bit_stride = num_elements ? + array_element_bit_stride * num_elements : + array_element_bit_stride; + } + } + else { - num_elements = *pos; - clang_type = ast.CreateArrayType (array_element_type, - num_elements, - is_vector); - array_element_type = clang_type; - array_element_bit_stride = num_elements ? array_element_bit_stride * num_elements : array_element_bit_stride; + clang_type = ast.CreateArrayType (array_element_type, 0, is_vector); } ConstString empty_name; type_sp.reset( new Type (MakeUserID(die->GetOffset()), @@ -7065,9 +6891,9 @@ SymbolFileDWARF::ParseType (const SymbolContext& sc, DWARFCompileUnit* dwarf_cu, switch (attr) { case DW_AT_type: - type_die_offset = form_value.Reference(dwarf_cu); break; + type_die_offset = form_value.Reference(); break; case DW_AT_containing_type: - containing_type_die_offset = form_value.Reference(dwarf_cu); break; + containing_type_die_offset = form_value.Reference(); break; } } } @@ -7114,7 +6940,7 @@ SymbolFileDWARF::ParseType (const SymbolContext& sc, DWARFCompileUnit* dwarf_cu, { symbol_context_scope = sc.comp_unit; } - else if (sc.function != NULL) + else if (sc.function != NULL && sc_parent_die) { symbol_context_scope = sc.function->GetBlock(true).FindBlockByID(MakeUserID(sc_parent_die->GetOffset())); if (symbol_context_scope == NULL) @@ -7347,7 +7173,6 @@ SymbolFileDWARF::ParseVariableDIE const lldb::addr_t func_low_pc ) { - VariableSP var_sp (m_die_to_variable_sp[die]); if (var_sp) return var_sp; // Already been parsed! @@ -7380,6 +7205,7 @@ SymbolFileDWARF::ParseVariableDIE { dw_attr_t attr = attributes.AttributeAtIndex(i); DWARFFormValue form_value; + if (attributes.ExtractFormValueAtIndex(this, i, form_value)) { switch (attr) @@ -7390,7 +7216,7 @@ SymbolFileDWARF::ParseVariableDIE case DW_AT_name: name = form_value.AsCString(&get_debug_str_data()); break; case DW_AT_linkage_name: case DW_AT_MIPS_linkage_name: mangled = form_value.AsCString(&get_debug_str_data()); break; - case DW_AT_type: type_uid = form_value.Reference(dwarf_cu); break; + case DW_AT_type: type_uid = form_value.Reference(); break; case DW_AT_external: is_external = form_value.Boolean(); break; case DW_AT_const_value: // If we have already found a DW_AT_location attribute, ignore this attribute. @@ -7409,7 +7235,7 @@ SymbolFileDWARF::ParseVariableDIE else if (DWARFFormValue::IsDataForm(form_value.Form())) { // Retrieve the value as a data expression. - const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (attributes.CompileUnitAtIndex(i)->GetAddressByteSize(), attributes.CompileUnitAtIndex(i)->IsDWARF64()); uint32_t data_offset = attributes.DIEOffsetAtIndex(i); uint32_t data_length = fixed_form_sizes[form_value.Form()]; if (data_length == 0) @@ -7417,7 +7243,7 @@ SymbolFileDWARF::ParseVariableDIE const uint8_t *data_pointer = form_value.BlockData(); if (data_pointer) { - data_length = form_value.Unsigned(); + form_value.Unsigned(); } else if (DWARFFormValue::IsDataForm(form_value.Form())) { @@ -7433,7 +7259,7 @@ SymbolFileDWARF::ParseVariableDIE // Retrieve the value as a string expression. if (form_value.Form() == DW_FORM_strp) { - const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (dwarf_cu->GetAddressByteSize()); + const uint8_t *fixed_form_sizes = DWARFFormValue::GetFixedFormSizesForAddressSize (attributes.CompileUnitAtIndex(i)->GetAddressByteSize(), attributes.CompileUnitAtIndex(i)->IsDWARF64()); uint32_t data_offset = attributes.DIEOffsetAtIndex(i); uint32_t data_length = fixed_form_sizes[form_value.Form()]; location.CopyOpcodeData(module, debug_info_data, data_offset, data_length); @@ -7470,7 +7296,7 @@ SymbolFileDWARF::ParseVariableDIE { location.CopyOpcodeData(module, debug_loc_data, debug_loc_offset, loc_list_length); assert (func_low_pc != LLDB_INVALID_ADDRESS); - location.SetLocationListSlide (func_low_pc - dwarf_cu->GetBaseAddress()); + location.SetLocationListSlide (func_low_pc - attributes.CompileUnitAtIndex(i)->GetBaseAddress()); } } } diff --git a/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 178e5142d94b..230e1a5d3984 100644 --- a/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -421,11 +421,6 @@ protected: const DWARFMappedHash::MemoryTable &memory_table, lldb_private::SymbolContextList& sc_list); - lldb::TypeSP FindDefinitionTypeForDIE ( - DWARFCompileUnit* dwarf_cu, - const DWARFDebugInfoEntry *die, - const lldb_private::ConstString &type_name); - lldb::TypeSP FindDefinitionTypeForDWARFDeclContext ( const DWARFDeclContext &die_decl_ctx); diff --git a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp index b8d56d3909e9..7e484bd8dc73 100644 --- a/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp +++ b/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp @@ -146,7 +146,8 @@ UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly (AddressRange& if (log && log->GetVerbose ()) { StreamString strm; - inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize (), show_address, show_bytes, NULL); + const char *disassemble_format = "${frame.pc}: "; + inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize (), show_address, show_bytes, NULL, NULL, NULL, disassemble_format); log->PutCString (strm.GetData()); } diff --git a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp index 32a21d2b8bb8..dbf37d8a4de0 100644 --- a/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp +++ b/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp @@ -23,6 +23,7 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/Target.h" #include "lldb/Target/UnwindAssembly.h" +#include "lldb/Utility/RegisterNumber.h" using namespace lldb; using namespace lldb_private; @@ -318,7 +319,8 @@ AssemblyParse_x86::nonvolatile_reg_p (int machine_regno) #define REX_W_DSTREG(opcode) ((opcode) & 0x1) // pushq %rbp [0x55] -bool AssemblyParse_x86::push_rbp_pattern_p () +bool +AssemblyParse_x86::push_rbp_pattern_p () { uint8_t *p = m_cur_insn_bytes; if (*p == 0x55) @@ -327,7 +329,8 @@ bool AssemblyParse_x86::push_rbp_pattern_p () } // pushq $0 ; the first instruction in start() [0x6a 0x00] -bool AssemblyParse_x86::push_0_pattern_p () +bool +AssemblyParse_x86::push_0_pattern_p () { uint8_t *p = m_cur_insn_bytes; if (*p == 0x6a && *(p + 1) == 0x0) @@ -337,7 +340,8 @@ bool AssemblyParse_x86::push_0_pattern_p () // pushq $0 // pushl $0 -bool AssemblyParse_x86::push_imm_pattern_p () +bool +AssemblyParse_x86::push_imm_pattern_p () { uint8_t *p = m_cur_insn_bytes; if (*p == 0x68 || *p == 0x6a) @@ -347,7 +351,8 @@ bool AssemblyParse_x86::push_imm_pattern_p () // 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 () +bool +AssemblyParse_x86::mov_rsp_rbp_pattern_p () { uint8_t *p = m_cur_insn_bytes; if (m_wordsize == 8 && *p == 0x48) @@ -360,7 +365,8 @@ bool AssemblyParse_x86::mov_rsp_rbp_pattern_p () } // subq $0x20, %rsp -bool AssemblyParse_x86::sub_rsp_pattern_p (int& amount) +bool +AssemblyParse_x86::sub_rsp_pattern_p (int& amount) { uint8_t *p = m_cur_insn_bytes; if (m_wordsize == 8 && *p == 0x48) @@ -381,7 +387,8 @@ bool AssemblyParse_x86::sub_rsp_pattern_p (int& amount) } // addq $0x20, %rsp -bool AssemblyParse_x86::add_rsp_pattern_p (int& amount) +bool +AssemblyParse_x86::add_rsp_pattern_p (int& amount) { uint8_t *p = m_cur_insn_bytes; if (m_wordsize == 8 && *p == 0x48) @@ -403,7 +410,8 @@ bool AssemblyParse_x86::add_rsp_pattern_p (int& amount) // pushq %rbx // pushl %ebx -bool AssemblyParse_x86::push_reg_p (int& regno) +bool +AssemblyParse_x86::push_reg_p (int& regno) { uint8_t *p = m_cur_insn_bytes; int regno_prefix_bit = 0; @@ -423,7 +431,8 @@ bool AssemblyParse_x86::push_reg_p (int& regno) // popq %rbx // popl %ebx -bool AssemblyParse_x86::pop_reg_p (int& regno) +bool +AssemblyParse_x86::pop_reg_p (int& regno) { uint8_t *p = m_cur_insn_bytes; int regno_prefix_bit = 0; @@ -443,14 +452,16 @@ bool AssemblyParse_x86::pop_reg_p (int& regno) // popq %rbp [0x5d] // popl %ebp [0x5d] -bool AssemblyParse_x86::pop_rbp_pattern_p () +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 () +bool +AssemblyParse_x86::call_next_insn_pattern_p () { uint8_t *p = m_cur_insn_bytes; return (*p == 0xe8) && (*(p+1) == 0x0) && (*(p+2) == 0x0) @@ -468,7 +479,8 @@ bool AssemblyParse_x86::call_next_insn_pattern_p () // the actual location. The positive value returned for the offset // is a convention used elsewhere for CFA offsets et al. -bool AssemblyParse_x86::mov_reg_to_local_stack_frame_p (int& regno, int& rbp_offset) +bool +AssemblyParse_x86::mov_reg_to_local_stack_frame_p (int& regno, int& rbp_offset) { uint8_t *p = m_cur_insn_bytes; int src_reg_prefix_bit = 0; @@ -602,7 +614,6 @@ bool AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan) { UnwindPlan::RowSP row(new UnwindPlan::Row); - int non_prologue_insn_count = 0; m_cur_insn = m_func_bounds.GetBaseAddress (); int current_func_text_offset = 0; int current_sp_bytes_offset_from_cfa = 0; @@ -638,20 +649,38 @@ AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan) *newrow = *row.get(); row.reset(newrow); + // Track which registers have been saved so far in the prologue. + // If we see another push of that register, it's not part of the prologue. + // The register numbers used here are the machine register #'s + // (i386_register_numbers, x86_64_register_numbers). + std::vector<bool> saved_registers(32, false); + const bool prefer_file_cache = true; + // Once the prologue has completed we'll save a copy of the unwind instructions + // If there is an epilogue in the middle of the function, after that epilogue we'll reinstate + // the unwind setup -- we assume that some code path jumps over the mid-function epilogue + + UnwindPlan::RowSP prologue_completed_row; // copy of prologue row of CFI + int prologue_completed_sp_bytes_offset_from_cfa; // The sp value before the epilogue started executed + std::vector<bool> prologue_completed_saved_registers; + Target *target = m_exe_ctx.GetTargetPtr(); - while (m_func_bounds.ContainsFileAddress (m_cur_insn) && non_prologue_insn_count < 10) + while (m_func_bounds.ContainsFileAddress (m_cur_insn)) { int stack_offset, insn_len; int machine_regno; // register numbers masked directly out of instructions uint32_t lldb_regno; // register numbers in lldb's eRegisterKindLLDB numbering scheme + bool in_epilogue = false; // we're in the middle of an epilogue sequence + bool row_updated = false; // The UnwindPlan::Row 'row' has been updated + if (!instruction_length (m_cur_insn, insn_len) || insn_len == 0 || insn_len > kMaxInstructionByteSize) { // An unrecognized/junk instruction break; } + if (target->ReadMemory (m_cur_insn, prefer_file_cache, m_cur_insn_bytes, insn_len, error) == static_cast<size_t>(-1)) { @@ -661,216 +690,193 @@ AssemblyParse_x86::get_non_call_site_unwind_plan (UnwindPlan &unwind_plan) if (push_rbp_pattern_p ()) { - row->SetOffset (current_func_text_offset + insn_len); current_sp_bytes_offset_from_cfa += m_wordsize; row->SetCFAOffset (current_sp_bytes_offset_from_cfa); UnwindPlan::Row::RegisterLocation regloc; regloc.SetAtCFAPlusOffset (-row->GetCFAOffset()); row->SetRegisterInfo (m_lldb_fp_regnum, regloc); - unwind_plan.AppendRow (row); - // Allocate a new Row, populate it with the existing Row contents. - newrow = new UnwindPlan::Row; - *newrow = *row.get(); - row.reset(newrow); - goto loopnext; + saved_registers[m_machine_fp_regnum] = true; + row_updated = true; } - if (mov_rsp_rbp_pattern_p ()) + else if (mov_rsp_rbp_pattern_p ()) { - row->SetOffset (current_func_text_offset + insn_len); row->SetCFARegister (m_lldb_fp_regnum); - unwind_plan.AppendRow (row); - // Allocate a new Row, populate it with the existing Row contents. - newrow = new UnwindPlan::Row; - *newrow = *row.get(); - row.reset(newrow); - goto loopnext; + row_updated = true; } // This is the start() function (or a pthread equivalent), it starts with a pushl $0x0 which puts the // saved pc value of 0 on the stack. In this case we want to pretend we didn't see a stack movement at all -- // normally the saved pc value is already on the stack by the time the function starts executing. - if (push_0_pattern_p ()) + else if (push_0_pattern_p ()) { - goto loopnext; } - if (push_reg_p (machine_regno)) + else if (push_reg_p (machine_regno)) { current_sp_bytes_offset_from_cfa += m_wordsize; - 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() == m_lldb_sp_regnum) { - need_to_push_row = true; row->SetCFAOffset (current_sp_bytes_offset_from_cfa); + row_updated = true; } // record where non-volatile (callee-saved, spilled) registers are saved on the stack - if (nonvolatile_reg_p (machine_regno) && machine_regno_to_lldb_regno (machine_regno, lldb_regno)) + if (nonvolatile_reg_p (machine_regno) + && machine_regno_to_lldb_regno (machine_regno, lldb_regno) + && saved_registers[machine_regno] == false) { - need_to_push_row = true; UnwindPlan::Row::RegisterLocation regloc; regloc.SetAtCFAPlusOffset (-current_sp_bytes_offset_from_cfa); row->SetRegisterInfo (lldb_regno, regloc); + saved_registers[machine_regno] = true; + row_updated = true; } - if (need_to_push_row) - { - row->SetOffset (current_func_text_offset + insn_len); - unwind_plan.AppendRow (row); - // Allocate a new Row, populate it with the existing Row contents. - newrow = new UnwindPlan::Row; - *newrow = *row.get(); - row.reset(newrow); - } - goto loopnext; } - if (mov_reg_to_local_stack_frame_p (machine_regno, stack_offset) && nonvolatile_reg_p (machine_regno)) + else if (pop_reg_p (machine_regno)) { - if (machine_regno_to_lldb_regno (machine_regno, lldb_regno)) + current_sp_bytes_offset_from_cfa -= m_wordsize; + + if (nonvolatile_reg_p (machine_regno) + && machine_regno_to_lldb_regno (machine_regno, lldb_regno) + && saved_registers[machine_regno] == true) { - row->SetOffset (current_func_text_offset + insn_len); - UnwindPlan::Row::RegisterLocation regloc; + saved_registers[machine_regno] = false; + row->RemoveRegisterInfo (lldb_regno); - // stack_offset for 'movq %r15, -80(%rbp)' will be 80. - // In the Row, we want to express this as the offset from the CFA. If the frame base - // is rbp (like the above instruction), the CFA offset for rbp is probably 16. So we - // want to say that the value is stored at the CFA address - 96. - regloc.SetAtCFAPlusOffset (-(stack_offset + row->GetCFAOffset())); + if (machine_regno == m_machine_fp_regnum) + { + row->SetCFARegister (m_lldb_sp_regnum); + } - row->SetRegisterInfo (lldb_regno, regloc); - unwind_plan.AppendRow (row); - // Allocate a new Row, populate it with the existing Row contents. - newrow = new UnwindPlan::Row; - *newrow = *row.get(); - row.reset(newrow); - goto loopnext; + in_epilogue = true; + row_updated = true; } - } - if (sub_rsp_pattern_p (stack_offset)) - { - current_sp_bytes_offset_from_cfa += stack_offset; + // the POP 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() == m_lldb_sp_regnum) { - row->SetOffset (current_func_text_offset + insn_len); row->SetCFAOffset (current_sp_bytes_offset_from_cfa); - unwind_plan.AppendRow (row); - // Allocate a new Row, populate it with the existing Row contents. - newrow = new UnwindPlan::Row; - *newrow = *row.get(); - row.reset(newrow); + row_updated = true; } - goto loopnext; } - if (ret_pattern_p ()) + else if (mov_reg_to_local_stack_frame_p (machine_regno, stack_offset) + && nonvolatile_reg_p (machine_regno) + && machine_regno_to_lldb_regno (machine_regno, lldb_regno) + && saved_registers[machine_regno] == false) { - // we know where the end of the function is; set the limit on the PlanValidAddressRange - // in case our initial "high pc" value was overly large - // int original_size = m_func_bounds.GetByteSize(); - // int calculated_size = m_cur_insn.GetOffset() - m_func_bounds.GetBaseAddress().GetOffset() + insn_len + 1; - // m_func_bounds.SetByteSize (calculated_size); - // unwind_plan.SetPlanValidAddressRange (m_func_bounds); - break; - } + saved_registers[machine_regno] = true; - // FIXME recognize the i386 picbase setup instruction sequence, - // 0x1f16: call 0x1f1b ; main + 11 at /private/tmp/a.c:3 - // 0x1f1b: popl %eax - // and record the temporary stack movements if the CFA is not expressed in terms of ebp. + UnwindPlan::Row::RegisterLocation regloc; - non_prologue_insn_count++; -loopnext: - m_cur_insn.SetOffset (m_cur_insn.GetOffset() + insn_len); - current_func_text_offset += insn_len; - } + // stack_offset for 'movq %r15, -80(%rbp)' will be 80. + // In the Row, we want to express this as the offset from the CFA. If the frame base + // is rbp (like the above instruction), the CFA offset for rbp is probably 16. So we + // want to say that the value is stored at the CFA address - 96. + regloc.SetAtCFAPlusOffset (-(stack_offset + row->GetCFAOffset())); - // Now look at the byte at the end of the AddressRange for a limited attempt at describing the - // epilogue. We're looking for the sequence + row->SetRegisterInfo (lldb_regno, regloc); - // [ 0x5d ] mov %rbp, %rsp (aka pop %rbp) - // [ 0xc3 ] ret + row_updated = true; + } - // or + else if (sub_rsp_pattern_p (stack_offset)) + { + current_sp_bytes_offset_from_cfa += stack_offset; + if (row->GetCFARegister() == m_lldb_sp_regnum) + { + row->SetCFAOffset (current_sp_bytes_offset_from_cfa); + row_updated = true; + } + } - // [ 0x5d ] mov %rbp, %rsp (aka pop %rbp) - // [ 0xe9 xx xx xx xx ] jmp objc_retainAutoreleaseReturnValue (this is sometimes the final insn in the function) + else if (add_rsp_pattern_p (stack_offset)) + { + current_sp_bytes_offset_from_cfa -= stack_offset; + if (row->GetCFARegister() == m_lldb_sp_regnum) + { + row->SetCFAOffset (current_sp_bytes_offset_from_cfa); + row_updated = true; + } + in_epilogue = true; + } - // or + else if (ret_pattern_p () && prologue_completed_row.get()) + { + // Reinstate the saved prologue setup for any instructions + // that come after the ret instruction - // [ 0x5d ] mov %rbp, %rsp (aka pop %rbp) - // [ 0xc3 ] ret - // [ 0xe8 xx xx xx xx ] call __stack_chk_fail (this is sometimes the final insn in the function) + UnwindPlan::Row *newrow = new UnwindPlan::Row; + *newrow = *prologue_completed_row.get(); + row.reset (newrow); + current_sp_bytes_offset_from_cfa = prologue_completed_sp_bytes_offset_from_cfa; - // We want to add a Row describing how to unwind when we're stopped on the 'ret' instruction where the - // CFA is no longer defined in terms of rbp, but is now defined in terms of rsp like on function entry. - // (or the 'jmp' instruction in the second case) + saved_registers.clear(); + saved_registers.resize(prologue_completed_saved_registers.size(), false); + for (size_t i = 0; i < prologue_completed_saved_registers.size(); ++i) + { + saved_registers[i] = prologue_completed_saved_registers[i]; + } - uint64_t ret_insn_offset = LLDB_INVALID_ADDRESS; - Address end_of_fun(m_func_bounds.GetBaseAddress()); - end_of_fun.SetOffset (end_of_fun.GetOffset() + m_func_bounds.GetByteSize()); + in_epilogue = true; + row_updated = true; + } - if (m_func_bounds.GetByteSize() > 7) - { - uint8_t bytebuf[7]; - Address last_seven_bytes(end_of_fun); - last_seven_bytes.SetOffset (last_seven_bytes.GetOffset() - 7); - if (target->ReadMemory (last_seven_bytes, prefer_file_cache, bytebuf, 7, - error) != static_cast<size_t>(-1)) + // call next instruction + // call 0 + // => pop %ebx + // This is used in i386 programs to get the PIC base address for finding global data + else if (call_next_insn_pattern_p ()) { - if (bytebuf[5] == 0x5d && bytebuf[6] == 0xc3) // mov & ret - { - ret_insn_offset = m_func_bounds.GetByteSize() - 1; - } - else if (bytebuf[1] == 0x5d && bytebuf[2] == 0xe9) // mov & jmp - { - // When the pc is sitting on the 'jmp' instruction, we have the same - // unwind state as if it was sitting on a 'ret' instruction. - ret_insn_offset = m_func_bounds.GetByteSize() - 5; - } - else if (bytebuf[0] == 0x5d && bytebuf[1] == 0xc3 && bytebuf[2] == 0xe8) // mov & ret & call + current_sp_bytes_offset_from_cfa += m_wordsize; + if (row->GetCFARegister() == m_lldb_sp_regnum) { - ret_insn_offset = m_func_bounds.GetByteSize() - 6; + row->SetCFAOffset (current_sp_bytes_offset_from_cfa); + row_updated = true; } } - } - else if (m_func_bounds.GetByteSize() > 2) - { - uint8_t bytebuf[2]; - Address last_two_bytes(end_of_fun); - last_two_bytes.SetOffset (last_two_bytes.GetOffset() - 2); - if (target->ReadMemory (last_two_bytes, prefer_file_cache, bytebuf, 2, - error) != static_cast<size_t>(-1)) + + if (row_updated) { - if (bytebuf[0] == 0x5d && bytebuf[1] == 0xc3) // mov & ret + if (current_func_text_offset + insn_len < m_func_bounds.GetByteSize()) { - ret_insn_offset = m_func_bounds.GetByteSize() - 1; + row->SetOffset (current_func_text_offset + insn_len); + unwind_plan.AppendRow (row); + // Allocate a new Row, populate it with the existing Row contents. + newrow = new UnwindPlan::Row; + *newrow = *row.get(); + row.reset(newrow); } } - } - if (ret_insn_offset != LLDB_INVALID_ADDRESS) - { - // Create a fresh, empty Row and RegisterLocation - don't mention any other registers - UnwindPlan::RowSP epi_row(new UnwindPlan::Row); - UnwindPlan::Row::RegisterLocation epi_regloc; - - // When the ret instruction is about to be executed, here's our state - epi_row->SetOffset (ret_insn_offset); - epi_row->SetCFARegister (m_lldb_sp_regnum); - epi_row->SetCFAOffset (m_wordsize); + if (in_epilogue == false && row_updated) + { + // If we're not in an epilogue sequence, save the updated Row + UnwindPlan::Row *newrow = new UnwindPlan::Row; + *newrow = *row.get(); + prologue_completed_row.reset (newrow); - // caller's stack pointer value before the call insn is the CFA address - epi_regloc.SetIsCFAPlusOffset (0); - epi_row->SetRegisterInfo (m_lldb_sp_regnum, epi_regloc); + prologue_completed_saved_registers.clear(); + prologue_completed_saved_registers.resize(saved_registers.size(), false); + for (size_t i = 0; i < saved_registers.size(); ++i) + { + prologue_completed_saved_registers[i] = saved_registers[i]; + } + } - // saved instruction pointer can be found at CFA - wordsize - epi_regloc.SetAtCFAPlusOffset (-m_wordsize); - epi_row->SetRegisterInfo (m_lldb_ip_regnum, epi_regloc); + // We may change the sp value without adding a new Row necessarily -- keep + // track of it either way. + if (in_epilogue == false) + { + prologue_completed_sp_bytes_offset_from_cfa = current_sp_bytes_offset_from_cfa; + } - unwind_plan.AppendRow (epi_row); + m_cur_insn.SetOffset (m_cur_insn.GetOffset() + insn_len); + current_func_text_offset += insn_len; } unwind_plan.SetSourceName ("assembly insn profiling"); @@ -902,12 +908,20 @@ AssemblyParse_x86::augment_unwind_plan_from_call_site (AddressRange& func, Unwin if (cfa_reg != m_lldb_sp_regnum || first_row->GetCFAOffset() != m_wordsize) return false; + UnwindPlan::RowSP original_last_row = unwind_plan.GetRowForFunctionOffset (-1); + Target *target = m_exe_ctx.GetTargetPtr(); m_cur_insn = func.GetBaseAddress(); uint64_t offset = 0; int row_id = 1; bool unwind_plan_updated = false; UnwindPlan::RowSP row(new UnwindPlan::Row(*first_row)); + + // After a mid-function epilogue we will need to re-insert the original unwind rules + // so unwinds work for the remainder of the function. These aren't common with clang/gcc + // on x86 but it is possible. + bool reinstate_unwind_state = false; + while (func.ContainsFileAddress (m_cur_insn)) { int insn_len; @@ -930,10 +944,29 @@ AssemblyParse_x86::augment_unwind_plan_from_call_site (AddressRange& func, Unwin offset += insn_len; m_cur_insn.SetOffset(m_cur_insn.GetOffset() + insn_len); + if (reinstate_unwind_state) + { + // that was the last instruction of this function + if (func.ContainsFileAddress (m_cur_insn) == false) + continue; + + UnwindPlan::RowSP new_row(new UnwindPlan::Row()); + *new_row = *original_last_row; + new_row->SetOffset (offset); + unwind_plan.AppendRow (new_row); + row.reset (new UnwindPlan::Row()); + *row = *new_row; + reinstate_unwind_state = false; + unwind_plan_updated = true; + continue; + } + // 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) { @@ -1032,6 +1065,11 @@ AssemblyParse_x86::augment_unwind_plan_from_call_site (AddressRange& func, Unwin unwind_plan_updated = true; continue; } + if (ret_pattern_p ()) + { + reinstate_unwind_state = true; + continue; + } } else if (cfa_reg == m_lldb_fp_regnum) { @@ -1053,6 +1091,7 @@ AssemblyParse_x86::augment_unwind_plan_from_call_site (AddressRange& func, Unwin UnwindPlan::RowSP new_row(new UnwindPlan::Row(*row)); unwind_plan.InsertRow (new_row); unwind_plan_updated = true; + reinstate_unwind_state = true; continue; } } @@ -1074,6 +1113,7 @@ AssemblyParse_x86::augment_unwind_plan_from_call_site (AddressRange& func, Unwin unwind_plan_source += " plus augmentation from assembly parsing"; unwind_plan.SetSourceName (unwind_plan_source.c_str()); unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolYes); } return true; } @@ -1239,17 +1279,130 @@ 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 do_augment_unwindplan = true; + + UnwindPlan::RowSP first_row = unwind_plan.GetRowForFunctionOffset (0); + UnwindPlan::RowSP last_row = unwind_plan.GetRowForFunctionOffset (-1); + + int wordsize = 8; + ProcessSP process_sp (thread.GetProcess()); + if (process_sp) + { + wordsize = process_sp->GetTarget().GetArchitecture().GetAddressByteSize(); + } + + RegisterNumber sp_regnum (thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + RegisterNumber pc_regnum (thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + + // Does this UnwindPlan describe the prologue? I want to see that the CFA is set + // in terms of the stack pointer plus an offset, and I want to see that rip is + // retrieved at the CFA-wordsize. + // If there is no description of the prologue, don't try to augment this eh_frame + // unwinder code, fall back to assembly parsing instead. + + if (first_row->GetCFAType() != UnwindPlan::Row::CFAType::CFAIsRegisterPlusOffset + || RegisterNumber (thread, unwind_plan.GetRegisterKind(), first_row->GetCFARegister()) != sp_regnum + || first_row->GetCFAOffset() != wordsize) + { + return false; + } + UnwindPlan::Row::RegisterLocation first_row_pc_loc; + if (first_row->GetRegisterInfo (pc_regnum.GetAsKind (unwind_plan.GetRegisterKind()), first_row_pc_loc) == false + || first_row_pc_loc.IsAtCFAPlusOffset() == false + || first_row_pc_loc.GetOffset() != -wordsize) + { + return false; + } + + + // It looks like the prologue is described. + // Is the epilogue described? If it is, no need to do any augmentation. + + if (first_row != last_row && first_row->GetOffset() != last_row->GetOffset()) + { + // The first & last row have the same CFA register + // and the same CFA offset value + // and the CFA register is esp/rsp (the stack pointer). + + // We're checking that both of them have an unwind rule like "CFA=esp+4" or CFA+rsp+8". + + if (first_row->GetCFAType() == last_row->GetCFAType() + && first_row->GetCFARegister() == last_row->GetCFARegister() + && first_row->GetCFAOffset() == last_row->GetCFAOffset()) + { + // Get the register locations for eip/rip from the first & last rows. + // Are they both CFA plus an offset? Is it the same offset? + + UnwindPlan::Row::RegisterLocation last_row_pc_loc; + if (last_row->GetRegisterInfo (pc_regnum.GetAsKind (unwind_plan.GetRegisterKind()), last_row_pc_loc)) + { + if (last_row_pc_loc.IsAtCFAPlusOffset() + && first_row_pc_loc.GetOffset() == last_row_pc_loc.GetOffset()) + { + + // One last sanity check: Is the unwind rule for getting the caller pc value + // "deref the CFA-4" or "deref the CFA-8"? + + // If so, we have an UnwindPlan that already describes the epilogue and we don't need + // to modify it at all. + + if (first_row_pc_loc.GetOffset() == -wordsize) + { + do_augment_unwindplan = false; + } + } + } + } + } + + if (do_augment_unwindplan) + { + 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); + } + + return false; } bool UnwindAssembly_x86::GetFastUnwindPlan (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.get_fast_unwind_plan (func, unwind_plan); + // if prologue is + // 55 pushl %ebp + // 89 e5 movl %esp, %ebp + // or + // 55 pushq %rbp + // 48 89 e5 movq %rsp, %rbp + + // We should pull in the ABI architecture default unwind plan and return that + + llvm::SmallVector <uint8_t, 4> opcode_data; + + ProcessSP process_sp = thread.GetProcess(); + if (process_sp) + { + Target &target (process_sp->GetTarget()); + const bool prefer_file_cache = true; + Error error; + if (target.ReadMemory (func.GetBaseAddress (), prefer_file_cache, opcode_data.data(), + 4, error) == 4) + { + uint8_t i386_push_mov[] = {0x55, 0x89, 0xe5}; + uint8_t x86_64_push_mov[] = {0x55, 0x48, 0x89, 0xe5}; + + if (memcmp (opcode_data.data(), i386_push_mov, sizeof (i386_push_mov)) == 0 + || memcmp (opcode_data.data(), x86_64_push_mov, sizeof (x86_64_push_mov)) == 0) + { + ABISP abi_sp = process_sp->GetABI(); + if (abi_sp) + { + return abi_sp->CreateDefaultUnwindPlan (unwind_plan); + } + } + } + } + return false; } bool diff --git a/source/Symbol/ClangASTContext.cpp b/source/Symbol/ClangASTContext.cpp index aae96bfa9a3b..d851ae792181 100644 --- a/source/Symbol/ClangASTContext.cpp +++ b/source/Symbol/ClangASTContext.cpp @@ -11,6 +11,7 @@ // C Includes // C++ Includes +#include <mutex> #include <string> // Other libraries and framework includes @@ -62,6 +63,7 @@ #include "lldb/Core/Flags.h" #include "lldb/Core/Log.h" #include "lldb/Core/RegularExpression.h" +#include "lldb/Core/ThreadSafeDenseMap.h" #include "lldb/Core/UniqueCStringMap.h" #include "lldb/Expression/ASTDumper.h" #include "lldb/Symbol/ClangExternalASTSourceCommon.h" @@ -79,13 +81,17 @@ using namespace lldb_private; using namespace llvm; using namespace clang; -typedef llvm::DenseMap<clang::ASTContext *, ClangASTContext*> ClangASTMap; +typedef lldb_private::ThreadSafeDenseMap<clang::ASTContext *, ClangASTContext*> ClangASTMap; static ClangASTMap & GetASTMap() { - static ClangASTMap g_map; - return g_map; + static ClangASTMap *g_map_ptr = nullptr; + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + g_map_ptr = new ClangASTMap(); // leaked on purpose to avoid spins + }); + return *g_map_ptr; } @@ -303,7 +309,7 @@ ClangASTContext::~ClangASTContext() { if (m_ast_ap.get()) { - GetASTMap().erase(m_ast_ap.get()); + GetASTMap().Erase(m_ast_ap.get()); } m_builtins_ap.reset(); @@ -409,7 +415,7 @@ ClangASTContext::getASTContext() m_ast_ap->getDiagnostics().setClient(getDiagnosticConsumer(), false); - GetASTMap().insert(std::make_pair(m_ast_ap.get(), this)); + GetASTMap().Insert(m_ast_ap.get(), this); } return m_ast_ap.get(); } @@ -417,7 +423,7 @@ ClangASTContext::getASTContext() ClangASTContext* ClangASTContext::GetASTContext (clang::ASTContext* ast) { - ClangASTContext *clang_ast = GetASTMap().lookup(ast); + ClangASTContext *clang_ast = GetASTMap().Lookup(ast); return clang_ast; } @@ -883,6 +889,13 @@ ClangASTContext::GetBuiltinTypeForDWARFEncodingAndBitSize (const char *type_name break; case DW_ATE_float: + if (streq(type_name, "float") && QualTypeMatchesBitSize (bit_size, ast, ast->FloatTy)) + return ClangASTType (ast, ast->FloatTy.getAsOpaquePtr()); + if (streq(type_name, "double") && QualTypeMatchesBitSize (bit_size, ast, ast->DoubleTy)) + return ClangASTType (ast, ast->DoubleTy.getAsOpaquePtr()); + if (streq(type_name, "long double") && QualTypeMatchesBitSize (bit_size, ast, ast->LongDoubleTy)) + return ClangASTType (ast, ast->LongDoubleTy.getAsOpaquePtr()); + // Fall back to not requring a name match if (QualTypeMatchesBitSize (bit_size, ast, ast->FloatTy)) return ClangASTType (ast, ast->FloatTy.getAsOpaquePtr()); if (QualTypeMatchesBitSize (bit_size, ast, ast->DoubleTy)) @@ -1119,6 +1132,16 @@ ClangASTContext::AreTypesSame (ClangASTType type1, return ast->hasSameType (type1_qual, type2_qual); } +ClangASTType +ClangASTContext::GetTypeForDecl (clang::NamedDecl *decl) +{ + if (clang::ObjCInterfaceDecl *interface_decl = llvm::dyn_cast<clang::ObjCInterfaceDecl>(decl)) + return GetTypeForDecl(interface_decl); + if (clang::TagDecl *tag_decl = llvm::dyn_cast<clang::TagDecl>(decl)) + return GetTypeForDecl(tag_decl); + return ClangASTType(); +} + ClangASTType ClangASTContext::GetTypeForDecl (TagDecl *decl) @@ -1126,7 +1149,7 @@ ClangASTContext::GetTypeForDecl (TagDecl *decl) // No need to call the getASTContext() accessor (which can create the AST // if it isn't created yet, because we can't have created a decl in this // AST if our AST didn't already exist... - ASTContext *ast = m_ast_ap.get(); + ASTContext *ast = &decl->getASTContext(); if (ast) return ClangASTType (ast, ast->getTagDeclType(decl).getAsOpaquePtr()); return ClangASTType(); @@ -1138,7 +1161,7 @@ ClangASTContext::GetTypeForDecl (ObjCInterfaceDecl *decl) // No need to call the getASTContext() accessor (which can create the AST // if it isn't created yet, because we can't have created a decl in this // AST if our AST didn't already exist... - ASTContext *ast = m_ast_ap.get(); + ASTContext *ast = &decl->getASTContext(); if (ast) return ClangASTType (ast, ast->getObjCInterfaceType(decl).getAsOpaquePtr()); return ClangASTType(); @@ -1733,7 +1756,7 @@ ClangASTContext::CreateFunctionDeclaration (DeclContext *decl_ctx, DeclarationName (&ast->Idents.get(name)), function_clang_type.GetQualType(), nullptr, - (FunctionDecl::StorageClass)storage, + (clang::StorageClass)storage, is_inline, hasWrittenPrototype, isConstexprSpecified); @@ -1747,7 +1770,7 @@ ClangASTContext::CreateFunctionDeclaration (DeclContext *decl_ctx, DeclarationName (), function_clang_type.GetQualType(), nullptr, - (FunctionDecl::StorageClass)storage, + (clang::StorageClass)storage, is_inline, hasWrittenPrototype, isConstexprSpecified); @@ -1799,7 +1822,7 @@ ClangASTContext::CreateParameterDeclaration (const char *name, const ClangASTTyp name && name[0] ? &ast->Idents.get(name) : nullptr, param_type.GetQualType(), nullptr, - (VarDecl::StorageClass)storage, + (clang::StorageClass)storage, nullptr); } @@ -1849,7 +1872,23 @@ ClangASTContext::CreateArrayType (const ClangASTType &element_type, return ClangASTType(); } - +ClangASTType +ClangASTContext::GetOrCreateStructForIdentifier (const ConstString &type_name, + const std::initializer_list< std::pair < const char *, ClangASTType > >& type_fields, + bool packed) +{ + ClangASTType type; + if ((type = GetTypeForIdentifier<clang::CXXRecordDecl>(type_name)).IsValid()) + return type; + type = CreateRecordType(nullptr, lldb::eAccessPublic, type_name.GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); + type.StartTagDeclarationDefinition(); + for (const auto& field : type_fields) + type.AddFieldToRecordType(field.first, field.second, lldb::eAccessPublic, 0); + if (packed) + type.SetIsPacked(); + type.CompleteTagDeclarationDefinition(); + return type; +} #pragma mark Enumeration Types @@ -2067,7 +2106,7 @@ ClangASTContext::SetMetadata (clang::ASTContext *ast, ClangASTMetadata &metadata) { ClangExternalASTSourceCommon *external_source = - static_cast<ClangExternalASTSourceCommon*>(ast->getExternalSource()); + ClangExternalASTSourceCommon::Lookup(ast->getExternalSource()); if (external_source) external_source->SetMetadata(object, metadata); @@ -2078,7 +2117,7 @@ ClangASTContext::GetMetadata (clang::ASTContext *ast, const void *object) { ClangExternalASTSourceCommon *external_source = - static_cast<ClangExternalASTSourceCommon*>(ast->getExternalSource()); + ClangExternalASTSourceCommon::Lookup(ast->getExternalSource()); if (external_source && external_source->HasMetadata(object)) return external_source->GetMetadata(object); diff --git a/source/Symbol/ClangASTImporter.cpp b/source/Symbol/ClangASTImporter.cpp index 6579afb2f748..a925f808f858 100644 --- a/source/Symbol/ClangASTImporter.cpp +++ b/source/Symbol/ClangASTImporter.cpp @@ -86,7 +86,7 @@ ClangASTImporter::CopyDecl (clang::ASTContext *dst_ast, if (log) { - lldb::user_id_t user_id; + lldb::user_id_t user_id = LLDB_INVALID_UID; ClangASTMetadata *metadata = GetDeclMetadata(decl); if (metadata) user_id = metadata->GetUserID(); diff --git a/source/Symbol/ClangASTType.cpp b/source/Symbol/ClangASTType.cpp index a0878ae442c8..8b672fac4312 100644 --- a/source/Symbol/ClangASTType.cpp +++ b/source/Symbol/ClangASTType.cpp @@ -47,6 +47,7 @@ #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" +#include <iterator> #include <mutex> using namespace lldb; @@ -515,7 +516,7 @@ ClangASTType::GetNumberOfFunctionArguments () const } ClangASTType -ClangASTType::GetFunctionArgumentAtIndex (const size_t index) +ClangASTType::GetFunctionArgumentAtIndex (const size_t index) const { if (IsValid()) { @@ -1713,7 +1714,7 @@ ClangASTType::GetFunctionArgumentCount () const } ClangASTType -ClangASTType::GetFunctionArgumentTypeAtIndex (size_t idx) +ClangASTType::GetFunctionArgumentTypeAtIndex (size_t idx) const { if (IsValid()) { @@ -1741,6 +1742,199 @@ ClangASTType::GetFunctionReturnType () const return ClangASTType(); } +size_t +ClangASTType::GetNumMemberFunctions () const +{ + size_t num_functions = 0; + if (IsValid()) + { + clang::QualType qual_type(GetCanonicalQualType()); + switch (qual_type->getTypeClass()) { + case clang::Type::Record: + if (GetCompleteQualType (m_ast, qual_type)) + { + const clang::RecordType *record_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr()); + const clang::RecordDecl *record_decl = record_type->getDecl(); + assert(record_decl); + const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(record_decl); + if (cxx_record_decl) + num_functions = std::distance(cxx_record_decl->method_begin(), cxx_record_decl->method_end()); + } + break; + + case clang::Type::ObjCObjectPointer: + if (GetCompleteType()) + { + const clang::ObjCObjectPointerType *objc_class_type = qual_type->getAsObjCInterfacePointerType(); + if (objc_class_type) + { + clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterfaceDecl(); + if (class_interface_decl) + num_functions = std::distance(class_interface_decl->meth_begin(), class_interface_decl->meth_end()); + } + } + break; + + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + if (GetCompleteType()) + { + const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast<clang::ObjCObjectType>(qual_type.getTypePtr()); + if (objc_class_type) + { + clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); + if (class_interface_decl) + num_functions = std::distance(class_interface_decl->meth_begin(), class_interface_decl->meth_end()); + } + } + break; + + + case clang::Type::Typedef: + return ClangASTType (m_ast, llvm::cast<clang::TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetNumMemberFunctions(); + + case clang::Type::Elaborated: + return ClangASTType (m_ast, llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType()).GetNumMemberFunctions(); + + case clang::Type::Paren: + return ClangASTType (m_ast, llvm::cast<clang::ParenType>(qual_type)->desugar()).GetNumMemberFunctions(); + + default: + break; + } + } + return num_functions; +} + +TypeMemberFunctionImpl +ClangASTType::GetMemberFunctionAtIndex (size_t idx) +{ + std::string name(""); + MemberFunctionKind kind(MemberFunctionKind::eMemberFunctionKindUnknown); + ClangASTType type{}; + clang::ObjCMethodDecl *method_decl(nullptr); + if (IsValid()) + { + clang::QualType qual_type(GetCanonicalQualType()); + switch (qual_type->getTypeClass()) { + case clang::Type::Record: + if (GetCompleteQualType (m_ast, qual_type)) + { + const clang::RecordType *record_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr()); + const clang::RecordDecl *record_decl = record_type->getDecl(); + assert(record_decl); + const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(record_decl); + if (cxx_record_decl) + { + auto method_iter = cxx_record_decl->method_begin(); + auto method_end = cxx_record_decl->method_end(); + if (idx < static_cast<size_t>(std::distance(method_iter, method_end))) + { + std::advance(method_iter, idx); + auto method_decl = method_iter->getCanonicalDecl(); + if (method_decl) + { + if (!method_decl->getName().empty()) + name.assign(method_decl->getName().data()); + else + name.clear(); + if (method_decl->isStatic()) + kind = lldb::eMemberFunctionKindStaticMethod; + else if (llvm::isa<clang::CXXConstructorDecl>(method_decl)) + kind = lldb::eMemberFunctionKindConstructor; + else if (llvm::isa<clang::CXXDestructorDecl>(method_decl)) + kind = lldb::eMemberFunctionKindDestructor; + else + kind = lldb::eMemberFunctionKindInstanceMethod; + type = ClangASTType(m_ast,method_decl->getType().getAsOpaquePtr()); + } + } + } + } + break; + + case clang::Type::ObjCObjectPointer: + if (GetCompleteType()) + { + const clang::ObjCObjectPointerType *objc_class_type = qual_type->getAsObjCInterfacePointerType(); + if (objc_class_type) + { + clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterfaceDecl(); + if (class_interface_decl) + { + auto method_iter = class_interface_decl->meth_begin(); + auto method_end = class_interface_decl->meth_end(); + if (idx < static_cast<size_t>(std::distance(method_iter, method_end))) + { + std::advance(method_iter, idx); + method_decl = method_iter->getCanonicalDecl(); + if (method_decl) + { + name = method_decl->getSelector().getAsString(); + if (method_decl->isClassMethod()) + kind = lldb::eMemberFunctionKindStaticMethod; + else + kind = lldb::eMemberFunctionKindInstanceMethod; + } + } + } + } + } + break; + + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + if (GetCompleteType()) + { + const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast<clang::ObjCObjectType>(qual_type.getTypePtr()); + if (objc_class_type) + { + clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); + if (class_interface_decl) + { + auto method_iter = class_interface_decl->meth_begin(); + auto method_end = class_interface_decl->meth_end(); + if (idx < static_cast<size_t>(std::distance(method_iter, method_end))) + { + std::advance(method_iter, idx); + method_decl = method_iter->getCanonicalDecl(); + if (method_decl) + { + name = method_decl->getSelector().getAsString(); + if (method_decl->isClassMethod()) + kind = lldb::eMemberFunctionKindStaticMethod; + else + kind = lldb::eMemberFunctionKindInstanceMethod; + } + } + } + } + } + break; + + case clang::Type::Typedef: + return ClangASTType (m_ast, llvm::cast<clang::TypedefType>(qual_type)->getDecl()->getUnderlyingType()).GetMemberFunctionAtIndex(idx); + + case clang::Type::Elaborated: + return ClangASTType (m_ast, llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType()).GetMemberFunctionAtIndex(idx); + + case clang::Type::Paren: + return ClangASTType (m_ast, llvm::cast<clang::ParenType>(qual_type)->desugar()).GetMemberFunctionAtIndex(idx); + + default: + break; + } + } + + if (kind == eMemberFunctionKindUnknown) + return TypeMemberFunctionImpl(); + if (method_decl) + return TypeMemberFunctionImpl(method_decl, name, kind); + if (type) + return TypeMemberFunctionImpl(type, name, kind); + + return TypeMemberFunctionImpl(); +} ClangASTType ClangASTType::GetLValueReferenceType () const @@ -3448,7 +3642,7 @@ ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx, case clang::Type::IncompleteArray: if (ignore_array_bounds || idx_is_valid) { - const clang::ArrayType *array = llvm::cast<clang::ArrayType>(parent_qual_type.getTypePtr()); + const clang::ArrayType *array = GetQualType()->getAsArrayTypeUnsafe(); if (array) { ClangASTType element_type (m_ast, array->getElementType()); @@ -4752,6 +4946,17 @@ ClangASTType::BuildIndirectFields () } } +void +ClangASTType::SetIsPacked () +{ + clang::RecordDecl *record_decl = GetAsRecordDecl(); + + if (!record_decl) + return; + + record_decl->addAttr(clang::PackedAttr::CreateImplicit(*m_ast)); +} + clang::VarDecl * ClangASTType::AddVariableToRecordType (const char *name, const ClangASTType &var_type, @@ -5176,9 +5381,12 @@ ClangASTType::AddObjCClassProperty (const char *property_name, if (getter && metadata) ClangASTContext::SetMetadata(m_ast, getter, *metadata); - getter->setMethodParams(*m_ast, llvm::ArrayRef<clang::ParmVarDecl*>(), llvm::ArrayRef<clang::SourceLocation>()); + if (getter) + { + getter->setMethodParams(*m_ast, llvm::ArrayRef<clang::ParmVarDecl*>(), llvm::ArrayRef<clang::SourceLocation>()); - class_interface_decl->addDecl(getter); + class_interface_decl->addDecl(getter); + } } if (!setter_sel.isNull() && !class_interface_decl->lookupInstanceMethod(setter_sel)) @@ -5223,9 +5431,12 @@ ClangASTType::AddObjCClassProperty (const char *property_name, clang::SC_Auto, nullptr)); - setter->setMethodParams(*m_ast, llvm::ArrayRef<clang::ParmVarDecl*>(params), llvm::ArrayRef<clang::SourceLocation>()); + if (setter) + { + setter->setMethodParams(*m_ast, llvm::ArrayRef<clang::ParmVarDecl*>(params), llvm::ArrayRef<clang::SourceLocation>()); - class_interface_decl->addDecl(setter); + class_interface_decl->addDecl(setter); + } } return true; diff --git a/source/Symbol/ClangExternalASTSourceCommon.cpp b/source/Symbol/ClangExternalASTSourceCommon.cpp index 650d252a8fc3..79cc9a91355a 100644 --- a/source/Symbol/ClangExternalASTSourceCommon.cpp +++ b/source/Symbol/ClangExternalASTSourceCommon.cpp @@ -9,30 +9,52 @@ #include "lldb/Symbol/ClangExternalASTSourceCommon.h" #include "lldb/Core/Stream.h" +#include "lldb/Host/Mutex.h" using namespace lldb_private; -#define ClangExternalASTSourceCommon_MAGIC (0x00112233aabbccddull) - uint64_t g_TotalSizeOfMetadata = 0; -ClangExternalASTSourceCommon::ClangExternalASTSourceCommon() : clang::ExternalASTSource() +typedef llvm::DenseMap<clang::ExternalASTSource *, ClangExternalASTSourceCommon *> ASTSourceMap; + +static ASTSourceMap &GetSourceMap() +{ + static ASTSourceMap s_source_map; + return s_source_map; +} + +ClangExternalASTSourceCommon * +ClangExternalASTSourceCommon::Lookup(clang::ExternalASTSource *source) { - m_magic = ClangExternalASTSourceCommon_MAGIC; + ASTSourceMap &source_map = GetSourceMap(); + + ASTSourceMap::iterator iter = source_map.find(source); + if (iter != source_map.end()) + { + return iter->second; + } + else + { + return nullptr; + } +} + +ClangExternalASTSourceCommon::ClangExternalASTSourceCommon() : clang::ExternalASTSource() +{ g_TotalSizeOfMetadata += m_metadata.size(); + GetSourceMap()[this] = this; } ClangExternalASTSourceCommon::~ClangExternalASTSourceCommon() { + GetSourceMap().erase(this); g_TotalSizeOfMetadata -= m_metadata.size(); } ClangASTMetadata * ClangExternalASTSourceCommon::GetMetadata (const void *object) { - assert (m_magic == ClangExternalASTSourceCommon_MAGIC); - if (HasMetadata (object)) return &m_metadata[object]; else @@ -42,8 +64,6 @@ ClangExternalASTSourceCommon::GetMetadata (const void *object) void ClangExternalASTSourceCommon::SetMetadata (const void *object, ClangASTMetadata &metadata) { - assert (m_magic == ClangExternalASTSourceCommon_MAGIC); - uint64_t orig_size = m_metadata.size(); m_metadata[object] = metadata; uint64_t new_size = m_metadata.size(); @@ -53,8 +73,6 @@ ClangExternalASTSourceCommon::SetMetadata (const void *object, ClangASTMetadata bool ClangExternalASTSourceCommon::HasMetadata (const void *object) { - assert (m_magic == ClangExternalASTSourceCommon_MAGIC); - return m_metadata.find(object) != m_metadata.end(); } diff --git a/source/Symbol/CompactUnwindInfo.cpp b/source/Symbol/CompactUnwindInfo.cpp new file mode 100644 index 000000000000..8c6a2e7214c3 --- /dev/null +++ b/source/Symbol/CompactUnwindInfo.cpp @@ -0,0 +1,1215 @@ +//===-- CompactUnwindInfo.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +#include <algorithm> + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompactUnwindInfo.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "llvm/Support/MathExtras.h" + +using namespace lldb; +using namespace lldb_private; + + +namespace lldb_private { + + // Constants from <mach-o/compact_unwind_encoding.h> + + enum { + UNWIND_IS_NOT_FUNCTION_START = 0x80000000, + UNWIND_HAS_LSDA = 0x40000000, + UNWIND_PERSONALITY_MASK = 0x30000000, + }; + + enum { + UNWIND_X86_MODE_MASK = 0x0F000000, + UNWIND_X86_MODE_EBP_FRAME = 0x01000000, + UNWIND_X86_MODE_STACK_IMMD = 0x02000000, + UNWIND_X86_MODE_STACK_IND = 0x03000000, + UNWIND_X86_MODE_DWARF = 0x04000000, + + UNWIND_X86_EBP_FRAME_REGISTERS = 0x00007FFF, + UNWIND_X86_EBP_FRAME_OFFSET = 0x00FF0000, + + UNWIND_X86_FRAMELESS_STACK_SIZE = 0x00FF0000, + UNWIND_X86_FRAMELESS_STACK_ADJUST = 0x0000E000, + UNWIND_X86_FRAMELESS_STACK_REG_COUNT = 0x00001C00, + UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF, + + UNWIND_X86_DWARF_SECTION_OFFSET = 0x00FFFFFF, + }; + + enum { + UNWIND_X86_REG_NONE = 0, + UNWIND_X86_REG_EBX = 1, + UNWIND_X86_REG_ECX = 2, + UNWIND_X86_REG_EDX = 3, + UNWIND_X86_REG_EDI = 4, + UNWIND_X86_REG_ESI = 5, + UNWIND_X86_REG_EBP = 6, + }; + enum { + UNWIND_X86_64_MODE_MASK = 0x0F000000, + UNWIND_X86_64_MODE_RBP_FRAME = 0x01000000, + UNWIND_X86_64_MODE_STACK_IMMD = 0x02000000, + UNWIND_X86_64_MODE_STACK_IND = 0x03000000, + UNWIND_X86_64_MODE_DWARF = 0x04000000, + + UNWIND_X86_64_RBP_FRAME_REGISTERS = 0x00007FFF, + UNWIND_X86_64_RBP_FRAME_OFFSET = 0x00FF0000, + + UNWIND_X86_64_FRAMELESS_STACK_SIZE = 0x00FF0000, + UNWIND_X86_64_FRAMELESS_STACK_ADJUST = 0x0000E000, + UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT = 0x00001C00, + UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF, + + UNWIND_X86_64_DWARF_SECTION_OFFSET = 0x00FFFFFF, + }; + + enum { + UNWIND_X86_64_REG_NONE = 0, + UNWIND_X86_64_REG_RBX = 1, + UNWIND_X86_64_REG_R12 = 2, + UNWIND_X86_64_REG_R13 = 3, + UNWIND_X86_64_REG_R14 = 4, + UNWIND_X86_64_REG_R15 = 5, + UNWIND_X86_64_REG_RBP = 6, + }; +}; + + +#ifndef UNWIND_SECOND_LEVEL_REGULAR +#define UNWIND_SECOND_LEVEL_REGULAR 2 +#endif + +#ifndef UNWIND_SECOND_LEVEL_COMPRESSED +#define UNWIND_SECOND_LEVEL_COMPRESSED 3 +#endif + +#ifndef UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET +#define UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry) (entry & 0x00FFFFFF) +#endif + +#ifndef UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX +#define UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry) ((entry >> 24) & 0xFF) +#endif + +#define EXTRACT_BITS(value, mask) \ + ( (value >> llvm::countTrailingZeros(static_cast<uint32_t>(mask), llvm::ZB_Width)) & \ + (((1 << llvm::CountPopulation_32(static_cast<uint32_t>(mask))))-1) ) + + + +//---------------------- +// constructor +//---------------------- + + +CompactUnwindInfo::CompactUnwindInfo(ObjectFile& objfile, SectionSP& section_sp) : + m_objfile (objfile), + m_section_sp (section_sp), + m_section_contents_if_encrypted (), + m_mutex (), + m_indexes (), + m_indexes_computed (eLazyBoolCalculate), + m_unwindinfo_data (), + m_unwindinfo_data_computed (false), + m_unwind_header () +{ + +} + +//---------------------- +// destructor +//---------------------- + +CompactUnwindInfo::~CompactUnwindInfo() +{ +} + +bool +CompactUnwindInfo::GetUnwindPlan (Target &target, Address addr, UnwindPlan& unwind_plan) +{ + if (!IsValid (target.GetProcessSP())) + { + return false; + } + FunctionInfo function_info; + if (GetCompactUnwindInfoForFunction (target, addr, function_info)) + { + // shortcut return for functions that have no compact unwind + if (function_info.encoding == 0) + return false; + + ArchSpec arch; + if (m_objfile.GetArchitecture (arch)) + { + + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); + if (log && log->GetVerbose()) + { + StreamString strm; + addr.Dump (&strm, NULL, Address::DumpStyle::DumpStyleResolvedDescriptionNoFunctionArguments, Address::DumpStyle::DumpStyleFileAddress, arch.GetAddressByteSize()); + log->Printf ("Got compact unwind encoding 0x%x for function %s", function_info.encoding, strm.GetData()); + } + + if (function_info.valid_range_offset_start != 0 && function_info.valid_range_offset_end != 0) + { + SectionList *sl = m_objfile.GetSectionList (); + if (sl) + { + addr_t func_range_start_file_addr = + function_info.valid_range_offset_start + m_objfile.GetHeaderAddress().GetFileAddress(); + AddressRange func_range (func_range_start_file_addr, + function_info.valid_range_offset_end - function_info.valid_range_offset_start, + sl); + unwind_plan.SetPlanValidAddressRange (func_range); + } + } + + if (arch.GetTriple().getArch() == llvm::Triple::x86_64) + { + return CreateUnwindPlan_x86_64 (target, function_info, unwind_plan, addr); + } + if (arch.GetTriple().getArch() == llvm::Triple::x86) + { + return CreateUnwindPlan_i386 (target, function_info, unwind_plan, addr); + } + } + } + return false; +} + +bool +CompactUnwindInfo::IsValid (const ProcessSP &process_sp) +{ + if (m_section_sp.get() == nullptr) + return false; + + if (m_indexes_computed == eLazyBoolYes && m_unwindinfo_data_computed) + return true; + + ScanIndex (process_sp); + + return m_indexes_computed == eLazyBoolYes && m_unwindinfo_data_computed; +} + +void +CompactUnwindInfo::ScanIndex (const ProcessSP &process_sp) +{ + Mutex::Locker locker(m_mutex); + if (m_indexes_computed == eLazyBoolYes && m_unwindinfo_data_computed) + return; + + // We can't read the index for some reason. + if (m_indexes_computed == eLazyBoolNo) + { + return; + } + + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND)); + if (log) + m_objfile.GetModule()->LogMessage(log, "Reading compact unwind first-level indexes"); + + if (m_unwindinfo_data_computed == false) + { + if (m_section_sp->IsEncrypted()) + { + // Can't get section contents of a protected/encrypted section until we have a live + // process and can read them out of memory. + if (process_sp.get() == nullptr) + return; + m_section_contents_if_encrypted.reset (new DataBufferHeap (m_section_sp->GetByteSize(), 0)); + Error error; + if (process_sp->ReadMemory ( + m_section_sp->GetLoadBaseAddress (&process_sp->GetTarget()), + m_section_contents_if_encrypted->GetBytes(), + m_section_sp->GetByteSize(), error) == m_section_sp->GetByteSize() && error.Success()) + { + m_unwindinfo_data.SetAddressByteSize (process_sp->GetTarget().GetArchitecture().GetAddressByteSize()); + m_unwindinfo_data.SetByteOrder (process_sp->GetTarget().GetArchitecture().GetByteOrder()); + m_unwindinfo_data.SetData (m_section_contents_if_encrypted, 0); + } + } + else + { + m_objfile.ReadSectionData (m_section_sp.get(), m_unwindinfo_data); + } + if (m_unwindinfo_data.GetByteSize() != m_section_sp->GetByteSize()) + return; + m_unwindinfo_data_computed = true; + } + + if (m_unwindinfo_data.GetByteSize() > 0) + { + offset_t offset = 0; + + // struct unwind_info_section_header + // { + // uint32_t version; // UNWIND_SECTION_VERSION + // uint32_t commonEncodingsArraySectionOffset; + // uint32_t commonEncodingsArrayCount; + // uint32_t personalityArraySectionOffset; + // uint32_t personalityArrayCount; + // uint32_t indexSectionOffset; + // uint32_t indexCount; + + m_unwind_header.version = m_unwindinfo_data.GetU32(&offset); + m_unwind_header.common_encodings_array_offset = m_unwindinfo_data.GetU32(&offset); + m_unwind_header.common_encodings_array_count = m_unwindinfo_data.GetU32(&offset); + m_unwind_header.personality_array_offset = m_unwindinfo_data.GetU32(&offset); + m_unwind_header.personality_array_count = m_unwindinfo_data.GetU32(&offset); + uint32_t indexSectionOffset = m_unwindinfo_data.GetU32(&offset); + + uint32_t indexCount = m_unwindinfo_data.GetU32(&offset); + + if (m_unwind_header.version != 1) + { + m_indexes_computed = eLazyBoolNo; + } + + // Parse the basic information from the indexes + // We wait to scan the second level page info until it's needed + + // struct unwind_info_section_header_index_entry + // { + // uint32_t functionOffset; + // uint32_t secondLevelPagesSectionOffset; + // uint32_t lsdaIndexArraySectionOffset; + // }; + + offset = indexSectionOffset; + for (uint32_t idx = 0; idx < indexCount; idx++) + { + uint32_t function_offset = m_unwindinfo_data.GetU32(&offset); // functionOffset + uint32_t second_level_offset = m_unwindinfo_data.GetU32(&offset); // secondLevelPagesSectionOffset + uint32_t lsda_offset = m_unwindinfo_data.GetU32(&offset); // lsdaIndexArraySectionOffset + + if (second_level_offset > m_section_sp->GetByteSize() || lsda_offset > m_section_sp->GetByteSize()) + { + m_indexes_computed = eLazyBoolNo; + } + + UnwindIndex this_index; + this_index.function_offset = function_offset; // + this_index.second_level = second_level_offset; + this_index.lsda_array_start = lsda_offset; + + if (m_indexes.size() > 0) + { + m_indexes[m_indexes.size() - 1].lsda_array_end = lsda_offset; + } + + if (second_level_offset == 0) + { + this_index.sentinal_entry = true; + } + + m_indexes.push_back (this_index); + } + m_indexes_computed = eLazyBoolYes; + } + else + { + m_indexes_computed = eLazyBoolNo; + } +} + +uint32_t +CompactUnwindInfo::GetLSDAForFunctionOffset (uint32_t lsda_offset, uint32_t lsda_count, uint32_t function_offset) +{ + // struct unwind_info_section_header_lsda_index_entry + // { + // uint32_t functionOffset; + // uint32_t lsdaOffset; + // }; + + offset_t first_entry = lsda_offset; + uint32_t low = 0; + uint32_t high = lsda_count; + while (low < high) + { + uint32_t mid = (low + high) / 2; + offset_t offset = first_entry + (mid * 8); + uint32_t mid_func_offset = m_unwindinfo_data.GetU32(&offset); // functionOffset + uint32_t mid_lsda_offset = m_unwindinfo_data.GetU32(&offset); // lsdaOffset + if (mid_func_offset == function_offset) + { + return mid_lsda_offset; + } + if (mid_func_offset < function_offset) + { + low = mid + 1; + } + else + { + high = mid; + } + } + return 0; +} + +lldb::offset_t +CompactUnwindInfo::BinarySearchRegularSecondPage (uint32_t entry_page_offset, uint32_t entry_count, uint32_t function_offset, uint32_t *entry_func_start_offset, uint32_t *entry_func_end_offset) +{ + // typedef uint32_t compact_unwind_encoding_t; + // struct unwind_info_regular_second_level_entry + // { + // uint32_t functionOffset; + // compact_unwind_encoding_t encoding; + + offset_t first_entry = entry_page_offset; + + uint32_t low = 0; + uint32_t high = entry_count; + uint32_t last = high - 1; + while (low < high) + { + uint32_t mid = (low + high) / 2; + offset_t offset = first_entry + (mid * 8); + uint32_t mid_func_offset = m_unwindinfo_data.GetU32(&offset); // functionOffset + uint32_t next_func_offset = 0; + if (mid < last) + { + offset = first_entry + ((mid + 1) * 8); + next_func_offset = m_unwindinfo_data.GetU32(&offset); // functionOffset + } + if (mid_func_offset <= function_offset) + { + if (mid == last || (next_func_offset > function_offset)) + { + if (entry_func_start_offset) + *entry_func_start_offset = mid_func_offset; + if (mid != last && entry_func_end_offset) + *entry_func_end_offset = next_func_offset; + return first_entry + (mid * 8); + } + else + { + low = mid + 1; + } + } + else + { + high = mid; + } + } + return LLDB_INVALID_OFFSET; +} + +uint32_t +CompactUnwindInfo::BinarySearchCompressedSecondPage (uint32_t entry_page_offset, uint32_t entry_count, uint32_t function_offset_to_find, uint32_t function_offset_base, uint32_t *entry_func_start_offset, uint32_t *entry_func_end_offset) +{ + offset_t first_entry = entry_page_offset; + + uint32_t low = 0; + uint32_t high = entry_count; + uint32_t last = high - 1; + while (low < high) + { + uint32_t mid = (low + high) / 2; + offset_t offset = first_entry + (mid * 4); + uint32_t entry = m_unwindinfo_data.GetU32(&offset); // entry + uint32_t mid_func_offset = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET (entry); + mid_func_offset += function_offset_base; + uint32_t next_func_offset = 0; + if (mid < last) + { + offset = first_entry + ((mid + 1) * 4); + uint32_t next_entry = m_unwindinfo_data.GetU32(&offset); // entry + next_func_offset = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET (next_entry); + next_func_offset += function_offset_base; + } + if (mid_func_offset <= function_offset_to_find) + { + if (mid == last || (next_func_offset > function_offset_to_find)) + { + if (entry_func_start_offset) + *entry_func_start_offset = mid_func_offset; + if (mid != last && entry_func_end_offset) + *entry_func_end_offset = next_func_offset; + return UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX (entry); + } + else + { + low = mid + 1; + } + } + else + { + high = mid; + } + } + + return UINT32_MAX; +} + +bool +CompactUnwindInfo::GetCompactUnwindInfoForFunction (Target &target, Address address, FunctionInfo &unwind_info) +{ + unwind_info.encoding = 0; + unwind_info.lsda_address.Clear(); + unwind_info.personality_ptr_address.Clear(); + + if (!IsValid (target.GetProcessSP())) + return false; + + addr_t text_section_file_address = LLDB_INVALID_ADDRESS; + SectionList *sl = m_objfile.GetSectionList (); + if (sl) + { + SectionSP text_sect = sl->FindSectionByType (eSectionTypeCode, true); + if (text_sect.get()) + { + text_section_file_address = text_sect->GetFileAddress(); + } + } + if (text_section_file_address == LLDB_INVALID_ADDRESS) + return false; + + addr_t function_offset = address.GetFileAddress() - m_objfile.GetHeaderAddress().GetFileAddress(); + + UnwindIndex key; + key.function_offset = function_offset; + + std::vector<UnwindIndex>::const_iterator it; + it = std::lower_bound (m_indexes.begin(), m_indexes.end(), key); + if (it == m_indexes.end()) + { + return false; + } + + if (it->function_offset != key.function_offset) + { + if (it != m_indexes.begin()) + --it; + } + + if (it->sentinal_entry == true) + { + return false; + } + + auto next_it = it + 1; + if (next_it != m_indexes.begin()) + { + // initialize the function offset end range to be the start of the + // next index offset. If we find an entry which is at the end of + // the index table, this will establish the range end. + unwind_info.valid_range_offset_end = next_it->function_offset; + } + + offset_t second_page_offset = it->second_level; + offset_t lsda_array_start = it->lsda_array_start; + offset_t lsda_array_count = (it->lsda_array_end - it->lsda_array_start) / 8; + + offset_t offset = second_page_offset; + uint32_t kind = m_unwindinfo_data.GetU32(&offset); // UNWIND_SECOND_LEVEL_REGULAR or UNWIND_SECOND_LEVEL_COMPRESSED + + if (kind == UNWIND_SECOND_LEVEL_REGULAR) + { + // struct unwind_info_regular_second_level_page_header + // { + // uint32_t kind; // UNWIND_SECOND_LEVEL_REGULAR + // uint16_t entryPageOffset; + // uint16_t entryCount; + + // typedef uint32_t compact_unwind_encoding_t; + // struct unwind_info_regular_second_level_entry + // { + // uint32_t functionOffset; + // compact_unwind_encoding_t encoding; + + uint16_t entry_page_offset = m_unwindinfo_data.GetU16(&offset); // entryPageOffset + uint16_t entry_count = m_unwindinfo_data.GetU16(&offset); // entryCount + + offset_t entry_offset = BinarySearchRegularSecondPage (second_page_offset + entry_page_offset, entry_count, function_offset, &unwind_info.valid_range_offset_start, &unwind_info.valid_range_offset_end); + if (entry_offset == LLDB_INVALID_OFFSET) + { + return false; + } + entry_offset += 4; // skip over functionOffset + unwind_info.encoding = m_unwindinfo_data.GetU32(&entry_offset); // encoding + if (unwind_info.encoding & UNWIND_HAS_LSDA) + { + SectionList *sl = m_objfile.GetSectionList (); + if (sl) + { + uint32_t lsda_offset = GetLSDAForFunctionOffset (lsda_array_start, lsda_array_count, function_offset); + addr_t objfile_header_file_address = m_objfile.GetHeaderAddress().GetFileAddress(); + unwind_info.lsda_address.ResolveAddressUsingFileSections (objfile_header_file_address + lsda_offset, sl); + } + } + if (unwind_info.encoding & UNWIND_PERSONALITY_MASK) + { + uint32_t personality_index = EXTRACT_BITS (unwind_info.encoding, UNWIND_PERSONALITY_MASK); + + if (personality_index > 0) + { + personality_index--; + if (personality_index < m_unwind_header.personality_array_count) + { + offset_t offset = m_unwind_header.personality_array_offset; + offset += 4 * personality_index; + SectionList *sl = m_objfile.GetSectionList (); + if (sl) + { + uint32_t personality_offset = m_unwindinfo_data.GetU32(&offset); + addr_t objfile_header_file_address = m_objfile.GetHeaderAddress().GetFileAddress(); + unwind_info.personality_ptr_address.ResolveAddressUsingFileSections (objfile_header_file_address + personality_offset, sl); + } + } + } + } + return true; + } + else if (kind == UNWIND_SECOND_LEVEL_COMPRESSED) + { + // struct unwind_info_compressed_second_level_page_header + // { + // uint32_t kind; // UNWIND_SECOND_LEVEL_COMPRESSED + // uint16_t entryPageOffset; // offset from this 2nd lvl page idx to array of entries + // // (an entry has a function offset and index into the encodings) + // // NB function offset from the entry in the compressed page + // // must be added to the index's functionOffset value. + // uint16_t entryCount; + // uint16_t encodingsPageOffset; // offset from this 2nd lvl page idx to array of encodings + // uint16_t encodingsCount; + + uint16_t entry_page_offset = m_unwindinfo_data.GetU16(&offset); // entryPageOffset + uint16_t entry_count = m_unwindinfo_data.GetU16(&offset); // entryCount + uint16_t encodings_page_offset = m_unwindinfo_data.GetU16(&offset); // encodingsPageOffset + uint16_t encodings_count = m_unwindinfo_data.GetU16(&offset); // encodingsCount + + uint32_t encoding_index = BinarySearchCompressedSecondPage (second_page_offset + entry_page_offset, entry_count, function_offset, it->function_offset, &unwind_info.valid_range_offset_start, &unwind_info.valid_range_offset_end); + if (encoding_index == UINT32_MAX || encoding_index >= encodings_count + m_unwind_header.common_encodings_array_count) + { + return false; + } + uint32_t encoding = 0; + if (encoding_index < m_unwind_header.common_encodings_array_count) + { + offset = m_unwind_header.common_encodings_array_offset + (encoding_index * sizeof (uint32_t)); + encoding = m_unwindinfo_data.GetU32(&offset); // encoding entry from the commonEncodingsArray + } + else + { + uint32_t page_specific_entry_index = encoding_index - m_unwind_header.common_encodings_array_count; + offset = second_page_offset + encodings_page_offset + (page_specific_entry_index * sizeof (uint32_t)); + encoding = m_unwindinfo_data.GetU32(&offset); // encoding entry from the page-specific encoding array + } + if (encoding == 0) + return false; + + unwind_info.encoding = encoding; + if (unwind_info.encoding & UNWIND_HAS_LSDA) + { + SectionList *sl = m_objfile.GetSectionList (); + if (sl) + { + uint32_t lsda_offset = GetLSDAForFunctionOffset (lsda_array_start, lsda_array_count, function_offset); + addr_t objfile_header_file_address = m_objfile.GetHeaderAddress().GetFileAddress(); + unwind_info.lsda_address.ResolveAddressUsingFileSections (objfile_header_file_address + lsda_offset, sl); + } + } + if (unwind_info.encoding & UNWIND_PERSONALITY_MASK) + { + uint32_t personality_index = EXTRACT_BITS (unwind_info.encoding, UNWIND_PERSONALITY_MASK); + + if (personality_index > 0) + { + personality_index--; + if (personality_index < m_unwind_header.personality_array_count) + { + offset_t offset = m_unwind_header.personality_array_offset; + offset += 4 * personality_index; + SectionList *sl = m_objfile.GetSectionList (); + if (sl) + { + uint32_t personality_offset = m_unwindinfo_data.GetU32(&offset); + addr_t objfile_header_file_address = m_objfile.GetHeaderAddress().GetFileAddress(); + unwind_info.personality_ptr_address.ResolveAddressUsingFileSections (objfile_header_file_address + personality_offset, sl); + } + } + } + } + return true; + } + return false; +} + +enum x86_64_eh_regnum { + rax = 0, + rdx = 1, + rcx = 2, + rbx = 3, + rsi = 4, + rdi = 5, + rbp = 6, + rsp = 7, + r8 = 8, + r9 = 9, + r10 = 10, + r11 = 11, + r12 = 12, + r13 = 13, + r14 = 14, + r15 = 15, + rip = 16 // this is officially the Return Address register number, but close enough +}; + +// Convert the compact_unwind_info.h register numbering scheme +// to eRegisterKindGCC (eh_frame) register numbering scheme. +uint32_t +translate_to_eh_frame_regnum_x86_64 (uint32_t unwind_regno) +{ + switch (unwind_regno) + { + case UNWIND_X86_64_REG_RBX: + return x86_64_eh_regnum::rbx; + case UNWIND_X86_64_REG_R12: + return x86_64_eh_regnum::r12; + case UNWIND_X86_64_REG_R13: + return x86_64_eh_regnum::r13; + case UNWIND_X86_64_REG_R14: + return x86_64_eh_regnum::r14; + case UNWIND_X86_64_REG_R15: + return x86_64_eh_regnum::r15; + case UNWIND_X86_64_REG_RBP: + return x86_64_eh_regnum::rbp; + default: + return LLDB_INVALID_REGNUM; + } +} + +bool +CompactUnwindInfo::CreateUnwindPlan_x86_64 (Target &target, FunctionInfo &function_info, UnwindPlan &unwind_plan, Address pc_or_function_start) +{ + unwind_plan.SetSourceName ("compact unwind info"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolYes); + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); + unwind_plan.SetRegisterKind (eRegisterKindGCC); + + unwind_plan.SetLSDAAddress (function_info.lsda_address); + unwind_plan.SetPersonalityFunctionPtr (function_info.personality_ptr_address); + + UnwindPlan::RowSP row (new UnwindPlan::Row); + + const int wordsize = 8; + int mode = function_info.encoding & UNWIND_X86_64_MODE_MASK; + switch (mode) + { + case UNWIND_X86_64_MODE_RBP_FRAME: + { + row->SetCFARegister (translate_to_eh_frame_regnum_x86_64 (UNWIND_X86_64_REG_RBP)); + row->SetCFAOffset (2 * wordsize); + row->SetOffset (0); + row->SetRegisterLocationToAtCFAPlusOffset (x86_64_eh_regnum::rbp, wordsize * -2, true); + row->SetRegisterLocationToAtCFAPlusOffset (x86_64_eh_regnum::rip, wordsize * -1, true); + row->SetRegisterLocationToIsCFAPlusOffset (x86_64_eh_regnum::rsp, 0, true); + + uint32_t saved_registers_offset = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_RBP_FRAME_OFFSET); + + uint32_t saved_registers_locations = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); + + saved_registers_offset += 2; + + for (int i = 0; i < 5; i++) + { + uint32_t regnum = saved_registers_locations & 0x7; + switch (regnum) + { + case UNWIND_X86_64_REG_NONE: + break; + case UNWIND_X86_64_REG_RBX: + case UNWIND_X86_64_REG_R12: + case UNWIND_X86_64_REG_R13: + case UNWIND_X86_64_REG_R14: + case UNWIND_X86_64_REG_R15: + row->SetRegisterLocationToAtCFAPlusOffset (translate_to_eh_frame_regnum_x86_64 (regnum), wordsize * -saved_registers_offset, true); + break; + } + saved_registers_offset--; + saved_registers_locations >>= 3; + } + unwind_plan.AppendRow (row); + return true; + } + break; + + case UNWIND_X86_64_MODE_STACK_IND: + { + // The clang in Xcode 6 is emitting incorrect compact unwind encodings for this + // style of unwind. It was fixed in llvm r217020. + return false; + } + break; + + case UNWIND_X86_64_MODE_STACK_IMMD: + { + uint32_t stack_size = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); + uint32_t register_count = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); + + if (mode == UNWIND_X86_64_MODE_STACK_IND && function_info.valid_range_offset_start != 0) + { + uint32_t stack_adjust = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); + + // offset into the function instructions; 0 == beginning of first instruction + uint32_t offset_to_subl_insn = EXTRACT_BITS (function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); + + SectionList *sl = m_objfile.GetSectionList (); + if (sl) + { + ProcessSP process_sp = target.GetProcessSP(); + if (process_sp) + { + Address subl_payload_addr (function_info.valid_range_offset_start, sl); + subl_payload_addr.Slide (offset_to_subl_insn); + Error error; + uint64_t large_stack_size = process_sp->ReadUnsignedIntegerFromMemory (subl_payload_addr.GetLoadAddress (&target), + 4, 0, error); + if (large_stack_size != 0 && error.Success ()) + { + // Got the large stack frame size correctly - use it + stack_size = large_stack_size + (stack_adjust * wordsize); + } + else + { + return false; + } + } + else + { + return false; + } + } + else + { + return false; + } + } + + row->SetCFARegister (x86_64_eh_regnum::rsp); + row->SetCFAOffset (stack_size * wordsize); + row->SetOffset (0); + row->SetRegisterLocationToAtCFAPlusOffset (x86_64_eh_regnum::rip, wordsize * -1, true); + row->SetRegisterLocationToIsCFAPlusOffset (x86_64_eh_regnum::rsp, 0, true); + + if (register_count > 0) + { + + // We need to include (up to) 6 registers in 10 bits. + // That would be 18 bits if we just used 3 bits per reg to indicate + // the order they're saved on the stack. + // + // This is done with Lehmer code permutation, e.g. see + // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms + int permunreg[6]; + + // This decodes the variable-base number in the 10 bits + // and gives us the Lehmer code sequence which can then + // be decoded. + + switch (register_count) + { + case 6: + permunreg[0] = permutation/120; // 120 == 5! + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; // 24 == 4! + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; // 6 == 3! + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; // 2 == 2! + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; // 1 == 1! + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation/60; + permutation -= (permunreg[0]*60); + permunreg[1] = permutation/12; + permutation -= (permunreg[1]*12); + permunreg[2] = permutation/3; + permutation -= (permunreg[2]*3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation/20; + permutation -= (permunreg[0]*20); + permunreg[1] = permutation/4; + permutation -= (permunreg[1]*4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation/5; + permutation -= (permunreg[0]*5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + + // Decode the Lehmer code for this permutation of + // the registers v. http://en.wikipedia.org/wiki/Lehmer_code + + int registers[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (uint32_t i = 0; i < register_count; i++) + { + int renum = 0; + for (int j = 1; j < 7; j++) + { + if (used[j] == false) + { + if (renum == permunreg[i]) + { + registers[i] = j; + used[j] = true; + break; + } + renum++; + } + } + } + + uint32_t saved_registers_offset = 1; + saved_registers_offset++; + + for (int i = (sizeof (registers) / sizeof (int)) - 1; i >= 0; i--) + { + switch (registers[i]) + { + case UNWIND_X86_64_REG_NONE: + break; + case UNWIND_X86_64_REG_RBX: + case UNWIND_X86_64_REG_R12: + case UNWIND_X86_64_REG_R13: + case UNWIND_X86_64_REG_R14: + case UNWIND_X86_64_REG_R15: + case UNWIND_X86_64_REG_RBP: + row->SetRegisterLocationToAtCFAPlusOffset (translate_to_eh_frame_regnum_x86_64 (registers[i]), wordsize * -saved_registers_offset, true); + break; + } + saved_registers_offset++; + } + } + unwind_plan.AppendRow (row); + return true; + } + break; + + case UNWIND_X86_64_MODE_DWARF: + { + return false; + } + break; + + case 0: + { + return false; + } + break; + } + return false; +} + +enum i386_eh_regnum { + eax = 0, + ecx = 1, + edx = 2, + ebx = 3, + ebp = 4, + esp = 5, + esi = 6, + edi = 7, + eip = 8 // this is officially the Return Address register number, but close enough +}; + +// Convert the compact_unwind_info.h register numbering scheme +// to eRegisterKindGCC (eh_frame) register numbering scheme. +uint32_t +translate_to_eh_frame_regnum_i386 (uint32_t unwind_regno) +{ + switch (unwind_regno) + { + case UNWIND_X86_REG_EBX: + return i386_eh_regnum::ebx; + case UNWIND_X86_REG_ECX: + return i386_eh_regnum::ecx; + case UNWIND_X86_REG_EDX: + return i386_eh_regnum::edx; + case UNWIND_X86_REG_EDI: + return i386_eh_regnum::edi; + case UNWIND_X86_REG_ESI: + return i386_eh_regnum::esi; + case UNWIND_X86_REG_EBP: + return i386_eh_regnum::ebp; + default: + return LLDB_INVALID_REGNUM; + } +} + + +bool +CompactUnwindInfo::CreateUnwindPlan_i386 (Target &target, FunctionInfo &function_info, UnwindPlan &unwind_plan, Address pc_or_function_start) +{ + unwind_plan.SetSourceName ("compact unwind info"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolYes); + unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo); + unwind_plan.SetRegisterKind (eRegisterKindGCC); + + unwind_plan.SetLSDAAddress (function_info.lsda_address); + unwind_plan.SetPersonalityFunctionPtr (function_info.personality_ptr_address); + + UnwindPlan::RowSP row (new UnwindPlan::Row); + + const int wordsize = 4; + int mode = function_info.encoding & UNWIND_X86_MODE_MASK; + switch (mode) + { + case UNWIND_X86_MODE_EBP_FRAME: + { + row->SetCFARegister (translate_to_eh_frame_regnum_i386 (UNWIND_X86_REG_EBP)); + row->SetCFAOffset (2 * wordsize); + row->SetOffset (0); + row->SetRegisterLocationToAtCFAPlusOffset (i386_eh_regnum::ebp, wordsize * -2, true); + row->SetRegisterLocationToAtCFAPlusOffset (i386_eh_regnum::eip, wordsize * -1, true); + row->SetRegisterLocationToIsCFAPlusOffset (i386_eh_regnum::esp, 0, true); + + uint32_t saved_registers_offset = EXTRACT_BITS (function_info.encoding, UNWIND_X86_EBP_FRAME_OFFSET); + + uint32_t saved_registers_locations = EXTRACT_BITS (function_info.encoding, UNWIND_X86_EBP_FRAME_REGISTERS); + + saved_registers_offset += 2; + + for (int i = 0; i < 5; i++) + { + uint32_t regnum = saved_registers_locations & 0x7; + switch (regnum) + { + case UNWIND_X86_REG_NONE: + break; + case UNWIND_X86_REG_EBX: + case UNWIND_X86_REG_ECX: + case UNWIND_X86_REG_EDX: + case UNWIND_X86_REG_EDI: + case UNWIND_X86_REG_ESI: + row->SetRegisterLocationToAtCFAPlusOffset (translate_to_eh_frame_regnum_i386 (regnum), wordsize * -saved_registers_offset, true); + break; + } + saved_registers_offset--; + saved_registers_locations >>= 3; + } + unwind_plan.AppendRow (row); + return true; + } + break; + + case UNWIND_X86_MODE_STACK_IND: + case UNWIND_X86_MODE_STACK_IMMD: + { + uint32_t stack_size = EXTRACT_BITS (function_info.encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); + uint32_t register_count = EXTRACT_BITS (function_info.encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = EXTRACT_BITS (function_info.encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); + + if (mode == UNWIND_X86_MODE_STACK_IND && function_info.valid_range_offset_start != 0) + { + uint32_t stack_adjust = EXTRACT_BITS (function_info.encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST); + + // offset into the function instructions; 0 == beginning of first instruction + uint32_t offset_to_subl_insn = EXTRACT_BITS (function_info.encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); + + SectionList *sl = m_objfile.GetSectionList (); + if (sl) + { + ProcessSP process_sp = target.GetProcessSP(); + if (process_sp) + { + Address subl_payload_addr (function_info.valid_range_offset_start, sl); + subl_payload_addr.Slide (offset_to_subl_insn); + Error error; + uint64_t large_stack_size = process_sp->ReadUnsignedIntegerFromMemory (subl_payload_addr.GetLoadAddress (&target), + 4, 0, error); + if (large_stack_size != 0 && error.Success ()) + { + // Got the large stack frame size correctly - use it + stack_size = large_stack_size + (stack_adjust * wordsize); + } + else + { + return false; + } + } + else + { + return false; + } + } + else + { + return false; + } + } + + row->SetCFARegister (i386_eh_regnum::esp); + row->SetCFAOffset (stack_size * wordsize); + row->SetOffset (0); + row->SetRegisterLocationToAtCFAPlusOffset (i386_eh_regnum::eip, wordsize * -1, true); + row->SetRegisterLocationToIsCFAPlusOffset (i386_eh_regnum::esp, 0, true); + + if (register_count > 0) + { + + // We need to include (up to) 6 registers in 10 bits. + // That would be 18 bits if we just used 3 bits per reg to indicate + // the order they're saved on the stack. + // + // This is done with Lehmer code permutation, e.g. see + // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms + int permunreg[6]; + + // This decodes the variable-base number in the 10 bits + // and gives us the Lehmer code sequence which can then + // be decoded. + + switch (register_count) + { + case 6: + permunreg[0] = permutation/120; // 120 == 5! + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; // 24 == 4! + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; // 6 == 3! + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; // 2 == 2! + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; // 1 == 1! + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation/120; + permutation -= (permunreg[0]*120); + permunreg[1] = permutation/24; + permutation -= (permunreg[1]*24); + permunreg[2] = permutation/6; + permutation -= (permunreg[2]*6); + permunreg[3] = permutation/2; + permutation -= (permunreg[3]*2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation/60; + permutation -= (permunreg[0]*60); + permunreg[1] = permutation/12; + permutation -= (permunreg[1]*12); + permunreg[2] = permutation/3; + permutation -= (permunreg[2]*3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation/20; + permutation -= (permunreg[0]*20); + permunreg[1] = permutation/4; + permutation -= (permunreg[1]*4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation/5; + permutation -= (permunreg[0]*5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + + // Decode the Lehmer code for this permutation of + // the registers v. http://en.wikipedia.org/wiki/Lehmer_code + + int registers[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (uint32_t i = 0; i < register_count; i++) + { + int renum = 0; + for (int j = 1; j < 7; j++) + { + if (used[j] == false) + { + if (renum == permunreg[i]) + { + registers[i] = j; + used[j] = true; + break; + } + renum++; + } + } + } + + uint32_t saved_registers_offset = 1; + saved_registers_offset++; + + for (int i = (sizeof (registers) / sizeof (int)) - 1; i >= 0; i--) + { + switch (registers[i]) + { + case UNWIND_X86_REG_NONE: + break; + case UNWIND_X86_REG_EBX: + case UNWIND_X86_REG_ECX: + case UNWIND_X86_REG_EDX: + case UNWIND_X86_REG_EDI: + case UNWIND_X86_REG_ESI: + case UNWIND_X86_REG_EBP: + row->SetRegisterLocationToAtCFAPlusOffset (translate_to_eh_frame_regnum_i386 (registers[i]), wordsize * -saved_registers_offset, true); + break; + } + saved_registers_offset++; + } + } + + unwind_plan.AppendRow (row); + return true; + } + break; + + case UNWIND_X86_MODE_DWARF: + { + return false; + } + break; + } + return false; +} diff --git a/source/Symbol/CompileUnit.cpp b/source/Symbol/CompileUnit.cpp index f99ca53d9629..6483258ee678 100644 --- a/source/Symbol/CompileUnit.cpp +++ b/source/Symbol/CompileUnit.cpp @@ -325,18 +325,19 @@ CompileUnit::ResolveSymbolContext // when finding file indexes std::vector<uint32_t> file_indexes; const bool full_match = (bool)file_spec.GetDirectory(); - bool file_spec_matches_cu_file_spec = FileSpec::Equal(file_spec, *this, full_match); + const bool remove_backup_dots = true; + bool file_spec_matches_cu_file_spec = FileSpec::Equal(file_spec, *this, full_match, remove_backup_dots); // If we are not looking for inlined functions and our file spec doesn't // match then we are done... if (file_spec_matches_cu_file_spec == false && check_inlines == false) return 0; - uint32_t file_idx = GetSupportFiles().FindFileIndex (1, file_spec, true); + uint32_t file_idx = GetSupportFiles().FindFileIndex (1, file_spec, true, remove_backup_dots); while (file_idx != UINT32_MAX) { file_indexes.push_back (file_idx); - file_idx = GetSupportFiles().FindFileIndex (file_idx + 1, file_spec, true); + file_idx = GetSupportFiles().FindFileIndex (file_idx + 1, file_spec, true, remove_backup_dots); } const size_t num_file_indexes = file_indexes.size(); diff --git a/source/Symbol/DWARFCallFrameInfo.cpp b/source/Symbol/DWARFCallFrameInfo.cpp index a9da631eb452..78d262307c24 100644 --- a/source/Symbol/DWARFCallFrameInfo.cpp +++ b/source/Symbol/DWARFCallFrameInfo.cpp @@ -218,20 +218,27 @@ DWARFCallFrameInfo::ParseCIE (const dw_offset_t cie_offset) // FDE, which is the address of a language-specific // data area (LSDA). The size of the LSDA pointer is // specified by the pointer encoding used. - m_cfi_data.GetU8(&offset); + cie_sp->lsda_addr_encoding = m_cfi_data.GetU8(&offset); break; case 'P': // Indicates the presence of two arguments in the - // Augmentation Data of the cie_sp-> The first argument + // Augmentation Data of the CIE. The first argument // is 1-byte and represents the pointer encoding // used for the second argument, which is the // address of a personality routine handler. The // size of the personality routine pointer is // specified by the pointer encoding used. + // + // The address of the personality function will + // be stored at this location. Pre-execution, it + // will be all zero's so don't read it until we're + // trying to do an unwind & the reloc has been + // resolved. { uint8_t arg_ptr_encoding = m_cfi_data.GetU8(&offset); - m_cfi_data.GetGNUEHPointer(&offset, arg_ptr_encoding, LLDB_INVALID_ADDRESS, LLDB_INVALID_ADDRESS, LLDB_INVALID_ADDRESS); + const lldb::addr_t pc_rel_addr = m_section_sp->GetFileAddress(); + cie_sp->personality_loc = m_cfi_data.GetGNUEHPointer(&offset, arg_ptr_encoding, pc_rel_addr, LLDB_INVALID_ADDRESS, LLDB_INVALID_ADDRESS); } break; @@ -449,11 +456,39 @@ DWARFCallFrameInfo::FDEToUnwindPlan (dw_offset_t dwarf_offset, Address startaddr AddressRange range (range_base, m_objfile.GetAddressByteSize(), m_objfile.GetSectionList()); range.SetByteSize (range_len); + addr_t lsda_data_file_address = LLDB_INVALID_ADDRESS; + if (cie->augmentation[0] == 'z') { uint32_t aug_data_len = (uint32_t)m_cfi_data.GetULEB128(&offset); + if (aug_data_len != 0 && cie->lsda_addr_encoding != DW_EH_PE_omit) + { + offset_t saved_offset = offset; + lsda_data_file_address = m_cfi_data.GetGNUEHPointer(&offset, cie->lsda_addr_encoding, pc_rel_addr, text_addr, data_addr); + if (offset - saved_offset != aug_data_len) + { + // There is more in the augmentation region than we know how to process; + // don't read anything. + lsda_data_file_address = LLDB_INVALID_ADDRESS; + } + offset = saved_offset; + } offset += aug_data_len; } + Address lsda_data; + Address personality_function_ptr; + + if (lsda_data_file_address != LLDB_INVALID_ADDRESS && cie->personality_loc != LLDB_INVALID_ADDRESS) + { + m_objfile.GetModule()->ResolveFileAddress (lsda_data_file_address, lsda_data); + m_objfile.GetModule()->ResolveFileAddress (cie->personality_loc, personality_function_ptr); + } + + if (lsda_data.IsValid() && personality_function_ptr.IsValid()) + { + unwind_plan.SetLSDAAddress (lsda_data); + unwind_plan.SetPersonalityFunctionPtr (personality_function_ptr); + } uint32_t reg_num = 0; int32_t op_offset = 0; diff --git a/source/Symbol/Declaration.cpp b/source/Symbol/Declaration.cpp index 3943f02c5474..c72ca045b6c7 100644 --- a/source/Symbol/Declaration.cpp +++ b/source/Symbol/Declaration.cpp @@ -110,7 +110,7 @@ lldb_private::operator == (const Declaration &lhs, const Declaration &rhs) return lhs.GetFile() == rhs.GetFile(); #else if (lhs.GetLine () == rhs.GetLine ()) - return lhs.GetFile() == rhs.GetFile(); + return FileSpec::Equal(lhs.GetFile(),rhs.GetFile(), true, true); #endif return false; } diff --git a/source/Symbol/FuncUnwinders.cpp b/source/Symbol/FuncUnwinders.cpp index 95fc81747859..1eb73ee3649b 100644 --- a/source/Symbol/FuncUnwinders.cpp +++ b/source/Symbol/FuncUnwinders.cpp @@ -11,6 +11,7 @@ #include "lldb/Core/Address.h" #include "lldb/Symbol/FuncUnwinders.h" #include "lldb/Symbol/DWARFCallFrameInfo.h" +#include "lldb/Symbol/CompactUnwindInfo.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/UnwindPlan.h" #include "lldb/Symbol/UnwindTable.h" @@ -24,145 +25,212 @@ using namespace lldb; using namespace lldb_private; +//------------------------------------------------ +/// constructor +//------------------------------------------------ -FuncUnwinders::FuncUnwinders -( - UnwindTable& unwind_table, - AddressRange range -) : - m_unwind_table(unwind_table), - m_range(range), +FuncUnwinders::FuncUnwinders (UnwindTable& unwind_table, AddressRange range) : + m_unwind_table (unwind_table), + m_range (range), m_mutex (Mutex::eMutexTypeRecursive), - m_unwind_plan_call_site_sp (), - m_unwind_plan_non_call_site_sp (), + m_unwind_plan_assembly_sp (), + m_unwind_plan_eh_frame_sp (), + m_unwind_plan_eh_frame_augmented_sp (), + m_unwind_plan_compact_unwind (), m_unwind_plan_fast_sp (), m_unwind_plan_arch_default_sp (), - m_tried_unwind_at_call_site (false), - m_tried_unwind_at_non_call_site (false), + m_unwind_plan_arch_default_at_func_entry_sp (), + m_tried_unwind_plan_assembly (false), + m_tried_unwind_plan_eh_frame (false), + m_tried_unwind_plan_eh_frame_augmented (false), + m_tried_unwind_plan_compact_unwind (false), m_tried_unwind_fast (false), m_tried_unwind_arch_default (false), m_tried_unwind_arch_default_at_func_entry (false), - m_first_non_prologue_insn() + m_first_non_prologue_insn () { } -FuncUnwinders::~FuncUnwinders () +//------------------------------------------------ +/// destructor +//------------------------------------------------ + +FuncUnwinders::~FuncUnwinders () { } UnwindPlanSP -FuncUnwinders::GetUnwindPlanAtCallSite (int current_offset) +FuncUnwinders::GetUnwindPlanAtCallSite (Target &target, 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 a call site unwind - // plan, that they get one and don't run into a race condition where one - // thread has started to create the unwind plan and has put it into - // m_unwind_plan_call_site_sp, and have another thread enter this function - // and return the partially filled in m_unwind_plan_call_site_sp pointer. - // We also want to make sure that we lock out other unwind plans from - // being accessed until this one is done creating itself in case someone - // had some code like: - // UnwindPlan *best_unwind_plan = ...GetUnwindPlanAtCallSite (...) - // if (best_unwind_plan == NULL) - // best_unwind_plan = GetUnwindPlanAtNonCallSite (...) Mutex::Locker locker (m_mutex); - if (m_tried_unwind_at_call_site == false && m_unwind_plan_call_site_sp.get() == nullptr) + + UnwindPlanSP unwind_plan_sp = GetEHFrameUnwindPlan (target, current_offset); + if (unwind_plan_sp.get() == nullptr) { - m_tried_unwind_at_call_site = true; - // We have cases (e.g. with _sigtramp on Mac OS X) where the hand-written eh_frame unwind info for a - // function does not cover the entire range of the function and so the FDE only lists a subset of the - // address range. If we try to look up the unwind info by the starting address of the function - // (i.e. m_range.GetBaseAddress()) we may not find the eh_frame FDE. We need to use the actual byte offset - // into the function when looking it up. - - if (m_range.GetBaseAddress().IsValid()) - { - Address current_pc (m_range.GetBaseAddress ()); - if (current_offset != -1) - current_pc.SetOffset (current_pc.GetOffset() + current_offset); + unwind_plan_sp = GetCompactUnwindUnwindPlan (target, current_offset); + } + + return unwind_plan_sp; +} - DWARFCallFrameInfo *eh_frame = m_unwind_table.GetEHFrameInfo(); - if (eh_frame) +UnwindPlanSP +FuncUnwinders::GetCompactUnwindUnwindPlan (Target &target, int current_offset) +{ + if (m_unwind_plan_compact_unwind.size() > 0) + return m_unwind_plan_compact_unwind[0]; // FIXME support multiple compact unwind plans for one func + if (m_tried_unwind_plan_compact_unwind) + return UnwindPlanSP(); + + Mutex::Locker lock (m_mutex); + m_tried_unwind_plan_compact_unwind = true; + if (m_range.GetBaseAddress().IsValid()) + { + Address current_pc (m_range.GetBaseAddress ()); + if (current_offset != -1) + current_pc.SetOffset (current_pc.GetOffset() + current_offset); + CompactUnwindInfo *compact_unwind = m_unwind_table.GetCompactUnwindInfo(); + if (compact_unwind) + { + UnwindPlanSP unwind_plan_sp (new UnwindPlan (lldb::eRegisterKindGeneric)); + if (compact_unwind->GetUnwindPlan (target, current_pc, *unwind_plan_sp)) { - m_unwind_plan_call_site_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); - if (!eh_frame->GetUnwindPlan (current_pc, *m_unwind_plan_call_site_sp)) - m_unwind_plan_call_site_sp.reset(); + m_unwind_plan_compact_unwind.push_back (unwind_plan_sp); + return m_unwind_plan_compact_unwind[0]; // FIXME support multiple compact unwind plans for one func } } } - return m_unwind_plan_call_site_sp; + return UnwindPlanSP(); } UnwindPlanSP -FuncUnwinders::GetUnwindPlanAtNonCallSite (Target& target, Thread& thread, int current_offset) +FuncUnwinders::GetEHFrameUnwindPlan (Target &target, 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 - // plan, that they get one and don't run into a race condition where one - // thread has started to create the unwind plan and has put it into - // the unique pointer member variable, and have another thread enter this function - // and return the partially filled pointer contained in the unique pointer. - // We also want to make sure that we lock out other unwind plans from - // being accessed until this one is done creating itself in case someone - // had some code like: - // UnwindPlan *best_unwind_plan = ...GetUnwindPlanAtCallSite (...) - // if (best_unwind_plan == NULL) - // best_unwind_plan = GetUnwindPlanAtNonCallSite (...) - Mutex::Locker locker (m_mutex); - if (m_tried_unwind_at_non_call_site == false && m_unwind_plan_non_call_site_sp.get() == nullptr) + if (m_unwind_plan_eh_frame_sp.get() || m_tried_unwind_plan_eh_frame) + return m_unwind_plan_eh_frame_sp; + + Mutex::Locker lock (m_mutex); + m_tried_unwind_plan_eh_frame = true; + if (m_range.GetBaseAddress().IsValid()) { - UnwindAssemblySP assembly_profiler_sp (GetUnwindAssemblyProfiler()); - if (assembly_profiler_sp) + Address current_pc (m_range.GetBaseAddress ()); + if (current_offset != -1) + current_pc.SetOffset (current_pc.GetOffset() + current_offset); + DWARFCallFrameInfo *eh_frame = m_unwind_table.GetEHFrameInfo(); + if (eh_frame) { - if (target.GetArchitecture().GetCore() == ArchSpec::eCore_x86_32_i386 - || target.GetArchitecture().GetCore() == ArchSpec::eCore_x86_64_x86_64 - || target.GetArchitecture().GetCore() == ArchSpec::eCore_x86_64_x86_64h) + m_unwind_plan_eh_frame_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + if (!eh_frame->GetUnwindPlan (current_pc, *m_unwind_plan_eh_frame_sp)) + m_unwind_plan_eh_frame_sp.reset(); + } + } + return m_unwind_plan_eh_frame_sp; +} + +UnwindPlanSP +FuncUnwinders::GetEHFrameAugmentedUnwindPlan (Target &target, Thread &thread, int current_offset) +{ + if (m_unwind_plan_eh_frame_augmented_sp.get() || m_tried_unwind_plan_eh_frame_augmented) + return m_unwind_plan_eh_frame_augmented_sp; + + // Only supported on x86 architectures where we get eh_frame from the compiler that describes + // the prologue instructions perfectly, and sometimes the epilogue instructions too. + if (target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_32_i386 + && target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64 + && target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64h) + { + m_tried_unwind_plan_eh_frame_augmented = true; + return m_unwind_plan_eh_frame_augmented_sp; + } + + Mutex::Locker lock (m_mutex); + m_tried_unwind_plan_eh_frame_augmented = true; + + if (m_range.GetBaseAddress().IsValid()) + { + Address current_pc (m_range.GetBaseAddress ()); + if (current_offset != -1) + current_pc.SetOffset (current_pc.GetOffset() + current_offset); + DWARFCallFrameInfo *eh_frame = m_unwind_table.GetEHFrameInfo(); + if (eh_frame) + { + m_unwind_plan_eh_frame_augmented_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + if (!eh_frame->GetUnwindPlan (current_pc, *m_unwind_plan_eh_frame_augmented_sp)) { - // 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) { - UnwindPlan* plan = new UnwindPlan (*m_unwind_plan_call_site_sp); - if (assembly_profiler_sp->AugmentUnwindPlanFromCallSite (m_range, thread, *plan)) { - m_unwind_plan_non_call_site_sp.reset (plan); - return m_unwind_plan_non_call_site_sp; + m_unwind_plan_eh_frame_augmented_sp.reset(); + } + else + { + // Augment the eh_frame instructions with epilogue descriptions if necessary so the + // UnwindPlan can be used at any instruction in the function. + + UnwindAssemblySP assembly_profiler_sp (GetUnwindAssemblyProfiler()); + if (assembly_profiler_sp) + { + if (!assembly_profiler_sp->AugmentUnwindPlanFromCallSite (m_range, thread, *m_unwind_plan_eh_frame_augmented_sp)) + { + m_unwind_plan_eh_frame_augmented_sp.reset(); } } + else + { + m_unwind_plan_eh_frame_augmented_sp.reset(); + } } + } + } + return m_unwind_plan_eh_frame_augmented_sp; +} + + +UnwindPlanSP +FuncUnwinders::GetAssemblyUnwindPlan (Target &target, Thread &thread, int current_offset) +{ + if (m_unwind_plan_assembly_sp.get() || m_tried_unwind_plan_assembly) + return m_unwind_plan_assembly_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(); + Mutex::Locker lock (m_mutex); + m_tried_unwind_plan_assembly = true; + + UnwindAssemblySP assembly_profiler_sp (GetUnwindAssemblyProfiler()); + if (assembly_profiler_sp) + { + m_unwind_plan_assembly_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + if (!assembly_profiler_sp->GetNonCallSiteUnwindPlanFromAssembly (m_range, thread, *m_unwind_plan_assembly_sp)) + { + m_unwind_plan_assembly_sp.reset(); } } - return m_unwind_plan_non_call_site_sp; + return m_unwind_plan_assembly_sp; +} + + +UnwindPlanSP +FuncUnwinders::GetUnwindPlanAtNonCallSite (Target& target, Thread& thread, int current_offset) +{ + UnwindPlanSP non_call_site_unwindplan_sp = GetEHFrameAugmentedUnwindPlan (target, thread, current_offset); + if (non_call_site_unwindplan_sp.get() == nullptr) + { + non_call_site_unwindplan_sp = GetAssemblyUnwindPlan (target, thread, current_offset); + } + return non_call_site_unwindplan_sp; } UnwindPlanSP FuncUnwinders::GetUnwindPlanFastUnwind (Thread& thread) { - // Lock the mutex to ensure we can always give out the most appropriate - // information. We want to make sure if someone requests an unwind - // plan, that they get one and don't run into a race condition where one - // thread has started to create the unwind plan and has put it into - // the unique pointer member variable, and have another thread enter this function - // and return the partially filled pointer contained in the unique pointer. - // We also want to make sure that we lock out other unwind plans from - // being accessed until this one is done creating itself in case someone - // had some code like: - // UnwindPlan *best_unwind_plan = ...GetUnwindPlanAtCallSite (...) - // if (best_unwind_plan == NULL) - // best_unwind_plan = GetUnwindPlanAtNonCallSite (...) + if (m_unwind_plan_fast_sp.get() || m_tried_unwind_fast) + return m_unwind_plan_fast_sp; + Mutex::Locker locker (m_mutex); - if (m_tried_unwind_fast == false && m_unwind_plan_fast_sp.get() == nullptr) + m_tried_unwind_fast = true; + + UnwindAssemblySP assembly_profiler_sp (GetUnwindAssemblyProfiler()); + if (assembly_profiler_sp) { - m_tried_unwind_fast = true; - UnwindAssemblySP assembly_profiler_sp (GetUnwindAssemblyProfiler()); - if (assembly_profiler_sp) + m_unwind_plan_fast_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + if (!assembly_profiler_sp->GetFastUnwindPlan (m_range, thread, *m_unwind_plan_fast_sp)) { - m_unwind_plan_fast_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); - if (!assembly_profiler_sp->GetFastUnwindPlan (m_range, thread, *m_unwind_plan_fast_sp)) - m_unwind_plan_fast_sp.reset(); + m_unwind_plan_fast_sp.reset(); } } return m_unwind_plan_fast_sp; @@ -171,32 +239,23 @@ FuncUnwinders::GetUnwindPlanFastUnwind (Thread& thread) UnwindPlanSP FuncUnwinders::GetUnwindPlanArchitectureDefault (Thread& thread) { - // Lock the mutex to ensure we can always give out the most appropriate - // information. We want to make sure if someone requests an unwind - // plan, that they get one and don't run into a race condition where one - // thread has started to create the unwind plan and has put it into - // the unique pointer member variable, and have another thread enter this function - // and return the partially filled pointer contained in the unique pointer. - // We also want to make sure that we lock out other unwind plans from - // being accessed until this one is done creating itself in case someone - // had some code like: - // UnwindPlan *best_unwind_plan = ...GetUnwindPlanAtCallSite (...) - // if (best_unwind_plan == NULL) - // best_unwind_plan = GetUnwindPlanAtNonCallSite (...) + if (m_unwind_plan_arch_default_sp.get() || m_tried_unwind_arch_default) + return m_unwind_plan_arch_default_sp; + Mutex::Locker locker (m_mutex); - if (m_tried_unwind_arch_default == false && m_unwind_plan_arch_default_sp.get() == nullptr) + m_tried_unwind_arch_default = true; + + Address current_pc; + ProcessSP process_sp (thread.CalculateProcess()); + if (process_sp) { - m_tried_unwind_arch_default = true; - Address current_pc; - ProcessSP process_sp (thread.CalculateProcess()); - if (process_sp) + ABI *abi = process_sp->GetABI().get(); + if (abi) { - ABI *abi = process_sp->GetABI().get(); - if (abi) + m_unwind_plan_arch_default_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + if (!abi->CreateDefaultUnwindPlan(*m_unwind_plan_arch_default_sp)) { - m_unwind_plan_arch_default_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); - if (m_unwind_plan_arch_default_sp) - abi->CreateDefaultUnwindPlan(*m_unwind_plan_arch_default_sp); + m_unwind_plan_arch_default_sp.reset(); } } } @@ -207,32 +266,23 @@ FuncUnwinders::GetUnwindPlanArchitectureDefault (Thread& thread) UnwindPlanSP FuncUnwinders::GetUnwindPlanArchitectureDefaultAtFunctionEntry (Thread& thread) { - // Lock the mutex to ensure we can always give out the most appropriate - // information. We want to make sure if someone requests an unwind - // plan, that they get one and don't run into a race condition where one - // thread has started to create the unwind plan and has put it into - // the unique pointer member variable, and have another thread enter this function - // and return the partially filled pointer contained in the unique pointer. - // We also want to make sure that we lock out other unwind plans from - // being accessed until this one is done creating itself in case someone - // had some code like: - // UnwindPlan *best_unwind_plan = ...GetUnwindPlanAtCallSite (...) - // if (best_unwind_plan == NULL) - // best_unwind_plan = GetUnwindPlanAtNonCallSite (...) + if (m_unwind_plan_arch_default_at_func_entry_sp.get() || m_tried_unwind_arch_default_at_func_entry) + return m_unwind_plan_arch_default_at_func_entry_sp; + Mutex::Locker locker (m_mutex); - if (m_tried_unwind_arch_default_at_func_entry == false && m_unwind_plan_arch_default_at_func_entry_sp.get() == nullptr) + m_tried_unwind_arch_default_at_func_entry = true; + + Address current_pc; + ProcessSP process_sp (thread.CalculateProcess()); + if (process_sp) { - m_tried_unwind_arch_default_at_func_entry = true; - Address current_pc; - ProcessSP process_sp (thread.CalculateProcess()); - if (process_sp) + ABI *abi = process_sp->GetABI().get(); + if (abi) { - ABI *abi = process_sp->GetABI().get(); - if (abi) + m_unwind_plan_arch_default_at_func_entry_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); + if (!abi->CreateFunctionEntryUnwindPlan(*m_unwind_plan_arch_default_at_func_entry_sp)) { - m_unwind_plan_arch_default_at_func_entry_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric)); - if (m_unwind_plan_arch_default_at_func_entry_sp) - abi->CreateFunctionEntryUnwindPlan(*m_unwind_plan_arch_default_at_func_entry_sp); + m_unwind_plan_arch_default_at_func_entry_sp.reset(); } } } @@ -246,10 +296,11 @@ FuncUnwinders::GetFirstNonPrologueInsn (Target& target) { if (m_first_non_prologue_insn.IsValid()) return m_first_non_prologue_insn; + + Mutex::Locker locker (m_mutex); ExecutionContext exe_ctx (target.shared_from_this(), false); UnwindAssemblySP assembly_profiler_sp (GetUnwindAssemblyProfiler()); if (assembly_profiler_sp) - if (assembly_profiler_sp) assembly_profiler_sp->FirstNonPrologueInsn (m_range, exe_ctx, m_first_non_prologue_insn); return m_first_non_prologue_insn; } @@ -260,16 +311,6 @@ FuncUnwinders::GetFunctionStartAddress () const return m_range.GetBaseAddress(); } -void -FuncUnwinders::InvalidateNonCallSiteUnwindPlan (lldb_private::Thread& thread) -{ - UnwindPlanSP arch_default = GetUnwindPlanArchitectureDefault (thread); - if (arch_default && m_tried_unwind_at_call_site) - { - m_unwind_plan_call_site_sp = arch_default; - } -} - lldb::UnwindAssemblySP FuncUnwinders::GetUnwindAssemblyProfiler () { @@ -281,3 +322,39 @@ FuncUnwinders::GetUnwindAssemblyProfiler () } return assembly_profiler_sp; } + +Address +FuncUnwinders::GetLSDAAddress (Target &target) +{ + Address lsda_addr; + + UnwindPlanSP unwind_plan_sp = GetEHFrameUnwindPlan (target, -1); + if (unwind_plan_sp.get() == nullptr) + { + unwind_plan_sp = GetCompactUnwindUnwindPlan (target, -1); + } + if (unwind_plan_sp.get() && unwind_plan_sp->GetLSDAAddress().IsValid()) + { + lsda_addr = unwind_plan_sp->GetLSDAAddress(); + } + return lsda_addr; +} + + +Address +FuncUnwinders::GetPersonalityRoutinePtrAddress (Target &target) +{ + Address personality_addr; + + UnwindPlanSP unwind_plan_sp = GetEHFrameUnwindPlan (target, -1); + if (unwind_plan_sp.get() == nullptr) + { + unwind_plan_sp = GetCompactUnwindUnwindPlan (target, -1); + } + if (unwind_plan_sp.get() && unwind_plan_sp->GetPersonalityFunctionPtr().IsValid()) + { + personality_addr = unwind_plan_sp->GetPersonalityFunctionPtr(); + } + + return personality_addr; +} diff --git a/source/Symbol/LineTable.cpp b/source/Symbol/LineTable.cpp index 4b4e33b2b0e9..92b243d80a91 100644 --- a/source/Symbol/LineTable.cpp +++ b/source/Symbol/LineTable.cpp @@ -562,7 +562,6 @@ LineTable::LinkLineTable (const FileRangeMap &file_range_map) // Append the sequence since we just terminated the previous one line_table_ap->InsertSequence (&sequence); sequence.Clear(); - prev_entry_was_linked = false; } // Now link the current entry diff --git a/source/Symbol/ObjectFile.cpp b/source/Symbol/ObjectFile.cpp index 11b540071208..c24e83260d4d 100644 --- a/source/Symbol/ObjectFile.cpp +++ b/source/Symbol/ObjectFile.cpp @@ -371,6 +371,7 @@ ObjectFile::GetAddressClass (addr_t file_addr) case eSectionTypeDWARFAppleObjC: return eAddressClassDebug; case eSectionTypeEHFrame: + case eSectionTypeCompactUnwind: return eAddressClassRuntime; case eSectionTypeELFSymbolTable: case eSectionTypeELFDynamicSymbols: @@ -458,6 +459,9 @@ ObjectFile::CopyData (lldb::offset_t offset, size_t length, void *dst) const size_t ObjectFile::ReadSectionData (const Section *section, lldb::offset_t section_offset, void *dst, size_t dst_len) const { + assert(section); + section_offset *= section->GetTargetByteSize(); + // If some other objectfile owns this data, pass this to them. if (section->GetObjectFile() != this) return section->GetObjectFile()->ReadSectionData (section, section_offset, dst, dst_len); @@ -555,8 +559,6 @@ ObjectFile::MemoryMapSectionData (const Section *section, DataExtractor& section // The object file now contains a full mmap'ed copy of the object file data, so just use this return GetData(section->GetFileOffset(), section->GetFileSize(), section_data); } - section_data.Clear(); - return 0; } diff --git a/source/Symbol/Symbol.cpp b/source/Symbol/Symbol.cpp index 880519955277..b6ed94610b0a 100644 --- a/source/Symbol/Symbol.cpp +++ b/source/Symbol/Symbol.cpp @@ -626,7 +626,7 @@ Symbol::ResolveCallableAddress(Target &target) const Address func_so_addr; - bool is_indirect; + bool is_indirect = IsIndirect(); if (GetType() == eSymbolTypeReExported) { Symbol *reexported_symbol = ResolveReExportedSymbol(target); diff --git a/source/Symbol/SymbolContext.cpp b/source/Symbol/SymbolContext.cpp index 0e390dd08c5f..129f4def0067 100644 --- a/source/Symbol/SymbolContext.cpp +++ b/source/Symbol/SymbolContext.cpp @@ -128,7 +128,8 @@ SymbolContext::DumpStopContext const Address &addr, bool show_fullpaths, bool show_module, - bool show_inlined_frames + bool show_inlined_frames, + bool show_function_arguments ) const { bool dumped_something = false; @@ -146,7 +147,12 @@ SymbolContext::DumpStopContext { SymbolContext inline_parent_sc; Address inline_parent_addr; - if (function->GetMangled().GetName()) + if (show_function_arguments == false && function->GetMangled().GetName(Mangled::ePreferDemangledWithoutArguments)) + { + dumped_something = true; + function->GetMangled().GetName(Mangled::ePreferDemangledWithoutArguments).Dump(s); + } + else if (function->GetMangled().GetName()) { dumped_something = true; function->GetMangled().GetName().Dump(s); @@ -188,7 +194,7 @@ SymbolContext::DumpStopContext { s->EOL(); s->Indent(); - return inline_parent_sc.DumpStopContext (s, exe_scope, inline_parent_addr, show_fullpaths, show_module, show_inlined_frames); + return inline_parent_sc.DumpStopContext (s, exe_scope, inline_parent_addr, show_fullpaths, show_module, show_inlined_frames, show_function_arguments); } } else diff --git a/source/Symbol/Type.cpp b/source/Symbol/Type.cpp index 4eb538fb1352..42b76590aaea 100644 --- a/source/Symbol/Type.cpp +++ b/source/Symbol/Type.cpp @@ -31,6 +31,7 @@ #include "llvm/ADT/StringRef.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" using namespace lldb; using namespace lldb_private; @@ -1291,6 +1292,117 @@ TypeImpl::GetDescription (lldb_private::Stream &strm, return true; } +TypeMemberFunctionImpl& +TypeMemberFunctionImpl::operator = (const TypeMemberFunctionImpl& rhs) +{ + if (this != &rhs) + { + m_type = rhs.m_type; + m_objc_method_decl = rhs.m_objc_method_decl; + m_name = rhs.m_name; + m_kind = rhs.m_kind; + } + return *this; +} + +bool +TypeMemberFunctionImpl::IsValid () +{ + return m_type.IsValid() && m_kind != lldb::eMemberFunctionKindUnknown; +} + +ConstString +TypeMemberFunctionImpl::GetName () const +{ + return m_name; +} + +ClangASTType +TypeMemberFunctionImpl::GetType () const +{ + return m_type; +} + +lldb::MemberFunctionKind +TypeMemberFunctionImpl::GetKind () const +{ + return m_kind; +} + +std::string +TypeMemberFunctionImpl::GetPrintableTypeName () +{ + if (m_type) + return m_type.GetTypeName().AsCString("<unknown>"); + if (m_objc_method_decl) + { + if (m_objc_method_decl->getClassInterface()) + { + return m_objc_method_decl->getClassInterface()->getName(); + } + } + return "<unknown>"; +} + +bool +TypeMemberFunctionImpl::GetDescription (Stream& stream) +{ + switch (m_kind) { + case lldb::eMemberFunctionKindUnknown: + return false; + case lldb::eMemberFunctionKindConstructor: + stream.Printf("constructor for %s", GetPrintableTypeName().c_str()); + break; + case lldb::eMemberFunctionKindDestructor: + stream.Printf("destructor for %s", GetPrintableTypeName().c_str()); + break; + case lldb::eMemberFunctionKindInstanceMethod: + stream.Printf("instance method %s of type %s", + m_name.AsCString(), + GetPrintableTypeName().c_str()); + break; + case lldb::eMemberFunctionKindStaticMethod: + stream.Printf("static method %s of type %s", + m_name.AsCString(), + GetPrintableTypeName().c_str()); + break; + } + return true; +} + +ClangASTType +TypeMemberFunctionImpl::GetReturnType () const +{ + if (m_type) + return m_type.GetFunctionReturnType(); + if (m_objc_method_decl) + return ClangASTType(&m_objc_method_decl->getASTContext(),m_objc_method_decl->getReturnType().getAsOpaquePtr()); + return ClangASTType(); +} + +size_t +TypeMemberFunctionImpl::GetNumArguments () const +{ + if (m_type) + return m_type.GetNumberOfFunctionArguments(); + if (m_objc_method_decl) + return m_objc_method_decl->param_size(); + return 0; +} + +ClangASTType +TypeMemberFunctionImpl::GetArgumentAtIndex (size_t idx) const +{ + if (m_type) + return m_type.GetFunctionArgumentAtIndex (idx); + if (m_objc_method_decl) + { + if (idx < m_objc_method_decl->param_size()) + return ClangASTType(&m_objc_method_decl->getASTContext(), m_objc_method_decl->parameters()[idx]->getOriginalType().getAsOpaquePtr()); + } + return ClangASTType(); +} + TypeEnumMemberImpl::TypeEnumMemberImpl (const clang::EnumConstantDecl* enum_member_decl, const lldb_private::ClangASTType& integer_type) : m_integer_type_sp(), diff --git a/source/Symbol/UnwindPlan.cpp b/source/Symbol/UnwindPlan.cpp index ff0468e314d8..87d0f49421c5 100644 --- a/source/Symbol/UnwindPlan.cpp +++ b/source/Symbol/UnwindPlan.cpp @@ -153,6 +153,7 @@ UnwindPlan::Row::RegisterLocation::Dump (Stream &s, const UnwindPlan* unwind_pla void UnwindPlan::Row::Clear () { + m_cfa_type = CFAIsRegisterPlusOffset; m_offset = 0; m_cfa_reg_num = LLDB_INVALID_REGNUM; m_cfa_offset = 0; @@ -167,7 +168,7 @@ UnwindPlan::Row::Dump (Stream& s, const UnwindPlan* unwind_plan, Thread* thread, if (base_addr != LLDB_INVALID_ADDRESS) s.Printf ("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset()); else - s.Printf ("0x%8.8" PRIx64 ": CFA=", GetOffset()); + s.Printf ("%4" PRId64 ": CFA=", GetOffset()); if (reg_info) s.Printf ("%s", reg_info->name); @@ -189,10 +190,11 @@ UnwindPlan::Row::Dump (Stream& s, const UnwindPlan* unwind_plan, Thread* thread, } UnwindPlan::Row::Row() : - m_offset(0), - m_cfa_reg_num(LLDB_INVALID_REGNUM), - m_cfa_offset(0), - m_register_locations() + m_offset (0), + m_cfa_type (CFAIsRegisterPlusOffset), + m_cfa_reg_num (LLDB_INVALID_REGNUM), + m_cfa_offset (0), + m_register_locations () { } @@ -209,6 +211,16 @@ UnwindPlan::Row::GetRegisterInfo (uint32_t reg_num, UnwindPlan::Row::RegisterLoc } void +UnwindPlan::Row::RemoveRegisterInfo (uint32_t reg_num) +{ + collection::const_iterator pos = m_register_locations.find(reg_num); + if (pos != m_register_locations.end()) + { + m_register_locations.erase(pos); + } +} + +void UnwindPlan::Row::SetRegisterInfo (uint32_t reg_num, const UnwindPlan::Row::RegisterLocation register_location) { m_register_locations[reg_num] = register_location; @@ -301,6 +313,23 @@ UnwindPlan::Row::operator == (const UnwindPlan::Row& rhs) const { if (m_offset != rhs.m_offset || m_cfa_reg_num != rhs.m_cfa_reg_num || m_cfa_offset != rhs.m_cfa_offset) return false; + + if (m_cfa_type != rhs.m_cfa_type) + return false; + + if (m_cfa_type == CFAIsRegisterPlusOffset) + { + if (m_cfa_reg_num != rhs.m_cfa_reg_num) + return false; + if (m_cfa_offset != rhs.m_cfa_offset) + return false; + } + if (m_cfa_type == CFAIsRegisterDereferenced) + { + if (m_cfa_reg_num != rhs.m_cfa_reg_num) + return false; + } + return m_register_locations == rhs.m_register_locations; } @@ -449,6 +478,44 @@ UnwindPlan::Dump (Stream& s, Thread *thread, lldb::addr_t base_addr) const { s.Printf ("This UnwindPlan originally sourced from %s\n", m_source_name.GetCString()); } + if (m_lsda_address.IsValid() && m_personality_func_addr.IsValid()) + { + TargetSP target_sp(thread->CalculateTarget()); + addr_t lsda_load_addr = m_lsda_address.GetLoadAddress (target_sp.get()); + addr_t personality_func_load_addr = m_personality_func_addr.GetLoadAddress (target_sp.get()); + + if (lsda_load_addr != LLDB_INVALID_ADDRESS && personality_func_load_addr != LLDB_INVALID_ADDRESS) + { + s.Printf("LSDA address 0x%" PRIx64 ", personality routine is at address 0x%" PRIx64 "\n", + lsda_load_addr, personality_func_load_addr); + } + } + s.Printf ("This UnwindPlan is sourced from the compiler: "); + switch (m_plan_is_sourced_from_compiler) + { + case eLazyBoolYes: + s.Printf ("yes.\n"); + break; + case eLazyBoolNo: + s.Printf ("no.\n"); + break; + case eLazyBoolCalculate: + s.Printf ("not specified.\n"); + break; + } + s.Printf ("This UnwindPlan is valid at all instruction locations: "); + switch (m_plan_is_valid_at_all_instruction_locations) + { + case eLazyBoolYes: + s.Printf ("yes.\n"); + break; + case eLazyBoolNo: + s.Printf ("no.\n"); + break; + case eLazyBoolCalculate: + s.Printf ("not specified.\n"); + break; + } if (m_plan_valid_address_range.GetBaseAddress().IsValid() && m_plan_valid_address_range.GetByteSize() > 0) { s.PutCString ("Address range of this UnwindPlan: "); diff --git a/source/Symbol/UnwindTable.cpp b/source/Symbol/UnwindTable.cpp index df9f5b932565..90b33a69a789 100644 --- a/source/Symbol/UnwindTable.cpp +++ b/source/Symbol/UnwindTable.cpp @@ -17,6 +17,7 @@ #include "lldb/Symbol/FuncUnwinders.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/DWARFCallFrameInfo.h" +#include "lldb/Symbol/CompactUnwindInfo.h" // There is one UnwindTable object per ObjectFile. // It contains a list of Unwind objects -- one per function, populated lazily -- for the ObjectFile. @@ -30,7 +31,8 @@ UnwindTable::UnwindTable (ObjectFile& objfile) : m_unwinds (), m_initialized (false), m_mutex (), - m_eh_frame (nullptr) + m_eh_frame (nullptr), + m_compact_unwind (nullptr) { } @@ -56,6 +58,11 @@ UnwindTable::Initialize () { m_eh_frame = new DWARFCallFrameInfo(m_object_file, sect, eRegisterKindGCC, true); } + sect = sl->FindSectionByType (eSectionTypeCompactUnwind, true); + if (sect.get()) + { + m_compact_unwind = new CompactUnwindInfo(m_object_file, sect); + } } m_initialized = true; @@ -154,6 +161,13 @@ UnwindTable::GetEHFrameInfo () return m_eh_frame; } +CompactUnwindInfo * +UnwindTable::GetCompactUnwindInfo () +{ + Initialize(); + return m_compact_unwind; +} + bool UnwindTable::GetArchitecture (lldb_private::ArchSpec &arch) { diff --git a/source/Symbol/Variable.cpp b/source/Symbol/Variable.cpp index e6a9b027fc13..b1a60f6b4a24 100644 --- a/source/Symbol/Variable.cpp +++ b/source/Symbol/Variable.cpp @@ -174,13 +174,15 @@ Variable::DumpDeclaration (Stream *s, bool show_fullpaths, bool show_module) sc.block = nullptr; sc.line_entry.Clear(); bool show_inlined_frames = false; + const bool show_function_arguments = true; dumped_declaration_info = sc.DumpStopContext (s, nullptr, Address(), show_fullpaths, show_module, - show_inlined_frames); + show_inlined_frames, + show_function_arguments); if (sc.function) s->PutChar(':'); diff --git a/source/Target/CPPLanguageRuntime.cpp b/source/Target/CPPLanguageRuntime.cpp index f5b7f7fc41a6..f048c6706a9b 100644 --- a/source/Target/CPPLanguageRuntime.cpp +++ b/source/Target/CPPLanguageRuntime.cpp @@ -11,6 +11,8 @@ #include <string.h> +#include "llvm/ADT/StringRef.h" + #include "lldb/Core/PluginManager.h" #include "lldb/Core/UniqueCStringMap.h" #include "lldb/Target/ExecutionContext.h" @@ -190,30 +192,17 @@ CPPLanguageRuntime::IsCPPMangledName (const char *name) } bool -CPPLanguageRuntime::StripNamespacesFromVariableName (const char *name, const char *&base_name_start, const char *&base_name_end) +CPPLanguageRuntime::ExtractContextAndIdentifier (const char *name, llvm::StringRef &context, llvm::StringRef &identifier) { - if (base_name_end == NULL) - base_name_end = name + strlen (name); - - const char *last_colon = strrchr (name, ':'); - - if (last_colon == NULL) - { - base_name_start = name; - return true; - } - - // Can't have a C++ name that begins with a single ':', nor contains an internal single ':' - if (last_colon == name) - return false; - else if (last_colon[-1] != ':') - return false; - else + static RegularExpression g_basename_regex("^(([A-Za-z_][A-Za-z_0-9]*::)*)([A-Za-z_][A-Za-z_0-9]*)$"); + RegularExpression::Match match(4); + if (g_basename_regex.Execute (name, &match)) { - // FIXME: should check if there is - base_name_start = last_colon + 1; + match.GetMatchAtIndex(name, 1, context); + match.GetMatchAtIndex(name, 3, identifier); return true; } + return false; } uint32_t @@ -282,6 +271,7 @@ ReverseFindMatchingChars (const llvm::StringRef &s, return false; } + void CPPLanguageRuntime::MethodName::Parse() { @@ -306,6 +296,7 @@ CPPLanguageRuntime::MethodName::Parse() if (arg_start > 0) { size_t basename_end = arg_start; + size_t context_start = 0; size_t context_end = llvm::StringRef::npos; if (basename_end > 0 && full[basename_end-1] == '>') { @@ -314,16 +305,35 @@ CPPLanguageRuntime::MethodName::Parse() size_t template_start, template_end; llvm::StringRef lt_gt("<>", 2); if (ReverseFindMatchingChars (full, lt_gt, template_start, template_end, basename_end)) + { context_end = full.rfind(':', template_start); + if (context_end == llvm::StringRef::npos) + { + // Check for templated functions that include return type like: + // 'void foo<Int>()' + context_end = full.rfind(' ', template_start); + if (context_end != llvm::StringRef::npos) + { + context_start = context_end; + } + } + } + else + { + context_end = full.rfind(':', basename_end); + } } - if (context_end == llvm::StringRef::npos) + else if (context_end == llvm::StringRef::npos) + { context_end = full.rfind(':', basename_end); + } if (context_end == llvm::StringRef::npos) m_basename = full.substr(0, basename_end); else { - m_context = full.substr(0, context_end - 1); + if (context_start < context_end) + m_context = full.substr(context_start, context_end - 1); const size_t basename_begin = context_end + 1; m_basename = full.substr(basename_begin, basename_end - basename_begin); } @@ -343,6 +353,30 @@ CPPLanguageRuntime::MethodName::Parse() // printf (" arguments = '%s'\n", m_arguments.str().c_str()); // if (!m_qualifiers.empty()) // printf ("qualifiers = '%s'\n", m_qualifiers.str().c_str()); + + // Make sure we have a valid C++ basename with optional template args + static RegularExpression g_identifier_regex("^~?([A-Za-z_][A-Za-z_0-9]*)(<.*>)?$"); + std::string basename_str(m_basename.str()); + bool basename_is_valid = g_identifier_regex.Execute (basename_str.c_str(), NULL); + if (!basename_is_valid) + { + // Check for C++ operators + if (m_basename.startswith("operator")) + { + static RegularExpression g_operator_regex("^(operator)( ?)([A-Za-z_][A-Za-z_0-9]*|\\(\\)|\\[\\]|[\\^<>=!\\/*+-]+)(<.*>)?(\\[\\])?$"); + basename_is_valid = g_operator_regex.Execute(basename_str.c_str(), NULL); + } + } + if (!basename_is_valid) + { + // The C++ basename doesn't match our regular expressions so this can't + // be a valid C++ method, clear everything out and indicate an error + m_context = llvm::StringRef(); + m_basename = llvm::StringRef(); + m_arguments = llvm::StringRef(); + m_qualifiers = llvm::StringRef(); + m_parse_error = true; + } } else { diff --git a/source/Target/FileAction.cpp b/source/Target/FileAction.cpp index 18b039998bc7..8611ff5f2d2c 100644 --- a/source/Target/FileAction.cpp +++ b/source/Target/FileAction.cpp @@ -13,6 +13,7 @@ #include "lldb/Host/Windows/win32.h" // For O_NOCTTY #endif +#include "lldb/Core/Stream.h" #include "lldb/Target/FileAction.h" using namespace lldb_private; @@ -21,11 +22,11 @@ using namespace lldb_private; // FileAction member functions //---------------------------------------------------------------------------- -FileAction::FileAction() - : m_action(eFileActionNone) - , m_fd(-1) - , m_arg(-1) - , m_path() +FileAction::FileAction() : + m_action(eFileActionNone), + m_fd(-1), + m_arg(-1), + m_path() { } @@ -93,3 +94,24 @@ FileAction::Duplicate(int fd, int dup_fd) } return m_fd >= 0; } + +void +FileAction::Dump(Stream &stream) const +{ + stream.PutCString("file action: "); + switch (m_action) + { + case eFileActionClose: + stream.Printf("close fd %d", m_fd); + break; + case eFileActionDuplicate: + stream.Printf("duplicate fd %d to %d", m_fd, m_arg); + break; + case eFileActionNone: + stream.PutCString("no action"); + break; + case eFileActionOpen: + stream.Printf("open fd %d with '%s', OFLAGS = 0x%x", m_fd, m_path.c_str(), m_arg); + break; + } +} diff --git a/source/Target/InstrumentationRuntime.cpp b/source/Target/InstrumentationRuntime.cpp new file mode 100644 index 000000000000..b3b2393b0234 --- /dev/null +++ b/source/Target/InstrumentationRuntime.cpp @@ -0,0 +1,48 @@ +//===-- InstrumentationRuntime.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/InstrumentationRuntime.h" + +using namespace lldb; +using namespace lldb_private; + +void +InstrumentationRuntime::ModulesDidLoad(lldb_private::ModuleList &module_list, lldb_private::Process *process, InstrumentationRuntimeCollection &runtimes) +{ + InstrumentationRuntimeCreateInstance create_callback = NULL; + InstrumentationRuntimeGetType get_type_callback; + for (uint32_t idx = 0; ; ++idx) + { + create_callback = PluginManager::GetInstrumentationRuntimeCreateCallbackAtIndex(idx); + if (create_callback == NULL) + break; + get_type_callback = PluginManager::GetInstrumentationRuntimeGetTypeCallbackAtIndex(idx); + InstrumentationRuntimeType type = get_type_callback(); + + InstrumentationRuntimeCollection::iterator pos; + pos = runtimes.find (type); + if (pos == runtimes.end()) { + runtimes[type] = create_callback(process->shared_from_this()); + } + } +} + +void +InstrumentationRuntime::ModulesDidLoad(lldb_private::ModuleList &module_list) +{ +} + +bool +InstrumentationRuntime::IsActive() +{ + return false; +} diff --git a/source/Target/InstrumentationRuntimeStopInfo.cpp b/source/Target/InstrumentationRuntimeStopInfo.cpp new file mode 100644 index 000000000000..cdbf93b5f720 --- /dev/null +++ b/source/Target/InstrumentationRuntimeStopInfo.cpp @@ -0,0 +1,36 @@ +//===-- InstrumentationRuntimeStopInfo.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/InstrumentationRuntimeStopInfo.h" + +#include "lldb/lldb-private.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/InstrumentationRuntime.h" + +using namespace lldb; +using namespace lldb_private; + +InstrumentationRuntimeStopInfo::InstrumentationRuntimeStopInfo(Thread &thread, std::string description, StructuredData::ObjectSP additional_data) : + StopInfo(thread, 0) +{ + m_extended_info = additional_data; + m_description = description; +} + +const char * +InstrumentationRuntimeStopInfo::GetDescription () +{ + return m_description.c_str(); +} + +StopInfoSP +InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData (Thread &thread, std::string description, StructuredData::ObjectSP additionalData) +{ + return StopInfoSP(new InstrumentationRuntimeStopInfo(thread, description, additionalData)); +} diff --git a/source/Target/LanguageRuntime.cpp b/source/Target/LanguageRuntime.cpp index 9d48d8b2de7f..358bffee3a4d 100644 --- a/source/Target/LanguageRuntime.cpp +++ b/source/Target/LanguageRuntime.cpp @@ -10,6 +10,7 @@ #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Core/SearchFilter.h" using namespace lldb; using namespace lldb_private; @@ -19,17 +20,22 @@ class ExceptionSearchFilter : public SearchFilter { public: ExceptionSearchFilter (const lldb::TargetSP &target_sp, - lldb::LanguageType language) : + lldb::LanguageType language, + bool update_module_list = true) : SearchFilter (target_sp), m_language (language), m_language_runtime (NULL), m_filter_sp () { - UpdateModuleListIfNeeded (); + if (update_module_list) + UpdateModuleListIfNeeded (); } - - virtual bool - ModulePasses (const lldb::ModuleSP &module_sp) + + virtual + ~ExceptionSearchFilter() {}; + + bool + ModulePasses (const lldb::ModuleSP &module_sp) override { UpdateModuleListIfNeeded (); if (m_filter_sp) @@ -37,8 +43,8 @@ public: return false; } - virtual bool - ModulePasses (const FileSpec &spec) + bool + ModulePasses (const FileSpec &spec) override { UpdateModuleListIfNeeded (); if (m_filter_sp) @@ -47,16 +53,16 @@ public: } - virtual void - Search (Searcher &searcher) + void + Search (Searcher &searcher) override { UpdateModuleListIfNeeded (); if (m_filter_sp) m_filter_sp->Search (searcher); } - virtual void - GetDescription (Stream *s) + void + GetDescription (Stream *s) override { UpdateModuleListIfNeeded (); if (m_filter_sp) @@ -68,6 +74,12 @@ protected: LanguageRuntime *m_language_runtime; SearchFilterSP m_filter_sp; + SearchFilterSP + DoCopyForBreakpoint(Breakpoint &breakpoint) override + { + return SearchFilterSP(new ExceptionSearchFilter(TargetSP(), m_language, false)); + } + void UpdateModuleListIfNeeded () { @@ -124,11 +136,11 @@ public: { } - virtual Searcher::CallbackReturn + Searcher::CallbackReturn SearchCallback (SearchFilter &filter, SymbolContext &context, Address *addr, - bool containing) + bool containing) override { if (SetActualResolver()) @@ -137,8 +149,8 @@ public: return eCallbackReturnStop; } - virtual Searcher::Depth - GetDepth () + Searcher::Depth + GetDepth () override { if (SetActualResolver()) return m_actual_resolver_sp->GetDepth(); @@ -146,8 +158,8 @@ public: return eDepthTarget; } - virtual void - GetDescription (Stream *s) + void + GetDescription (Stream *s) override { s->Printf ("Exception breakpoint (catch: %s throw: %s)", m_catch_bp ? "on" : "off", @@ -163,8 +175,8 @@ public: s->Printf (" the correct runtime exception handler will be determined when you run"); } - virtual void - Dump (Stream *s) const + void + Dump (Stream *s) const override { } @@ -174,6 +186,12 @@ public: return V->getResolverID() == BreakpointResolver::ExceptionResolver; } protected: + BreakpointResolverSP + CopyForBreakpoint (Breakpoint &breakpoint) override + { + return BreakpointResolverSP(new ExceptionBreakpointResolver(m_language, m_catch_bp, m_throw_bp)); + } + bool SetActualResolver() { diff --git a/source/Target/Memory.cpp b/source/Target/Memory.cpp index b212fcd23a45..934bc967b68b 100644 --- a/source/Target/Memory.cpp +++ b/source/Target/Memory.cpp @@ -26,7 +26,7 @@ using namespace lldb_private; //---------------------------------------------------------------------- MemoryCache::MemoryCache(Process &process) : m_process (process), - m_cache_line_byte_size (512), + m_cache_line_byte_size (process.GetMemoryCacheLineSize()), m_mutex (Mutex::eMutexTypeRecursive), m_cache (), m_invalid_ranges () @@ -47,6 +47,7 @@ MemoryCache::Clear(bool clear_invalid_ranges) m_cache.clear(); if (clear_invalid_ranges) m_invalid_ranges.Clear(); + m_cache_line_byte_size = m_process.GetMemoryCacheLineSize(); } void @@ -250,7 +251,8 @@ AllocatedBlock::ReserveBlock (uint32_t size) { m_offset_to_chunk_size[0] = needed_chunks; if (log) - log->Printf ("[1] AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks", this, size, size, 0, needed_chunks, m_chunk_size); + log->Printf("[1] AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks", (void *)this, + size, size, 0, needed_chunks, m_chunk_size); addr = m_addr; } else @@ -268,7 +270,9 @@ AllocatedBlock::ReserveBlock (uint32_t size) { m_offset_to_chunk_size[last_offset] = needed_chunks; if (log) - log->Printf ("[2] AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks - num_chunks %lu", this, size, size, last_offset, needed_chunks, m_chunk_size, m_offset_to_chunk_size.size()); + log->Printf("[2] AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks - " + "num_chunks %lu", + (void *)this, size, size, last_offset, needed_chunks, m_chunk_size, m_offset_to_chunk_size.size()); addr = m_addr + last_offset; break; } @@ -284,7 +288,9 @@ AllocatedBlock::ReserveBlock (uint32_t size) { m_offset_to_chunk_size[last_offset] = needed_chunks; if (log) - log->Printf ("[3] AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks - num_chunks %lu", this, size, size, last_offset, needed_chunks, m_chunk_size, m_offset_to_chunk_size.size()); + log->Printf("[3] AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks - " + "num_chunks %lu", + (void *)this, size, size, last_offset, needed_chunks, m_chunk_size, m_offset_to_chunk_size.size()); addr = m_addr + last_offset; break; } @@ -345,7 +351,7 @@ AllocatedBlock::ReserveBlock (uint32_t size) } if (log) - log->Printf ("AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => 0x%16.16" PRIx64, this, size, size, (uint64_t)addr); + log->Printf("AllocatedBlock::ReserveBlock(%p) (size = %u (0x%x)) => 0x%16.16" PRIx64, (void *)this, size, size, (uint64_t)addr); return addr; } @@ -362,7 +368,8 @@ AllocatedBlock::FreeBlock (addr_t addr) } Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); if (log) - log->Printf ("AllocatedBlock::FreeBlock(%p) (addr = 0x%16.16" PRIx64 ") => %i, num_chunks: %lu", this, (uint64_t)addr, success, m_offset_to_chunk_size.size()); + log->Printf("AllocatedBlock::FreeBlock(%p) (addr = 0x%16.16" PRIx64 ") => %i, num_chunks: %lu", (void *)this, (uint64_t)addr, + success, m_offset_to_chunk_size.size()); return success; } diff --git a/source/Target/MemoryHistory.cpp b/source/Target/MemoryHistory.cpp new file mode 100644 index 000000000000..b97096b06d76 --- /dev/null +++ b/source/Target/MemoryHistory.cpp @@ -0,0 +1,30 @@ +//===-- MemoryHistory.cpp -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/MemoryHistory.h" + +#include "lldb/Core/PluginManager.h" + +using namespace lldb; +using namespace lldb_private; + +lldb::MemoryHistorySP +MemoryHistory::FindPlugin (const ProcessSP process) +{ + MemoryHistoryCreateInstance create_callback = NULL; + + for (uint32_t idx = 0; (create_callback = PluginManager::GetMemoryHistoryCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + MemoryHistorySP memory_history_sp (create_callback (process)); + if (memory_history_sp.get()) + return memory_history_sp; + } + + return MemoryHistorySP(); +} diff --git a/source/Target/ObjCLanguageRuntime.cpp b/source/Target/ObjCLanguageRuntime.cpp index 1f2abd876c84..b182407bf68e 100644 --- a/source/Target/ObjCLanguageRuntime.cpp +++ b/source/Target/ObjCLanguageRuntime.cpp @@ -603,20 +603,20 @@ ObjCLanguageRuntime::GetNonKVOClassDescriptor (ObjCISA isa) ClangASTType -ObjCLanguageRuntime::EncodingToType::RealizeType (const char* name, bool allow_unknownanytype) +ObjCLanguageRuntime::EncodingToType::RealizeType (const char* name, bool for_expression) { if (m_scratch_ast_ctx_ap) - return RealizeType(*m_scratch_ast_ctx_ap, name, allow_unknownanytype); + return RealizeType(*m_scratch_ast_ctx_ap, name, for_expression); return ClangASTType(); } ClangASTType -ObjCLanguageRuntime::EncodingToType::RealizeType (ClangASTContext& ast_ctx, const char* name, bool allow_unknownanytype) +ObjCLanguageRuntime::EncodingToType::RealizeType (ClangASTContext& ast_ctx, const char* name, bool for_expression) { clang::ASTContext *clang_ast = ast_ctx.getASTContext(); if (!clang_ast) return ClangASTType(); - return RealizeType(*clang_ast, name, allow_unknownanytype); + return RealizeType(*clang_ast, name, for_expression); } ObjCLanguageRuntime::EncodingToType::~EncodingToType() {} diff --git a/source/Target/Platform.cpp b/source/Target/Platform.cpp index fe73be2d05b9..4ebd18b8bfd0 100644 --- a/source/Target/Platform.cpp +++ b/source/Target/Platform.cpp @@ -32,26 +32,12 @@ using namespace lldb_private; // Use a singleton function for g_local_platform_sp to avoid init // constructors since LLDB is often part of a shared library static PlatformSP& -GetDefaultPlatformSP () +GetHostPlatformSP () { - static PlatformSP g_default_platform_sp; - return g_default_platform_sp; + static PlatformSP g_platform_sp; + return g_platform_sp; } -static Mutex & -GetConnectedPlatformListMutex () -{ - static Mutex g_remote_connected_platforms_mutex (Mutex::eMutexTypeRecursive); - return g_remote_connected_platforms_mutex; -} -static std::vector<PlatformSP> & -GetConnectedPlatformList () -{ - static std::vector<PlatformSP> g_remote_connected_platforms; - return g_remote_connected_platforms; -} - - const char * Platform::GetHostPlatformName () { @@ -69,17 +55,37 @@ Platform::GetHostPlatformName () /// or attaching to processes unless another platform is specified. //------------------------------------------------------------------ PlatformSP -Platform::GetDefaultPlatform () +Platform::GetHostPlatform () { - return GetDefaultPlatformSP (); + return GetHostPlatformSP (); +} + +static std::vector<PlatformSP> & +GetPlatformList() +{ + static std::vector<PlatformSP> g_platform_list; + return g_platform_list; +} + +static Mutex & +GetPlatformListMutex () +{ + static Mutex g_mutex(Mutex::eMutexTypeRecursive); + return g_mutex; } void -Platform::SetDefaultPlatform (const lldb::PlatformSP &platform_sp) +Platform::SetHostPlatform (const lldb::PlatformSP &platform_sp) { // The native platform should use its static void Platform::Initialize() // function to register itself as the native platform. - GetDefaultPlatformSP () = platform_sp; + GetHostPlatformSP () = platform_sp; + + if (platform_sp) + { + Mutex::Locker locker(GetPlatformListMutex ()); + GetPlatformList().push_back(platform_sp); + } } Error @@ -98,36 +104,36 @@ Platform::LocateExecutableScriptingResources (Target *target, Module &module, St return FileSpecList(); } -Platform* -Platform::FindPlugin (Process *process, const ConstString &plugin_name) -{ - PlatformCreateInstance create_callback = NULL; - if (plugin_name) - { - create_callback = PluginManager::GetPlatformCreateCallbackForPluginName (plugin_name); - if (create_callback) - { - ArchSpec arch; - if (process) - { - arch = process->GetTarget().GetArchitecture(); - } - std::unique_ptr<Platform> instance_ap(create_callback(process, &arch)); - if (instance_ap.get()) - return instance_ap.release(); - } - } - else - { - for (uint32_t idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex(idx)) != NULL; ++idx) - { - std::unique_ptr<Platform> instance_ap(create_callback(process, nullptr)); - if (instance_ap.get()) - return instance_ap.release(); - } - } - return NULL; -} +//PlatformSP +//Platform::FindPlugin (Process *process, const ConstString &plugin_name) +//{ +// PlatformCreateInstance create_callback = NULL; +// if (plugin_name) +// { +// create_callback = PluginManager::GetPlatformCreateCallbackForPluginName (plugin_name); +// if (create_callback) +// { +// ArchSpec arch; +// if (process) +// { +// arch = process->GetTarget().GetArchitecture(); +// } +// PlatformSP platform_sp(create_callback(process, &arch)); +// if (platform_sp) +// return platform_sp; +// } +// } +// else +// { +// for (uint32_t idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex(idx)) != NULL; ++idx) +// { +// PlatformSP platform_sp(create_callback(process, nullptr)); +// if (platform_sp) +// return platform_sp; +// } +// } +// return PlatformSP(); +//} Error Platform::GetSharedModule (const ModuleSpec &module_spec, @@ -153,21 +159,50 @@ Platform::GetSharedModule (const ModuleSpec &module_spec, } PlatformSP -Platform::Create (const char *platform_name, Error &error) +Platform::Find (const ConstString &name) +{ + if (name) + { + static ConstString g_host_platform_name ("host"); + if (name == g_host_platform_name) + return GetHostPlatform(); + + Mutex::Locker locker(GetPlatformListMutex ()); + for (const auto &platform_sp : GetPlatformList()) + { + if (platform_sp->GetName() == name) + return platform_sp; + } + } + return PlatformSP(); +} + +PlatformSP +Platform::Create (const ConstString &name, Error &error) { PlatformCreateInstance create_callback = NULL; lldb::PlatformSP platform_sp; - if (platform_name && platform_name[0]) + if (name) { - ConstString const_platform_name (platform_name); - create_callback = PluginManager::GetPlatformCreateCallbackForPluginName (const_platform_name); + static ConstString g_host_platform_name ("host"); + if (name == g_host_platform_name) + return GetHostPlatform(); + + create_callback = PluginManager::GetPlatformCreateCallbackForPluginName (name); if (create_callback) - platform_sp.reset(create_callback(true, NULL)); + platform_sp = create_callback(true, NULL); else - error.SetErrorStringWithFormat ("unable to find a plug-in for the platform named \"%s\"", platform_name); + error.SetErrorStringWithFormat ("unable to find a plug-in for the platform named \"%s\"", name.GetCString()); } else error.SetErrorString ("invalid platform name"); + + if (platform_sp) + { + Mutex::Locker locker(GetPlatformListMutex ()); + GetPlatformList().push_back(platform_sp); + } + return platform_sp; } @@ -178,28 +213,52 @@ Platform::Create (const ArchSpec &arch, ArchSpec *platform_arch_ptr, Error &erro lldb::PlatformSP platform_sp; if (arch.IsValid()) { - uint32_t idx; + // Scope for locker + { + // First try exact arch matches across all platforms already created + Mutex::Locker locker(GetPlatformListMutex ()); + for (const auto &platform_sp : GetPlatformList()) + { + if (platform_sp->IsCompatibleArchitecture(arch, true, platform_arch_ptr)) + return platform_sp; + } + + // Next try compatible arch matches across all platforms already created + for (const auto &platform_sp : GetPlatformList()) + { + if (platform_sp->IsCompatibleArchitecture(arch, false, platform_arch_ptr)) + return platform_sp; + } + } + PlatformCreateInstance create_callback; // First try exact arch matches across all platform plug-ins - bool exact = true; + uint32_t idx; for (idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex (idx)); ++idx) { if (create_callback) { - platform_sp.reset(create_callback(false, &arch)); - if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, exact, platform_arch_ptr)) + platform_sp = create_callback(false, &arch); + if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, true, platform_arch_ptr)) + { + Mutex::Locker locker(GetPlatformListMutex ()); + GetPlatformList().push_back(platform_sp); return platform_sp; + } } } // Next try compatible arch matches across all platform plug-ins - exact = false; for (idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex (idx)); ++idx) { if (create_callback) { - platform_sp.reset(create_callback(false, &arch)); - if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, exact, platform_arch_ptr)) + platform_sp = create_callback(false, &arch); + if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, false, platform_arch_ptr)) + { + Mutex::Locker locker(GetPlatformListMutex ()); + GetPlatformList().push_back(platform_sp); return platform_sp; + } } } } @@ -211,25 +270,6 @@ Platform::Create (const ArchSpec &arch, ArchSpec *platform_arch_ptr, Error &erro return platform_sp; } -uint32_t -Platform::GetNumConnectedRemotePlatforms () -{ - Mutex::Locker locker (GetConnectedPlatformListMutex ()); - return GetConnectedPlatformList().size(); -} - -PlatformSP -Platform::GetConnectedRemotePlatformAtIndex (uint32_t idx) -{ - PlatformSP platform_sp; - { - Mutex::Locker locker (GetConnectedPlatformListMutex ()); - if (idx < GetConnectedPlatformList().size()) - platform_sp = GetConnectedPlatformList ()[idx]; - } - return platform_sp; -} - //------------------------------------------------------------------ /// Default Constructor //------------------------------------------------------------------ @@ -422,6 +462,20 @@ Platform::GetOSKernelDescription (std::string &s) return GetRemoteOSKernelDescription (s); } +void +Platform::AddClangModuleCompilationOptions (std::vector<std::string> &options) +{ + std::vector<std::string> default_compilation_options = + { + "-x", "c++", "-Xclang", "-nostdsysteminc", "-Xclang", "-nostdsysteminc" + }; + + options.insert(options.end(), + default_compilation_options.begin(), + default_compilation_options.end()); +} + + ConstString Platform::GetWorkingDirectory () { @@ -537,11 +591,11 @@ RecurseCopy_Callback (void *baton, case FileSpec::eFileTypeInvalid: case FileSpec::eFileTypeOther: case FileSpec::eFileTypeUnknown: - default: rc_baton->error.SetErrorStringWithFormat("invalid file detected during copy: %s", src.GetPath().c_str()); return FileSpec::eEnumerateDirectoryResultQuit; // got an error, bail out break; } + llvm_unreachable("Unhandled FileSpec::FileType!"); } Error @@ -842,15 +896,13 @@ Platform::SetOSVersion (uint32_t major, Error -Platform::ResolveExecutable (const FileSpec &exe_file, - const ArchSpec &exe_arch, +Platform::ResolveExecutable (const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp, const FileSpecList *module_search_paths_ptr) { Error error; - if (exe_file.Exists()) + if (module_spec.GetFileSpec().Exists()) { - ModuleSpec module_spec (exe_file, exe_arch); if (module_spec.GetArchitecture().IsValid()) { error = ModuleList::GetSharedModule (module_spec, @@ -864,9 +916,10 @@ Platform::ResolveExecutable (const FileSpec &exe_file, // No valid architecture was specified, ask the platform for // the architectures that we should be using (in the correct order) // and see if we can find a match that way - for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, module_spec.GetArchitecture()); ++idx) + ModuleSpec arch_module_spec(module_spec); + for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, arch_module_spec.GetArchitecture()); ++idx) { - error = ModuleList::GetSharedModule (module_spec, + error = ModuleList::GetSharedModule (arch_module_spec, exe_module_sp, module_search_paths_ptr, NULL, @@ -880,7 +933,7 @@ Platform::ResolveExecutable (const FileSpec &exe_file, else { error.SetErrorStringWithFormat ("'%s' does not exist", - exe_file.GetPath().c_str()); + module_spec.GetFileSpec().GetPath().c_str()); } return error; } @@ -1005,7 +1058,11 @@ Error Platform::LaunchProcess (ProcessLaunchInfo &launch_info) { Error error; - // Take care of the host case so that each subclass can just + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf ("Platform::%s()", __FUNCTION__); + + // Take care of the host case so that each subclass can just // call this function to get the host functionality. if (IsHost()) { @@ -1018,6 +1075,16 @@ Platform::LaunchProcess (ProcessLaunchInfo &launch_info) const bool will_debug = launch_info.GetFlags().Test(eLaunchFlagDebug); const bool first_arg_is_full_shell_command = false; uint32_t num_resumes = GetResumeCountForLaunchInfo (launch_info); + if (log) + { + const FileSpec &shell = launch_info.GetShell(); + const char *shell_str = (shell) ? shell.GetPath().c_str() : "<null>"; + log->Printf ("Platform::%s GetResumeCountForLaunchInfo() returned %" PRIu32 ", shell is '%s'", + __FUNCTION__, + num_resumes, + shell_str); + } + if (!launch_info.ConvertArgumentsForLaunchingInShell (error, is_localhost, will_debug, @@ -1026,6 +1093,9 @@ Platform::LaunchProcess (ProcessLaunchInfo &launch_info) return error; } + if (log) + log->Printf ("Platform::%s final launch_info resume count: %" PRIu32, __FUNCTION__, launch_info.GetResumeCount ()); + error = Host::LaunchProcess (launch_info); } else @@ -1037,9 +1107,12 @@ lldb::ProcessSP Platform::DebugProcess (ProcessLaunchInfo &launch_info, Debugger &debugger, Target *target, // Can be NULL, if NULL create a new target, else use existing one - Listener &listener, Error &error) { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf ("Platform::%s entered (target %p)", __FUNCTION__, static_cast<void*>(target)); + ProcessSP process_sp; // Make sure we stop at the entry point launch_info.GetFlags ().Set (eLaunchFlagDebug); @@ -1051,12 +1124,16 @@ Platform::DebugProcess (ProcessLaunchInfo &launch_info, error = LaunchProcess (launch_info); if (error.Success()) { + if (log) + log->Printf ("Platform::%s LaunchProcess() call succeeded (pid=%" PRIu64 ")", __FUNCTION__, launch_info.GetProcessID ()); if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID) { ProcessAttachInfo attach_info (launch_info); - process_sp = Attach (attach_info, debugger, target, listener, error); + process_sp = Attach (attach_info, debugger, target, error); if (process_sp) { + if (log) + log->Printf ("Platform::%s Attach() succeeded, Process plugin: %s", __FUNCTION__, process_sp->GetPluginName ().AsCString ()); launch_info.SetHijackListener(attach_info.GetHijackListener()); // Since we attached to the process, it will think it needs to detach @@ -1075,8 +1152,24 @@ Platform::DebugProcess (ProcessLaunchInfo &launch_info, process_sp->SetSTDIOFileDescriptor(pty_fd); } } + else + { + if (log) + log->Printf ("Platform::%s Attach() failed: %s", __FUNCTION__, error.AsCString ()); + } } + else + { + if (log) + log->Printf ("Platform::%s LaunchProcess() returned launch_info with invalid process id", __FUNCTION__); + } + } + else + { + if (log) + log->Printf ("Platform::%s LaunchProcess() failed: %s", __FUNCTION__, error.AsCString ()); } + return process_sp; } diff --git a/source/Target/Process.cpp b/source/Target/Process.cpp index a1049787d821..106678da2684 100644 --- a/source/Target/Process.cpp +++ b/source/Target/Process.cpp @@ -16,22 +16,25 @@ #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Breakpoint/BreakpointLocation.h" #include "lldb/Core/Event.h" -#include "lldb/Core/ConnectionFileDescriptor.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" -#include "lldb/Symbol/Symbol.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/State.h" #include "lldb/Core/StreamFile.h" #include "lldb/Expression/ClangUserExpression.h" -#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" #include "lldb/Host/Pipe.h" #include "lldb/Host/Terminal.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/Symbol.h" #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/JITLoader.h" +#include "lldb/Target/MemoryHistory.h" #include "lldb/Target/OperatingSystem.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/CPPLanguageRuntime.h" @@ -45,6 +48,7 @@ #include "lldb/Target/Thread.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanBase.h" +#include "lldb/Target/InstrumentationRuntime.h" #include "Plugins/Process/Utility/InferiorCallPOSIX.h" using namespace lldb; @@ -108,6 +112,7 @@ g_properties[] = { "python-os-plugin-path", OptionValue::eTypeFileSpec, false, true, NULL, NULL, "A path to a python OS plug-in module file that contains a OperatingSystemPlugIn class." }, { "stop-on-sharedlibrary-events" , OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true, stop when a shared library is loaded or unloaded." }, { "detach-keeps-stopped" , OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true, detach will attempt to keep the process stopped." }, + { "memory-cache-line-size" , OptionValue::eTypeUInt64, false, 512, NULL, NULL, "The memory cache line size" }, { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL } }; @@ -118,14 +123,17 @@ enum { ePropertyUnwindOnErrorInExpressions, ePropertyPythonOSPluginPath, ePropertyStopOnSharedLibraryEvents, - ePropertyDetachKeepsStopped + ePropertyDetachKeepsStopped, + ePropertyMemCacheLineSize }; -ProcessProperties::ProcessProperties (bool is_global) : - Properties () +ProcessProperties::ProcessProperties (lldb_private::Process *process) : + Properties (), + m_process (process) // Can be NULL for global ProcessProperties { - if (is_global) + if (process == NULL) { + // Global process properties, set them up one time m_collection_sp.reset (new ProcessOptionValueProperties(ConstString("process"))); m_collection_sp->Initialize(g_properties); m_collection_sp->AppendProperty(ConstString("thread"), @@ -134,13 +142,24 @@ ProcessProperties::ProcessProperties (bool is_global) : Thread::GetGlobalProperties()->GetValueProperties()); } else + { m_collection_sp.reset (new ProcessOptionValueProperties(Process::GetGlobalProperties().get())); + m_collection_sp->SetValueChangedCallback(ePropertyPythonOSPluginPath, ProcessProperties::OptionValueChangedCallback, this); + } } ProcessProperties::~ProcessProperties() { } +void +ProcessProperties::OptionValueChangedCallback (void *baton, OptionValue *option_value) +{ + ProcessProperties *properties = (ProcessProperties *)baton; + if (properties->m_process) + properties->m_process->LoadOperatingSystemPlugin(true); +} + bool ProcessProperties::GetDisableMemoryCache() const { @@ -148,6 +167,13 @@ ProcessProperties::GetDisableMemoryCache() const return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); } +uint64_t +ProcessProperties::GetMemoryCacheLineSize() const +{ + const uint32_t idx = ePropertyMemCacheLineSize; + return m_collection_sp->GetPropertyAtIndexAsUInt64 (NULL, idx, g_properties[idx].default_uint_value); +} + Args ProcessProperties::GetExtraStartupCommands () const { @@ -467,9 +493,9 @@ ProcessLaunchCommandOptions::SetOptionValue (uint32_t option_idx, const char *op case 'c': if (option_arg && option_arg[0]) - launch_info.SetShell (option_arg); + launch_info.SetShell (FileSpec(option_arg, false)); else - launch_info.SetShell (LLDB_DEFAULT_SHELL); + launch_info.SetShell (HostInfo::GetDefaultShell()); break; case 'v': @@ -660,7 +686,7 @@ Process::Process(Target &target, Listener &listener) : } Process::Process(Target &target, Listener &listener, const UnixSignalsSP &unix_signals_sp) : - ProcessProperties (false), + ProcessProperties (this), UserID (LLDB_INVALID_PROCESS_ID), Broadcaster (&(target.GetDebugger()), "lldb.process"), m_target (target), @@ -670,13 +696,13 @@ Process::Process(Target &target, Listener &listener, const UnixSignalsSP &unix_s m_private_state_control_broadcaster (NULL, "lldb.process.internal_state_control_broadcaster"), m_private_state_listener ("lldb.process.internal_state_listener"), m_private_state_control_wait(), - m_private_state_thread (LLDB_INVALID_HOST_THREAD), m_mod_id (), m_process_unique_id(0), m_thread_index_id (0), m_thread_id_to_index_id_map (), m_exit_status (-1), m_exit_string (), + m_exit_status_mutex(), m_thread_mutex (Mutex::eMutexTypeRecursive), m_thread_list_real (this), m_thread_list (this), @@ -706,6 +732,7 @@ Process::Process(Target &target, Listener &listener, const UnixSignalsSP &unix_s m_public_run_lock (), m_private_run_lock (), m_currently_handling_event(false), + m_stop_info_override_callback (NULL), m_finalize_called(false), m_clear_thread_plans_on_stop (false), m_force_next_event_delivery(false), @@ -760,6 +787,11 @@ Process::~Process() if (log) log->Printf ("%p Process::~Process()", static_cast<void*>(this)); StopPrivateStateThread(); + + // ThreadList::Clear() will try to acquire this process's mutex, so + // explicitly clear the thread list here to ensure that the mutex + // is not destroyed before the thread list. + m_thread_list.Clear(); } const ProcessPropertiesSP & @@ -767,7 +799,7 @@ Process::GetGlobalProperties() { static ProcessPropertiesSP g_settings_sp; if (!g_settings_sp) - g_settings_sp.reset (new ProcessProperties (true)); + g_settings_sp.reset (new ProcessProperties (NULL)); return g_settings_sp; } @@ -826,7 +858,9 @@ Process::Finalize() m_memory_cache.Clear(); m_allocated_memory_cache.Clear(); m_language_runtimes.clear(); + m_instrumentation_runtimes.clear(); m_next_event_action_ap.reset(); + m_stop_info_override_callback = NULL; //#ifdef LLDB_CONFIGURATION_DEBUG // StreamFile s(stdout, false); // EventSP event_sp; @@ -924,7 +958,7 @@ Process::SyncIOHandler (uint64_t timeout_msec) log->Printf ("Process::%s pid %" PRIu64 ": SUCCESS", __FUNCTION__, GetID ()); } - // reset sync one-shot so it will be ready for next time + // reset sync one-shot so it will be ready for next launch m_iohandler_sync.SetValue(false, eBroadcastNever); } @@ -932,7 +966,11 @@ Process::SyncIOHandler (uint64_t timeout_msec) } StateType -Process::WaitForProcessToStop (const TimeValue *timeout, lldb::EventSP *event_sp_ptr, bool wait_always, Listener *hijack_listener) +Process::WaitForProcessToStop (const TimeValue *timeout, + EventSP *event_sp_ptr, + bool wait_always, + Listener *hijack_listener, + Stream *stream) { // We can't just wait for a "stopped" event, because the stopped event may have restarted the target. // We have to actually check each event, and in the case of a stopped event check the restarted flag @@ -966,6 +1004,9 @@ Process::WaitForProcessToStop (const TimeValue *timeout, lldb::EventSP *event_sp if (event_sp_ptr && event_sp) *event_sp_ptr = event_sp; + bool pop_process_io_handler = hijack_listener != NULL; + Process::HandleProcessStateChangedEvent (event_sp, stream, pop_process_io_handler); + switch (state) { case eStateCrashed: @@ -995,6 +1036,195 @@ Process::WaitForProcessToStop (const TimeValue *timeout, lldb::EventSP *event_sp return state; } +bool +Process::HandleProcessStateChangedEvent (const EventSP &event_sp, + Stream *stream, + bool &pop_process_io_handler) +{ + const bool handle_pop = pop_process_io_handler == true; + + pop_process_io_handler = false; + ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event_sp.get()); + + if (!process_sp) + return false; + + StateType event_state = Process::ProcessEventData::GetStateFromEvent (event_sp.get()); + if (event_state == eStateInvalid) + return false; + + switch (event_state) + { + case eStateInvalid: + case eStateUnloaded: + case eStateAttaching: + case eStateLaunching: + case eStateStepping: + case eStateDetached: + { + if (stream) + stream->Printf ("Process %" PRIu64 " %s\n", + process_sp->GetID(), + StateAsCString (event_state)); + + if (event_state == eStateDetached) + pop_process_io_handler = true; + } + break; + + case eStateConnected: + case eStateRunning: + // Don't be chatty when we run... + break; + + case eStateExited: + if (stream) + process_sp->GetStatus(*stream); + pop_process_io_handler = true; + break; + + case eStateStopped: + case eStateCrashed: + case eStateSuspended: + // Make sure the program hasn't been auto-restarted: + if (Process::ProcessEventData::GetRestartedFromEvent (event_sp.get())) + { + if (stream) + { + size_t num_reasons = Process::ProcessEventData::GetNumRestartedReasons(event_sp.get()); + if (num_reasons > 0) + { + // FIXME: Do we want to report this, or would that just be annoyingly chatty? + if (num_reasons == 1) + { + const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex (event_sp.get(), 0); + stream->Printf ("Process %" PRIu64 " stopped and restarted: %s\n", + process_sp->GetID(), + reason ? reason : "<UNKNOWN REASON>"); + } + else + { + stream->Printf ("Process %" PRIu64 " stopped and restarted, reasons:\n", + process_sp->GetID()); + + + for (size_t i = 0; i < num_reasons; i++) + { + const char *reason = Process::ProcessEventData::GetRestartedReasonAtIndex (event_sp.get(), i); + stream->Printf("\t%s\n", reason ? reason : "<UNKNOWN REASON>"); + } + } + } + } + } + else + { + // Lock the thread list so it doesn't change on us, this is the scope for the locker: + { + ThreadList &thread_list = process_sp->GetThreadList(); + Mutex::Locker locker (thread_list.GetMutex()); + + ThreadSP curr_thread (thread_list.GetSelectedThread()); + ThreadSP thread; + StopReason curr_thread_stop_reason = eStopReasonInvalid; + if (curr_thread) + curr_thread_stop_reason = curr_thread->GetStopReason(); + if (!curr_thread || + !curr_thread->IsValid() || + curr_thread_stop_reason == eStopReasonInvalid || + curr_thread_stop_reason == eStopReasonNone) + { + // Prefer a thread that has just completed its plan over another thread as current thread. + ThreadSP plan_thread; + ThreadSP other_thread; + const size_t num_threads = thread_list.GetSize(); + size_t i; + for (i = 0; i < num_threads; ++i) + { + thread = thread_list.GetThreadAtIndex(i); + StopReason thread_stop_reason = thread->GetStopReason(); + switch (thread_stop_reason) + { + case eStopReasonInvalid: + case eStopReasonNone: + break; + + case eStopReasonTrace: + case eStopReasonBreakpoint: + case eStopReasonWatchpoint: + case eStopReasonSignal: + case eStopReasonException: + case eStopReasonExec: + case eStopReasonThreadExiting: + case eStopReasonInstrumentation: + if (!other_thread) + other_thread = thread; + break; + case eStopReasonPlanComplete: + if (!plan_thread) + plan_thread = thread; + break; + } + } + if (plan_thread) + thread_list.SetSelectedThreadByID (plan_thread->GetID()); + else if (other_thread) + thread_list.SetSelectedThreadByID (other_thread->GetID()); + else + { + if (curr_thread && curr_thread->IsValid()) + thread = curr_thread; + else + thread = thread_list.GetThreadAtIndex(0); + + if (thread) + thread_list.SetSelectedThreadByID (thread->GetID()); + } + } + } + // Drop the ThreadList mutex by here, since GetThreadStatus below might have to run code, + // e.g. for Data formatters, and if we hold the ThreadList mutex, then the process is going to + // have a hard time restarting the process. + if (stream) + { + Debugger &debugger = process_sp->GetTarget().GetDebugger(); + if (debugger.GetTargetList().GetSelectedTarget().get() == &process_sp->GetTarget()) + { + const bool only_threads_with_stop_reason = true; + const uint32_t start_frame = 0; + const uint32_t num_frames = 1; + const uint32_t num_frames_with_source = 1; + process_sp->GetStatus(*stream); + process_sp->GetThreadStatus (*stream, + only_threads_with_stop_reason, + start_frame, + num_frames, + num_frames_with_source); + } + else + { + uint32_t target_idx = debugger.GetTargetList().GetIndexOfTarget(process_sp->GetTarget().shared_from_this()); + if (target_idx != UINT32_MAX) + stream->Printf ("Target %d: (", target_idx); + else + stream->Printf ("Target <unknown index>: ("); + process_sp->GetTarget().Dump (stream, eDescriptionLevelBrief); + stream->Printf (") stopped.\n"); + } + } + + // Pop the process IO handler + pop_process_io_handler = true; + } + break; + } + + if (handle_pop && pop_process_io_handler) + process_sp->PopProcessIOHandler(); + + return true; +} + StateType Process::WaitForState @@ -1170,6 +1400,8 @@ Process::IsRunning () const int Process::GetExitStatus () { + Mutex::Locker locker (m_exit_status_mutex); + if (m_public_state.GetValue() == eStateExited) return m_exit_status; return -1; @@ -1179,6 +1411,8 @@ Process::GetExitStatus () const char * Process::GetExitDescription () { + Mutex::Locker locker (m_exit_status_mutex); + if (m_public_state.GetValue() == eStateExited && !m_exit_string.empty()) return m_exit_string.c_str(); return NULL; @@ -1203,11 +1437,16 @@ Process::SetExitStatus (int status, const char *cstr) return false; } - m_exit_status = status; - if (cstr) - m_exit_string = cstr; - else - m_exit_string.clear(); + // use a mutex to protect the status and string during updating + { + Mutex::Locker locker (m_exit_status_mutex); + + m_exit_status = status; + if (cstr) + m_exit_string = cstr; + else + m_exit_string.clear(); + } DidExit (); @@ -1392,6 +1631,17 @@ Process::GetState() return m_public_state.GetValue (); } +bool +Process::StateChangedIsExternallyHijacked() +{ + if (IsHijackedForEvent(eBroadcastBitStateChanged)) + { + if (strcmp(m_hijacking_listeners.back()->GetName(), "lldb.Process.ResumeSynchronous.hijack")) + return true; + } + return false; +} + void Process::SetPublicState (StateType new_state, bool restarted) { @@ -1404,7 +1654,7 @@ Process::SetPublicState (StateType new_state, bool restarted) // On the transition from Run to Stopped, we unlock the writer end of the // run lock. The lock gets locked in Resume, which is the public API // to tell the program to run. - if (!IsHijackedForEvent(eBroadcastBitStateChanged)) + if (!StateChangedIsExternallyHijacked()) { if (new_state == eStateDetached) { @@ -1445,6 +1695,36 @@ Process::Resume () return PrivateResume(); } +Error +Process::ResumeSynchronous (Stream *stream) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf("Process::ResumeSynchronous -- locking run lock"); + if (!m_public_run_lock.TrySetRunning()) + { + Error error("Resume request failed - process still running."); + if (log) + log->Printf ("Process::Resume: -- TrySetRunning failed, not resuming."); + return error; + } + + ListenerSP listener_sp (new Listener("lldb.Process.ResumeSynchronous.hijack")); + HijackProcessEvents(listener_sp.get()); + + Error error = PrivateResume(); + + StateType state = WaitForProcessToStop (NULL, NULL, true, listener_sp.get(), stream); + + // Undo the hijacking of process events... + RestoreProcessEvents(); + + if (error.Success() && !StateIsStoppedState(state, false)) + error.SetErrorStringWithFormat("process not in stopped state after synchronous resume: %s", StateAsCString(state)); + + return error; +} + StateType Process::GetPrivateState () { @@ -1617,12 +1897,17 @@ Process::LoadImage (const FileSpec &image_spec, Error &error) { if (error_str_sp->IsCStringContainer(true)) { - StreamString s; - size_t num_chars = error_str_sp->ReadPointedString (s, error); + DataBufferSP buffer_sp(new DataBufferHeap(10240,0)); + size_t num_chars = error_str_sp->ReadPointedString (buffer_sp, error, 10240); if (error.Success() && num_chars > 0) { error.Clear(); - error.SetErrorStringWithFormat("dlopen error: %s", s.GetData()); + error.SetErrorStringWithFormat("dlopen error: %s", buffer_sp->GetBytes()); + } + else + { + error.Clear(); + error.SetErrorStringWithFormat("dlopen failed for unknown reasons."); } } } @@ -2507,10 +2792,10 @@ Process::WriteMemory (addr_t addr, const void *buf, size_t size, Error &error) }); if (bytes_written < size) - bytes_written += WriteMemoryPrivate (addr + bytes_written, - ubuf + bytes_written, - size - bytes_written, - error); + WriteMemoryPrivate (addr + bytes_written, + ubuf + bytes_written, + size - bytes_written, + error); } } else @@ -2722,6 +3007,16 @@ Process::WaitForProcessStopPrivate (const TimeValue *timeout, EventSP &event_sp) return state; } +void +Process::LoadOperatingSystemPlugin(bool flush) +{ + if (flush) + m_thread_list.Clear(); + m_os_ap.reset (OperatingSystem::FindPlugin (this, NULL)); + if (flush) + Flush(); +} + Error Process::Launch (ProcessLaunchInfo &launch_info) { @@ -2732,6 +3027,7 @@ Process::Launch (ProcessLaunchInfo &launch_info) m_system_runtime_ap.reset(); m_os_ap.reset(); m_process_input_reader.reset(); + m_stop_info_override_callback = NULL; Module *exe_module = m_target.GetExecutableModulePointer(); if (exe_module) @@ -2811,15 +3107,20 @@ Process::Launch (ProcessLaunchInfo &launch_info) if (system_runtime) system_runtime->DidLaunch(); - m_os_ap.reset (OperatingSystem::FindPlugin (this, NULL)); - // This delays passing the stopped event to listeners till DidLaunch gets - // a chance to complete... - HandlePrivateEvent (event_sp); + LoadOperatingSystemPlugin(false); + + // Note, the stop event was consumed above, but not handled. This was done + // to give DidLaunch a chance to run. The target is either stopped or crashed. + // Directly set the state. This is done to prevent a stop message with a bunch + // of spurious output on thread status, as well as not pop a ProcessIOHandler. + SetPublicState(state, false); if (PrivateStateThreadIsValid ()) ResumePrivateStateThread (); else StartPrivateStateThread (); + + m_stop_info_override_callback = GetTarget().GetArchitecture().GetStopInfoOverrideCallback(); } else if (state == eStateExited) { @@ -2979,6 +3280,15 @@ Process::AttachCompletionHandler::GetExitString () return m_exit_string.c_str(); } +Listener & +ProcessAttachInfo::GetListenerForProcess (Debugger &debugger) +{ + if (m_listener_sp) + return *m_listener_sp; + else + return debugger.GetListener(); +} + Error Process::Attach (ProcessAttachInfo &attach_info) { @@ -2988,6 +3298,7 @@ Process::Attach (ProcessAttachInfo &attach_info) m_jit_loaders_ap.reset(); m_system_runtime_ap.reset(); m_os_ap.reset(); + m_stop_info_override_callback = NULL; lldb::pid_t attach_pid = attach_info.GetProcessID(); Error error; @@ -3115,14 +3426,13 @@ Process::Attach (ProcessAttachInfo &attach_info) else { if (GetID() != LLDB_INVALID_PROCESS_ID) - { SetID (LLDB_INVALID_PROCESS_ID); - const char *error_string = error.AsCString(); - if (error_string == NULL) - error_string = "attach failed"; - SetExitStatus(-1, error_string); - } + const char *error_string = error.AsCString(); + if (error_string == NULL) + error_string = "attach failed"; + + SetExitStatus(-1, error_string); } } } @@ -3246,6 +3556,8 @@ Process::CompleteAttach () exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str () : "<none>"); } } + + m_stop_info_override_callback = process_arch.GetStopInfoOverrideCallback(); } Error @@ -3823,26 +4135,25 @@ Process::StartPrivateStateThread (bool force) // events make it to clients (into the DCProcess event queue). char thread_name[1024]; - if (Host::MAX_THREAD_NAME_LENGTH <= 16) + if (HostInfo::GetMaxThreadNameLength() <= 30) { - // On platforms with abbreviated thread name lengths, choose thread names that fit within the limit. - if (already_running) - snprintf(thread_name, sizeof(thread_name), "intern-state-OV"); - else - snprintf(thread_name, sizeof(thread_name), "intern-state"); + // On platforms with abbreviated thread name lengths, choose thread names that fit within the limit. + if (already_running) + snprintf(thread_name, sizeof(thread_name), "intern-state-OV"); + else + snprintf(thread_name, sizeof(thread_name), "intern-state"); } else { if (already_running) - snprintf(thread_name, sizeof(thread_name), "<lldb.process.internal-state-override(pid=%" PRIu64 ")>", GetID()); + snprintf(thread_name, sizeof(thread_name), "<lldb.process.internal-state-override(pid=%" PRIu64 ")>", GetID()); else - snprintf(thread_name, sizeof(thread_name), "<lldb.process.internal-state(pid=%" PRIu64 ")>", GetID()); + snprintf(thread_name, sizeof(thread_name), "<lldb.process.internal-state(pid=%" PRIu64 ")>", GetID()); } // Create the private state thread, and start it running. - m_private_state_thread = Host::ThreadCreate (thread_name, Process::PrivateStateThread, this, NULL); - bool success = IS_VALID_LLDB_HOST_THREAD(m_private_state_thread); - if (success) + m_private_state_thread = ThreadLauncher::LaunchThread(thread_name, Process::PrivateStateThread, this, NULL); + if (m_private_state_thread.IsJoinable()) { ResumePrivateStateThread(); return true; @@ -3891,8 +4202,8 @@ Process::ControlPrivateStateThread (uint32_t signal) // Signal the private state thread. First we should copy this is case the // thread starts exiting since the private state thread will NULL this out // when it exits - const lldb::thread_t private_state_thread = m_private_state_thread; - if (IS_VALID_LLDB_HOST_THREAD(private_state_thread)) + HostThread private_state_thread(m_private_state_thread); + if (private_state_thread.IsJoinable()) { TimeValue timeout_time; bool timed_out; @@ -3910,8 +4221,7 @@ Process::ControlPrivateStateThread (uint32_t signal) { if (timed_out) { - Error error; - Host::ThreadCancel (private_state_thread, &error); + Error error = private_state_thread.Cancel(); if (log) log->Printf ("Timed out responding to the control event, cancel got error: \"%s\".", error.AsCString()); } @@ -3922,8 +4232,8 @@ Process::ControlPrivateStateThread (uint32_t signal) } thread_result_t result = NULL; - Host::ThreadJoin (private_state_thread, &result, NULL); - m_private_state_thread = LLDB_INVALID_HOST_THREAD; + private_state_thread.Join(&result); + m_private_state_thread.Reset(); } } else @@ -4005,7 +4315,8 @@ Process::HandlePrivateEvent (EventSP &event_sp) { // Only push the input handler if we aren't fowarding events, // as this means the curses GUI is in use... - if (!GetTarget().GetDebugger().IsForwardingEvents()) + // Or don't push it if we are launching since it will come up stopped. + if (!GetTarget().GetDebugger().IsForwardingEvents() && new_state != eStateLaunching) PushProcessIOHandler (); m_iohandler_sync.SetValue(true, eBroadcastAlways); } @@ -4167,7 +4478,7 @@ Process::RunPrivateStateThread () m_public_run_lock.SetStopped(); m_private_state_control_wait.SetValue (true, eBroadcastAlways); - m_private_state_thread = LLDB_INVALID_HOST_THREAD; + m_private_state_thread.Reset(); return NULL; } @@ -4621,7 +4932,7 @@ class IOHandlerProcessSTDIO : public: IOHandlerProcessSTDIO (Process *process, int write_fd) : - IOHandler(process->GetTarget().GetDebugger()), + IOHandler(process->GetTarget().GetDebugger(), IOHandler::Type::ProcessIO), m_process (process), m_read_file (), m_write_file (write_fd, false), @@ -4639,9 +4950,10 @@ public: bool OpenPipes () { - if (m_pipe.IsValid()) + if (m_pipe.CanRead() && m_pipe.CanWrite()) return true; - return m_pipe.Open(); + Error result = m_pipe.CreateNew(false); + return result.Success(); } void @@ -4702,8 +5014,10 @@ public: } if (FD_ISSET (pipe_read_fd, &read_fdset)) { + size_t bytes_read; // Consume the interrupt byte - if (m_pipe.Read (&ch, 1) == 1) + Error error = m_pipe.Read(&ch, 1, bytes_read); + if (error.Success()) { switch (ch) { @@ -4751,7 +5065,8 @@ public: Cancel () { char ch = 'q'; // Send 'q' for quit - m_pipe.Write (&ch, 1); + size_t bytes_written = 0; + m_pipe.Write(&ch, 1, bytes_written); } virtual bool @@ -4765,7 +5080,9 @@ public: if (m_active) { char ch = 'i'; // Send 'i' for interrupt - return m_pipe.Write (&ch, 1) == 1; + size_t bytes_written = 0; + Error result = m_pipe.Write(&ch, 1, bytes_written); + return result.Success(); } else { @@ -4944,12 +5261,12 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, selected_tid = LLDB_INVALID_THREAD_ID; } - lldb::thread_t backup_private_state_thread = LLDB_INVALID_HOST_THREAD; - lldb::StateType old_state; + HostThread backup_private_state_thread; + lldb::StateType old_state = eStateInvalid; lldb::ThreadPlanSP stopper_base_plan_sp; Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS)); - if (Host::GetCurrentThread() == m_private_state_thread) + if (m_private_state_thread.EqualsThread(Host::GetCurrentThread())) { // Yikes, we are running on the private state thread! So we can't wait for public events on this thread, since // we are the thread that is generating public events. @@ -5554,7 +5871,7 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, } // END WAIT LOOP // If we had to start up a temporary private state thread to run this thread plan, shut it down now. - if (IS_VALID_LLDB_HOST_THREAD(backup_private_state_thread)) + if (backup_private_state_thread.IsJoinable()) { StopPrivateStateThread(); Error error; @@ -5563,8 +5880,8 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx, { thread->DiscardThreadPlansUpToPlan(stopper_base_plan_sp); } - m_public_state.SetValueNoLock(old_state); - + if (old_state != eStateInvalid) + m_public_state.SetValueNoLock(old_state); } // Restore the thread state if we are going to discard the plan execution. There are three cases where this @@ -5848,21 +6165,21 @@ Process::GetThreadStatus (Stream &strm, // ID's, and look them up one by one: uint32_t num_threads; - std::vector<uint32_t> thread_index_array; + std::vector<lldb::tid_t> thread_id_array; //Scope for thread list locker; { Mutex::Locker locker (GetThreadList().GetMutex()); ThreadList &curr_thread_list = GetThreadList(); num_threads = curr_thread_list.GetSize(); uint32_t idx; - thread_index_array.resize(num_threads); + thread_id_array.resize(num_threads); for (idx = 0; idx < num_threads; ++idx) - thread_index_array[idx] = curr_thread_list.GetThreadAtIndex(idx)->GetID(); + thread_id_array[idx] = curr_thread_list.GetThreadAtIndex(idx)->GetID(); } for (uint32_t i = 0; i < num_threads; i++) { - ThreadSP thread_sp(GetThreadList().FindThreadByID(thread_index_array[i])); + ThreadSP thread_sp(GetThreadList().FindThreadByID(thread_id_array[i])); if (thread_sp) { if (only_threads_with_stop_reason) @@ -5915,7 +6232,8 @@ Process::RunPreResumeActions () struct PreResumeCallbackAndBaton action = m_pre_resume_actions.back(); m_pre_resume_actions.pop_back(); bool this_result = action.callback (action.baton); - if (result == true) result = this_result; + if (result == true) + result = this_result; } return result; } @@ -5955,8 +6273,10 @@ Process::DidExec () m_image_tokens.clear(); m_allocated_memory_cache.Clear(); m_language_runtimes.clear(); + m_instrumentation_runtimes.clear(); m_thread_list.DiscardThreadPlans(); m_memory_cache.Clear(true); + m_stop_info_override_callback = NULL; DoDidExec(); CompleteAttach (); // Flush the process (threads and all stack frames) after running CompleteAttach() @@ -6005,11 +6325,51 @@ Process::ResolveIndirectFunction(const Address *address, Error &error) void Process::ModulesDidLoad (ModuleList &module_list) { - SystemRuntime *sys_runtime = GetSystemRuntime(); - if (sys_runtime) - { - sys_runtime->ModulesDidLoad (module_list); - } + SystemRuntime *sys_runtime = GetSystemRuntime(); + if (sys_runtime) + { + sys_runtime->ModulesDidLoad (module_list); + } + + GetJITLoaders().ModulesDidLoad (module_list); + + // Give runtimes a chance to be created. + InstrumentationRuntime::ModulesDidLoad(module_list, this, m_instrumentation_runtimes); + + // Tell runtimes about new modules. + for (auto pos = m_instrumentation_runtimes.begin(); pos != m_instrumentation_runtimes.end(); ++pos) + { + InstrumentationRuntimeSP runtime = pos->second; + runtime->ModulesDidLoad(module_list); + } + +} + +ThreadCollectionSP +Process::GetHistoryThreads(lldb::addr_t addr) +{ + ThreadCollectionSP threads; + + const MemoryHistorySP &memory_history = MemoryHistory::FindPlugin(shared_from_this()); + + if (! memory_history.get()) { + return threads; + } + + threads.reset(new ThreadCollection(memory_history->GetHistoryThreads(addr))); + + return threads; +} - GetJITLoaders().ModulesDidLoad (module_list); +InstrumentationRuntimeSP +Process::GetInstrumentationRuntime(lldb::InstrumentationRuntimeType type) +{ + InstrumentationRuntimeCollection::iterator pos; + pos = m_instrumentation_runtimes.find (type); + if (pos == m_instrumentation_runtimes.end()) + { + return InstrumentationRuntimeSP(); + } + else + return (*pos).second; } diff --git a/source/Target/ProcessLaunchInfo.cpp b/source/Target/ProcessLaunchInfo.cpp index 830f1470ed98..451c42d18bfa 100644 --- a/source/Target/ProcessLaunchInfo.cpp +++ b/source/Target/ProcessLaunchInfo.cpp @@ -9,10 +9,8 @@ #include "lldb/Host/Config.h" -#ifndef LLDB_DISABLE_POSIX -#include <spawn.h> -#endif - +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Target/FileAction.h" #include "lldb/Target/Target.h" @@ -28,32 +26,32 @@ ProcessLaunchInfo::ProcessLaunchInfo () : ProcessInfo(), m_working_dir (), m_plugin_name (), - m_shell (), m_flags (0), m_file_actions (), - m_pty (), + m_pty (new lldb_utility::PseudoTerminal), m_resume_count (0), m_monitor_callback (NULL), m_monitor_callback_baton (NULL), m_monitor_signals (false), + m_listener_sp (), m_hijack_listener_sp () { } ProcessLaunchInfo::ProcessLaunchInfo(const char *stdin_path, const char *stdout_path, const char *stderr_path, - const char *working_directory, uint32_t launch_flags) - : ProcessInfo() - , m_working_dir() - , m_plugin_name() - , m_shell() - , m_flags(launch_flags) - , m_file_actions() - , m_pty() - , m_resume_count(0) - , m_monitor_callback(NULL) - , m_monitor_callback_baton(NULL) - , m_monitor_signals(false) - , m_hijack_listener_sp() + const char *working_directory, uint32_t launch_flags) : + ProcessInfo(), + m_working_dir(), + m_plugin_name(), + m_flags(launch_flags), + m_file_actions(), + m_pty(new lldb_utility::PseudoTerminal), + m_resume_count(0), + m_monitor_callback(NULL), + m_monitor_callback_baton(NULL), + m_monitor_signals(false), + m_listener_sp (), + m_hijack_listener_sp() { if (stdin_path) { @@ -184,27 +182,23 @@ ProcessLaunchInfo::SetProcessPluginName (const char *plugin) m_plugin_name.clear(); } -const char * +const FileSpec & ProcessLaunchInfo::GetShell () const { - if (m_shell.empty()) - return NULL; - return m_shell.c_str(); + return m_shell; } void -ProcessLaunchInfo::SetShell (const char * path) +ProcessLaunchInfo::SetShell (const FileSpec &shell) { - if (path && path[0]) + m_shell = shell; + if (m_shell) { - m_shell.assign (path); + m_shell.ResolveExecutableLocation(); m_flags.Set (lldb::eLaunchFlagLaunchInShell); } else - { - m_shell.clear(); m_flags.Clear (lldb::eLaunchFlagLaunchInShell); - } } void @@ -223,10 +217,11 @@ ProcessLaunchInfo::Clear () ProcessInfo::Clear(); m_working_dir.clear(); m_plugin_name.clear(); - m_shell.clear(); + m_shell.Clear(); m_flags.Clear(); m_file_actions.clear(); m_resume_count = 0; + m_listener_sp.reset(); m_hijack_listener_sp.reset(); } @@ -266,13 +261,23 @@ ProcessLaunchInfo::SetDetachOnError (bool enable) void ProcessLaunchInfo::FinalizeFileActions (Target *target, bool default_to_use_pty) { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + // If nothing for stdin or stdout or stderr was specified, then check the process for any default // settings that were set with "settings set" - if (GetFileActionForFD(STDIN_FILENO) == NULL || GetFileActionForFD(STDOUT_FILENO) == NULL || + if (GetFileActionForFD(STDIN_FILENO) == NULL || + GetFileActionForFD(STDOUT_FILENO) == NULL || GetFileActionForFD(STDERR_FILENO) == NULL) { + if (log) + log->Printf ("ProcessLaunchInfo::%s at least one of stdin/stdout/stderr was not set, evaluating default handling", + __FUNCTION__); + if (m_flags.Test(eLaunchFlagDisableSTDIO)) { + if (log) + log->Printf ("ProcessLaunchInfo::%s eLaunchFlagDisableSTDIO set, adding suppression action for stdin, stdout and stderr", + __FUNCTION__); AppendSuppressFileAction (STDIN_FILENO , true, false); AppendSuppressFileAction (STDOUT_FILENO, false, true); AppendSuppressFileAction (STDERR_FILENO, false, true); @@ -288,34 +293,79 @@ ProcessLaunchInfo::FinalizeFileActions (Target *target, bool default_to_use_pty) FileSpec err_path; if (target) { - in_path = target->GetStandardInputPath(); - out_path = target->GetStandardOutputPath(); - err_path = target->GetStandardErrorPath(); + // Only override with the target settings if we don't already have + // an action for in, out or error + if (GetFileActionForFD(STDIN_FILENO) == NULL) + in_path = target->GetStandardInputPath(); + if (GetFileActionForFD(STDOUT_FILENO) == NULL) + out_path = target->GetStandardOutputPath(); + if (GetFileActionForFD(STDERR_FILENO) == NULL) + err_path = target->GetStandardErrorPath(); } + if (log) + log->Printf ("ProcessLaunchInfo::%s target stdin='%s', target stdout='%s', stderr='%s'", + __FUNCTION__, + in_path ? in_path.GetPath().c_str () : "<null>", + out_path ? out_path.GetPath().c_str () : "<null>", + err_path ? err_path.GetPath().c_str () : "<null>"); + char path[PATH_MAX]; if (in_path && in_path.GetPath(path, sizeof(path))) + { AppendOpenFileAction(STDIN_FILENO, path, true, false); + if (log) + log->Printf ("ProcessLaunchInfo::%s appended stdin open file action for %s", + __FUNCTION__, + in_path.GetPath().c_str ()); + } if (out_path && out_path.GetPath(path, sizeof(path))) + { AppendOpenFileAction(STDOUT_FILENO, path, false, true); + if (log) + log->Printf ("ProcessLaunchInfo::%s appended stdout open file action for %s", + __FUNCTION__, + out_path.GetPath().c_str ()); + } if (err_path && err_path.GetPath(path, sizeof(path))) + { + if (log) + log->Printf ("ProcessLaunchInfo::%s appended stderr open file action for %s", + __FUNCTION__, + err_path.GetPath().c_str ()); AppendOpenFileAction(STDERR_FILENO, path, false, true); + } + + if (default_to_use_pty && (!in_path || !out_path || !err_path)) + { + if (log) + log->Printf ("ProcessLaunchInfo::%s default_to_use_pty is set, and at least one stdin/stderr/stdout is unset, so generating a pty to use for it", + __FUNCTION__); - if (default_to_use_pty && (!in_path || !out_path || !err_path)) { - if (m_pty.OpenFirstAvailableMaster(O_RDWR| O_NOCTTY, NULL, 0)) { - const char *slave_path = m_pty.GetSlaveName(NULL, 0); + if (m_pty->OpenFirstAvailableMaster(O_RDWR| O_NOCTTY, NULL, 0)) + { + const char *slave_path = m_pty->GetSlaveName(NULL, 0); - if (!in_path) { + // Only use the slave tty if we don't have anything specified for + // input and don't have an action for stdin + if (!in_path && GetFileActionForFD(STDIN_FILENO) == NULL) + { AppendOpenFileAction(STDIN_FILENO, slave_path, true, false); } - if (!out_path) { + // Only use the slave tty if we don't have anything specified for + // output and don't have an action for stdout + if (!out_path && GetFileActionForFD(STDOUT_FILENO) == NULL) + { AppendOpenFileAction(STDOUT_FILENO, slave_path, false, true); } - if (!err_path) { + // Only use the slave tty if we don't have anything specified for + // error and don't have an action for stderr + if (!err_path && GetFileActionForFD(STDERR_FILENO) == NULL) + { AppendOpenFileAction(STDERR_FILENO, slave_path, false, true); } } @@ -336,42 +386,30 @@ ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error, if (GetFlags().Test (eLaunchFlagLaunchInShell)) { - const char *shell_executable = GetShell(); - if (shell_executable) + if (m_shell) { - char shell_resolved_path[PATH_MAX]; - - if (localhost) - { - FileSpec shell_filespec (shell_executable, true); - - if (!shell_filespec.Exists()) - { - // Resolve the path in case we just got "bash", "sh" or "tcsh" - if (!shell_filespec.ResolveExecutableLocation ()) - { - error.SetErrorStringWithFormat("invalid shell path '%s'", shell_executable); - return false; - } - } - shell_filespec.GetPath (shell_resolved_path, sizeof(shell_resolved_path)); - shell_executable = shell_resolved_path; - } + std::string shell_executable = m_shell.GetPath(); const char **argv = GetArguments().GetConstArgumentVector (); if (argv == NULL || argv[0] == NULL) return false; Args shell_arguments; std::string safe_arg; - shell_arguments.AppendArgument (shell_executable); - shell_arguments.AppendArgument ("-c"); + shell_arguments.AppendArgument (shell_executable.c_str()); + const llvm::Triple &triple = GetArchitecture().GetTriple(); + if (triple.getOS() == llvm::Triple::Win32 && !triple.isWindowsCygwinEnvironment()) + shell_arguments.AppendArgument("/C"); + else + shell_arguments.AppendArgument("-c"); + StreamString shell_command; if (will_debug) { // Add a modified PATH environment variable in case argv[0] - // is a relative path + // is a relative path. const char *argv0 = argv[0]; - if (argv0 && (argv0[0] != '/' && argv0[0] != '~')) + FileSpec arg_spec(argv0, false); + if (arg_spec.IsRelativeToCurrentWorkingDirectory()) { // We have a relative path to our executable which may not work if // we just try to run "a.out" (without it being converted to "./a.out") @@ -402,7 +440,8 @@ ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error, shell_command.PutCString(new_path.c_str()); } - shell_command.PutCString ("exec"); + if (triple.getOS() != llvm::Triple::Win32 || triple.isWindowsCygwinEnvironment()) + shell_command.PutCString("exec"); // Only Apple supports /usr/bin/arch being able to specify the architecture if (GetArchitecture().IsValid() && // Valid architecture @@ -442,7 +481,7 @@ ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error, } } shell_arguments.AppendArgument (shell_command.GetString().c_str()); - m_executable.SetFile(shell_executable, false); + m_executable = m_shell; m_arguments = shell_arguments; return true; } @@ -457,3 +496,12 @@ ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error, } return false; } + +Listener & +ProcessLaunchInfo::GetListenerForProcess (Debugger &debugger) +{ + if (m_listener_sp) + return *m_listener_sp; + else + return debugger.GetListener(); +} diff --git a/source/Target/StackFrame.cpp b/source/Target/StackFrame.cpp index e497b176ccfe..c46db4cfadf9 100644 --- a/source/Target/StackFrame.cpp +++ b/source/Target/StackFrame.cpp @@ -70,7 +70,8 @@ StackFrame::StackFrame (const ThreadSP &thread_sp, m_is_history_frame (is_history_frame), m_variable_list_sp (), m_variable_list_value_objects (), - m_disassembly () + m_disassembly (), + m_mutex (Mutex::eMutexTypeRecursive) { // If we don't have a CFA value, use the frame index for our StackID so that recursive // functions properly aren't confused with one another on a history stack. @@ -109,7 +110,8 @@ StackFrame::StackFrame (const ThreadSP &thread_sp, m_is_history_frame (false), m_variable_list_sp (), m_variable_list_value_objects (), - m_disassembly () + m_disassembly (), + m_mutex (Mutex::eMutexTypeRecursive) { if (sc_ptr != NULL) { @@ -148,7 +150,8 @@ StackFrame::StackFrame (const ThreadSP &thread_sp, m_is_history_frame (false), m_variable_list_sp (), m_variable_list_value_objects (), - m_disassembly () + m_disassembly (), + m_mutex (Mutex::eMutexTypeRecursive) { if (sc_ptr != NULL) { @@ -189,6 +192,7 @@ StackFrame::~StackFrame() StackID& StackFrame::GetStackID() { + Mutex::Locker locker(m_mutex); // Make sure we have resolved the StackID object's symbol context scope if // we already haven't looked it up. @@ -235,6 +239,7 @@ StackFrame::GetFrameIndex () const void StackFrame::SetSymbolContextScope (SymbolContextScope *symbol_scope) { + Mutex::Locker locker(m_mutex); m_flags.Set (RESOLVED_FRAME_ID_SYMBOL_SCOPE); m_id.SetSymbolContextScope (symbol_scope); } @@ -242,6 +247,7 @@ StackFrame::SetSymbolContextScope (SymbolContextScope *symbol_scope) const Address& StackFrame::GetFrameCodeAddress() { + Mutex::Locker locker(m_mutex); if (m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR) && !m_frame_code_addr.IsSectionOffset()) { m_flags.Set (RESOLVED_FRAME_CODE_ADDR); @@ -272,6 +278,7 @@ StackFrame::GetFrameCodeAddress() bool StackFrame::ChangePC (addr_t pc) { + Mutex::Locker locker(m_mutex); // We can't change the pc value of a history stack frame - it is immutable. if (m_is_history_frame) return false; @@ -287,6 +294,7 @@ StackFrame::ChangePC (addr_t pc) const char * StackFrame::Disassemble () { + Mutex::Locker locker(m_mutex); if (m_disassembly.GetSize() == 0) { ExecutionContext exe_ctx (shared_from_this()); @@ -346,6 +354,7 @@ StackFrame::GetFrameBlock () const SymbolContext& StackFrame::GetSymbolContext (uint32_t resolve_scope) { + Mutex::Locker locker(m_mutex); // Copy our internal symbol context into "sc". if ((m_flags.Get() & resolve_scope) != resolve_scope) { @@ -375,7 +384,31 @@ StackFrame::GetSymbolContext (uint32_t resolve_scope) { addr_t offset = lookup_addr.GetOffset(); if (offset > 0) + { lookup_addr.SetOffset(offset - 1); + + } + else + { + // lookup_addr is the start of a section. We need + // do the math on the actual load address and re-compute + // the section. We're working with a 'noreturn' function + // at the end of a section. + ThreadSP thread_sp (GetThread()); + if (thread_sp) + { + TargetSP target_sp (thread_sp->CalculateTarget()); + if (target_sp) + { + addr_t addr_minus_one = lookup_addr.GetLoadAddress(target_sp.get()) - 1; + lookup_addr.SetLoadAddress (addr_minus_one, target_sp.get()); + } + else + { + lookup_addr.SetOffset(offset - 1); + } + } + } } @@ -504,6 +537,7 @@ StackFrame::GetSymbolContext (uint32_t resolve_scope) VariableList * StackFrame::GetVariableList (bool get_file_globals) { + Mutex::Locker locker(m_mutex); if (m_flags.IsClear(RESOLVED_VARIABLES)) { m_flags.Set(RESOLVED_VARIABLES); @@ -544,6 +578,7 @@ StackFrame::GetVariableList (bool get_file_globals) VariableListSP StackFrame::GetInScopeVariableList (bool get_file_globals) { + Mutex::Locker locker(m_mutex); // We can't fetch variable information for a history stack frame. if (m_is_history_frame) return VariableListSP(); @@ -680,8 +715,8 @@ StackFrame::GetValueForVariableExpressionPath (const char *var_expr_cstr, // Make sure we aren't trying to deref an objective // C ivar if this is not allowed const uint32_t pointer_type_flags = valobj_sp->GetClangType().GetTypeInfo (NULL); - if ((pointer_type_flags & ClangASTType::eTypeIsObjC) && - (pointer_type_flags & ClangASTType::eTypeIsPointer)) + if ((pointer_type_flags & eTypeIsObjC) && + (pointer_type_flags & eTypeIsPointer)) { // This was an objective C object pointer and // it was requested we skip any fragile ivars @@ -1142,6 +1177,7 @@ StackFrame::GetValueForVariableExpressionPath (const char *var_expr_cstr, bool StackFrame::GetFrameBaseValue (Scalar &frame_base, Error *error_ptr) { + Mutex::Locker locker(m_mutex); if (m_cfa_is_valid == false) { m_frame_base_error.SetErrorString("No frame base available for this historical stack frame."); @@ -1191,6 +1227,7 @@ StackFrame::GetFrameBaseValue (Scalar &frame_base, Error *error_ptr) RegisterContextSP StackFrame::GetRegisterContext () { + Mutex::Locker locker(m_mutex); if (!m_reg_context_sp) { ThreadSP thread_sp (GetThread()); @@ -1211,6 +1248,7 @@ StackFrame::HasDebugInformation () ValueObjectSP StackFrame::GetValueObjectForFrameVariable (const VariableSP &variable_sp, DynamicValueType use_dynamic) { + Mutex::Locker locker(m_mutex); ValueObjectSP valobj_sp; if (m_is_history_frame) { @@ -1246,6 +1284,7 @@ StackFrame::GetValueObjectForFrameVariable (const VariableSP &variable_sp, Dynam ValueObjectSP StackFrame::TrackGlobalVariable (const VariableSP &variable_sp, DynamicValueType use_dynamic) { + Mutex::Locker locker(m_mutex); if (m_is_history_frame) return ValueObjectSP(); @@ -1365,17 +1404,20 @@ StackFrame::Dump (Stream *strm, bool show_frame_index, bool show_fullpaths) GetSymbolContext(eSymbolContextEverything); const bool show_module = true; const bool show_inline = true; + const bool show_function_arguments = true; m_sc.DumpStopContext (strm, exe_ctx.GetBestExecutionContextScope(), GetFrameCodeAddress(), show_fullpaths, show_module, - show_inline); + show_inline, + show_function_arguments); } void StackFrame::UpdateCurrentFrameFromPreviousFrame (StackFrame &prev_frame) { + Mutex::Locker locker(m_mutex); assert (GetStackID() == prev_frame.GetStackID()); // TODO: remove this after some testing m_variable_list_sp = prev_frame.m_variable_list_sp; m_variable_list_value_objects.Swap (prev_frame.m_variable_list_value_objects); @@ -1387,6 +1429,7 @@ StackFrame::UpdateCurrentFrameFromPreviousFrame (StackFrame &prev_frame) void StackFrame::UpdatePreviousFrameFromCurrentFrame (StackFrame &curr_frame) { + Mutex::Locker locker(m_mutex); assert (GetStackID() == curr_frame.GetStackID()); // TODO: remove this after some testing m_id.SetPC (curr_frame.m_id.GetPC()); // Update the Stack ID PC value assert (GetThread() == curr_frame.GetThread()); diff --git a/source/Target/StackFrameList.cpp b/source/Target/StackFrameList.cpp index 99234dc61f1d..9a132618288d 100644 --- a/source/Target/StackFrameList.cpp +++ b/source/Target/StackFrameList.cpp @@ -288,8 +288,8 @@ StackFrameList::GetFramesUpTo(uint32_t end_idx) do { uint32_t idx = m_concrete_frames_fetched++; - lldb::addr_t pc; - lldb::addr_t cfa; + lldb::addr_t pc = LLDB_INVALID_ADDRESS; + lldb::addr_t cfa = LLDB_INVALID_ADDRESS; if (idx == 0) { // We might have already created frame zero, only create it @@ -625,11 +625,14 @@ StackFrameList::GetFrameWithStackID (const StackID &stack_id) if (begin != end) { collection::const_iterator pos = std::lower_bound (begin, end, stack_id, CompareStackID); - if (pos != end && (*pos)->GetStackID() == stack_id) - return *pos; + if (pos != end) + { + if ((*pos)->GetStackID() == stack_id) + return *pos; + } - if (m_frames.back()->GetStackID() < stack_id) - frame_idx = m_frames.size(); +// if (m_frames.back()->GetStackID() < stack_id) +// frame_idx = m_frames.size(); } do { diff --git a/source/Target/StopInfo.cpp b/source/Target/StopInfo.cpp index a37a4079ff11..4b57ca65a2df 100644 --- a/source/Target/StopInfo.cpp +++ b/source/Target/StopInfo.cpp @@ -40,7 +40,8 @@ StopInfo::StopInfo (Thread &thread, uint64_t value) : m_resume_id (thread.GetProcess()->GetResumeID()), m_value (value), m_override_should_notify (eLazyBoolCalculate), - m_override_should_stop (eLazyBoolCalculate) + m_override_should_stop (eLazyBoolCalculate), + m_extended_info() { } @@ -181,6 +182,7 @@ public: { ExecutionContext exe_ctx (thread_sp->GetStackFrameAtIndex(0)); StoppointCallbackContext context (event_ptr, exe_ctx, true); + bp_site_sp->BumpHitCounts(); m_should_stop = bp_site_sp->ShouldStop (&context); } else @@ -402,10 +404,21 @@ protected: // Let's copy the breakpoint locations out of the site and store them in a local list. That way if // one of the breakpoint actions changes the site, then we won't be operating on a bad list. + // For safety's sake let's also grab an extra reference to the breakpoint owners of the locations we're + // going to examine, since the locations are going to have to get back to their breakpoints, and the + // locations don't keep their owners alive. I'm just sticking the BreakpointSP's in a vector since + // I'm only really using it to locally increment their retain counts. BreakpointLocationCollection site_locations; + std::vector<lldb::BreakpointSP> location_owners; + for (size_t j = 0; j < num_owners; j++) - site_locations.Add(bp_site_sp->GetOwnerAtIndex(j)); + { + BreakpointLocationSP loc(bp_site_sp->GetOwnerAtIndex(j)); + site_locations.Add(loc); + location_owners.push_back(loc->GetBreakpoint().shared_from_this()); + + } for (size_t j = 0; j < num_owners; j++) { @@ -689,14 +702,13 @@ protected: assert (stored_stop_info_sp.get() == this); ThreadPlanSP new_plan_sp(thread_sp->QueueThreadPlanForStepSingleInstruction(false, // step-over - false, // abort_other_plans - true)); // stop_other_threads + false, // abort_other_plans + true)); // stop_other_threads new_plan_sp->SetIsMasterPlan (true); new_plan_sp->SetOkayToDiscard (false); new_plan_sp->SetPrivate (true); process->GetThreadList().SetSelectedThreadByID (thread_sp->GetID()); - process->Resume (); - process->WaitForProcessToStop (NULL); + process->ResumeSynchronous(NULL); process->GetThreadList().SetSelectedThreadByID (thread_sp->GetID()); thread_sp->SetStopInfo(stored_stop_info_sp); } diff --git a/source/Target/Target.cpp b/source/Target/Target.cpp index d2d0b5098555..e9393d1be0b4 100644 --- a/source/Target/Target.cpp +++ b/source/Target/Target.cpp @@ -35,6 +35,7 @@ #include "lldb/Core/ValueObject.h" #include "lldb/Expression/ClangASTSource.h" #include "lldb/Expression/ClangUserExpression.h" +#include "lldb/Host/FileSpec.h" #include "lldb/Host/Host.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" @@ -63,7 +64,7 @@ Target::GetStaticBroadcasterClass () //---------------------------------------------------------------------- // Target constructor //---------------------------------------------------------------------- -Target::Target(Debugger &debugger, const ArchSpec &target_arch, const lldb::PlatformSP &platform_sp) : +Target::Target(Debugger &debugger, const ArchSpec &target_arch, const lldb::PlatformSP &platform_sp, bool is_dummy_target) : TargetProperties (this), Broadcaster (&debugger, Target::GetStaticBroadcasterClass().AsCString()), ExecutionContextScope (), @@ -87,7 +88,9 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch, const lldb::Plat m_stop_hooks (), m_stop_hook_next_id (0), m_valid (true), - m_suppress_stop_hooks (false) + m_suppress_stop_hooks (false), + m_is_dummy_target(is_dummy_target) + { SetEventName (eBroadcastBitBreakpointChanged, "breakpoint-changed"); SetEventName (eBroadcastBitModulesLoaded, "modules-loaded"); @@ -106,6 +109,24 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch, const lldb::Plat } } +void +Target::PrimeFromDummyTarget(Target *target) +{ + if (!target) + return; + + m_stop_hooks = target->m_stop_hooks; + + for (BreakpointSP breakpoint_sp : target->m_breakpoint_list.Breakpoints()) + { + if (breakpoint_sp->IsInternal()) + continue; + + BreakpointSP new_bp (new Breakpoint (*this, *breakpoint_sp.get())); + AddBreakpoint (new_bp, false); + } +} + //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- @@ -330,7 +351,7 @@ Target::CreateBreakpoint (lldb::addr_t addr, bool internal, bool hardware) BreakpointSP Target::CreateBreakpoint (Address &addr, bool internal, bool hardware) { - SearchFilterSP filter_sp(new SearchFilterForNonModuleSpecificSearches (shared_from_this())); + SearchFilterSP filter_sp(new SearchFilterForUnconstrainedSearches (shared_from_this())); BreakpointResolverSP resolver_sp (new BreakpointResolverAddress (NULL, addr)); return CreateBreakpoint (filter_sp, resolver_sp, internal, hardware, false); } @@ -430,7 +451,7 @@ Target::GetSearchFilterForModule (const FileSpec *containingModule) else { if (m_search_filter_sp.get() == NULL) - m_search_filter_sp.reset (new SearchFilterForNonModuleSpecificSearches (shared_from_this())); + m_search_filter_sp.reset (new SearchFilterForUnconstrainedSearches (shared_from_this())); filter_sp = m_search_filter_sp; } return filter_sp; @@ -449,7 +470,7 @@ Target::GetSearchFilterForModuleList (const FileSpecList *containingModules) else { if (m_search_filter_sp.get() == NULL) - m_search_filter_sp.reset (new SearchFilterForNonModuleSpecificSearches (shared_from_this())); + m_search_filter_sp.reset (new SearchFilterForUnconstrainedSearches (shared_from_this())); filter_sp = m_search_filter_sp; } return filter_sp; @@ -510,29 +531,35 @@ Target::CreateBreakpoint (SearchFilterSP &filter_sp, BreakpointResolverSP &resol { bp_sp.reset(new Breakpoint (*this, filter_sp, resolver_sp, request_hardware, resolve_indirect_symbols)); resolver_sp->SetBreakpoint (bp_sp.get()); + AddBreakpoint (bp_sp, internal); + } + return bp_sp; +} - if (internal) - m_internal_breakpoint_list.Add (bp_sp, false); - else - m_breakpoint_list.Add (bp_sp, true); - - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); - if (log) - { - StreamString s; - bp_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); - log->Printf ("Target::%s (internal = %s) => break_id = %s\n", __FUNCTION__, internal ? "yes" : "no", s.GetData()); - } +void +Target::AddBreakpoint (lldb::BreakpointSP bp_sp, bool internal) +{ + if (!bp_sp) + return; + if (internal) + m_internal_breakpoint_list.Add (bp_sp, false); + else + m_breakpoint_list.Add (bp_sp, true); - bp_sp->ResolveBreakpoint(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + { + StreamString s; + bp_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); + log->Printf ("Target::%s (internal = %s) => break_id = %s\n", __FUNCTION__, bp_sp->IsInternal() ? "yes" : "no", s.GetData()); } - - if (!internal && bp_sp) + + bp_sp->ResolveBreakpoint(); + + if (!internal) { m_last_created_breakpoint = bp_sp; } - - return bp_sp; } bool @@ -1214,6 +1241,7 @@ Target::ModulesDidUnload (ModuleList &module_list, bool delete_locations) { if (m_valid && module_list.GetSize()) { + UnloadModuleSections (module_list); m_breakpoint_list.UpdateBreakpoints (module_list, false, delete_locations); // TODO: make event data that packages up the module_list BroadcastEvent (eBroadcastBitModulesUnloaded, NULL); @@ -1221,7 +1249,7 @@ Target::ModulesDidUnload (ModuleList &module_list, bool delete_locations) } bool -Target::ModuleIsExcludedForNonModuleSpecificSearches (const FileSpec &module_file_spec) +Target::ModuleIsExcludedForUnconstrainedSearches (const FileSpec &module_file_spec) { if (GetBreakpointsConsultPlatformAvoidList()) { @@ -1235,7 +1263,7 @@ Target::ModuleIsExcludedForNonModuleSpecificSearches (const FileSpec &module_fil { for (size_t i = 0; i < num_modules; i++) { - if (!ModuleIsExcludedForNonModuleSpecificSearches (matchingModules.GetModuleAtIndex(i))) + if (!ModuleIsExcludedForUnconstrainedSearches (matchingModules.GetModuleAtIndex(i))) return false; } return true; @@ -1245,12 +1273,12 @@ Target::ModuleIsExcludedForNonModuleSpecificSearches (const FileSpec &module_fil } bool -Target::ModuleIsExcludedForNonModuleSpecificSearches (const lldb::ModuleSP &module_sp) +Target::ModuleIsExcludedForUnconstrainedSearches (const lldb::ModuleSP &module_sp) { if (GetBreakpointsConsultPlatformAvoidList()) { if (m_platform_sp) - return m_platform_sp->ModuleIsExcludedForNonModuleSpecificSearches (*this, module_sp); + return m_platform_sp->ModuleIsExcludedForUnconstrainedSearches (*this, module_sp); } return false; } @@ -1355,9 +1383,9 @@ Target::ReadMemory (const Address& addr, ModuleSP addr_module_sp (resolved_addr.GetModule()); if (addr_module_sp && addr_module_sp->GetFileSpec()) error.SetErrorStringWithFormat("%s[0x%" PRIx64 "] can't be resolved, %s in not currently loaded", - addr_module_sp->GetFileSpec().GetFilename().AsCString(), + addr_module_sp->GetFileSpec().GetFilename().AsCString("<Unknown>"), resolved_addr.GetFileAddress(), - addr_module_sp->GetFileSpec().GetFilename().AsCString()); + addr_module_sp->GetFileSpec().GetFilename().AsCString("<Unknonw>")); else error.SetErrorStringWithFormat("0x%" PRIx64 " can't be resolved", resolved_addr.GetFileAddress()); } @@ -1436,7 +1464,12 @@ Target::ReadCStringFromMemory (const Address& addr, char *dst, size_t dst_max_le Error error; addr_t curr_addr = addr.GetLoadAddress(this); Address address(addr); + + // We could call m_process_sp->GetMemoryCacheLineSize() but I don't + // think this really needs to be tied to the memory cache subsystem's + // cache line size, so leave this as a fixed constant. const size_t cache_line_size = 512; + size_t bytes_left = dst_max_len - 1; char *curr_dst = dst; @@ -2006,6 +2039,22 @@ Target::GetSourceManager () return *m_source_manager_ap; } +ClangModulesDeclVendor * +Target::GetClangModulesDeclVendor () +{ + static Mutex s_clang_modules_decl_vendor_mutex; // If this is contended we can make it per-target + + { + Mutex::Locker clang_modules_decl_vendor_locker(s_clang_modules_decl_vendor_mutex); + + if (!m_clang_modules_decl_vendor_ap) + { + m_clang_modules_decl_vendor_ap.reset(ClangModulesDeclVendor::Create(*this)); + } + } + + return m_clang_modules_decl_vendor_ap.get(); +} Target::StopHookSP Target::CreateStopHook () @@ -2173,18 +2222,17 @@ Target::RunStopHooks () if (print_thread_header) result.AppendMessageWithFormat("-- Thread %d\n", exc_ctx_with_reasons[i].GetThreadPtr()->GetIndexID()); - - bool stop_on_continue = true; - bool stop_on_error = true; - bool echo_commands = false; - bool print_results = true; - GetDebugger().GetCommandInterpreter().HandleCommands (cur_hook_sp->GetCommands(), - &exc_ctx_with_reasons[i], - stop_on_continue, - stop_on_error, - echo_commands, - print_results, - eLazyBoolNo, + + CommandInterpreterRunOptions options; + options.SetStopOnContinue (true); + options.SetStopOnError (true); + options.SetEchoCommands (false); + options.SetPrintResults (true); + options.SetAddToHistory (false); + + GetDebugger().GetCommandInterpreter().HandleCommands (cur_hook_sp->GetCommands(), + &exc_ctx_with_reasons[i], + options, result); // If the command started the target going again, we should bag out of @@ -2279,6 +2327,12 @@ Target::ResolveLoadAddress (addr_t load_addr, Address &so_addr, uint32_t stop_id } bool +Target::ResolveFileAddress (lldb::addr_t file_addr, Address &resolved_addr) +{ + return m_images.ResolveFileAddress(file_addr, resolved_addr); +} + +bool Target::SetSectionLoadAddress (const SectionSP §ion_sp, addr_t new_section_load_addr, bool warn_multiple) { const addr_t old_section_load_addr = m_section_load_history.GetSectionLoadAddress (SectionLoadHistory::eStopIDNow, section_sp); @@ -2297,6 +2351,40 @@ Target::SetSectionLoadAddress (const SectionSP §ion_sp, addr_t new_section_l } +size_t +Target::UnloadModuleSections (const ModuleList &module_list) +{ + size_t section_unload_count = 0; + size_t num_modules = module_list.GetSize(); + for (size_t i=0; i<num_modules; ++i) + { + section_unload_count += UnloadModuleSections (module_list.GetModuleAtIndex(i)); + } + return section_unload_count; +} + +size_t +Target::UnloadModuleSections (const lldb::ModuleSP &module_sp) +{ + uint32_t stop_id = 0; + ProcessSP process_sp(GetProcessSP()); + if (process_sp) + stop_id = process_sp->GetStopID(); + else + stop_id = m_section_load_history.GetLastStopID(); + SectionList *sections = module_sp->GetSectionList(); + size_t section_unload_count = 0; + if (sections) + { + const uint32_t num_sections = sections->GetNumSections(0); + for (uint32_t i = 0; i < num_sections; ++i) + { + section_unload_count += m_section_load_history.SetSectionUnloaded(stop_id, sections->GetSectionAtIndex(i)); + } + } + return section_unload_count; +} + bool Target::SetSectionUnloaded (const lldb::SectionSP §ion_sp) { @@ -2329,10 +2417,14 @@ Target::ClearAllLoadedSections () Error -Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) +Target::Launch (ProcessLaunchInfo &launch_info, Stream *stream) { Error error; - + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TARGET)); + + if (log) + log->Printf ("Target::%s() called for %s", __FUNCTION__, launch_info.GetExecutableFile().GetPath().c_str ()); + StateType state = eStateInvalid; // Scope to temporarily get the process state in case someone has manually @@ -2342,7 +2434,16 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) ProcessSP process_sp (GetProcessSP()); if (process_sp) + { state = process_sp->GetState(); + if (log) + log->Printf ("Target::%s the process exists, and its current state is %s", __FUNCTION__, StateAsCString (state)); + } + else + { + if (log) + log->Printf ("Target::%s the process instance doesn't currently exist.", __FUNCTION__); + } } launch_info.GetFlags().Set (eLaunchFlagDebug); @@ -2358,6 +2459,13 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) // Finalize the file actions, and if none were given, default to opening // up a pseudo terminal const bool default_to_use_pty = platform_sp ? platform_sp->IsHost() : false; + if (log) + log->Printf ("Target::%s have platform=%s, platform_sp->IsHost()=%s, default_to_use_pty=%s", + __FUNCTION__, + platform_sp ? "true" : "false", + platform_sp ? (platform_sp->IsHost () ? "true" : "false") : "n/a", + default_to_use_pty ? "true" : "false"); + launch_info.FinalizeFileActions (this, default_to_use_pty); if (state == eStateConnected) @@ -2375,14 +2483,19 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) // If we're not already connected to the process, and if we have a platform that can launch a process for debugging, go ahead and do that here. if (state != eStateConnected && platform_sp && platform_sp->CanDebugProcess ()) { + if (log) + log->Printf ("Target::%s asking the platform to debug the process", __FUNCTION__); + m_process_sp = GetPlatform()->DebugProcess (launch_info, debugger, this, - listener, error); } else { + if (log) + log->Printf ("Target::%s the platform doesn't know how to debug a process, getting a process plugin to do this for us.", __FUNCTION__); + if (state == eStateConnected) { assert(m_process_sp); @@ -2391,7 +2504,7 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) { // Use a Process plugin to construct the process. const char *plugin_name = launch_info.GetProcessPluginName(); - CreateProcess (listener, plugin_name, NULL); + CreateProcess (launch_info.GetListenerForProcess(debugger), plugin_name, NULL); } // Since we didn't have a platform launch the process, launch it here. @@ -2411,8 +2524,8 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) if (launch_info.GetFlags().Test(eLaunchFlagStopAtEntry) == false) { ListenerSP hijack_listener_sp (launch_info.GetHijackListener()); - - StateType state = m_process_sp->WaitForProcessToStop (NULL, NULL, false, hijack_listener_sp.get()); + + StateType state = m_process_sp->WaitForProcessToStop (NULL, NULL, false, hijack_listener_sp.get(), NULL); if (state == eStateStopped) { @@ -2430,7 +2543,7 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) if (synchronous_execution) { - state = m_process_sp->WaitForProcessToStop (NULL, NULL, true, hijack_listener_sp.get()); + state = m_process_sp->WaitForProcessToStop (NULL, NULL, true, hijack_listener_sp.get(), stream); const bool must_be_alive = false; // eStateExited is ok, so this must be false if (!StateIsStoppedState(state, must_be_alive)) { @@ -2447,7 +2560,7 @@ Target::Launch (Listener &listener, ProcessLaunchInfo &launch_info) } else if (state == eStateExited) { - bool with_shell = launch_info.GetShell(); + bool with_shell = !!launch_info.GetShell(); const int exit_status = m_process_sp->GetExitStatus(); const char *exit_desc = m_process_sp->GetExitDescription(); #define LAUNCH_SHELL_MESSAGE "\n'r' and 'run' are aliases that default to launching through a shell.\nTry launching without going through a shell by using 'process launch'." @@ -2632,7 +2745,7 @@ g_properties[] = { { "default-arch" , OptionValue::eTypeArch , true , 0 , NULL, NULL, "Default architecture to choose, when there's a choice." }, { "expr-prefix" , OptionValue::eTypeFileSpec , false, 0 , NULL, NULL, "Path to a file containing expressions to be prepended to all expressions." }, - { "prefer-dynamic-value" , OptionValue::eTypeEnum , false, eNoDynamicValues , NULL, g_dynamic_value_types, "Should printed values be shown as their dynamic value." }, + { "prefer-dynamic-value" , OptionValue::eTypeEnum , false, eDynamicDontRunTarget , NULL, g_dynamic_value_types, "Should printed values be shown as their dynamic value." }, { "enable-synthetic-value" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "Should synthetic values be used by default whenever available." }, { "skip-prologue" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "Skip function prologues when setting breakpoints by name." }, { "source-map" , OptionValue::eTypePathMap , false, 0 , NULL, NULL, "Source path remappings used to track the change of location between a source file when built, and " @@ -2656,12 +2769,13 @@ g_properties[] = { "detach-on-error" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "debugserver will detach (rather than killing) a process if it loses connection with lldb." }, { "disable-aslr" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "Disable Address Space Layout Randomization (ASLR)" }, { "disable-stdio" , OptionValue::eTypeBoolean , false, false , NULL, NULL, "Disable stdin/stdout for process (e.g. for a GUI application)" }, - { "inline-breakpoint-strategy" , OptionValue::eTypeEnum , false, eInlineBreakpointsHeaders , NULL, g_inline_breakpoint_enums, "The strategy to use when settings breakpoints by file and line. " + { "inline-breakpoint-strategy" , OptionValue::eTypeEnum , false, eInlineBreakpointsAlways , NULL, g_inline_breakpoint_enums, "The strategy to use when settings breakpoints by file and line. " "Breakpoint locations can end up being inlined by the compiler, so that a compile unit 'a.c' might contain an inlined function from another source file. " "Usually this is limitted to breakpoint locations from inlined functions from header or other include files, or more accurately non-implementation source files. " "Sometimes code might #include implementation files and cause inlined breakpoint locations in inlined implementation files. " - "Always checking for inlined breakpoint locations can be expensive (memory and time), so we try to minimize the " - "times we look for inlined locations. This setting allows you to control exactly which strategy is used when settings " + "Always checking for inlined breakpoint locations can be expensive (memory and time), so if you have a project with many headers " + "and find that setting breakpoints is slow, then you can change this setting to headers. " + "This setting allows you to control exactly which strategy is used when setting " "file and line breakpoints." }, // FIXME: This is the wrong way to do per-architecture settings, but we don't have a general per architecture settings system in place yet. { "x86-disassembly-flavor" , OptionValue::eTypeEnum , false, eX86DisFlavorDefault, NULL, g_x86_dis_flavor_value_types, "The default disassembly flavor to use for x86 or x86-64 targets." }, diff --git a/source/Target/TargetList.cpp b/source/Target/TargetList.cpp index 5ee75ff74449..28ad47e3d81d 100644 --- a/source/Target/TargetList.cpp +++ b/source/Target/TargetList.cpp @@ -21,6 +21,7 @@ #include "lldb/Core/State.h" #include "lldb/Core/Timer.h" #include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/OptionGroupPlatform.h" #include "lldb/Symbol/ObjectFile.h" @@ -69,6 +70,41 @@ TargetList::CreateTarget (Debugger &debugger, const OptionGroupPlatform *platform_options, TargetSP &target_sp) { + return CreateTargetInternal (debugger, + user_exe_path, + triple_cstr, + get_dependent_files, + platform_options, + target_sp, + false); +} + +Error +TargetList::CreateTarget (Debugger &debugger, + const char *user_exe_path, + const ArchSpec& specified_arch, + bool get_dependent_files, + PlatformSP &platform_sp, + TargetSP &target_sp) +{ + return CreateTargetInternal (debugger, + user_exe_path, + specified_arch, + get_dependent_files, + platform_sp, + target_sp, + false); +} + +Error +TargetList::CreateTargetInternal (Debugger &debugger, + const char *user_exe_path, + const char *triple_cstr, + bool get_dependent_files, + const OptionGroupPlatform *platform_options, + TargetSP &target_sp, + bool is_dummy_target) +{ Error error; PlatformSP platform_sp; @@ -170,7 +206,7 @@ TargetList::CreateTarget (Debugger &debugger, typedef std::vector<PlatformSP> PlatformList; PlatformList platforms; - PlatformSP host_platform_sp = Platform::GetDefaultPlatform(); + PlatformSP host_platform_sp = Platform::GetHostPlatform(); for (size_t i=0; i<num_specs; ++i) { ModuleSpec module_spec; @@ -258,7 +294,11 @@ TargetList::CreateTarget (Debugger &debugger, if (!prefer_platform_arch && arch.IsValid()) { if (!platform_sp->IsCompatibleArchitecture(arch, false, &platform_arch)) + { platform_sp = Platform::GetPlatformForArchitecture(arch, &platform_arch); + if (platform_sp) + debugger.GetPlatformList().SetSelectedPlatform(platform_sp); + } } else if (platform_arch.IsValid()) { @@ -266,30 +306,69 @@ TargetList::CreateTarget (Debugger &debugger, // a single architecture which should be used ArchSpec fixed_platform_arch; if (!platform_sp->IsCompatibleArchitecture(platform_arch, false, &fixed_platform_arch)) + { platform_sp = Platform::GetPlatformForArchitecture(platform_arch, &fixed_platform_arch); + if (platform_sp) + debugger.GetPlatformList().SetSelectedPlatform(platform_sp); + } } } if (!platform_arch.IsValid()) platform_arch = arch; - error = TargetList::CreateTarget (debugger, - user_exe_path, - platform_arch, - get_dependent_files, - platform_sp, - target_sp); + error = TargetList::CreateTargetInternal (debugger, + user_exe_path, + platform_arch, + get_dependent_files, + platform_sp, + target_sp, + is_dummy_target); return error; } +lldb::TargetSP +TargetList::GetDummyTarget (lldb_private::Debugger &debugger) +{ + // FIXME: Maybe the dummy target should be per-Debugger + if (!m_dummy_target_sp || !m_dummy_target_sp->IsValid()) + { + ArchSpec arch(Target::GetDefaultArchitecture()); + if (!arch.IsValid()) + arch = HostInfo::GetArchitecture(); + Error err = CreateDummyTarget(debugger, + arch.GetTriple().getTriple().c_str(), + m_dummy_target_sp); + } + + return m_dummy_target_sp; +} + Error -TargetList::CreateTarget (Debugger &debugger, - const char *user_exe_path, - const ArchSpec& specified_arch, - bool get_dependent_files, - PlatformSP &platform_sp, - TargetSP &target_sp) +TargetList::CreateDummyTarget (Debugger &debugger, + const char *specified_arch_name, + lldb::TargetSP &target_sp) { + PlatformSP host_platform_sp(Platform::GetHostPlatform()); + return CreateTargetInternal (debugger, + (const char *) nullptr, + specified_arch_name, + false, + (const OptionGroupPlatform *) nullptr, + target_sp, + true); +} + +Error +TargetList::CreateTargetInternal (Debugger &debugger, + const char *user_exe_path, + const ArchSpec& specified_arch, + bool get_dependent_files, + lldb::PlatformSP &platform_sp, + lldb::TargetSP &target_sp, + bool is_dummy_target) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, "TargetList::CreateTarget (file = '%s', arch = '%s')", user_exe_path, @@ -332,7 +411,7 @@ TargetList::CreateTarget (Debugger &debugger, if (file.GetFileType() == FileSpec::eFileTypeDirectory) user_exe_path_is_bundle = true; - if (file.IsRelativeToCurrentWorkingDirectory()) + if (file.IsRelativeToCurrentWorkingDirectory() && user_exe_path) { // Ignore paths that start with "./" and "../" if (!((user_exe_path[0] == '.' && user_exe_path[1] == '/') || @@ -355,8 +434,8 @@ TargetList::CreateTarget (Debugger &debugger, if (platform_sp) { FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths()); - error = platform_sp->ResolveExecutable (file, - arch, + ModuleSpec module_spec(file, arch); + error = platform_sp->ResolveExecutable (module_spec, exe_module_sp, executable_search_paths.GetSize() ? &executable_search_paths : NULL); } @@ -378,7 +457,7 @@ TargetList::CreateTarget (Debugger &debugger, } return error; } - target_sp.reset(new Target(debugger, arch, platform_sp)); + target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target)); target_sp->SetExecutableModule (exe_module_sp, get_dependent_files); if (user_exe_path_is_bundle) exe_module_sp->GetFileSpec().GetPath(resolved_bundle_exe_path, sizeof(resolved_bundle_exe_path)); @@ -388,7 +467,7 @@ TargetList::CreateTarget (Debugger &debugger, { // No file was specified, just create an empty target with any arch // if a valid arch was specified - target_sp.reset(new Target(debugger, arch, platform_sp)); + target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target)); } if (target_sp) @@ -415,11 +494,20 @@ TargetList::CreateTarget (Debugger &debugger, file_dir.GetDirectory() = file.GetDirectory(); target_sp->GetExecutableSearchPaths ().Append (file_dir); } - Mutex::Locker locker(m_target_list_mutex); - m_selected_target_idx = m_target_list.size(); - m_target_list.push_back(target_sp); - - + + // Don't put the dummy target in the target list, it's held separately. + if (!is_dummy_target) + { + Mutex::Locker locker(m_target_list_mutex); + m_selected_target_idx = m_target_list.size(); + m_target_list.push_back(target_sp); + // Now prime this from the dummy target: + target_sp->PrimeFromDummyTarget(debugger.GetDummyTarget()); + } + else + { + m_dummy_target_sp = target_sp; + } } return error; diff --git a/source/Target/Thread.cpp b/source/Target/Thread.cpp index a445517da6a8..b532d8d71c8f 100644 --- a/source/Target/Thread.cpp +++ b/source/Target/Thread.cpp @@ -32,6 +32,7 @@ #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanBase.h" +#include "lldb/Target/ThreadPlanPython.h" #include "lldb/Target/ThreadPlanStepInstruction.h" #include "lldb/Target/ThreadPlanStepOut.h" #include "lldb/Target/ThreadPlanStepOverBreakpoint.h" @@ -43,7 +44,7 @@ #include "lldb/Target/ThreadSpec.h" #include "lldb/Target/Unwind.h" #include "Plugins/Process/Utility/UnwindLLDB.h" -#include "UnwindMacOSXFrameBackchain.h" +#include "Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h" using namespace lldb; @@ -278,6 +279,7 @@ Thread::Thread (Process &process, lldb::tid_t tid, bool use_invalid_index_id) : m_process_wp (process.shared_from_this()), m_stop_info_sp (), m_stop_info_stop_id (0), + m_stop_info_override_stop_id (0), m_index_id (use_invalid_index_id ? LLDB_INVALID_INDEX32 : process.GetNextThreadIndexID(tid)), m_reg_context_sp (), m_state (eStateUnloaded), @@ -465,6 +467,24 @@ Thread::GetPrivateStopInfo () SetStopInfo (StopInfoSP()); } } + + // The stop info can be manually set by calling Thread::SetStopInfo() + // prior to this function ever getting called, so we can't rely on + // "m_stop_info_stop_id != process_stop_id" as the condition for + // the if statement below, we must also check the stop info to see + // if we need to override it. See the header documentation in + // Process::GetStopInfoOverrideCallback() for more information on + // the stop info override callback. + if (m_stop_info_override_stop_id != process_stop_id) + { + m_stop_info_override_stop_id = process_stop_id; + if (m_stop_info_sp) + { + ArchSpec::StopInfoOverrideCallbackType callback = GetProcess()->GetStopInfoOverrideCallback(); + if (callback) + callback(*this); + } + } } return m_stop_info_sp; } @@ -642,7 +662,8 @@ Thread::SetupForResume () lldb::RegisterContextSP reg_ctx_sp (GetRegisterContext()); if (reg_ctx_sp) { - BreakpointSiteSP bp_site_sp = GetProcess()->GetBreakpointSiteList().FindByAddress(reg_ctx_sp->GetPC()); + const addr_t thread_pc = reg_ctx_sp->GetPC(); + BreakpointSiteSP bp_site_sp = GetProcess()->GetBreakpointSiteList().FindByAddress(thread_pc); if (bp_site_sp) { // Note, don't assume there's a ThreadPlanStepOverBreakpoint, the target may not require anything @@ -650,19 +671,30 @@ Thread::SetupForResume () ThreadPlan *cur_plan = GetCurrentPlan(); - if (cur_plan->GetKind() != ThreadPlan::eKindStepOverBreakpoint) + bool push_step_over_bp_plan = false; + if (cur_plan->GetKind() == ThreadPlan::eKindStepOverBreakpoint) { - ThreadPlanStepOverBreakpoint *step_bp_plan = new ThreadPlanStepOverBreakpoint (*this); - if (step_bp_plan) + ThreadPlanStepOverBreakpoint *bp_plan = (ThreadPlanStepOverBreakpoint *)cur_plan; + if (bp_plan->GetBreakpointLoadAddress() != thread_pc) + push_step_over_bp_plan = true; + } + else + push_step_over_bp_plan = true; + + if (push_step_over_bp_plan) + { + ThreadPlanSP step_bp_plan_sp (new ThreadPlanStepOverBreakpoint (*this)); + if (step_bp_plan_sp) { - ThreadPlanSP step_bp_plan_sp; - step_bp_plan->SetPrivate (true); + ; + step_bp_plan_sp->SetPrivate (true); if (GetCurrentPlan()->RunState() != eStateStepping) { + ThreadPlanStepOverBreakpoint *step_bp_plan + = static_cast<ThreadPlanStepOverBreakpoint *>(step_bp_plan_sp.get()); step_bp_plan->SetAutoContinue(true); } - step_bp_plan_sp.reset (step_bp_plan); QueueThreadPlan (step_bp_plan_sp, false); } } @@ -941,30 +973,30 @@ Thread::ShouldStop (Event* event_ptr) if (over_ride_stop) should_stop = false; - // One other potential problem is that we set up a master plan, then stop in before it is complete - for instance - // by hitting a breakpoint during a step-over - then do some step/finish/etc operations that wind up - // past the end point condition of the initial plan. We don't want to strand the original plan on the stack, - // This code clears stale plans off the stack. + } + + // One other potential problem is that we set up a master plan, then stop in before it is complete - for instance + // by hitting a breakpoint during a step-over - then do some step/finish/etc operations that wind up + // past the end point condition of the initial plan. We don't want to strand the original plan on the stack, + // This code clears stale plans off the stack. - if (should_stop) + if (should_stop) + { + ThreadPlan *plan_ptr = GetCurrentPlan(); + while (!PlanIsBasePlan(plan_ptr)) { - ThreadPlan *plan_ptr = GetCurrentPlan(); - while (!PlanIsBasePlan(plan_ptr)) - { - bool stale = plan_ptr->IsPlanStale (); - ThreadPlan *examined_plan = plan_ptr; - plan_ptr = GetPreviousPlan (examined_plan); + bool stale = plan_ptr->IsPlanStale (); + ThreadPlan *examined_plan = plan_ptr; + plan_ptr = GetPreviousPlan (examined_plan); - if (stale) - { - if (log) - log->Printf("Plan %s being discarded in cleanup, it says it is already done.", - examined_plan->GetName()); - DiscardThreadPlansUpToPlan(examined_plan); - } + if (stale) + { + if (log) + log->Printf("Plan %s being discarded in cleanup, it says it is already done.", + examined_plan->GetName()); + DiscardThreadPlansUpToPlan(examined_plan); } } - } if (log) @@ -1290,6 +1322,36 @@ Thread::SetTracer (lldb::ThreadPlanTracerSP &tracer_sp) m_plan_stack[i]->SetThreadPlanTracer(tracer_sp); } +bool +Thread::DiscardUserThreadPlansUpToIndex (uint32_t thread_index) +{ + // Count the user thread plans from the back end to get the number of the one we want + // to discard: + + uint32_t idx = 0; + ThreadPlan *up_to_plan_ptr = nullptr; + + for (ThreadPlanSP plan_sp : m_plan_stack) + { + if (plan_sp->GetPrivate()) + continue; + if (idx == thread_index) + { + up_to_plan_ptr = plan_sp.get(); + break; + } + else + idx++; + } + + if (up_to_plan_ptr == nullptr) + return false; + + DiscardThreadPlansUpToPlan(up_to_plan_ptr); + return true; +} + + void Thread::DiscardThreadPlansUpToPlan (lldb::ThreadPlanSP &up_to_plan_sp) { @@ -1483,18 +1545,16 @@ Thread::QueueThreadPlanForStepInRange LazyBool step_out_avoids_code_without_debug_info ) { - ThreadPlanSP thread_plan_sp; - ThreadPlanStepInRange *plan = new ThreadPlanStepInRange (*this, + ThreadPlanSP thread_plan_sp (new ThreadPlanStepInRange (*this, range, addr_context, stop_other_threads, step_in_avoids_code_without_debug_info, - step_out_avoids_code_without_debug_info); + step_out_avoids_code_without_debug_info)); + ThreadPlanStepInRange *plan = static_cast<ThreadPlanStepInRange *>(thread_plan_sp.get()); if (step_in_target) plan->SetStepInTarget(step_in_target); - - thread_plan_sp.reset (plan); QueueThreadPlan (thread_plan_sp, abort_other_plans); return thread_plan_sp; @@ -1546,17 +1606,18 @@ Thread::QueueThreadPlanForStepOutNoShouldStop uint32_t frame_idx ) { - ThreadPlanStepOut *new_plan = new ThreadPlanStepOut (*this, + ThreadPlanSP thread_plan_sp(new ThreadPlanStepOut (*this, addr_context, first_insn, stop_other_threads, stop_vote, run_vote, frame_idx, - eLazyBoolNo); + eLazyBoolNo)); + + ThreadPlanStepOut *new_plan = static_cast<ThreadPlanStepOut *>(thread_plan_sp.get()); new_plan->ClearShouldStopHereCallbacks(); - ThreadPlanSP thread_plan_sp(new_plan); - + if (thread_plan_sp->ValidatePlan(NULL)) { QueueThreadPlan (thread_plan_sp, abort_other_plans); @@ -1602,61 +1663,105 @@ Thread::QueueThreadPlanForStepUntil (bool abort_other_plans, } +lldb::ThreadPlanSP +Thread::QueueThreadPlanForStepScripted (bool abort_other_plans, + const char *class_name, + bool stop_other_threads) +{ + ThreadPlanSP thread_plan_sp (new ThreadPlanPython (*this, class_name)); + QueueThreadPlan (thread_plan_sp, abort_other_plans); + // This seems a little funny, but I don't want to have to split up the constructor and the + // DidPush in the scripted plan, that seems annoying. + // That means the constructor has to be in DidPush. + // So I have to validate the plan AFTER pushing it, and then take it off again... + if (!thread_plan_sp->ValidatePlan(nullptr)) + { + DiscardThreadPlansUpToPlan(thread_plan_sp); + return ThreadPlanSP(); + } + else + return thread_plan_sp; + +} + uint32_t Thread::GetIndexID () const { return m_index_id; } -void -Thread::DumpThreadPlans (lldb_private::Stream *s) const +static void +PrintPlanElement (Stream *s, const ThreadPlanSP &plan, lldb::DescriptionLevel desc_level, int32_t elem_idx) { - uint32_t stack_size = m_plan_stack.size(); - int i; - s->Indent(); - s->Printf ("Plan Stack for thread #%u: tid = 0x%4.4" PRIx64 ", stack_size = %d\n", GetIndexID(), GetID(), stack_size); - for (i = stack_size - 1; i >= 0; i--) - { s->IndentMore(); s->Indent(); - s->Printf ("Element %d: ", i); - m_plan_stack[i]->GetDescription (s, eDescriptionLevelFull); + s->Printf ("Element %d: ", elem_idx); + plan->GetDescription (s, desc_level); s->EOL(); s->IndentLess(); +} + +static void +PrintPlanStack (Stream *s, const std::vector<lldb::ThreadPlanSP> &plan_stack, lldb::DescriptionLevel desc_level, bool include_internal) +{ + int32_t print_idx = 0; + for (ThreadPlanSP plan_sp : plan_stack) + { + if (include_internal || !plan_sp->GetPrivate()) + { + PrintPlanElement (s, plan_sp, desc_level, print_idx++); + } } +} - stack_size = m_completed_plan_stack.size(); - if (stack_size > 0) +void +Thread::DumpThreadPlans (Stream *s, + lldb::DescriptionLevel desc_level, + bool include_internal, + bool ignore_boring_threads) const +{ + uint32_t stack_size; + + if (ignore_boring_threads) { - s->Indent(); - s->Printf ("Completed Plan Stack: %d elements.\n", stack_size); - for (i = stack_size - 1; i >= 0; i--) + uint32_t stack_size = m_plan_stack.size(); + uint32_t completed_stack_size = m_completed_plan_stack.size(); + uint32_t discarded_stack_size = m_discarded_plan_stack.size(); + if (stack_size == 1 && completed_stack_size == 0 && discarded_stack_size == 0) { + s->Printf ("thread #%u: tid = 0x%4.4" PRIx64 "\n", GetIndexID(), GetID()); s->IndentMore(); s->Indent(); - s->Printf ("Element %d: ", i); - m_completed_plan_stack[i]->GetDescription (s, eDescriptionLevelFull); - s->EOL(); + s->Printf("No active thread plans\n"); s->IndentLess(); + return; } } + s->Indent(); + s->Printf ("thread #%u: tid = 0x%4.4" PRIx64 ":\n", GetIndexID(), GetID()); + s->IndentMore(); + s->Indent(); + s->Printf ("Active plan stack:\n"); + PrintPlanStack (s, m_plan_stack, desc_level, include_internal); + + stack_size = m_completed_plan_stack.size(); + if (stack_size > 0) + { + s->Indent(); + s->Printf ("Completed Plan Stack:\n"); + PrintPlanStack (s, m_completed_plan_stack, desc_level, include_internal); + } + stack_size = m_discarded_plan_stack.size(); if (stack_size > 0) { s->Indent(); - s->Printf ("Discarded Plan Stack: %d elements.\n", stack_size); - for (i = stack_size - 1; i >= 0; i--) - { - s->IndentMore(); - s->Indent(); - s->Printf ("Element %d: ", i); - m_discarded_plan_stack[i]->GetDescription (s, eDescriptionLevelFull); - s->EOL(); - s->IndentLess(); - } + s->Printf ("Discarded Plan Stack:\n"); + PrintPlanStack (s, m_discarded_plan_stack, desc_level, include_internal); } + s->IndentLess(); } TargetSP @@ -1785,7 +1890,7 @@ Thread::ReturnFromFrame (lldb::StackFrameSP frame_sp, lldb::ValueObjectSP return // FIXME: ValueObject::Cast doesn't currently work correctly, at least not for scalars. // Turn that back on when that works. - if (0 && sc.function != NULL) + if (/* DISABLES CODE */ (0) && sc.function != NULL) { Type *function_type = sc.function->GetType(); if (function_type) @@ -2011,6 +2116,7 @@ Thread::StopReasonAsCString (lldb::StopReason reason) case eStopReasonExec: return "exec"; case eStopReasonPlanComplete: return "plan complete"; case eStopReasonThreadExiting: return "thread exiting"; + case eStopReasonInstrumentation: return "instrumentation break"; } @@ -2090,17 +2196,28 @@ Thread::GetStatus (Stream &strm, uint32_t start_frame, uint32_t num_frames, uint } bool -Thread::GetDescription (Stream &strm, lldb::DescriptionLevel level, bool print_json) +Thread::GetDescription (Stream &strm, lldb::DescriptionLevel level, bool print_json_thread, bool print_json_stopinfo) { DumpUsingSettingsFormat (strm, 0); strm.Printf("\n"); StructuredData::ObjectSP thread_info = GetExtendedInfo(); - - if (thread_info && print_json) + StructuredData::ObjectSP stop_info = m_stop_info_sp->GetExtendedInfo(); + + if (print_json_thread || print_json_stopinfo) { - thread_info->Dump (strm); - strm.Printf("\n"); + if (thread_info && print_json_thread) + { + thread_info->Dump (strm); + strm.Printf("\n"); + } + + if (stop_info && print_json_stopinfo) + { + stop_info->Dump (strm); + strm.Printf("\n"); + } + return true; } @@ -2194,6 +2311,8 @@ Thread::GetUnwinder () case llvm::Triple::aarch64: case llvm::Triple::thumb: case llvm::Triple::mips64: + case llvm::Triple::ppc: + case llvm::Triple::ppc64: case llvm::Triple::hexagon: m_unwinder_ap.reset (new UnwindLLDB (*this)); break; diff --git a/source/Target/ThreadCollection.cpp b/source/Target/ThreadCollection.cpp new file mode 100644 index 000000000000..dc1e38e02420 --- /dev/null +++ b/source/Target/ThreadCollection.cpp @@ -0,0 +1,62 @@ +//===-- ThreadCollection.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include <stdlib.h> + +#include <algorithm> + +#include "lldb/Target/ThreadCollection.h" + +using namespace lldb; +using namespace lldb_private; + +ThreadCollection::ThreadCollection() : + m_threads(), + m_mutex() +{ +} + +ThreadCollection::ThreadCollection(collection threads) : + m_threads(threads), + m_mutex() +{ +} + +void +ThreadCollection::AddThread (const ThreadSP &thread_sp) +{ + Mutex::Locker locker(GetMutex()); + m_threads.push_back (thread_sp); +} + +void +ThreadCollection::InsertThread (const lldb::ThreadSP &thread_sp, uint32_t idx) +{ + Mutex::Locker locker(GetMutex()); + if (idx < m_threads.size()) + m_threads.insert(m_threads.begin() + idx, thread_sp); + else + m_threads.push_back (thread_sp); +} + +uint32_t +ThreadCollection::GetSize () +{ + Mutex::Locker locker(GetMutex()); + return m_threads.size(); +} + +ThreadSP +ThreadCollection::GetThreadAtIndex (uint32_t idx) +{ + Mutex::Locker locker(GetMutex()); + ThreadSP thread_sp; + if (idx < m_threads.size()) + thread_sp = m_threads[idx]; + return thread_sp; +} diff --git a/source/Target/ThreadList.cpp b/source/Target/ThreadList.cpp index 7fb16fdac5c0..db4407b4b579 100644 --- a/source/Target/ThreadList.cpp +++ b/source/Target/ThreadList.cpp @@ -22,17 +22,17 @@ using namespace lldb; using namespace lldb_private; ThreadList::ThreadList (Process *process) : + ThreadCollection(), m_process (process), m_stop_id (0), - m_threads(), m_selected_tid (LLDB_INVALID_THREAD_ID) { } ThreadList::ThreadList (const ThreadList &rhs) : + ThreadCollection(), m_process (rhs.m_process), m_stop_id (rhs.m_stop_id), - m_threads (), m_selected_tid () { // Use the assignment operator since it uses the mutex @@ -77,25 +77,6 @@ ThreadList::SetStopID (uint32_t stop_id) m_stop_id = stop_id; } - -void -ThreadList::AddThread (const ThreadSP &thread_sp) -{ - Mutex::Locker locker(GetMutex()); - m_threads.push_back(thread_sp); -} - -void -ThreadList::InsertThread (const lldb::ThreadSP &thread_sp, uint32_t idx) -{ - Mutex::Locker locker(GetMutex()); - if (idx < m_threads.size()) - m_threads.insert(m_threads.begin() + idx, thread_sp); - else - m_threads.push_back (thread_sp); -} - - uint32_t ThreadList::GetSize (bool can_update) { diff --git a/source/Target/ThreadPlanPython.cpp b/source/Target/ThreadPlanPython.cpp new file mode 100644 index 000000000000..e196d81c897a --- /dev/null +++ b/source/Target/ThreadPlanPython.cpp @@ -0,0 +1,192 @@ +//===-- ThreadPlan.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Target/ThreadPlan.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanPython.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ThreadPlanPython +//---------------------------------------------------------------------- + +ThreadPlanPython::ThreadPlanPython (Thread &thread, const char *class_name) : + ThreadPlan (ThreadPlan::eKindPython, + "Python based Thread Plan", + thread, + eVoteNoOpinion, + eVoteNoOpinion), + m_class_name (class_name) +{ + SetIsMasterPlan (true); + SetOkayToDiscard (true); + SetPrivate (false); +} + +ThreadPlanPython::~ThreadPlanPython () +{ + // FIXME, do I need to decrement the ref count on this implementation object to make it go away? +} + +bool +ThreadPlanPython::ValidatePlan (Stream *error) +{ + // I have to postpone setting up the implementation till after the constructor because I need to call + // shared_from_this, which you can't do in the constructor. So I'll do it here. + if (m_implementation_sp) + return true; + else + return false; +} + +void +ThreadPlanPython::DidPush() +{ + // We set up the script side in DidPush, so that it can push other plans in the constructor, + // and doesn't have to care about the details of DidPush. + + if (!m_class_name.empty()) + { + ScriptInterpreter *script_interp = m_thread.GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (script_interp) + { + m_implementation_sp = script_interp->CreateScriptedThreadPlan (m_class_name.c_str(), this->shared_from_this()); + } + } +} + +bool +ThreadPlanPython::ShouldStop (Event *event_ptr) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("%s called on Python Thread Plan: %s )", + __PRETTY_FUNCTION__, m_class_name.c_str()); + + bool should_stop = true; + if (m_implementation_sp) + { + ScriptInterpreter *script_interp = m_thread.GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (script_interp) + { + bool script_error; + should_stop = script_interp->ScriptedThreadPlanShouldStop (m_implementation_sp, event_ptr, script_error); + if (script_error) + SetPlanComplete(false); + } + } + return should_stop; +} + +bool +ThreadPlanPython::DoPlanExplainsStop (Event *event_ptr) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("%s called on Python Thread Plan: %s )", + __PRETTY_FUNCTION__, m_class_name.c_str()); + + bool explains_stop = true; + if (m_implementation_sp) + { + ScriptInterpreter *script_interp = m_thread.GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (script_interp) + { + bool script_error; + explains_stop = script_interp->ScriptedThreadPlanExplainsStop (m_implementation_sp, event_ptr, script_error); + if (script_error) + SetPlanComplete(false); + } + } + return explains_stop; +} + +bool +ThreadPlanPython::MischiefManaged () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("%s called on Python Thread Plan: %s )", + __PRETTY_FUNCTION__, m_class_name.c_str()); + bool mischief_managed = true; + if (m_implementation_sp) + { + // I don't really need mischief_managed, since it's simpler to just call SetPlanComplete in should_stop. + mischief_managed = IsPlanComplete(); + if (mischief_managed) + m_implementation_sp.reset(); + } + return mischief_managed; +} + +lldb::StateType +ThreadPlanPython::GetPlanRunState () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("%s called on Python Thread Plan: %s )", + __PRETTY_FUNCTION__, + m_class_name.c_str()); + lldb::StateType run_state = eStateRunning; + if (m_implementation_sp) + { + ScriptInterpreter *script_interp = m_thread.GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (script_interp) + { + bool script_error; + run_state = script_interp->ScriptedThreadPlanGetRunState (m_implementation_sp, script_error); + } + } + return run_state; +} + +// The ones below are not currently exported to Python. + +bool +ThreadPlanPython::StopOthers () +{ + // For now Python plans run all threads, but we should add some controls for this. + return false; +} + +void +ThreadPlanPython::GetDescription (Stream *s, + lldb::DescriptionLevel level) +{ + s->Printf ("Python thread plan implemented by class %s.", m_class_name.c_str()); +} + +bool +ThreadPlanPython::WillStop () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("%s called on Python Thread Plan: %s )", + __PRETTY_FUNCTION__, m_class_name.c_str()); + return true; +} diff --git a/source/Target/ThreadPlanStepInRange.cpp b/source/Target/ThreadPlanStepInRange.cpp index 3e9abef65573..e5f057c183fb 100644 --- a/source/Target/ThreadPlanStepInRange.cpp +++ b/source/Target/ThreadPlanStepInRange.cpp @@ -105,7 +105,6 @@ ThreadPlanStepInRange::SetupAvoidNoDebug(LazyBool step_in_avoids_code_without_de else GetFlags().Clear (ThreadPlanShouldStopHere::eStepInAvoidNoDebug); - avoid_nodebug = true; switch (step_out_avoids_code_without_debug_info) { case eLazyBoolYes: @@ -128,17 +127,31 @@ void ThreadPlanStepInRange::GetDescription (Stream *s, lldb::DescriptionLevel level) { if (level == lldb::eDescriptionLevelBrief) + { s->Printf("step in"); - else + return; + } + + s->Printf ("Stepping in"); + bool printed_line_info = false; + if (m_addr_context.line_entry.IsValid()) + { + s->Printf (" through line "); + m_addr_context.line_entry.DumpStopContext (s, false); + printed_line_info = true; + } + + const char *step_into_target = m_step_into_target.AsCString(); + if (step_into_target && step_into_target[0] != '\0') + s->Printf (" targeting %s", m_step_into_target.AsCString()); + + if (!printed_line_info || level == eDescriptionLevelVerbose) { - s->Printf ("Stepping through range (stepping into functions): "); + s->Printf (" using ranges:"); DumpRanges(s); - const char *step_into_target = m_step_into_target.AsCString(); - if (step_into_target && step_into_target[0] != '\0') - s->Printf (" targeting %s.", m_step_into_target.AsCString()); - else - s->PutChar('.'); } + + s->PutChar('.'); } bool @@ -303,6 +316,7 @@ ThreadPlanStepInRange::ShouldStop (Event *event_ptr) else { m_no_more_plans = false; + m_sub_plan_sp->SetPrivate(true); return false; } } diff --git a/source/Target/ThreadPlanStepInstruction.cpp b/source/Target/ThreadPlanStepInstruction.cpp index fabf63b1e9d6..0f6d7b78a9ce 100644 --- a/source/Target/ThreadPlanStepInstruction.cpp +++ b/source/Target/ThreadPlanStepInstruction.cpp @@ -150,7 +150,16 @@ ThreadPlanStepInstruction::ShouldStop (Event *event_ptr) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); - StackID cur_frame_zero_id = m_thread.GetStackFrameAtIndex(0)->GetStackID(); + StackFrameSP cur_frame_sp = m_thread.GetStackFrameAtIndex(0); + if (!cur_frame_sp) + { + if (log) + log->Printf ("ThreadPlanStepInstruction couldn't get the 0th frame, stopping."); + SetPlanComplete(); + return true; + } + + StackID cur_frame_zero_id = cur_frame_sp->GetStackID(); if (cur_frame_zero_id == m_stack_id || m_stack_id < cur_frame_zero_id) { @@ -180,6 +189,24 @@ ThreadPlanStepInstruction::ShouldStop (Event *event_ptr) { if (return_frame->GetStackID() != m_parent_frame_id || m_start_has_symbol) { + // next-instruction shouldn't step out of inlined functions. But we may have stepped into a + // real function that starts with an inlined function, and we do want to step out of that... + + if (cur_frame_sp->IsInlined()) + { + StackFrameSP parent_frame_sp = m_thread.GetFrameWithStackID(m_stack_id); + + if(parent_frame_sp && parent_frame_sp->GetConcreteFrameIndex() == cur_frame_sp->GetConcreteFrameIndex()) + { + SetPlanComplete(); + if (log) + { + log->Printf("Frame we stepped into is inlined into the frame we were stepping from, stopping."); + } + return true; + } + } + if (log) { StreamString s; diff --git a/source/Target/ThreadPlanStepOut.cpp b/source/Target/ThreadPlanStepOut.cpp index b62f557319a8..0ded99b3091d 100644 --- a/source/Target/ThreadPlanStepOut.cpp +++ b/source/Target/ThreadPlanStepOut.cpp @@ -54,7 +54,6 @@ ThreadPlanStepOut::ThreadPlanStepOut m_return_addr (LLDB_INVALID_ADDRESS), m_stop_others (stop_others), m_immediate_step_from_function(NULL) - { SetFlagsToDefault(); SetupAvoidNoDebug(step_out_avoids_code_without_debug_info); @@ -90,6 +89,7 @@ ThreadPlanStepOut::ThreadPlanStepOut frame_idx - 1, eLazyBoolNo)); static_cast<ThreadPlanStepOut *>(m_step_out_to_inline_plan_sp.get())->SetShouldStopHereCallbacks(nullptr, nullptr); + m_step_out_to_inline_plan_sp->SetPrivate(true); } else { @@ -177,10 +177,34 @@ ThreadPlanStepOut::GetDescription (Stream *s, lldb::DescriptionLevel level) else if (m_step_through_inline_plan_sp) s->Printf ("Stepping out by stepping through inlined function."); else - s->Printf ("Stepping out from address 0x%" PRIx64 " to return address 0x%" PRIx64 " using breakpoint site %d", - (uint64_t)m_step_from_insn, - (uint64_t)m_return_addr, - m_return_bp_id); + { + s->Printf ("Stepping out from "); + Address tmp_address; + if (tmp_address.SetLoadAddress (m_step_from_insn, &GetTarget())) + { + tmp_address.Dump(s, &GetThread(), Address::DumpStyleResolvedDescription, Address::DumpStyleLoadAddress); + } + else + { + s->Printf ("address 0x%" PRIx64 "", (uint64_t)m_step_from_insn); + } + + // FIXME: find some useful way to present the m_return_id, since there may be multiple copies of the + // same function on the stack. + + s->Printf ("returning to frame at "); + if (tmp_address.SetLoadAddress (m_return_addr, &GetTarget())) + { + tmp_address.Dump(s, &GetThread(), Address::DumpStyleResolvedDescription, Address::DumpStyleLoadAddress); + } + else + { + s->Printf ("address 0x%" PRIx64 "", (uint64_t)m_return_addr); + } + + if (level == eDescriptionLevelVerbose) + s->Printf(" using breakpoint site %d", m_return_bp_id); + } } } @@ -474,11 +498,16 @@ ThreadPlanStepOut::QueueInlinedStepPlan (bool queue_now) inlined_sc.target_sp = GetTarget().shared_from_this(); RunMode run_mode = m_stop_others ? lldb::eOnlyThisThread : lldb::eAllThreads; const LazyBool avoid_no_debug = eLazyBoolNo; - ThreadPlanStepOverRange *step_through_inline_plan_ptr = new ThreadPlanStepOverRange(m_thread, - inline_range, - inlined_sc, - run_mode, - avoid_no_debug); + + m_step_through_inline_plan_sp.reset (new ThreadPlanStepOverRange(m_thread, + inline_range, + inlined_sc, + run_mode, + avoid_no_debug)); + ThreadPlanStepOverRange *step_through_inline_plan_ptr + = static_cast<ThreadPlanStepOverRange *>(m_step_through_inline_plan_sp.get()); + m_step_through_inline_plan_sp->SetPrivate(true); + step_through_inline_plan_ptr->SetOkayToDiscard(true); StreamString errors; if (!step_through_inline_plan_ptr->ValidatePlan(&errors)) @@ -493,7 +522,7 @@ ThreadPlanStepOut::QueueInlinedStepPlan (bool queue_now) if (inlined_block->GetRangeAtIndex (i, inline_range)) step_through_inline_plan_ptr->AddRange (inline_range); } - m_step_through_inline_plan_sp.reset (step_through_inline_plan_ptr); + if (queue_now) m_thread.QueueThreadPlan (m_step_through_inline_plan_sp, false); return true; diff --git a/source/Target/ThreadPlanStepOverBreakpoint.cpp b/source/Target/ThreadPlanStepOverBreakpoint.cpp index dc011e545402..6f285e25fd0b 100644 --- a/source/Target/ThreadPlanStepOverBreakpoint.cpp +++ b/source/Target/ThreadPlanStepOverBreakpoint.cpp @@ -64,11 +64,33 @@ ThreadPlanStepOverBreakpoint::DoPlanExplainsStop (Event *event_ptr) StopInfoSP stop_info_sp = GetPrivateStopInfo (); if (stop_info_sp) { + // It's a little surprising that we stop here for a breakpoint hit. However, when you single step ONTO a breakpoint + // we still want to call that a breakpoint hit, and trigger the actions, etc. Otherwise you would see the + // PC at the breakpoint without having triggered the actions, then you'd continue, the PC wouldn't change, + // and you'd see the breakpoint hit, which would be odd. + // So the lower levels fake "step onto breakpoint address" and return that as a breakpoint. So our trace + // step COULD appear as a breakpoint hit if the next instruction also contained a breakpoint. StopReason reason = stop_info_sp->GetStopReason(); - if (reason == eStopReasonTrace || reason == eStopReasonNone) + + switch (reason) + { + case eStopReasonTrace: + case eStopReasonNone: return true; - else + case eStopReasonBreakpoint: + // It's a little surprising that we stop here for a breakpoint hit. However, when you single step ONTO a + // breakpoint we still want to call that a breakpoint hit, and trigger the actions, etc. Otherwise you + // would see the PC at the breakpoint without having triggered the actions, then you'd continue, the PC + // wouldn't change, and you'd see the breakpoint hit, which would be odd. + // So the lower levels fake "step onto breakpoint address" and return that as a breakpoint hit. So our trace + // step COULD appear as a breakpoint hit if the next instruction also contained a breakpoint. We don't want + // to handle that, since we really don't know what to do with breakpoint hits. But make sure we don't set + // ourselves to auto-continue or we'll wrench control away from the plans that can deal with this. + SetAutoContinue(false); + return false; + default: return false; + } } return false; } @@ -76,7 +98,7 @@ ThreadPlanStepOverBreakpoint::DoPlanExplainsStop (Event *event_ptr) bool ThreadPlanStepOverBreakpoint::ShouldStop (Event *event_ptr) { - return false; + return !ShouldAutoContinue(event_ptr); } bool @@ -163,3 +185,10 @@ ThreadPlanStepOverBreakpoint::ShouldAutoContinue (Event *event_ptr) { return m_auto_continue; } + +bool +ThreadPlanStepOverBreakpoint::IsPlanStale() +{ + return m_thread.GetRegisterContext()->GetPC() != m_breakpoint_addr; +} + diff --git a/source/Target/ThreadPlanStepOverRange.cpp b/source/Target/ThreadPlanStepOverRange.cpp index a4f3743346e2..a6d65e4d8116 100644 --- a/source/Target/ThreadPlanStepOverRange.cpp +++ b/source/Target/ThreadPlanStepOverRange.cpp @@ -62,12 +62,26 @@ void ThreadPlanStepOverRange::GetDescription (Stream *s, lldb::DescriptionLevel level) { if (level == lldb::eDescriptionLevelBrief) + { s->Printf("step over"); - else + return; + } + s->Printf ("Stepping over"); + bool printed_line_info = false; + if (m_addr_context.line_entry.IsValid()) + { + s->Printf (" line "); + m_addr_context.line_entry.DumpStopContext (s, false); + printed_line_info = true; + } + + if (!printed_line_info || level == eDescriptionLevelVerbose) { - s->Printf ("stepping through range (stepping over functions): "); - DumpRanges(s); + s->Printf (" using ranges: "); + DumpRanges(s); } + + s->PutChar('.'); } void @@ -317,11 +331,15 @@ ThreadPlanStepOverRange::ShouldStop (Event *event_ptr) { new_plan_sp = CheckShouldStopHereAndQueueStepOut (frame_order); } - + if (!new_plan_sp) m_no_more_plans = true; else + { + // Any new plan will be an implementation plan, so mark it private: + new_plan_sp->SetPrivate(true); m_no_more_plans = false; + } if (!new_plan_sp) { diff --git a/source/Target/ThreadPlanStepRange.cpp b/source/Target/ThreadPlanStepRange.cpp index 82ca59fbca39..adc515cebc08 100644 --- a/source/Target/ThreadPlanStepRange.cpp +++ b/source/Target/ThreadPlanStepRange.cpp @@ -44,7 +44,8 @@ ThreadPlanStepRange::ThreadPlanStepRange (ThreadPlanKind kind, Thread &thread, const AddressRange &range, const SymbolContext &addr_context, - lldb::RunMode stop_others) : + lldb::RunMode stop_others, + bool given_ranges_only) : ThreadPlan (kind, name, thread, eVoteNoOpinion, eVoteNoOpinion), m_addr_context (addr_context), m_address_ranges (), @@ -53,7 +54,8 @@ ThreadPlanStepRange::ThreadPlanStepRange (ThreadPlanKind kind, m_parent_stack_id(), m_no_more_plans (false), m_first_run_event (true), - m_use_fast_step(false) + m_use_fast_step(false), + m_given_ranges_only (given_ranges_only) { m_use_fast_step = GetTarget().GetUseFastStepping(); AddRange(range); @@ -149,7 +151,7 @@ ThreadPlanStepRange::InRange () break; } - if (!ret_value) + if (!ret_value && !m_given_ranges_only) { // See if we've just stepped to another part of the same line number... StackFrame *frame = m_thread.GetStackFrameAtIndex(0).get(); diff --git a/source/Target/ThreadPlanStepThrough.cpp b/source/Target/ThreadPlanStepThrough.cpp index 18da6cdbd59c..5b50a41a6398 100644 --- a/source/Target/ThreadPlanStepThrough.cpp +++ b/source/Target/ThreadPlanStepThrough.cpp @@ -87,7 +87,10 @@ ThreadPlanStepThrough::DidPush () void ThreadPlanStepThrough::LookForPlanToStepThroughFromCurrentPC() { - m_sub_plan_sp = m_thread.GetProcess()->GetDynamicLoader()->GetStepThroughTrampolinePlan (m_thread, m_stop_others); + DynamicLoader *loader = m_thread.GetProcess()->GetDynamicLoader(); + if (loader) + m_sub_plan_sp = loader->GetStepThroughTrampolinePlan (m_thread, m_stop_others); + // If that didn't come up with anything, try the ObjC runtime plugin: if (!m_sub_plan_sp.get()) { diff --git a/source/Target/ThreadPlanTracer.cpp b/source/Target/ThreadPlanTracer.cpp index 5188df6d9193..d2b039d69f67 100644 --- a/source/Target/ThreadPlanTracer.cpp +++ b/source/Target/ThreadPlanTracer.cpp @@ -212,11 +212,15 @@ ThreadPlanAssemblyTracer::Log () const bool show_bytes = true; const bool show_address = true; Instruction *instruction = instruction_list.GetInstructionAtIndex(0).get(); + const char *disassemble_format = "${addr-file-or-load}: "; instruction->Dump (stream, max_opcode_byte_size, show_address, show_bytes, - NULL); + NULL, + NULL, + NULL, + disassemble_format); } } } diff --git a/source/Utility/PseudoTerminal.cpp b/source/Utility/PseudoTerminal.cpp index c906ea273005..e90955d37d4c 100644 --- a/source/Utility/PseudoTerminal.cpp +++ b/source/Utility/PseudoTerminal.cpp @@ -31,6 +31,8 @@ char *ptsname(int fd) { return 0; } pid_t fork(void) { return 0; } pid_t setsid(void) { return 0; } +#elif defined(__ANDROID_NDK__) +#include "lldb/Host/android/Android.h" #endif using namespace lldb_utility; @@ -66,7 +68,11 @@ PseudoTerminal::CloseMasterFileDescriptor () { if (m_master_fd >= 0) { + // Don't call 'close' on m_master_fd for Windows as a dummy implementation of + // posix_openpt above always gives it a 0 value. +#ifndef _WIN32 ::close (m_master_fd); +#endif m_master_fd = invalid_fd; } } diff --git a/source/Utility/RegisterNumber.cpp b/source/Utility/RegisterNumber.cpp new file mode 100644 index 000000000000..8116cda10fe5 --- /dev/null +++ b/source/Utility/RegisterNumber.cpp @@ -0,0 +1,157 @@ +//===--------------------- RegisterNumber.cpp -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/RegisterNumber.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/RegisterContext.h" + +using namespace lldb_private; + + +RegisterNumber::RegisterNumber (lldb_private::Thread &thread, lldb::RegisterKind kind, uint32_t num) : + m_reg_ctx_sp (thread.GetRegisterContext()), + m_regnum (num), + m_kind (kind), + m_kind_regnum_map (), + m_name ("") +{ + if (m_reg_ctx_sp.get()) + { + const lldb_private::RegisterInfo *reginfo = m_reg_ctx_sp->GetRegisterInfoAtIndex (GetAsKind (lldb::eRegisterKindLLDB)); + if (reginfo && reginfo->name) + { + m_name = reginfo->name; + } + } +} + +RegisterNumber::RegisterNumber () : + m_reg_ctx_sp(), + m_regnum (LLDB_INVALID_REGNUM), + m_kind (lldb::kNumRegisterKinds), + m_kind_regnum_map (), + m_name (nullptr) +{ +} + +void +RegisterNumber::init (lldb_private::Thread &thread, lldb::RegisterKind kind, uint32_t num) +{ + m_reg_ctx_sp = thread.GetRegisterContext(); + m_regnum = num; + m_kind = kind; + if (m_reg_ctx_sp.get()) + { + const lldb_private::RegisterInfo *reginfo = m_reg_ctx_sp->GetRegisterInfoAtIndex (GetAsKind (lldb::eRegisterKindLLDB)); + if (reginfo && reginfo->name) + { + m_name = reginfo->name; + } + } +} + +const RegisterNumber & +RegisterNumber::operator = (const RegisterNumber &rhs) +{ + m_reg_ctx_sp = rhs.m_reg_ctx_sp; + m_regnum = rhs.m_regnum; + m_kind = rhs.m_kind; + for (auto it : rhs.m_kind_regnum_map) + m_kind_regnum_map[it.first] = it.second; + m_name = rhs.m_name; + return *this; +} + +bool +RegisterNumber::operator == (RegisterNumber &rhs) +{ + if (IsValid() != rhs.IsValid()) + return false; + + if (m_kind == rhs.m_kind) + { + if (m_regnum == rhs.m_regnum) + return true; + else + return false; + } + + uint32_t rhs_regnum = rhs.GetAsKind (m_kind); + if (rhs_regnum != LLDB_INVALID_REGNUM) + { + if (m_regnum == rhs_regnum) + return true; + else + return false; + } + uint32_t lhs_regnum = GetAsKind (rhs.m_kind); + { + if (lhs_regnum == rhs.m_regnum) + return true; + else + return false; + } + return false; +} + +bool +RegisterNumber::operator != (RegisterNumber &rhs) +{ + return !(*this == rhs); +} + +bool +RegisterNumber::IsValid () const +{ + return m_reg_ctx_sp.get() + && m_kind != lldb::kNumRegisterKinds + && m_regnum != LLDB_INVALID_REGNUM; +} + +uint32_t +RegisterNumber::GetAsKind (lldb::RegisterKind kind) +{ + if (m_regnum == LLDB_INVALID_REGNUM) + return LLDB_INVALID_REGNUM; + + if (kind == m_kind) + return m_regnum; + + Collection::iterator iter = m_kind_regnum_map.find (kind); + if (iter != m_kind_regnum_map.end()) + { + return iter->second; + } + uint32_t output_regnum = LLDB_INVALID_REGNUM; + if (m_reg_ctx_sp + && m_reg_ctx_sp->ConvertBetweenRegisterKinds (m_kind, m_regnum, kind, output_regnum) + && output_regnum != LLDB_INVALID_REGNUM) + { + m_kind_regnum_map[kind] = output_regnum; + } + return output_regnum; +} + +uint32_t +RegisterNumber::GetRegisterNumber () const +{ + return m_regnum; +} + +lldb::RegisterKind +RegisterNumber::GetRegisterKind () const +{ + return m_kind; +} + +const char * +RegisterNumber::GetName () +{ + return m_name; +} diff --git a/source/Utility/StringExtractor.cpp b/source/Utility/StringExtractor.cpp index 8747853213cb..a2cbe6cd4869 100644 --- a/source/Utility/StringExtractor.cpp +++ b/source/Utility/StringExtractor.cpp @@ -16,43 +16,6 @@ // Other libraries and framework includes // Project includes -static const uint8_t -g_hex_ascii_to_hex_integer[256] = { - - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, - 0x8, 0x9, 255, 255, 255, 255, 255, 255, - 255, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, -}; - static inline int xdigit_to_sint (char ch) { @@ -60,7 +23,9 @@ xdigit_to_sint (char ch) return 10 + ch - 'a'; if (ch >= 'A' && ch <= 'F') return 10 + ch - 'A'; - return ch - '0'; + if (ch >= '0' && ch <= '9') + return ch - '0'; + return -1; } //---------------------------------------------------------------------- @@ -129,25 +94,45 @@ StringExtractor::GetChar (char fail_value) } //---------------------------------------------------------------------- +// If a pair of valid hex digits exist at the head of the +// StringExtractor they are decoded into an unsigned byte and returned +// by this function +// +// If there is not a pair of valid hex digits at the head of the +// StringExtractor, it is left unchanged and -1 is returned +//---------------------------------------------------------------------- +int +StringExtractor::DecodeHexU8() +{ + if (GetBytesLeft() < 2) + { + return -1; + } + const int hi_nibble = xdigit_to_sint(m_packet[m_index]); + const int lo_nibble = xdigit_to_sint(m_packet[m_index+1]); + if (hi_nibble == -1 || lo_nibble == -1) + { + return -1; + } + m_index += 2; + return (uint8_t)((hi_nibble << 4) + lo_nibble); +} + +//---------------------------------------------------------------------- // Extract an unsigned character from two hex ASCII chars in the packet // string //---------------------------------------------------------------------- uint8_t StringExtractor::GetHexU8 (uint8_t fail_value, bool set_eof_on_fail) { - if (GetBytesLeft() >= 2) + int byte = DecodeHexU8(); + if (byte == -1) { - const uint8_t hi_nibble = g_hex_ascii_to_hex_integer[static_cast<uint8_t>(m_packet[m_index])]; - const uint8_t lo_nibble = g_hex_ascii_to_hex_integer[static_cast<uint8_t>(m_packet[m_index+1])]; - if (hi_nibble < 16 && lo_nibble < 16) - { - m_index += 2; - return (hi_nibble << 4) + lo_nibble; - } + if (set_eof_on_fail || m_index >= m_packet.size()) + m_index = UINT64_MAX; + return fail_value; } - if (set_eof_on_fail || m_index >= m_packet.size()) - m_index = UINT64_MAX; - return fail_value; + return (uint8_t)byte; } uint32_t @@ -372,6 +357,28 @@ StringExtractor::GetHexBytes (void *dst_void, size_t dst_len, uint8_t fail_fill_ return bytes_extracted; } +//---------------------------------------------------------------------- +// Decodes all valid hex encoded bytes at the head of the +// StringExtractor, limited by dst_len. +// +// Returns the number of bytes successfully decoded +//---------------------------------------------------------------------- +size_t +StringExtractor::GetHexBytesAvail (void *dst_void, size_t dst_len) +{ + uint8_t *dst = (uint8_t*)dst_void; + size_t bytes_extracted = 0; + while (bytes_extracted < dst_len) + { + int decode = DecodeHexU8(); + if (decode == -1) + { + break; + } + dst[bytes_extracted++] = (uint8_t)decode; + } + return bytes_extracted; +} // Consume ASCII hex nibble character pairs until we have decoded byte_size // bytes of data. diff --git a/source/Utility/StringExtractor.h b/source/Utility/StringExtractor.h index 697499309ced..49dfe99bd358 100644 --- a/source/Utility/StringExtractor.h +++ b/source/Utility/StringExtractor.h @@ -92,9 +92,13 @@ public: return m_packet.size() - m_index; return 0; } + char GetChar (char fail_value = '\0'); + int + DecodeHexU8(); + uint8_t GetHexU8 (uint8_t fail_value = 0, bool set_eof_on_fail = true); @@ -122,6 +126,9 @@ public: size_t GetHexBytes (void *dst, size_t dst_len, uint8_t fail_fill_value); + size_t + GetHexBytesAvail (void *dst, size_t dst_len); + uint64_t GetHexWithFixedSize (uint32_t byte_size, bool little_endian, uint64_t fail_value); diff --git a/source/Utility/StringExtractorGDBRemote.cpp b/source/Utility/StringExtractorGDBRemote.cpp index 17717dbe6e20..161ac6a026f2 100644 --- a/source/Utility/StringExtractorGDBRemote.cpp +++ b/source/Utility/StringExtractorGDBRemote.cpp @@ -346,14 +346,15 @@ StringExtractorGDBRemote::GetError () size_t StringExtractorGDBRemote::GetEscapedBinaryData (std::string &str) { + // Just get the data bytes in the string as GDBRemoteCommunication::CheckForPacket() + // already removes any 0x7d escaped characters. If any 0x7d characters are left in + // the packet, then they are supposed to be there... str.clear(); - char ch; - while (GetBytesLeft()) + const size_t bytes_left = GetBytesLeft(); + if (bytes_left > 0) { - ch = GetChar(); - if (ch == 0x7d) - ch = (GetChar() ^ 0x20); - str.append(1,ch); + str.assign(m_packet, m_index, bytes_left); + m_index += bytes_left; } return str.size(); } diff --git a/source/Utility/StringLexer.cpp b/source/Utility/StringLexer.cpp index bde2fc6a4202..2f62d2cedb40 100644 --- a/source/Utility/StringLexer.cpp +++ b/source/Utility/StringLexer.cpp @@ -10,28 +10,24 @@ #include "lldb/Utility/StringLexer.h" #include <algorithm> +#include <assert.h> using namespace lldb_utility; StringLexer::StringLexer (std::string s) : -m_data(s), -m_position(0), -m_putback_data() + m_data(s), + m_position(0) { } StringLexer::StringLexer (const StringLexer& rhs) : -m_data(rhs.m_data), -m_position(rhs.m_position), -m_putback_data(rhs.m_putback_data) + m_data(rhs.m_data), + m_position(rhs.m_position) { } StringLexer::Character StringLexer::Peek () { - if (m_putback_data.empty()) - return m_data[m_position]; - else - return m_putback_data.front(); + return m_data[m_position]; } bool @@ -46,6 +42,42 @@ StringLexer::NextIf (Character c) return false; } +std::pair<bool, StringLexer::Character> +StringLexer::NextIf (std::initializer_list<Character> cs) +{ + auto val = Peek(); + for (auto c : cs) + { + if (val == c) + { + Next(); + return {true,c}; + } + } + return {false,0}; +} + +bool +StringLexer::AdvanceIf (const std::string& token) +{ + auto pos = m_position; + bool matches = true; + for (auto c : token) + { + if (!NextIf(c)) + { + matches = false; + break; + } + } + if (!matches) + { + m_position = pos; + return false; + } + return true; +} + StringLexer::Character StringLexer::Next () { @@ -57,35 +89,32 @@ StringLexer::Next () bool StringLexer::HasAtLeast (Size s) { - auto in_m_data = m_data.size()-m_position; - auto in_putback = m_putback_data.size(); - return (in_m_data + in_putback >= s); + return (m_data.size() - m_position) >= s; } - void -StringLexer::PutBack (Character c) +StringLexer::PutBack (Size s) { - m_putback_data.push_back(c); + assert (m_position >= s); + m_position -= s; } bool StringLexer::HasAny (Character c) { - const auto begin(m_putback_data.begin()); - const auto end(m_putback_data.end()); - if (std::find(begin, end, c) != end) - return true; return m_data.find(c, m_position) != std::string::npos; } +std::string +StringLexer::GetUnlexed () +{ + return std::string(m_data, m_position); +} + void StringLexer::Consume() { - if (m_putback_data.empty()) - m_position++; - else - m_putback_data.pop_front(); + m_position++; } StringLexer& @@ -95,7 +124,6 @@ StringLexer::operator = (const StringLexer& rhs) { m_data = rhs.m_data; m_position = rhs.m_position; - m_putback_data = rhs.m_putback_data; } return *this; } diff --git a/source/Utility/UriParser.cpp b/source/Utility/UriParser.cpp new file mode 100644 index 000000000000..bf1e601485b4 --- /dev/null +++ b/source/Utility/UriParser.cpp @@ -0,0 +1,58 @@ +//===-- UriParser.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Utility/UriParser.h" + +// C Includes +#include <stdlib.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes + +//---------------------------------------------------------------------- +// UriParser::Parse +//---------------------------------------------------------------------- +bool +UriParser::Parse(const char* uri, + std::string& scheme, + std::string& hostname, + int& port, + std::string& path + ) +{ + char scheme_buf[100] = {0}; + char hostname_buf[256] = {0}; + char port_buf[11] = {0}; // 10==strlen(2^32) + char path_buf[2049] = {'/', 0}; + + bool ok = false; + if (4==sscanf(uri, "%99[^:/]://%255[^/:]:%[^/]/%2047s", scheme_buf, hostname_buf, port_buf, path_buf+1)) { ok = true; } + else if (3==sscanf(uri, "%99[^:/]://%255[^/:]:%[^/]", scheme_buf, hostname_buf, port_buf)) { ok = true; } + else if (3==sscanf(uri, "%99[^:/]://%255[^/]/%2047s", scheme_buf, hostname_buf, path_buf+1)) { ok = true; } + else if (2==sscanf(uri, "%99[^:/]://%255[^/]", scheme_buf, hostname_buf)) { ok = true; } + + char* end = port_buf; + int port_tmp = strtoul(port_buf, &end, 10); + if (*end != 0) + { + // there are invalid characters in port_buf + return false; + } + + if (ok) + { + scheme.assign(scheme_buf); + hostname.assign(hostname_buf); + port = port_tmp; + path.assign(path_buf); + } + return ok; +} + diff --git a/source/Utility/UriParser.h b/source/Utility/UriParser.h new file mode 100644 index 000000000000..c46628d3bd68 --- /dev/null +++ b/source/Utility/UriParser.h @@ -0,0 +1,31 @@ +//===-- UriParser.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef utility_UriParser_h_ +#define utility_UriParser_h_ + +// C Includes +// C++ Includes +#include <string> + +// Other libraries and framework includes +// Project includes + +class UriParser +{ +public: + static bool Parse(const char* uri, + std::string& scheme, + std::string& hostname, + int& port, + std::string& path + ); +}; + +#endif // utility_UriParser_h_ diff --git a/source/lldb-log.cpp b/source/lldb-log.cpp index 4311c1ad084f..463de137f623 100644 --- a/source/lldb-log.cpp +++ b/source/lldb-log.cpp @@ -11,6 +11,7 @@ // C Includes // C++ Includes +#include <atomic> // Other libraries and framework includes // Project includes #include "lldb/Interpreter/Args.h" @@ -27,7 +28,7 @@ using namespace lldb_private; // that will construct the static g_lob_sp the first time this function is // called. -static bool g_log_enabled = false; +static std::atomic<bool> g_log_enabled {false}; static Log * g_log = NULL; static Log * GetLog () diff --git a/source/lldb.cpp b/source/lldb.cpp index cd620b7945b9..2ac8296b4d04 100644 --- a/source/lldb.cpp +++ b/source/lldb.cpp @@ -31,6 +31,8 @@ #include "Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h" #include "Plugins/ABI/MacOSX-arm64/ABIMacOSX_arm64.h" #include "Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h" +#include "Plugins/ABI/SysV-ppc/ABISysV_ppc.h" +#include "Plugins/ABI/SysV-ppc64/ABISysV_ppc64.h" #include "Plugins/Disassembler/llvm/DisassemblerLLVMC.h" #include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h" #include "Plugins/Instruction/ARM/EmulateInstructionARM.h" @@ -81,6 +83,8 @@ #endif #if defined (_WIN32) +#include "lldb/Host/windows/windows.h" +#include "Plugins/Process/Windows/DynamicLoaderWindows.h" #include "Plugins/Process/Windows/ProcessWindows.h" #endif @@ -92,6 +96,8 @@ #include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h" #include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" +#include "Plugins/MemoryHistory/asan/MemoryHistoryASan.h" +#include "Plugins/InstrumentationRuntime/AddressSanitizer/AddressSanitizerRuntime.h" using namespace lldb; using namespace lldb_private; @@ -113,6 +119,25 @@ lldb_private::Initialize () if (!g_inited) { g_inited = true; + +#if defined(_MSC_VER) + const char *disable_crash_dialog_var = getenv("LLDB_DISABLE_CRASH_DIALOG"); + if (disable_crash_dialog_var && llvm::StringRef(disable_crash_dialog_var).equals_lower("true")) + { + // This will prevent Windows from displaying a dialog box requiring user interaction when + // LLDB crashes. This is mostly useful when automating LLDB, for example via the test + // suite, so that a crash in LLDB does not prevent completion of the test suite. + ::SetErrorMode(GetErrorMode() | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); + } +#endif + Log::Initialize(); HostInfo::Initialize(); Timer::Initialize (); @@ -130,6 +155,8 @@ lldb_private::Initialize () ABIMacOSX_arm::Initialize(); ABIMacOSX_arm64::Initialize(); ABISysV_x86_64::Initialize(); + ABISysV_ppc::Initialize(); + ABISysV_ppc64::Initialize(); DisassemblerLLVMC::Initialize(); ObjectContainerBSDArchive::Initialize(); ObjectFileELF::Initialize(); @@ -154,6 +181,8 @@ lldb_private::Initialize () #endif JITLoaderGDB::Initialize(); ProcessElfCore::Initialize(); + MemoryHistoryASan::Initialize(); + AddressSanitizerRuntime::Initialize(); #if defined (__APPLE__) //---------------------------------------------------------------------- @@ -181,6 +210,7 @@ lldb_private::Initialize () ProcessLinux::Initialize(); #endif #if defined(_WIN32) + DynamicLoaderWindows::Initialize(); ProcessWindows::Initialize(); #endif #if defined (__FreeBSD__) @@ -221,6 +251,8 @@ lldb_private::Terminate () ABIMacOSX_arm::Terminate(); ABIMacOSX_arm64::Terminate(); ABISysV_x86_64::Terminate(); + ABISysV_ppc::Terminate(); + ABISysV_ppc64::Terminate(); DisassemblerLLVMC::Terminate(); ObjectContainerBSDArchive::Terminate(); ObjectFileELF::Terminate(); @@ -244,6 +276,8 @@ lldb_private::Terminate () #endif JITLoaderGDB::Terminate(); ProcessElfCore::Terminate(); + MemoryHistoryASan::Terminate(); + AddressSanitizerRuntime::Terminate(); #if defined (__APPLE__) DynamicLoaderMacOSXDYLD::Terminate(); @@ -264,6 +298,10 @@ lldb_private::Terminate () Debugger::SettingsTerminate (); +#if defined (_WIN32) + DynamicLoaderWindows::Terminate(); +#endif + #if defined (__linux__) ProcessLinux::Terminate(); #endif @@ -425,6 +463,7 @@ lldb_private::GetSectionTypeAsCString (SectionType sect_type) case eSectionTypeDWARFAppleNamespaces: return "apple-namespaces"; case eSectionTypeDWARFAppleObjC: return "apple-objc"; case eSectionTypeEHFrame: return "eh-frame"; + case eSectionTypeCompactUnwind: return "compact-unwind"; case eSectionTypeOther: return "regular"; } return "unknown"; |