diff options
Diffstat (limited to 'lldb/source')
204 files changed, 7378 insertions, 2229 deletions
diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt index ce59ee5..ac47580 100644 --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -69,6 +69,7 @@ add_lldb_library(liblldb SHARED ${option_framework} SBFileSpecList.cpp SBFormat.cpp SBFrame.cpp + SBFrameList.cpp SBFunction.cpp SBHostOS.cpp SBInstruction.cpp diff --git a/lldb/source/API/SBCommandReturnObject.cpp b/lldb/source/API/SBCommandReturnObject.cpp index e78e213a..da7e288 100644 --- a/lldb/source/API/SBCommandReturnObject.cpp +++ b/lldb/source/API/SBCommandReturnObject.cpp @@ -15,6 +15,7 @@ #include "lldb/API/SBValue.h" #include "lldb/API/SBValueList.h" #include "lldb/Core/StructuredDataImpl.h" +#include "lldb/Host/File.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/Instrumentation.h" @@ -275,14 +276,16 @@ void SBCommandReturnObject::SetImmediateErrorFile(FILE *fh) { void SBCommandReturnObject::SetImmediateOutputFile(FILE *fh, bool transfer_ownership) { LLDB_INSTRUMENT_VA(this, fh, transfer_ownership); - FileSP file = std::make_shared<NativeFile>(fh, transfer_ownership); + FileSP file = std::make_shared<NativeFile>(fh, File::eOpenOptionWriteOnly, + transfer_ownership); ref().SetImmediateOutputFile(file); } void SBCommandReturnObject::SetImmediateErrorFile(FILE *fh, bool transfer_ownership) { LLDB_INSTRUMENT_VA(this, fh, transfer_ownership); - FileSP file = std::make_shared<NativeFile>(fh, transfer_ownership); + FileSP file = std::make_shared<NativeFile>(fh, File::eOpenOptionWriteOnly, + transfer_ownership); ref().SetImmediateErrorFile(file); } diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp index 5c4c653..3f34e7a 100644 --- a/lldb/source/API/SBDebugger.cpp +++ b/lldb/source/API/SBDebugger.cpp @@ -179,7 +179,7 @@ void SBDebugger::Initialize() { lldb::SBError SBDebugger::InitializeWithErrorHandling() { LLDB_INSTRUMENT(); - SBError error; + SBError error((Status())); if (auto e = g_debugger_lifetime->Initialize( std::make_unique<SystemInitializerFull>())) { error.SetError(Status::FromError(std::move(e))); @@ -327,8 +327,8 @@ void SBDebugger::SkipAppInitFiles(bool b) { void SBDebugger::SetInputFileHandle(FILE *fh, bool transfer_ownership) { LLDB_INSTRUMENT_VA(this, fh, transfer_ownership); if (m_opaque_sp) - m_opaque_sp->SetInputFile( - (FileSP)std::make_shared<NativeFile>(fh, transfer_ownership)); + m_opaque_sp->SetInputFile((FileSP)std::make_shared<NativeFile>( + fh, File::eOpenOptionReadOnly, transfer_ownership)); } SBError SBDebugger::SetInputString(const char *data) { @@ -385,7 +385,8 @@ SBError SBDebugger::SetOutputFile(FileSP file_sp) { void SBDebugger::SetOutputFileHandle(FILE *fh, bool transfer_ownership) { LLDB_INSTRUMENT_VA(this, fh, transfer_ownership); - SetOutputFile((FileSP)std::make_shared<NativeFile>(fh, transfer_ownership)); + SetOutputFile((FileSP)std::make_shared<NativeFile>( + fh, File::eOpenOptionWriteOnly, transfer_ownership)); } SBError SBDebugger::SetOutputFile(SBFile file) { @@ -405,7 +406,8 @@ SBError SBDebugger::SetOutputFile(SBFile file) { void SBDebugger::SetErrorFileHandle(FILE *fh, bool transfer_ownership) { LLDB_INSTRUMENT_VA(this, fh, transfer_ownership); - SetErrorFile((FileSP)std::make_shared<NativeFile>(fh, transfer_ownership)); + SetErrorFile((FileSP)std::make_shared<NativeFile>( + fh, File::eOpenOptionWriteOnly, transfer_ownership)); } SBError SBDebugger::SetErrorFile(FileSP file_sp) { @@ -576,8 +578,10 @@ void SBDebugger::HandleProcessEvent(const SBProcess &process, FILE *err) { LLDB_INSTRUMENT_VA(this, process, event, out, err); - FileSP outfile = std::make_shared<NativeFile>(out, false); - FileSP errfile = std::make_shared<NativeFile>(err, false); + FileSP outfile = + std::make_shared<NativeFile>(out, File::eOpenOptionWriteOnly, false); + FileSP errfile = + std::make_shared<NativeFile>(err, File::eOpenOptionWriteOnly, false); return HandleProcessEvent(process, event, outfile, errfile); } @@ -705,61 +709,11 @@ const char *SBDebugger::StateAsCString(StateType state) { return lldb_private::StateAsCString(state); } -static void AddBoolConfigEntry(StructuredData::Dictionary &dict, - llvm::StringRef name, bool value, - llvm::StringRef description) { - auto entry_up = std::make_unique<StructuredData::Dictionary>(); - entry_up->AddBooleanItem("value", value); - entry_up->AddStringItem("description", description); - dict.AddItem(name, std::move(entry_up)); -} - -static void AddLLVMTargets(StructuredData::Dictionary &dict) { - auto array_up = std::make_unique<StructuredData::Array>(); -#define LLVM_TARGET(target) \ - array_up->AddItem(std::make_unique<StructuredData::String>(#target)); -#include "llvm/Config/Targets.def" - auto entry_up = std::make_unique<StructuredData::Dictionary>(); - entry_up->AddItem("value", std::move(array_up)); - entry_up->AddStringItem("description", "A list of configured LLVM targets."); - dict.AddItem("targets", std::move(entry_up)); -} - SBStructuredData SBDebugger::GetBuildConfiguration() { LLDB_INSTRUMENT(); - auto config_up = std::make_unique<StructuredData::Dictionary>(); - AddBoolConfigEntry( - *config_up, "xml", XMLDocument::XMLEnabled(), - "A boolean value that indicates if XML support is enabled in LLDB"); - AddBoolConfigEntry( - *config_up, "curl", LLVM_ENABLE_CURL, - "A boolean value that indicates if CURL support is enabled in LLDB"); - AddBoolConfigEntry( - *config_up, "curses", LLDB_ENABLE_CURSES, - "A boolean value that indicates if curses support is enabled in LLDB"); - AddBoolConfigEntry( - *config_up, "editline", LLDB_ENABLE_LIBEDIT, - "A boolean value that indicates if editline support is enabled in LLDB"); - AddBoolConfigEntry(*config_up, "editline_wchar", LLDB_EDITLINE_USE_WCHAR, - "A boolean value that indicates if editline wide " - "characters support is enabled in LLDB"); - AddBoolConfigEntry( - *config_up, "lzma", LLDB_ENABLE_LZMA, - "A boolean value that indicates if lzma support is enabled in LLDB"); - AddBoolConfigEntry( - *config_up, "python", LLDB_ENABLE_PYTHON, - "A boolean value that indicates if python support is enabled in LLDB"); - AddBoolConfigEntry( - *config_up, "lua", LLDB_ENABLE_LUA, - "A boolean value that indicates if lua support is enabled in LLDB"); - AddBoolConfigEntry(*config_up, "fbsdvmcore", LLDB_ENABLE_FBSDVMCORE, - "A boolean value that indicates if fbsdvmcore support is " - "enabled in LLDB"); - AddLLVMTargets(*config_up); - SBStructuredData data; - data.m_impl_up->SetObjectSP(std::move(config_up)); + data.m_impl_up->SetObjectSP(Debugger::GetBuildConfiguration()); return data; } diff --git a/lldb/source/API/SBFile.cpp b/lldb/source/API/SBFile.cpp index 2ae4b14..5690992 100644 --- a/lldb/source/API/SBFile.cpp +++ b/lldb/source/API/SBFile.cpp @@ -39,7 +39,22 @@ SBFile::SBFile() { LLDB_INSTRUMENT_VA(this); } SBFile::SBFile(FILE *file, bool transfer_ownership) { LLDB_INSTRUMENT_VA(this, file, transfer_ownership); - m_opaque_sp = std::make_shared<NativeFile>(file, transfer_ownership); + // For backwards comptability, this defaulted to ReadOnly previously. + m_opaque_sp = std::make_shared<NativeFile>(file, File::eOpenOptionReadOnly, + transfer_ownership); +} + +SBFile::SBFile(FILE *file, const char *mode, bool transfer_ownership) { + LLDB_INSTRUMENT_VA(this, file, transfer_ownership); + + auto options = File::GetOptionsFromMode(mode); + if (!options) { + llvm::consumeError(options.takeError()); + return; + } + + m_opaque_sp = + std::make_shared<NativeFile>(file, options.get(), transfer_ownership); } SBFile::SBFile(int fd, const char *mode, bool transfer_owndership) { diff --git a/lldb/source/API/SBFrameList.cpp b/lldb/source/API/SBFrameList.cpp new file mode 100644 index 0000000..d5fa955 --- /dev/null +++ b/lldb/source/API/SBFrameList.cpp @@ -0,0 +1,97 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBFrameList.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBThread.h" +#include "lldb/Target/StackFrameList.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Instrumentation.h" + +using namespace lldb; +using namespace lldb_private; + +SBFrameList::SBFrameList() : m_opaque_sp() { LLDB_INSTRUMENT_VA(this); } + +SBFrameList::SBFrameList(const SBFrameList &rhs) + : m_opaque_sp(rhs.m_opaque_sp) { + LLDB_INSTRUMENT_VA(this, rhs); +} + +SBFrameList::~SBFrameList() = default; + +const SBFrameList &SBFrameList::operator=(const SBFrameList &rhs) { + LLDB_INSTRUMENT_VA(this, rhs); + + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +SBFrameList::SBFrameList(const lldb::StackFrameListSP &frame_list_sp) + : m_opaque_sp(frame_list_sp) {} + +void SBFrameList::SetFrameList(const lldb::StackFrameListSP &frame_list_sp) { + m_opaque_sp = frame_list_sp; +} + +SBFrameList::operator bool() const { + LLDB_INSTRUMENT_VA(this); + + return m_opaque_sp.get() != nullptr; +} + +bool SBFrameList::IsValid() const { + LLDB_INSTRUMENT_VA(this); + return this->operator bool(); +} + +uint32_t SBFrameList::GetSize() const { + LLDB_INSTRUMENT_VA(this); + + if (m_opaque_sp) + return m_opaque_sp->GetNumFrames(); + return 0; +} + +SBFrame SBFrameList::GetFrameAtIndex(uint32_t idx) const { + LLDB_INSTRUMENT_VA(this, idx); + + SBFrame sb_frame; + if (m_opaque_sp) + sb_frame.SetFrameSP(m_opaque_sp->GetFrameAtIndex(idx)); + return sb_frame; +} + +SBThread SBFrameList::GetThread() const { + LLDB_INSTRUMENT_VA(this); + + SBThread sb_thread; + if (m_opaque_sp) + sb_thread.SetThread(m_opaque_sp->GetThread().shared_from_this()); + return sb_thread; +} + +void SBFrameList::Clear() { + LLDB_INSTRUMENT_VA(this); + + if (m_opaque_sp) + m_opaque_sp->Clear(); +} + +bool SBFrameList::GetDescription(SBStream &description) const { + LLDB_INSTRUMENT_VA(this, description); + + if (!m_opaque_sp) + return false; + + Stream &strm = description.ref(); + m_opaque_sp->Dump(&strm); + return true; +} diff --git a/lldb/source/API/SBInstruction.cpp b/lldb/source/API/SBInstruction.cpp index 6755089..5921511 100644 --- a/lldb/source/API/SBInstruction.cpp +++ b/lldb/source/API/SBInstruction.cpp @@ -10,8 +10,8 @@ #include "lldb/Utility/Instrumentation.h" #include "lldb/API/SBAddress.h" -#include "lldb/API/SBFrame.h" #include "lldb/API/SBFile.h" +#include "lldb/API/SBFrame.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBTarget.h" @@ -268,7 +268,8 @@ bool SBInstruction::GetDescription(lldb::SBStream &s) { void SBInstruction::Print(FILE *outp) { LLDB_INSTRUMENT_VA(this, outp); - FileSP out = std::make_shared<NativeFile>(outp, /*take_ownership=*/false); + FileSP out = std::make_shared<NativeFile>(outp, File::eOpenOptionWriteOnly, + /*take_ownership=*/false); Print(out); } diff --git a/lldb/source/API/SBLineEntry.cpp b/lldb/source/API/SBLineEntry.cpp index 0f4936f..2257294 100644 --- a/lldb/source/API/SBLineEntry.cpp +++ b/lldb/source/API/SBLineEntry.cpp @@ -132,6 +132,8 @@ void SBLineEntry::SetLine(uint32_t line) { LLDB_INSTRUMENT_VA(this, line); ref().line = line; + if (!ref().range.IsValid()) + ref().synthetic = true; } void SBLineEntry::SetColumn(uint32_t column) { diff --git a/lldb/source/API/SBModule.cpp b/lldb/source/API/SBModule.cpp index 5a57f45..32067ac 100644 --- a/lldb/source/API/SBModule.cpp +++ b/lldb/source/API/SBModule.cpp @@ -37,8 +37,8 @@ SBModule::SBModule(const SBModuleSpec &module_spec) { LLDB_INSTRUMENT_VA(this, module_spec); ModuleSP module_sp; - Status error = ModuleList::GetSharedModule( - *module_spec.m_opaque_up, module_sp, nullptr, nullptr, nullptr); + Status error = ModuleList::GetSharedModule(*module_spec.m_opaque_up, + module_sp, nullptr, nullptr); if (module_sp) SetSP(module_sp); } diff --git a/lldb/source/API/SBModuleSpec.cpp b/lldb/source/API/SBModuleSpec.cpp index fbbcfea..031ba12 100644 --- a/lldb/source/API/SBModuleSpec.cpp +++ b/lldb/source/API/SBModuleSpec.cpp @@ -9,6 +9,7 @@ #include "lldb/API/SBModuleSpec.h" #include "Utils.h" #include "lldb/API/SBStream.h" +#include "lldb/API/SBTarget.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Host/Host.h" @@ -174,6 +175,18 @@ void SBModuleSpec::SetObjectSize(uint64_t object_size) { m_opaque_up->SetObjectSize(object_size); } +SBTarget SBModuleSpec::GetTarget() { + LLDB_INSTRUMENT_VA(this); + + return SBTarget(m_opaque_up->GetTargetSP()); +} + +void SBModuleSpec::SetTarget(SBTarget target) { + LLDB_INSTRUMENT_VA(this, target); + + m_opaque_up->SetTarget(target.GetSP()); +} + SBModuleSpecList::SBModuleSpecList() : m_opaque_up(new ModuleSpecList()) { LLDB_INSTRUMENT_VA(this); } diff --git a/lldb/source/API/SBProcess.cpp b/lldb/source/API/SBProcess.cpp index d4be64b..14aa943 100644 --- a/lldb/source/API/SBProcess.cpp +++ b/lldb/source/API/SBProcess.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "lldb/API/SBProcess.h" +#include "lldb/Host/File.h" #include "lldb/Utility/Instrumentation.h" #include <cinttypes> @@ -310,7 +311,8 @@ void SBProcess::ReportEventState(const SBEvent &event, SBFile out) const { void SBProcess::ReportEventState(const SBEvent &event, FILE *out) const { LLDB_INSTRUMENT_VA(this, event, out); - FileSP outfile = std::make_shared<NativeFile>(out, false); + FileSP outfile = + std::make_shared<NativeFile>(out, File::eOpenOptionWriteOnly, false); return ReportEventState(event, outfile); } diff --git a/lldb/source/API/SBStream.cpp b/lldb/source/API/SBStream.cpp index fc8f09a..2fc5fcf 100644 --- a/lldb/source/API/SBStream.cpp +++ b/lldb/source/API/SBStream.cpp @@ -116,7 +116,8 @@ void SBStream::RedirectToFile(const char *path, bool append) { void SBStream::RedirectToFileHandle(FILE *fh, bool transfer_fh_ownership) { LLDB_INSTRUMENT_VA(this, fh, transfer_fh_ownership); - FileSP file = std::make_unique<NativeFile>(fh, transfer_fh_ownership); + FileSP file = std::make_unique<NativeFile>(fh, File::eOpenOptionReadWrite, + transfer_fh_ownership); return RedirectToFile(file); } diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index 98d10aa..78c2d49 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -23,6 +23,7 @@ #include "lldb/API/SBStringList.h" #include "lldb/API/SBStructuredData.h" #include "lldb/API/SBSymbolContextList.h" +#include "lldb/API/SBThreadCollection.h" #include "lldb/API/SBTrace.h" #include "lldb/Breakpoint/BreakpointID.h" #include "lldb/Breakpoint/BreakpointIDList.h" @@ -39,6 +40,7 @@ #include "lldb/Core/Section.h" #include "lldb/Core/StructuredDataImpl.h" #include "lldb/Host/Host.h" +#include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h" #include "lldb/Symbol/DeclVendor.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolFile.h" @@ -50,6 +52,7 @@ #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" +#include "lldb/Target/SyntheticFrameProvider.h" #include "lldb/Target/Target.h" #include "lldb/Target/TargetList.h" #include "lldb/Utility/ArchSpec.h" @@ -59,6 +62,7 @@ #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/ProcessInfo.h" #include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/ScriptedMetadata.h" #include "lldb/ValueObject/ValueObjectConstResult.h" #include "lldb/ValueObject/ValueObjectList.h" #include "lldb/ValueObject/ValueObjectVariable.h" @@ -128,6 +132,12 @@ SBTarget SBTarget::GetTargetFromEvent(const SBEvent &event) { return Target::TargetEventData::GetTargetFromEvent(event.get()); } +SBTarget SBTarget::GetCreatedTargetFromEvent(const SBEvent &event) { + LLDB_INSTRUMENT_VA(event); + + return Target::TargetEventData::GetCreatedTargetFromEvent(event.get()); +} + uint32_t SBTarget::GetNumModulesFromEvent(const SBEvent &event) { LLDB_INSTRUMENT_VA(event); @@ -1614,6 +1624,19 @@ const char *SBTarget::GetTriple() { return nullptr; } +const char *SBTarget::GetArchName() { + LLDB_INSTRUMENT_VA(this); + + if (TargetSP target_sp = GetSP()) { + llvm::StringRef arch_name = + target_sp->GetArchitecture().GetTriple().getArchName(); + ConstString const_arch_name(arch_name); + + return const_arch_name.GetCString(); + } + return nullptr; +} + const char *SBTarget::GetABIName() { LLDB_INSTRUMENT_VA(this); @@ -1641,6 +1664,14 @@ lldb::user_id_t SBTarget::GetGloballyUniqueID() const { return LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID; } +const char *SBTarget::GetTargetSessionName() const { + LLDB_INSTRUMENT_VA(this); + + if (TargetSP target_sp = GetSP()) + return ConstString(target_sp->GetTargetSessionName()).AsCString(); + return nullptr; +} + SBError SBTarget::SetLabel(const char *label) { LLDB_INSTRUMENT_VA(this, label); @@ -2408,3 +2439,81 @@ lldb::SBMutex SBTarget::GetAPIMutex() const { return lldb::SBMutex(target_sp); return lldb::SBMutex(); } + +uint32_t +SBTarget::RegisterScriptedFrameProvider(const char *class_name, + lldb::SBStructuredData args_dict, + lldb::SBError &error) { + LLDB_INSTRUMENT_VA(this, class_name, args_dict, error); + + TargetSP target_sp = GetSP(); + if (!target_sp) { + error.SetErrorString("invalid target"); + return 0; + } + + if (!class_name || !class_name[0]) { + error.SetErrorString("invalid class name"); + return 0; + } + + // Extract the dictionary from SBStructuredData. + StructuredData::DictionarySP dict_sp; + if (args_dict.IsValid() && args_dict.m_impl_up) { + StructuredData::ObjectSP obj_sp = args_dict.m_impl_up->GetObjectSP(); + if (obj_sp && obj_sp->GetType() != lldb::eStructuredDataTypeDictionary) { + error.SetErrorString("SBStructuredData argument isn't a dictionary"); + return 0; + } + dict_sp = std::make_shared<StructuredData::Dictionary>(obj_sp); + } + + // Create the ScriptedMetadata. + ScriptedMetadataSP metadata_sp = + std::make_shared<ScriptedMetadata>(class_name, dict_sp); + + // Create the interface for calling static methods. + ScriptedFrameProviderInterfaceSP interface_sp = + target_sp->GetDebugger() + .GetScriptInterpreter() + ->CreateScriptedFrameProviderInterface(); + + // Create a descriptor (applies to all threads by default). + ScriptedFrameProviderDescriptor descriptor(metadata_sp); + descriptor.interface_sp = interface_sp; + + llvm::Expected<uint32_t> descriptor_id_or_err = + target_sp->AddScriptedFrameProviderDescriptor(descriptor); + if (!descriptor_id_or_err) { + error.SetErrorString( + llvm::toString(descriptor_id_or_err.takeError()).c_str()); + return 0; + } + + // Register the descriptor with the target. + return *descriptor_id_or_err; +} + +lldb::SBError SBTarget::RemoveScriptedFrameProvider(uint32_t provider_id) { + LLDB_INSTRUMENT_VA(this, provider_id); + + SBError error; + TargetSP target_sp = GetSP(); + if (!target_sp) { + error.SetErrorString("invalid target"); + return error; + } + + if (!provider_id) { + error.SetErrorString("invalid provider id"); + return error; + } + + if (!target_sp->RemoveScriptedFrameProviderDescriptor(provider_id)) { + error.SetErrorStringWithFormat("no frame provider named '%u' found", + provider_id); + return error; + } + + return {}; +} diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp index f58a1b5..f32c5c5 100644 --- a/lldb/source/API/SBThread.cpp +++ b/lldb/source/API/SBThread.cpp @@ -14,6 +14,7 @@ #include "lldb/API/SBFileSpec.h" #include "lldb/API/SBFormat.h" #include "lldb/API/SBFrame.h" +#include "lldb/API/SBFrameList.h" #include "lldb/API/SBProcess.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStructuredData.h" @@ -1102,6 +1103,26 @@ SBFrame SBThread::GetFrameAtIndex(uint32_t idx) { return sb_frame; } +lldb::SBFrameList SBThread::GetFrames() { + LLDB_INSTRUMENT_VA(this); + + SBFrameList sb_frame_list; + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBFrameList(); + } + + if (exe_ctx->HasThreadScope()) { + StackFrameListSP frame_list_sp = + exe_ctx->GetThreadPtr()->GetStackFrameList(); + sb_frame_list.SetFrameList(frame_list_sp); + } + + return sb_frame_list; +} + lldb::SBFrame SBThread::GetSelectedFrame() { LLDB_INSTRUMENT_VA(this); diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp index f25209c..25285be 100644 --- a/lldb/source/Breakpoint/BreakpointLocation.cpp +++ b/lldb/source/Breakpoint/BreakpointLocation.cpp @@ -251,7 +251,7 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx, } m_user_expression_sp.reset(GetTarget().GetUserExpressionForLanguage( - condition.GetText(), llvm::StringRef(), language, + condition.GetText(), llvm::StringRef(), SourceLanguage{language}, Expression::eResultTypeAny, EvaluateExpressionOptions(), nullptr, error)); if (error.Fail()) { diff --git a/lldb/source/Breakpoint/BreakpointLocationCollection.cpp b/lldb/source/Breakpoint/BreakpointLocationCollection.cpp index 9771583..adff429 100644 --- a/lldb/source/Breakpoint/BreakpointLocationCollection.cpp +++ b/lldb/source/Breakpoint/BreakpointLocationCollection.cpp @@ -24,7 +24,7 @@ BreakpointLocationCollection::BreakpointLocationCollection(bool preserving) BreakpointLocationCollection::~BreakpointLocationCollection() = default; void BreakpointLocationCollection::Add(const BreakpointLocationSP &bp_loc) { - std::lock_guard<std::mutex> guard(m_collection_mutex); + std::lock_guard<std::recursive_mutex> guard(m_collection_mutex); BreakpointLocationSP old_bp_loc = FindByIDPair(bp_loc->GetBreakpoint().GetID(), bp_loc->GetID()); if (!old_bp_loc.get()) { @@ -44,7 +44,7 @@ void BreakpointLocationCollection::Add(const BreakpointLocationSP &bp_loc) { bool BreakpointLocationCollection::Remove(lldb::break_id_t bp_id, lldb::break_id_t bp_loc_id) { - std::lock_guard<std::mutex> guard(m_collection_mutex); + std::lock_guard<std::recursive_mutex> guard(m_collection_mutex); collection::iterator pos = GetIDPairIterator(bp_id, bp_loc_id); // Predicate if (pos != m_break_loc_collection.end()) { if (m_preserving_bkpts) { @@ -117,7 +117,7 @@ const BreakpointLocationSP BreakpointLocationCollection::FindByIDPair( } BreakpointLocationSP BreakpointLocationCollection::GetByIndex(size_t i) { - std::lock_guard<std::mutex> guard(m_collection_mutex); + std::lock_guard<std::recursive_mutex> guard(m_collection_mutex); BreakpointLocationSP stop_sp; if (i < m_break_loc_collection.size()) stop_sp = m_break_loc_collection[i]; @@ -127,7 +127,7 @@ BreakpointLocationSP BreakpointLocationCollection::GetByIndex(size_t i) { const BreakpointLocationSP BreakpointLocationCollection::GetByIndex(size_t i) const { - std::lock_guard<std::mutex> guard(m_collection_mutex); + std::lock_guard<std::recursive_mutex> guard(m_collection_mutex); BreakpointLocationSP stop_sp; if (i < m_break_loc_collection.size()) stop_sp = m_break_loc_collection[i]; @@ -168,7 +168,7 @@ bool BreakpointLocationCollection::ShouldStop( } bool BreakpointLocationCollection::ValidForThisThread(Thread &thread) { - std::lock_guard<std::mutex> guard(m_collection_mutex); + std::lock_guard<std::recursive_mutex> guard(m_collection_mutex); collection::iterator pos, begin = m_break_loc_collection.begin(), end = m_break_loc_collection.end(); @@ -180,7 +180,7 @@ bool BreakpointLocationCollection::ValidForThisThread(Thread &thread) { } bool BreakpointLocationCollection::IsInternal() const { - std::lock_guard<std::mutex> guard(m_collection_mutex); + std::lock_guard<std::recursive_mutex> guard(m_collection_mutex); collection::const_iterator pos, begin = m_break_loc_collection.begin(), end = m_break_loc_collection.end(); @@ -197,7 +197,7 @@ bool BreakpointLocationCollection::IsInternal() const { void BreakpointLocationCollection::GetDescription( Stream *s, lldb::DescriptionLevel level) { - std::lock_guard<std::mutex> guard(m_collection_mutex); + std::lock_guard<std::recursive_mutex> guard(m_collection_mutex); collection::iterator pos, begin = m_break_loc_collection.begin(), end = m_break_loc_collection.end(); @@ -212,8 +212,10 @@ BreakpointLocationCollection &BreakpointLocationCollection::operator=( const BreakpointLocationCollection &rhs) { if (this != &rhs) { std::lock(m_collection_mutex, rhs.m_collection_mutex); - std::lock_guard<std::mutex> lhs_guard(m_collection_mutex, std::adopt_lock); - std::lock_guard<std::mutex> rhs_guard(rhs.m_collection_mutex, std::adopt_lock); + std::lock_guard<std::recursive_mutex> lhs_guard(m_collection_mutex, + std::adopt_lock); + std::lock_guard<std::recursive_mutex> rhs_guard(rhs.m_collection_mutex, + std::adopt_lock); m_break_loc_collection = rhs.m_break_loc_collection; } return *this; diff --git a/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp b/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp index a94e9e2..cef1ef1 100644 --- a/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp +++ b/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp @@ -139,7 +139,7 @@ void BreakpointResolverFileLine::FilterContexts(SymbolContextList &sc_list) { if (!sc.block) continue; - SupportFileSP file_sp; + SupportFileNSP file_sp = std::make_shared<SupportFile>(); uint32_t line; const Block *inline_block = sc.block->GetContainingInlinedBlock(); if (inline_block) { diff --git a/lldb/source/Breakpoint/BreakpointResolverName.cpp b/lldb/source/Breakpoint/BreakpointResolverName.cpp index 4f252f9..2025f59 100644 --- a/lldb/source/Breakpoint/BreakpointResolverName.cpp +++ b/lldb/source/Breakpoint/BreakpointResolverName.cpp @@ -218,19 +218,22 @@ StructuredData::ObjectSP BreakpointResolverName::SerializeToStructuredData() { void BreakpointResolverName::AddNameLookup(ConstString name, FunctionNameType name_type_mask) { - - Module::LookupInfo lookup(name, name_type_mask, m_language); - m_lookups.emplace_back(lookup); + std::vector<Module::LookupInfo> infos = + Module::LookupInfo::MakeLookupInfos(name, name_type_mask, m_language); + llvm::append_range(m_lookups, infos); auto add_variant_funcs = [&](Language *lang) { for (Language::MethodNameVariant variant : lang->GetMethodNameVariants(name)) { // FIXME: Should we be adding variants that aren't of type Full? if (variant.GetType() & lldb::eFunctionNameTypeFull) { - Module::LookupInfo variant_lookup(name, variant.GetType(), - lang->GetLanguageType()); - variant_lookup.SetLookupName(variant.GetName()); - m_lookups.emplace_back(variant_lookup); + std::vector<Module::LookupInfo> variant_lookups = + Module::LookupInfo::MakeLookupInfos(name, variant.GetType(), + lang->GetLanguageType()); + llvm::for_each(variant_lookups, [&](auto &variant_lookup) { + variant_lookup.SetLookupName(variant.GetName()); + }); + llvm::append_range(m_lookups, variant_lookups); } } return IterationAction::Continue; @@ -401,14 +404,22 @@ void BreakpointResolverName::GetDescription(Stream *s) { if (m_match_type == Breakpoint::Regexp) s->Printf("regex = '%s'", m_regex.GetText().str().c_str()); else { - size_t num_names = m_lookups.size(); - if (num_names == 1) - s->Printf("name = '%s'", m_lookups[0].GetName().GetCString()); + // Since there may be many lookups objects for the same name breakpoint (one + // per language available), unique them by name, and operate on those unique + // names. + std::vector<ConstString> unique_lookups; + for (auto &lookup : m_lookups) { + if (!llvm::is_contained(unique_lookups, lookup.GetName())) + unique_lookups.push_back(lookup.GetName()); + } + if (unique_lookups.size() == 1) + s->Printf("name = '%s'", unique_lookups[0].GetCString()); else { + size_t num_names = unique_lookups.size(); s->Printf("names = {"); for (size_t i = 0; i < num_names; i++) { s->Printf("%s'%s'", (i == 0 ? "" : ", "), - m_lookups[i].GetName().GetCString()); + unique_lookups[i].GetCString()); } s->Printf("}"); } diff --git a/lldb/source/Breakpoint/BreakpointSite.cpp b/lldb/source/Breakpoint/BreakpointSite.cpp index fd7666b..8639379 100644 --- a/lldb/source/Breakpoint/BreakpointSite.cpp +++ b/lldb/source/Breakpoint/BreakpointSite.cpp @@ -168,6 +168,22 @@ bool BreakpointSite::ValidForThisThread(Thread &thread) { return m_constituents.ValidForThisThread(thread); } +bool BreakpointSite::ContainsUserBreakpointForThread(Thread &thread) { + if (ThreadSP backed_thread = thread.GetBackedThread()) + return ContainsUserBreakpointForThread(*backed_thread); + + std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex); + for (const BreakpointLocationSP &bp_loc : + m_constituents.BreakpointLocations()) { + const Breakpoint &bp = bp_loc->GetBreakpoint(); + if (bp.IsInternal()) + continue; + if (bp_loc->ValidForThisThread(thread)) + return true; + } + return false; +} + void BreakpointSite::BumpHitCounts() { std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex); for (BreakpointLocationSP loc_sp : m_constituents.BreakpointLocations()) { diff --git a/lldb/source/Commands/CMakeLists.txt b/lldb/source/Commands/CMakeLists.txt index 69e4c45..f2e6224 100644 --- a/lldb/source/Commands/CMakeLists.txt +++ b/lldb/source/Commands/CMakeLists.txt @@ -58,6 +58,9 @@ add_lldb_library(lldbCommands NO_PLUGIN_DEPENDENCIES lldbUtility lldbValueObject lldbVersion + CLANG_LIBS + clangFrontend + clangSerialization ) add_dependencies(lldbCommands LLDBOptionsGen) diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp index 5a55126..75dc890 100644 --- a/lldb/source/Commands/CommandObjectBreakpoint.cpp +++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -45,6 +45,38 @@ static void AddBreakpointDescription(Stream *s, Breakpoint *bp, s->EOL(); } +static bool GetDefaultFile(Target &target, StackFrame *cur_frame, + FileSpec &file, CommandReturnObject &result) { + // First use the Source Manager's default file. Then use the current stack + // frame's file. + if (auto maybe_file_and_line = + target.GetSourceManager().GetDefaultFileAndLine()) { + file = maybe_file_and_line->support_file_nsp->GetSpecOnly(); + return true; + } + + if (cur_frame == nullptr) { + result.AppendError("No selected frame to use to find the default file."); + return false; + } + if (!cur_frame->HasDebugInformation()) { + result.AppendError("Cannot use the selected frame to find the default " + "file, it has no debug info."); + return false; + } + + const SymbolContext &sc = + cur_frame->GetSymbolContext(eSymbolContextLineEntry); + if (sc.line_entry.GetFile()) { + file = sc.line_entry.GetFile(); + } else { + result.AppendError("Can't find the file for the selected frame to " + "use as the default file."); + return false; + } + return true; +} + // Modifiable Breakpoint Options #pragma mark Modify::CommandOptions #define LLDB_OPTIONS_breakpoint_modify @@ -200,6 +232,53 @@ public: BreakpointOptions m_bp_opts; }; +// This is the Breakpoint Names option group - used to add Names to breakpoints +// while making them. Not to be confused with the "Breakpoint Name" option +// group which is the common options of various "breakpoint name" commands. +#define LLDB_OPTIONS_breakpoint_names +#include "CommandOptions.inc" + +class BreakpointNamesOptionGroup : public OptionGroup { +public: + BreakpointNamesOptionGroup() = default; + + ~BreakpointNamesOptionGroup() override = default; + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return g_breakpoint_names_options; + } + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, + ExecutionContext *execution_context) override { + Status error; + const int short_option = GetDefinitions()[option_idx].short_option; + const char *long_option = GetDefinitions()[option_idx].long_option; + + switch (short_option) { + case 'N': + if (BreakpointID::StringIsBreakpointName(option_value, error)) + m_breakpoint_names.push_back(std::string(option_value)); + else + error = Status::FromError( + CreateOptionParsingError(option_value, short_option, long_option, + "Invalid breakpoint name")); + break; + } + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_breakpoint_names.clear(); + } + + const std::vector<std::string> &GetBreakpointNames() { + return m_breakpoint_names; + } + +protected: + std::vector<std::string> m_breakpoint_names; +}; + #define LLDB_OPTIONS_breakpoint_dummy #include "CommandOptions.inc" @@ -237,6 +316,1190 @@ public: bool m_use_dummy; }; +#pragma mark AddAddress::CommandOptions +#define LLDB_OPTIONS_breakpoint_add_address +#include "CommandOptions.inc" + +#pragma mark Add Address + +static bool CopyOverBreakpointOptions(BreakpointSP bp_sp, + BreakpointOptionGroup &bp_opts, + const std::vector<std::string> &bp_names, + CommandReturnObject &result) { + assert(bp_sp && "CopyOverBreakpointOptions called with no breakpoint"); + + bp_sp->GetOptions().CopyOverSetOptions(bp_opts.GetBreakpointOptions()); + Target &target = bp_sp->GetTarget(); + if (!bp_names.empty()) { + Status name_error; + for (auto name : bp_names) { + target.AddNameToBreakpoint(bp_sp, name.c_str(), name_error); + if (name_error.Fail()) { + result.AppendErrorWithFormat("Invalid breakpoint name: %s", + name.c_str()); + target.RemoveBreakpointByID(bp_sp->GetID()); + return false; + } + } + } + return true; +} + +static llvm::Expected<LanguageType> +GetExceptionLanguageForLanguage(llvm::StringRef lang_name, + char short_option = '\0', + llvm::StringRef long_option = {}) { + llvm::Expected<LanguageType> exception_language = + Language::GetExceptionLanguageForLanguage(lang_name); + if (!exception_language) { + std::string error_msg = llvm::toString(exception_language.takeError()); + return CreateOptionParsingError(lang_name, short_option, long_option, + error_msg); + } + return exception_language; +} + +static Status CompleteLineEntry(ExecutionContext &exe_ctx, + OptionValueFileColonLine &line_entry) { + Status error; + uint32_t line_num = line_entry.GetLineNumber(); + if (!line_entry.GetFileSpec()) { + FileSpec default_file_spec; + std::string error_msg; + Target *target = exe_ctx.GetTargetPtr(); + if (!target) { + error.FromErrorString("Can't complete a line entry with no " + "target"); + return error; + } + Debugger &dbg = target->GetDebugger(); + CommandReturnObject result(dbg.GetUseColor()); + if (!GetDefaultFile(*target, exe_ctx.GetFramePtr(), default_file_spec, + result)) { + error.FromErrorStringWithFormatv("{0}/nCouldn't get default file for " + "line {1}: {2}", + result.GetErrorString(), line_num, + error_msg); + return error; + } + line_entry.SetFile(default_file_spec); + } + return error; +} + +class CommandObjectBreakpointAddAddress : public CommandObjectParsed { +public: + CommandObjectBreakpointAddAddress(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "breakpoint add address", + "Add breakpoints by raw address", nullptr) { + CommandArgumentData bp_id_arg; + + // Define the first (and only) variant of this arg. + m_all_options.Append(&m_bp_opts, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_all_options.Append(&m_name_opts); + m_all_options.Append(&m_dummy_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_all_options.Append(&m_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_all_options.Finalize(); + + AddSimpleArgumentList(eArgTypeAddress, eArgRepeatPlus); + } + + ~CommandObjectBreakpointAddAddress() override = default; + + Options *GetOptions() override { return &m_all_options; } + + class CommandOptions : public OptionGroup { + public: + CommandOptions() = default; + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = GetDefinitions()[option_idx].short_option; + const char *long_option = GetDefinitions()[option_idx].long_option; + + switch (short_option) { + case 'H': + m_hardware = true; + break; + + case 's': + if (m_modules.GetSize() == 0) + m_modules.AppendIfUnique(FileSpec(option_arg)); + else + error = Status::FromError( + CreateOptionParsingError(option_arg, short_option, long_option, + "Only one shared library can be " + "specified for address breakpoints.")); + break; + + default: + llvm_unreachable("Unimplemented option"); + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_hardware = false; + m_modules.Clear(); + } + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::ArrayRef(g_breakpoint_add_address_options); + } + + // Instance variables to hold the values for command options. + bool m_hardware = false; // FIXME - this can go in the "modify" options. + FileSpecList m_modules; + }; + +protected: + void DoExecute(Args &command, CommandReturnObject &result) override { + // We've already asserted that there can only be one entry in m_modules: + const ExecutionContext &exe_ctx = m_interpreter.GetExecutionContext(); + // We don't set address breakpoints in the dummy target. + if (!exe_ctx.HasTargetScope() || exe_ctx.GetTargetPtr()->IsDummyTarget()) { + result.AppendError( + "can't set address breakpoints without a real target."); + return; + } + // Commands can't set internal breakpoints: + const bool internal = false; + + Target &target = exe_ctx.GetTargetRef(); + + FileSpec module_spec; + bool has_module = false; + if (m_options.m_modules.GetSize() != 0) { + has_module = true; + module_spec = m_options.m_modules.GetFileSpecAtIndex(0); + } + BreakpointSP bp_sp; + // Let's process the arguments first so we can short-circuit if there are + // any errors: + std::vector<lldb::addr_t> bp_addrs; + for (const Args::ArgEntry &arg_entry : command) { + Address bp_address; + Status error; + lldb::addr_t bp_load_addr = OptionArgParser::ToAddress( + &exe_ctx, arg_entry.ref(), LLDB_INVALID_ADDRESS, &error); + if (error.Fail()) { + result.AppendErrorWithFormatv("invalid argument value '{0}': {1}", + arg_entry.ref(), error); + return; + } + bp_addrs.push_back(bp_load_addr); + } + for (auto bp_addr : bp_addrs) { + if (has_module) + bp_sp = target.CreateAddressInModuleBreakpoint( + bp_addr, internal, module_spec, m_options.m_hardware); + else + // ENHANCEMENT: we should see if bp_addr is in a single loaded module, + // and pass that module in if it is. + bp_sp = + target.CreateBreakpoint(bp_addr, internal, m_options.m_hardware); + } + + if (bp_sp) { + CopyOverBreakpointOptions(bp_sp, m_bp_opts, + m_name_opts.GetBreakpointNames(), result); + Stream &output_stream = result.GetOutputStream(); + bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial, + /*show_locations=*/false); + result.SetStatus(eReturnStatusSuccessFinishResult); + } else { + result.AppendError("Breakpoint creation failed: No breakpoint created."); + } + } + +private: + BreakpointOptionGroup m_bp_opts; + BreakpointNamesOptionGroup m_name_opts; + BreakpointDummyOptionGroup m_dummy_options; + CommandOptions m_options; + OptionGroupOptions m_all_options; +}; + +#pragma mark AddException::CommandOptions +#define LLDB_OPTIONS_breakpoint_add_exception +#include "CommandOptions.inc" + +#pragma mark Add Exception + +class CommandObjectBreakpointAddException : public CommandObjectParsed { +public: + CommandObjectBreakpointAddException(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "breakpoint add exception", + "Add breakpoints on language exceptions. If no language is " + "specified, break on exceptions for all supported languages", + nullptr) { + // Define the first (and only) variant of this arg. + AddSimpleArgumentList(eArgTypeLanguage, eArgRepeatStar); + + // Next add all the options. + m_all_options.Append(&m_bp_opts, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_all_options.Append(&m_name_opts); + m_all_options.Append(&m_dummy_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_all_options.Append(&m_options); + m_all_options.Finalize(); + } + + ~CommandObjectBreakpointAddException() override = default; + + Options *GetOptions() override { return &m_all_options; } + + class CommandOptions : public OptionGroup { + public: + CommandOptions() = default; + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = GetDefinitions()[option_idx].short_option; + + switch (short_option) { + case 'E': { + uint32_t this_val = (uint32_t)OptionArgParser::ToOptionEnum( + option_arg, GetDefinitions()[option_idx].enum_values, + eExceptionStageThrow, error); + if (error.Fail()) + return error; + m_exception_stage |= this_val; + } break; + case 'H': + m_hardware = true; + break; + + case 'O': + m_exception_extra_args.AppendArgument("-O"); + m_exception_extra_args.AppendArgument(option_arg); + break; + + default: + llvm_unreachable("Unimplemented option"); + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_hardware = false; + m_exception_extra_args.Clear(); + m_exception_stage = eExceptionStageThrow; + } + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::ArrayRef(g_breakpoint_add_exception_options); + } + + // Instance variables to hold the values for command options. + bool m_hardware = false; // FIXME - this can go in the "modify" options. + Args m_exception_extra_args; + uint32_t m_exception_stage = eExceptionStageThrow; + }; + +protected: + void DoExecute(Args &command, CommandReturnObject &result) override { + Target &target = + m_dummy_options.m_use_dummy ? GetDummyTarget() : GetTarget(); + BreakpointSP bp_sp; + LanguageType exception_language = eLanguageTypeUnknown; + + if (command.size() == 0) { + result.AppendError("no languages specified"); + } else if (command.size() > 1) { + result.AppendError( + "can only set exception breakpoints on one language at a time"); + } else { + llvm::Expected<LanguageType> language = + GetExceptionLanguageForLanguage(command[0].ref()); + if (language) + exception_language = *language; + else { + result.SetError(language.takeError()); + return; + } + } + Status precond_error; + const bool internal = false; + bool catch_bp = (m_options.m_exception_stage & eExceptionStageCatch) != 0; + bool throw_bp = (m_options.m_exception_stage & eExceptionStageThrow) != 0; + bp_sp = target.CreateExceptionBreakpoint( + exception_language, catch_bp, throw_bp, internal, + &m_options.m_exception_extra_args, &precond_error); + if (precond_error.Fail()) { + result.AppendErrorWithFormat( + "Error setting extra exception arguments: %s", + precond_error.AsCString()); + target.RemoveBreakpointByID(bp_sp->GetID()); + return; + } + + if (bp_sp) { + CopyOverBreakpointOptions(bp_sp, m_bp_opts, + m_name_opts.GetBreakpointNames(), result); + Stream &output_stream = result.GetOutputStream(); + bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial, + /*show_locations=*/false); + // Note, we don't print a "got no locations" warning for exception + // breakpoints. They can get set in the dummy target, and we won't know + // how to actually set the breakpoint till we know what version of the + // relevant LanguageRuntime gets loaded. + if (&target == &GetDummyTarget()) + output_stream.Printf("Breakpoint set in dummy target, will get copied " + "into future targets.\n"); + result.SetStatus(eReturnStatusSuccessFinishResult); + } else { + result.AppendError("Breakpoint creation failed: No breakpoint created."); + } + } + +private: + BreakpointOptionGroup m_bp_opts; + BreakpointNamesOptionGroup m_name_opts; + BreakpointDummyOptionGroup m_dummy_options; + CommandOptions m_options; + OptionGroupOptions m_all_options; +}; + +#pragma mark AddFile::CommandOptions +#define LLDB_OPTIONS_breakpoint_add_file +#include "CommandOptions.inc" + +#pragma mark Add File + +class CommandObjectBreakpointAddFile : public CommandObjectParsed { +public: + CommandObjectBreakpointAddFile(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "breakpoint add file", + "Add breakpoints on lines in specified source files", nullptr) { + CommandArgumentEntry arg1; + CommandArgumentData linespec_arg; + CommandArgumentData no_arg; + + // Any number of linespecs in group 1: + linespec_arg.arg_type = eArgTypeFileLineColumn; + linespec_arg.arg_repetition = eArgRepeatPlus; + linespec_arg.arg_opt_set_association = LLDB_OPT_SET_1; + + arg1.push_back(linespec_arg); + + // Leave arg2 empty, there are no arguments to this variant. + CommandArgumentEntry arg2; + no_arg.arg_type = eArgTypeNone; + no_arg.arg_repetition = eArgRepeatOptional; + no_arg.arg_opt_set_association = LLDB_OPT_SET_2; + + arg2.push_back(linespec_arg); + + // Push the data for the first argument into the m_arguments vector. + m_arguments.push_back(arg1); + m_arguments.push_back(arg2); + + // Define the first (and only) variant of this arg. + m_all_options.Append(&m_bp_opts, LLDB_OPT_SET_ALL, + LLDB_OPT_SET_1 | LLDB_OPT_SET_2); + m_all_options.Append(&m_name_opts); + m_all_options.Append(&m_dummy_options, LLDB_OPT_SET_ALL, + LLDB_OPT_SET_1 | LLDB_OPT_SET_2); + m_all_options.Append(&m_options); + m_all_options.Finalize(); + } + + ~CommandObjectBreakpointAddFile() override = default; + + Options *GetOptions() override { return &m_all_options; } + + class CommandOptions : public OptionGroup { + public: + CommandOptions() = default; + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = GetDefinitions()[option_idx].short_option; + const char *long_option = GetDefinitions()[option_idx].long_option; + + switch (short_option) { + case 'f': + m_cur_value.SetFile(FileSpec(option_arg)); + break; + case 'l': + uint32_t line_num; + if (option_arg.getAsInteger(0, line_num)) + error = Status::FromError( + CreateOptionParsingError(option_arg, short_option, long_option, + g_int_parsing_error_message)); + else { + // The line number is the only required part of the options for a + // specifying the location - since we will fill in the file with the + // default file. So when we see a new line, the old line entry we + // were building is done. If we haven't gotten a file, try to fill + // in the default file, and then finish up this linespec and start + // the next one. + if (m_cur_value.GetLineNumber() != LLDB_INVALID_LINE_NUMBER) { + // FIXME: It should be possible to create a breakpoint with a list + // of file, line, column values. But for now we can only create + // one, so return an error here. The commented out code is what we + // will do when I come back to add that capability. + return Status::FromErrorString("Can only specify one file and line " + "pair at a time."); +#if 0 // This code will be appropriate once we have a resolver that can take + // more than one linespec at a time. + error = CompleteLineEntry(*execution_context, m_cur_value); + if (error.Fail()) + return error; + + m_line_specs.push_back(m_cur_value); + m_cur_value.Clear(); +#endif + } + m_cur_value.SetLine(line_num); + } + break; + case 'u': + uint32_t column_num; + if (option_arg.getAsInteger(0, column_num)) + error = Status::FromError( + CreateOptionParsingError(option_arg, short_option, long_option, + g_int_parsing_error_message)); + else + m_cur_value.SetColumn(column_num); + break; + case 'K': { + bool success; + bool value; + value = OptionArgParser::ToBoolean(option_arg, true, &success); + if (value) + m_skip_prologue = eLazyBoolYes; + else + m_skip_prologue = eLazyBoolNo; + + if (!success) + error = Status::FromError( + CreateOptionParsingError(option_arg, short_option, long_option, + g_bool_parsing_error_message)); + } break; + case 'm': { + bool success; + bool value; + value = OptionArgParser::ToBoolean(option_arg, true, &success); + if (value) + m_move_to_nearest_code = eLazyBoolYes; + else + m_move_to_nearest_code = eLazyBoolNo; + + if (!success) + error = Status::FromError( + CreateOptionParsingError(option_arg, short_option, long_option, + g_bool_parsing_error_message)); + } break; + case 's': + m_modules.AppendIfUnique(FileSpec(option_arg)); + break; + case 'H': + m_hardware = true; + break; + case 'S': { + lldb::addr_t tmp_offset_addr; + tmp_offset_addr = OptionArgParser::ToAddress(execution_context, + option_arg, 0, &error); + if (error.Success()) + m_offset_addr = tmp_offset_addr; + } break; + + default: + llvm_unreachable("Unimplemented option"); + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_hardware = false; + m_line_specs.clear(); + m_cur_value.Clear(); + m_skip_prologue = eLazyBoolCalculate; + m_modules.Clear(); + m_move_to_nearest_code = eLazyBoolCalculate; + m_offset_addr = 0; + } + + Status OptionParsingFinished(ExecutionContext *execution_context) override { + // We were supplied at least a line from the options, so fill in the + // default file if needed. + if (m_cur_value.GetLineNumber() != LLDB_INVALID_LINE_NUMBER) { + Status error = CompleteLineEntry(*execution_context, m_cur_value); + if (error.Fail()) + return error; + m_line_specs.push_back(m_cur_value); + m_cur_value.Clear(); + } + return {}; + } + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::ArrayRef(g_breakpoint_add_file_options); + } + + // Instance variables to hold the values for command options. + bool m_hardware = false; // FIXME - this can go in the "modify" options. + std::vector<OptionValueFileColonLine> m_line_specs; + LazyBool m_skip_prologue = eLazyBoolCalculate; + OptionValueFileColonLine m_cur_value; + FileSpecList m_modules; + LazyBool m_move_to_nearest_code = eLazyBoolCalculate; + lldb::addr_t m_offset_addr = 0; + }; + +protected: + void DoExecute(Args &command, CommandReturnObject &result) override { + bool internal = false; + Target &target = + m_dummy_options.m_use_dummy ? GetDummyTarget() : GetTarget(); + // FIXME: At present we can only make file & line breakpoints for one file + // and line pair. It wouldn't be hard to extend that, but I'm not adding + // features at this point so I'll leave that for a future patch. For now, + // flag this as an error. + + // I'm leaving this as a loop since that's how it should be when we can + // do more than one linespec at a time. + FileSpec default_file; + for (const Args::ArgEntry &this_arg : command) { + OptionValueFileColonLine value; + uint32_t line_value = LLDB_INVALID_LINE_NUMBER; + if (!this_arg.ref().getAsInteger(0, line_value)) { + // The argument is a plain number. Treat that as a line number, and + // allow it if we can find a default file & line. + std::string error_msg; + if (!GetDefaultFile(target, m_exe_ctx.GetFramePtr(), default_file, + result)) { + result.AppendErrorWithFormatv("Couldn't find default file for line " + "input: {0} - {1}", + line_value, error_msg); + return; + } + value.SetLine(line_value); + value.SetFile(default_file); + } else { + Status error = value.SetValueFromString(this_arg.c_str()); + if (error.Fail()) { + result.AppendErrorWithFormatv("Failed to parse linespec: {0}", error); + return; + } + } + m_options.m_line_specs.push_back(value); + } + + if (m_options.m_line_specs.size() != 1) { + result.AppendError("Can only make file and line breakpoints with one " + "specification at a time."); + return; + } + + BreakpointSP bp_sp; + // Only check for inline functions if + LazyBool check_inlines = eLazyBoolCalculate; + + OptionValueFileColonLine &this_spec = m_options.m_line_specs[0]; + bp_sp = target.CreateBreakpoint( + &(m_options.m_modules), this_spec.GetFileSpec(), + this_spec.GetLineNumber(), this_spec.GetColumnNumber(), + m_options.m_offset_addr, check_inlines, m_options.m_skip_prologue, + internal, m_options.m_hardware, m_options.m_move_to_nearest_code); + + if (bp_sp) { + CopyOverBreakpointOptions(bp_sp, m_bp_opts, + m_name_opts.GetBreakpointNames(), result); + Stream &output_stream = result.GetOutputStream(); + bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial, + /*show_locations=*/false); + if (&target == &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_sp->GetNumLocations() == 0) { + output_stream.Printf("WARNING: Unable to resolve breakpoint to any " + "actual locations.\n"); + } + } + result.SetStatus(eReturnStatusSuccessFinishResult); + } else { + result.AppendError("Breakpoint creation failed: No breakpoint created."); + } + } + +private: + BreakpointOptionGroup m_bp_opts; + BreakpointNamesOptionGroup m_name_opts; + BreakpointDummyOptionGroup m_dummy_options; + CommandOptions m_options; + OptionGroupOptions m_all_options; +}; + +#pragma mark AddName::CommandOptions +#define LLDB_OPTIONS_breakpoint_add_name +#include "CommandOptions.inc" + +#pragma mark Add Name + +class CommandObjectBreakpointAddName : public CommandObjectParsed { +public: + CommandObjectBreakpointAddName(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "breakpoint add name", + "Add breakpoints matching function or symbol names", + nullptr) { + // FIXME: Add a completer that's aware of the name match style. + // Define the first (and only) variant of this arg. + AddSimpleArgumentList(eArgTypeFunctionOrSymbol, eArgRepeatPlus); + + // Now add all the options groups. + m_all_options.Append(&m_bp_opts, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_all_options.Append(&m_name_opts); + m_all_options.Append(&m_dummy_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_all_options.Append(&m_options); + m_all_options.Finalize(); + } + + ~CommandObjectBreakpointAddName() override = default; + + Options *GetOptions() override { return &m_all_options; } + + class CommandOptions : public OptionGroup { + public: + CommandOptions() = default; + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = GetDefinitions()[option_idx].short_option; + const char *long_option = GetDefinitions()[option_idx].long_option; + + switch (short_option) { + case 'f': + m_files.AppendIfUnique(FileSpec(option_arg)); + break; + case 'K': { + bool success; + bool value; + value = OptionArgParser::ToBoolean(option_arg, true, &success); + if (!success) + error = Status::FromError( + CreateOptionParsingError(option_arg, short_option, long_option, + g_bool_parsing_error_message)); + else { + if (value) + m_skip_prologue = eLazyBoolYes; + else + m_skip_prologue = eLazyBoolNo; + } + } break; + case 'L': { + m_language = Language::GetLanguageTypeFromString(option_arg); + if (m_language == eLanguageTypeUnknown) + error = Status::FromError( + CreateOptionParsingError(option_arg, short_option, long_option, + g_language_parsing_error_message)); + } break; + case 'm': { + uint32_t this_val = (uint32_t)OptionArgParser::ToOptionEnum( + option_arg, GetDefinitions()[option_idx].enum_values, + eNameMatchStyleAuto, error); + if (error.Fail()) + return error; + m_lookup_style = (NameMatchStyle)this_val; + } break; + case 's': + m_modules.AppendIfUnique(FileSpec(option_arg)); + break; + case 'H': + m_hardware = true; + break; + case 'S': { + lldb::addr_t tmp_offset_addr; + tmp_offset_addr = OptionArgParser::ToAddress(execution_context, + option_arg, 0, &error); + if (error.Success()) + m_offset_addr = tmp_offset_addr; + } break; + + default: + llvm_unreachable("Unimplemented option"); + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_hardware = false; + m_skip_prologue = eLazyBoolCalculate; + m_files.Clear(); + m_language = eLanguageTypeUnknown; + m_modules.Clear(); + m_offset_addr = 0; + m_lookup_style = eNameMatchStyleAuto; + } + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::ArrayRef(g_breakpoint_add_name_options); + } + + // Instance variables to hold the values for command options. + bool m_hardware = false; // FIXME - this can go in the "modify" options. + LazyBool m_skip_prologue = eLazyBoolCalculate; + FileSpecList m_modules; + LanguageType m_language = eLanguageTypeUnknown; + FileSpecList m_files; + LazyBool m_move_to_nearest_code = eLazyBoolCalculate; + lldb::addr_t m_offset_addr = 0; + NameMatchStyle m_lookup_style = eNameMatchStyleAuto; + }; + +protected: + void DoExecute(Args &command, CommandReturnObject &result) override { + const bool internal = false; + Target &target = + m_dummy_options.m_use_dummy ? GetDummyTarget() : GetTarget(); + // Parse the argument list - this is a simple list of names. + std::vector<std::string> func_names; + for (const Args::ArgEntry &this_arg : command) { + func_names.push_back(this_arg.ref().str()); + } + BreakpointSP bp_sp; + if (!(m_options.m_lookup_style & eNameMatchStyleRegex)) + bp_sp = target.CreateBreakpoint( + &m_options.m_modules, &m_options.m_files, func_names, + (FunctionNameType)m_options.m_lookup_style, m_options.m_language, + m_options.m_offset_addr, m_options.m_skip_prologue, internal, + m_options.m_hardware); + else { + if (func_names.size() != 1) { + result.AppendError("Can only set function regular expression " + "breakpoints on one regex at a time."); + return; + } + std::string &func_regexp = func_names[0]; + RegularExpression regexp(func_regexp); + if (llvm::Error err = regexp.GetError()) { + result.AppendErrorWithFormat( + "Function name regular expression could not be compiled: %s", + llvm::toString(std::move(err)).c_str()); + // Check if the incorrect regex looks like a globbing expression and + // warn the user about it. + if (!func_regexp.empty()) { + if (func_regexp[0] == '*' || func_regexp[0] == '?') + result.AppendWarning( + "Function name regex does not accept glob patterns."); + } + return; + } + + bp_sp = target.CreateFuncRegexBreakpoint( + &(m_options.m_modules), &(m_options.m_files), std::move(regexp), + m_options.m_language, m_options.m_skip_prologue, internal, + m_options.m_hardware); + } + if (bp_sp) { + CopyOverBreakpointOptions(bp_sp, m_bp_opts, + m_name_opts.GetBreakpointNames(), result); + Stream &output_stream = result.GetOutputStream(); + bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial, + /*show_locations=*/false); + if (&target == &GetDummyTarget()) + output_stream.Printf("Breakpoint set in dummy target, will get copied " + "into future targets.\n"); + else { + if (bp_sp->GetNumLocations() == 0) { + output_stream.Printf("WARNING: Unable to resolve breakpoint to any " + "actual locations.\n"); + } + } + result.SetStatus(eReturnStatusSuccessFinishResult); + } else { + result.AppendError("Breakpoint creation failed: No breakpoint created."); + } + } + +private: + BreakpointOptionGroup m_bp_opts; + BreakpointNamesOptionGroup m_name_opts; + BreakpointDummyOptionGroup m_dummy_options; + CommandOptions m_options; + OptionGroupOptions m_all_options; +}; + +#pragma mark AddPattern::CommandOptions +#define LLDB_OPTIONS_breakpoint_add_pattern +#include "CommandOptions.inc" + +#pragma mark Add Pattern + +class CommandObjectBreakpointAddPattern : public CommandObjectRaw { +public: + CommandObjectBreakpointAddPattern(CommandInterpreter &interpreter) + : CommandObjectRaw(interpreter, "breakpoint add pattern", + "Add breakpoints matching patterns in the source text", + "breakpoint add pattern [options] -- <pattern>") { + AddSimpleArgumentList(eArgTypeRegularExpression, eArgRepeatPlain); + // Now add all the options groups. + m_all_options.Append(&m_bp_opts, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_all_options.Append(&m_name_opts); + m_all_options.Append(&m_dummy_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_all_options.Append(&m_options); + m_all_options.Finalize(); + } + + ~CommandObjectBreakpointAddPattern() override = default; + + Options *GetOptions() override { return &m_all_options; } + + class CommandOptions : public OptionGroup { + public: + CommandOptions() = default; + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = GetDefinitions()[option_idx].short_option; + const char *long_option = GetDefinitions()[option_idx].long_option; + + switch (short_option) { + case 'a': { + bool success; + bool value; + value = OptionArgParser::ToBoolean(option_arg, true, &success); + if (!success) + error = Status::FromError( + CreateOptionParsingError(option_arg, short_option, long_option, + g_bool_parsing_error_message)); + else + m_all_files = value; + } break; + case 'f': + m_files.AppendIfUnique(FileSpec(option_arg)); + break; + case 'm': { + bool success; + bool value; + value = OptionArgParser::ToBoolean(option_arg, true, &success); + if (!success) + error = Status::FromError( + CreateOptionParsingError(option_arg, short_option, long_option, + g_bool_parsing_error_message)); + else { + if (value) + m_move_to_nearest_code = eLazyBoolYes; + else + m_move_to_nearest_code = eLazyBoolNo; + } + } break; + case 'n': + m_func_names.insert(option_arg.str()); + break; + case 's': + m_modules.AppendIfUnique(FileSpec(option_arg)); + break; + case 'H': + m_hardware = true; + break; + default: + llvm_unreachable("Unimplemented option"); + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_hardware = false; + m_skip_prologue = eLazyBoolCalculate; + m_modules.Clear(); + m_files.Clear(); + m_func_names.clear(); + m_all_files = false; + m_move_to_nearest_code = eLazyBoolCalculate; + } + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::ArrayRef(g_breakpoint_add_pattern_options); + } + + // Instance variables to hold the values for command options. + bool m_hardware = false; // FIXME - this can go in the "modify" options. + LazyBool m_skip_prologue = eLazyBoolCalculate; + FileSpecList m_modules; + FileSpecList m_files; + std::unordered_set<std::string> m_func_names; + bool m_all_files = false; + LazyBool m_move_to_nearest_code = eLazyBoolCalculate; + }; + +protected: + void DoExecute(llvm::StringRef command, + CommandReturnObject &result) override { + const bool internal = false; + ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext(); + m_all_options.NotifyOptionParsingStarting(&exe_ctx); + + if (command.empty()) { + result.AppendError("no pattern to seek."); + return; + } + + OptionsWithRaw args(command); + + if (args.HasArgs()) { + if (!ParseOptionsAndNotify(args.GetArgs(), result, m_all_options, + exe_ctx)) + return; + } + llvm::StringRef pattern = args.GetRawPart(); + if (pattern.empty()) { + result.AppendError("no pattern to seek"); + return; + } + + Target &target = + m_dummy_options.m_use_dummy ? GetDummyTarget() : GetTarget(); + + BreakpointSP bp_sp; + const size_t num_files = m_options.m_files.GetSize(); + + if (num_files == 0 && !m_options.m_all_files) { + FileSpec file; + if (!GetDefaultFile(target, m_exe_ctx.GetFramePtr(), file, result)) { + result.AppendError( + "No files provided and could not find default file."); + return; + } else { + m_options.m_files.Append(file); + } + } + + RegularExpression regexp(pattern); + if (llvm::Error err = regexp.GetError()) { + result.AppendErrorWithFormat( + "Source text regular expression could not be compiled: \"%s\"", + llvm::toString(std::move(err)).c_str()); + return; + } + bp_sp = target.CreateSourceRegexBreakpoint( + &(m_options.m_modules), &(m_options.m_files), m_options.m_func_names, + std::move(regexp), internal, m_options.m_hardware, + m_options.m_move_to_nearest_code); + + if (bp_sp) { + CopyOverBreakpointOptions(bp_sp, m_bp_opts, + m_name_opts.GetBreakpointNames(), result); + Stream &output_stream = result.GetOutputStream(); + bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial, + /*show_locations=*/false); + if (&target == &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_sp->GetNumLocations() == 0) { + output_stream.Printf("WARNING: Unable to resolve breakpoint to any " + "actual locations.\n"); + } + } + result.SetStatus(eReturnStatusSuccessFinishResult); + } else { + result.AppendError("Breakpoint creation failed: No breakpoint created."); + } + } + +private: + BreakpointOptionGroup m_bp_opts; + BreakpointNamesOptionGroup m_name_opts; + BreakpointDummyOptionGroup m_dummy_options; + CommandOptions m_options; + OptionGroupOptions m_all_options; +}; + +#pragma mark AddScripted::CommandOptions +#define LLDB_OPTIONS_breakpoint_add_scripted +#include "CommandOptions.inc" + +#pragma mark Add Scripted + +class CommandObjectBreakpointAddScripted : public CommandObjectParsed { +public: + CommandObjectBreakpointAddScripted(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "breakpoint add scripted", + "Add breakpoints using a scripted breakpoint resolver.", nullptr), + m_python_class_options("scripted breakpoint", true, 'P') { + // We're picking up all the normal options, commands and disable. + m_all_options.Append(&m_python_class_options, + LLDB_OPT_SET_1 | LLDB_OPT_SET_2, LLDB_OPT_SET_1); + // Define the first (and only) variant of this arg. + m_all_options.Append(&m_bp_opts, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_all_options.Append(&m_name_opts); + m_all_options.Append(&m_dummy_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_all_options.Append(&m_options); + m_all_options.Finalize(); + } + + ~CommandObjectBreakpointAddScripted() override = default; + + Options *GetOptions() override { return &m_all_options; } + + class CommandOptions : public OptionGroup { + public: + CommandOptions() = default; + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = GetDefinitions()[option_idx].short_option; + + switch (short_option) { + case 'f': + m_files.Append(FileSpec(option_arg)); + break; + case 's': + m_modules.AppendIfUnique(FileSpec(option_arg)); + break; + case 'H': + m_hardware = true; + break; + + default: + llvm_unreachable("Unimplemented option"); + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_hardware = false; + m_files.Clear(); + m_modules.Clear(); + } + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::ArrayRef(g_breakpoint_add_scripted_options); + } + + // Instance variables to hold the values for command options. + bool m_hardware = false; // FIXME - this can go in the "modify" options. + FileSpecList m_files; + FileSpecList m_modules; + }; + +protected: + void DoExecute(Args &command, CommandReturnObject &result) override { + Target &target = + m_dummy_options.m_use_dummy ? GetDummyTarget() : GetTarget(); + + BreakpointSP bp_sp; + Status error; + bp_sp = target.CreateScriptedBreakpoint( + m_python_class_options.GetName().c_str(), &(m_options.m_modules), + &(m_options.m_files), false, m_options.m_hardware, + m_python_class_options.GetStructuredData(), &error); + if (error.Fail()) { + result.AppendErrorWithFormat( + "error setting extra exception arguments: %s", error.AsCString()); + target.RemoveBreakpointByID(bp_sp->GetID()); + return; + } + + if (bp_sp) { + CopyOverBreakpointOptions(bp_sp, m_bp_opts, + m_name_opts.GetBreakpointNames(), result); + Stream &output_stream = result.GetOutputStream(); + bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial, + /*show_locations=*/false); + if (&target == &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_sp->GetNumLocations() == 0) { + output_stream.Printf("WARNING: Unable to resolve breakpoint to any " + "actual locations.\n"); + } + } + result.SetStatus(eReturnStatusSuccessFinishResult); + } else { + result.AppendError("breakpoint creation failed: No breakpoint created."); + } + } + +private: + BreakpointOptionGroup m_bp_opts; + BreakpointNamesOptionGroup m_name_opts; + BreakpointDummyOptionGroup m_dummy_options; + OptionGroupPythonClassWithDict m_python_class_options; + CommandOptions m_options; + OptionGroupOptions m_all_options; +}; + +#pragma mark Add::CommandOptions +#define LLDB_OPTIONS_breakpoint_add +#include "CommandOptions.inc" + +#pragma mark Add + +class CommandObjectBreakpointAdd : public CommandObjectMultiword { +public: + CommandObjectBreakpointAdd(CommandInterpreter &interpreter) + : CommandObjectMultiword(interpreter, "add", + "Commands to add breakpoints of various types") { + SetHelpLong( + R"( +Access the breakpoint search kernels built into lldb. Along with specifying the +search kernel, each breakpoint add operation can specify a common set of +"reaction" options for each breakpoint. The reaction options can also be +modified after breakpoint creation using the "breakpoint modify" command. + )"); + CommandObjectSP address_command_object( + new CommandObjectBreakpointAddAddress(interpreter)); + CommandObjectSP exception_command_object( + new CommandObjectBreakpointAddException(interpreter)); + CommandObjectSP file_command_object( + new CommandObjectBreakpointAddFile(interpreter)); + CommandObjectSP name_command_object( + new CommandObjectBreakpointAddName(interpreter)); + CommandObjectSP pattern_command_object( + new CommandObjectBreakpointAddPattern(interpreter)); + CommandObjectSP scripted_command_object( + new CommandObjectBreakpointAddScripted(interpreter)); + + LoadSubCommand("address", address_command_object); + LoadSubCommand("exception", exception_command_object); + LoadSubCommand("file", file_command_object); + LoadSubCommand("name", name_command_object); + LoadSubCommand("pattern", pattern_command_object); + LoadSubCommand("scripted", scripted_command_object); + } +}; + #define LLDB_OPTIONS_breakpoint_set #include "CommandOptions.inc" @@ -313,42 +1576,12 @@ public: break; case 'E': { - LanguageType language = Language::GetLanguageTypeFromString(option_arg); - - llvm::StringRef error_context; - switch (language) { - case eLanguageTypeC89: - case eLanguageTypeC: - case eLanguageTypeC99: - case eLanguageTypeC11: - m_exception_language = eLanguageTypeC; - break; - case eLanguageTypeC_plus_plus: - case eLanguageTypeC_plus_plus_03: - case eLanguageTypeC_plus_plus_11: - case eLanguageTypeC_plus_plus_14: - m_exception_language = eLanguageTypeC_plus_plus; - break; - case eLanguageTypeObjC_plus_plus: - error_context = - "Set exception breakpoints separately for c++ and objective-c"; - break; - case eLanguageTypeUnknown: - error_context = "Unknown language type for exception breakpoint"; - break; - default: - if (Language *languagePlugin = Language::FindPlugin(language)) { - if (languagePlugin->SupportsExceptionBreakpointsOnThrow() || - languagePlugin->SupportsExceptionBreakpointsOnCatch()) { - m_exception_language = language; - break; - } - } - error_context = "Unsupported language type for exception breakpoint"; - } - if (!error_context.empty()) - error = Status::FromError(CreateOptionParsingError( - option_arg, short_option, long_option, error_context)); + llvm::Expected<LanguageType> language = GetExceptionLanguageForLanguage( + option_arg, short_option, long_option); + if (language) + m_exception_language = *language; + else + error = Status::FromError(language.takeError()); } break; case 'f': @@ -608,8 +1841,8 @@ protected: FileSpec file; const size_t num_files = m_options.m_filenames.GetSize(); if (num_files == 0) { - if (!GetDefaultFile(target, file, result)) { - result.AppendError("no file supplied and no default file available"); + if (!GetDefaultFile(target, m_exe_ctx.GetFramePtr(), file, result)) { + result.AppendError("no file supplied and no default file available."); return; } } else if (num_files > 1) { @@ -694,7 +1927,7 @@ protected: if (num_files == 0 && !m_options.m_all_files) { FileSpec file; - if (!GetDefaultFile(target, file, result)) { + if (!GetDefaultFile(target, m_exe_ctx.GetFramePtr(), file, result)) { result.AppendError( "No files provided and could not find default file."); return; @@ -789,40 +2022,6 @@ protected: } private: - bool GetDefaultFile(Target &target, FileSpec &file, - CommandReturnObject &result) { - // First use the Source Manager's default file. Then use the current stack - // frame's file. - if (auto maybe_file_and_line = - target.GetSourceManager().GetDefaultFileAndLine()) { - file = maybe_file_and_line->support_file_sp->GetSpecOnly(); - return true; - } - - StackFrame *cur_frame = m_exe_ctx.GetFramePtr(); - if (cur_frame == nullptr) { - result.AppendError( - "No selected frame to use to find the default file."); - return false; - } - if (!cur_frame->HasDebugInformation()) { - result.AppendError("Cannot use the selected frame to find the default " - "file, it has no debug info."); - return false; - } - - const SymbolContext &sc = - cur_frame->GetSymbolContext(eSymbolContextLineEntry); - if (sc.line_entry.GetFile()) { - file = sc.line_entry.GetFile(); - } else { - result.AppendError("Can't find the file for the selected frame to " - "use as the default file."); - return false; - } - return true; - } - BreakpointOptionGroup m_bp_opts; BreakpointDummyOptionGroup m_dummy_options; OptionGroupPythonClassWithDict m_python_class_options; @@ -2408,6 +3607,8 @@ CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint( new CommandObjectBreakpointDelete(interpreter)); CommandObjectSP set_command_object( new CommandObjectBreakpointSet(interpreter)); + CommandObjectSP add_command_object( + new CommandObjectBreakpointAdd(interpreter)); CommandObjectSP command_command_object( new CommandObjectBreakpointCommand(interpreter)); CommandObjectSP modify_command_object( @@ -2425,6 +3626,7 @@ CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint( clear_command_object->SetCommandName("breakpoint clear"); delete_command_object->SetCommandName("breakpoint delete"); set_command_object->SetCommandName("breakpoint set"); + add_command_object->SetCommandName("breakpoint add"); command_command_object->SetCommandName("breakpoint command"); modify_command_object->SetCommandName("breakpoint modify"); name_command_object->SetCommandName("breakpoint name"); @@ -2437,6 +3639,7 @@ CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint( LoadSubCommand("clear", clear_command_object); LoadSubCommand("delete", delete_command_object); LoadSubCommand("set", set_command_object); + LoadSubCommand("add", add_command_object); LoadSubCommand("command", command_command_object); LoadSubCommand("modify", modify_command_object); LoadSubCommand("name", name_command_object); diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp index 0d9eb45..40f00c9 100644 --- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp +++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp @@ -95,9 +95,9 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, StackFrame *frame = m_exe_ctx.GetFramePtr(); // Either the language was explicitly specified, or we check the frame. - lldb::LanguageType language = m_expr_options.language; - if (language == lldb::eLanguageTypeUnknown && frame) - language = frame->GuessLanguage().AsLanguageType(); + SourceLanguage language{m_expr_options.language}; + if (!language && frame) + language = frame->GuessLanguage(); // Add a hint if object description was requested, but no description // function was implemented. @@ -119,8 +119,8 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, "^<\\S+: 0x[[:xdigit:]]{5,}>\\s*$"); if (GetDebugger().GetShowDontUsePoHint() && target_ptr && - (language == lldb::eLanguageTypeSwift || - language == lldb::eLanguageTypeObjC) && + (language.AsLanguageType() == lldb::eLanguageTypeSwift || + language.IsObjC()) && std::regex_match(output.data(), swift_class_regex)) { result.AppendNote( @@ -193,7 +193,8 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, // Second, try `expr` as a persistent variable. if (expr.starts_with("$")) - if (auto *state = target.GetPersistentExpressionStateForLanguage(language)) + if (auto *state = target.GetPersistentExpressionStateForLanguage( + language.AsLanguageType())) if (auto var_sp = state->GetVariable(expr)) if (auto valobj_sp = var_sp->GetValueObject()) { dump_val_object(*valobj_sp); diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp index 197bffe9..4919bd3 100644 --- a/lldb/source/Commands/CommandObjectExpression.cpp +++ b/lldb/source/Commands/CommandObjectExpression.cpp @@ -13,6 +13,7 @@ #include "lldb/Expression/UserExpression.h" #include "lldb/Host/OptionParser.h" #include "lldb/Host/StreamFile.h" +#include "lldb/Host/common/DiagnosticsRendering.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandOptionArgumentTable.h" #include "lldb/Interpreter/CommandReturnObject.h" @@ -21,7 +22,6 @@ #include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" -#include "lldb/Utility/DiagnosticsRendering.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" #include "lldb/lldb-private-enumerations.h" diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp index 88a02dc..9133359 100644 --- a/lldb/source/Commands/CommandObjectFrame.cpp +++ b/lldb/source/Commands/CommandObjectFrame.cpp @@ -265,6 +265,29 @@ public: Options *GetOptions() override { return &m_options; } +private: + void SkipHiddenFrames(Thread &thread, uint32_t frame_idx) { + uint32_t candidate_idx = frame_idx; + const unsigned max_depth = 12; + for (unsigned num_try = 0; num_try < max_depth; ++num_try) { + if (candidate_idx == 0 && *m_options.relative_frame_offset == -1) { + candidate_idx = UINT32_MAX; + break; + } + candidate_idx += *m_options.relative_frame_offset; + if (auto candidate_sp = thread.GetStackFrameAtIndex(candidate_idx)) { + if (candidate_sp->IsHidden()) + continue; + // Now candidate_idx is the first non-hidden frame. + break; + } + candidate_idx = UINT32_MAX; + break; + }; + if (candidate_idx != UINT32_MAX) + m_options.relative_frame_offset = candidate_idx - frame_idx; + } + protected: void DoExecute(Args &command, CommandReturnObject &result) override { // No need to check "thread" for validity as eCommandRequiresThread ensures @@ -278,28 +301,13 @@ protected: if (frame_idx == UINT32_MAX) frame_idx = 0; - // If moving up/down by one, skip over hidden frames. - if (*m_options.relative_frame_offset == 1 || - *m_options.relative_frame_offset == -1) { - uint32_t candidate_idx = frame_idx; - const unsigned max_depth = 12; - for (unsigned num_try = 0; num_try < max_depth; ++num_try) { - if (candidate_idx == 0 && *m_options.relative_frame_offset == -1) { - candidate_idx = UINT32_MAX; - break; - } - candidate_idx += *m_options.relative_frame_offset; - if (auto candidate_sp = thread->GetStackFrameAtIndex(candidate_idx)) { - if (candidate_sp->IsHidden()) - continue; - // Now candidate_idx is the first non-hidden frame. - break; - } - candidate_idx = UINT32_MAX; - break; - }; - if (candidate_idx != UINT32_MAX) - m_options.relative_frame_offset = candidate_idx - frame_idx; + // If moving up/down by one, skip over hidden frames, unless we started + // in a hidden frame. + if ((*m_options.relative_frame_offset == 1 || + *m_options.relative_frame_offset == -1)) { + if (auto current_frame_sp = thread->GetStackFrameAtIndex(frame_idx); + !current_frame_sp->IsHidden()) + SkipHiddenFrames(*thread, frame_idx); } if (*m_options.relative_frame_offset < 0) { diff --git a/lldb/source/Commands/CommandObjectMultiword.cpp b/lldb/source/Commands/CommandObjectMultiword.cpp index a369557..e08b33c 100644 --- a/lldb/source/Commands/CommandObjectMultiword.cpp +++ b/lldb/source/Commands/CommandObjectMultiword.cpp @@ -205,7 +205,7 @@ void CommandObjectMultiword::Execute(const char *args_string, .str()); } error_msg.append("\n"); - result.AppendRawError(error_msg.c_str()); + result.AppendError(error_msg); } std::string CommandObjectMultiword::GetSubcommandsHintText() { diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index 7d326404..c17f12f 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -1603,8 +1603,8 @@ public: Options *GetOptions() override { return &m_options; } void PrintSignalHeader(Stream &str) { - str.Printf("NAME PASS STOP NOTIFY\n"); - str.Printf("=========== ===== ===== ======\n"); + str.Printf("NAME PASS STOP NOTIFY DESCRIPTION\n"); + str.Printf("=========== ===== ===== ====== ===================\n"); } void PrintSignal(Stream &str, int32_t signo, llvm::StringRef sig_name, @@ -1615,9 +1615,16 @@ public: str.Format("{0, -11} ", sig_name); if (signals_sp->GetSignalInfo(signo, suppress, stop, notify)) { - bool pass = !suppress; + const bool pass = !suppress; str.Printf("%s %s %s", (pass ? "true " : "false"), (stop ? "true " : "false"), (notify ? "true " : "false")); + + const llvm::StringRef sig_description = + signals_sp->GetSignalNumberDescription(signo); + if (!sig_description.empty()) { + str.PutCString(" "); + str.PutCString(sig_description); + } } str.Printf("\n"); } diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp index 0b4599b..c9835e7 100644 --- a/lldb/source/Commands/CommandObjectSource.cpp +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -777,7 +777,7 @@ protected: if (sc.function) { Target &target = GetTarget(); - SupportFileSP start_file = std::make_shared<SupportFile>(); + SupportFileNSP start_file = std::make_shared<SupportFile>(); uint32_t start_line; uint32_t end_line; FileSpec end_file; @@ -1194,7 +1194,7 @@ protected: // file(s) will be found and assigned to // sc.comp_unit->GetPrimarySupportFile, which is NOT what we want to // print. Instead, we want to print the one from the line entry. - lldb::SupportFileSP found_file_sp = sc.line_entry.file_sp; + SupportFileNSP found_file_sp = sc.line_entry.file_sp; target.GetSourceManager().DisplaySourceLinesWithLineNumbers( found_file_sp, m_options.start_line, column, 0, diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index 8de6521..322dd6c 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -51,6 +51,7 @@ #include "lldb/Utility/ConstString.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/ScriptedMetadata.h" #include "lldb/Utility/State.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StructuredData.h" @@ -60,6 +61,7 @@ #include "lldb/lldb-forward.h" #include "lldb/lldb-private-enumerations.h" +#include "clang/Driver/CreateInvocationFromArgs.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendActions.h" @@ -5121,6 +5123,15 @@ public: : CommandObjectParsed(interpreter, "target stop-hook delete", "Delete a stop-hook.", "target stop-hook delete [<idx>]") { + SetHelpLong( + R"( +Deletes the stop hook by index. + +At any given stop, all enabled stop hooks that pass the stop filter will +get a chance to run. That means if one stop-hook deletes another stop hook +while executing, the deleted stop hook will still fire for the stop at which +it was deleted. + )"); AddSimpleArgumentList(eArgTypeStopHookID, eArgRepeatStar); } @@ -5392,6 +5403,200 @@ public: ~CommandObjectTargetDump() override = default; }; +#pragma mark CommandObjectTargetFrameProvider + +#define LLDB_OPTIONS_target_frame_provider_register +#include "CommandOptions.inc" + +class CommandObjectTargetFrameProviderRegister : public CommandObjectParsed { +public: + CommandObjectTargetFrameProviderRegister(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "target frame-provider register", + "Register frame provider for all threads in this target.", nullptr, + eCommandRequiresTarget), + + m_class_options("target frame-provider", true, 'C', 'k', 'v', 0) { + m_all_options.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2, + LLDB_OPT_SET_ALL); + m_all_options.Finalize(); + } + + ~CommandObjectTargetFrameProviderRegister() override = default; + + Options *GetOptions() override { return &m_all_options; } + + std::optional<std::string> GetRepeatCommand(Args ¤t_command_args, + uint32_t index) override { + return std::string(""); + } + +protected: + void DoExecute(Args &command, CommandReturnObject &result) override { + ScriptedMetadataSP metadata_sp = std::make_shared<ScriptedMetadata>( + m_class_options.GetName(), m_class_options.GetStructuredData()); + + Target *target = m_exe_ctx.GetTargetPtr(); + if (!target) + target = &GetDebugger().GetDummyTarget(); + + // Create the interface for calling static methods. + ScriptedFrameProviderInterfaceSP interface_sp = + GetDebugger() + .GetScriptInterpreter() + ->CreateScriptedFrameProviderInterface(); + + // Create a descriptor from the metadata (applies to all threads by + // default). + ScriptedFrameProviderDescriptor descriptor(metadata_sp); + descriptor.interface_sp = interface_sp; + + auto id_or_err = target->AddScriptedFrameProviderDescriptor(descriptor); + if (!id_or_err) { + result.SetError(id_or_err.takeError()); + return; + } + + result.AppendMessageWithFormat( + "successfully registered scripted frame provider '%s' for target\n", + m_class_options.GetName().c_str()); + } + + OptionGroupPythonClassWithDict m_class_options; + OptionGroupOptions m_all_options; +}; + +class CommandObjectTargetFrameProviderClear : public CommandObjectParsed { +public: + CommandObjectTargetFrameProviderClear(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "target frame-provider clear", + "Clear all registered frame providers from this target.", nullptr, + eCommandRequiresTarget) {} + + ~CommandObjectTargetFrameProviderClear() override = default; + +protected: + void DoExecute(Args &command, CommandReturnObject &result) override { + Target *target = m_exe_ctx.GetTargetPtr(); + if (!target) { + result.AppendError("invalid target"); + return; + } + + target->ClearScriptedFrameProviderDescriptors(); + + result.SetStatus(eReturnStatusSuccessFinishResult); + } +}; + +class CommandObjectTargetFrameProviderList : public CommandObjectParsed { +public: + CommandObjectTargetFrameProviderList(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "target frame-provider list", + "List all registered frame providers for the target.", nullptr, + eCommandRequiresTarget) {} + + ~CommandObjectTargetFrameProviderList() override = default; + +protected: + void DoExecute(Args &command, CommandReturnObject &result) override { + Target *target = m_exe_ctx.GetTargetPtr(); + if (!target) + target = &GetDebugger().GetDummyTarget(); + + const auto &descriptors = target->GetScriptedFrameProviderDescriptors(); + if (descriptors.empty()) { + result.AppendMessage("no frame providers registered for this target."); + result.SetStatus(eReturnStatusSuccessFinishResult); + return; + } + + result.AppendMessageWithFormat("%u frame provider(s) registered:\n\n", + descriptors.size()); + + for (const auto &entry : descriptors) { + const ScriptedFrameProviderDescriptor &descriptor = entry.second; + descriptor.Dump(&result.GetOutputStream()); + result.GetOutputStream().PutChar('\n'); + } + + result.SetStatus(eReturnStatusSuccessFinishResult); + } +}; + +class CommandObjectTargetFrameProviderRemove : public CommandObjectParsed { +public: + CommandObjectTargetFrameProviderRemove(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "target frame-provider remove", + "Remove a registered frame provider from the target by id.", + "target frame-provider remove <provider-id>", + eCommandRequiresTarget) { + AddSimpleArgumentList(eArgTypeUnsignedInteger, eArgRepeatPlus); + } + + ~CommandObjectTargetFrameProviderRemove() override = default; + +protected: + void DoExecute(Args &command, CommandReturnObject &result) override { + Target *target = m_exe_ctx.GetTargetPtr(); + if (!target) + target = &GetDebugger().GetDummyTarget(); + + std::vector<uint32_t> removed_provider_ids; + for (size_t i = 0; i < command.GetArgumentCount(); i++) { + uint32_t provider_id = 0; + if (!llvm::to_integer(command[i].ref(), provider_id)) { + result.AppendError("target frame-provider remove requires integer " + "provider id argument"); + return; + } + + if (!target->RemoveScriptedFrameProviderDescriptor(provider_id)) { + result.AppendErrorWithFormat( + "no frame provider named '%u' found in target\n", provider_id); + return; + } + removed_provider_ids.push_back(provider_id); + } + + if (size_t num_removed_providers = removed_provider_ids.size()) { + result.AppendMessageWithFormat( + "Successfully removed %zu frame-providers.\n", num_removed_providers); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } else { + result.AppendError("0 frame providers removed.\n"); + } + } +}; + +class CommandObjectTargetFrameProvider : public CommandObjectMultiword { +public: + CommandObjectTargetFrameProvider(CommandInterpreter &interpreter) + : CommandObjectMultiword( + interpreter, "target frame-provider", + "Commands for registering and viewing frame providers for the " + "target.", + "target frame-provider [<sub-command-options>] ") { + LoadSubCommand("register", + CommandObjectSP(new CommandObjectTargetFrameProviderRegister( + interpreter))); + LoadSubCommand("clear", + CommandObjectSP( + new CommandObjectTargetFrameProviderClear(interpreter))); + LoadSubCommand( + "list", + CommandObjectSP(new CommandObjectTargetFrameProviderList(interpreter))); + LoadSubCommand( + "remove", CommandObjectSP( + new CommandObjectTargetFrameProviderRemove(interpreter))); + } + + ~CommandObjectTargetFrameProvider() override = default; +}; + #pragma mark CommandObjectMultiwordTarget // CommandObjectMultiwordTarget @@ -5407,6 +5612,9 @@ CommandObjectMultiwordTarget::CommandObjectMultiwordTarget( CommandObjectSP(new CommandObjectTargetDelete(interpreter))); LoadSubCommand("dump", CommandObjectSP(new CommandObjectTargetDump(interpreter))); + LoadSubCommand( + "frame-provider", + CommandObjectSP(new CommandObjectTargetFrameProvider(interpreter))); LoadSubCommand("list", CommandObjectSP(new CommandObjectTargetList(interpreter))); LoadSubCommand("select", diff --git a/lldb/source/Commands/CommandObjectVersion.cpp b/lldb/source/Commands/CommandObjectVersion.cpp index f13ec18..fb7e399 100644 --- a/lldb/source/Commands/CommandObjectVersion.cpp +++ b/lldb/source/Commands/CommandObjectVersion.cpp @@ -8,13 +8,21 @@ #include "CommandObjectVersion.h" +#include "lldb/Core/Debugger.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Version/Version.h" +#include "llvm/ADT/StringExtras.h" using namespace lldb; using namespace lldb_private; -// CommandObjectVersion +#define LLDB_OPTIONS_version +#include "CommandOptions.inc" + +llvm::ArrayRef<OptionDefinition> +CommandObjectVersion::CommandOptions::GetDefinitions() { + return llvm::ArrayRef(g_version_options); +} CommandObjectVersion::CommandObjectVersion(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "version", @@ -22,7 +30,45 @@ CommandObjectVersion::CommandObjectVersion(CommandInterpreter &interpreter) CommandObjectVersion::~CommandObjectVersion() = default; +// Dump the array values on a single line. +static void dump(const StructuredData::Array &array, Stream &s) { + std::vector<std::string> values; + array.ForEach([&](StructuredData::Object *object) -> bool { + values.emplace_back(object->GetStringValue().str()); + return true; + }); + + s << '[' << llvm::join(values, ", ") << ']'; +} + +// The default dump output is too verbose. +static void dump(const StructuredData::Dictionary &config, Stream &s) { + config.ForEach( + [&](llvm::StringRef key, StructuredData::Object *object) -> bool { + assert(object); + + StructuredData::Dictionary *value_dict = object->GetAsDictionary(); + assert(value_dict); + + StructuredData::ObjectSP value_sp = value_dict->GetValueForKey("value"); + assert(value_sp); + + s << " " << key << ": "; + if (StructuredData::Boolean *boolean = value_sp->GetAsBoolean()) + s << (boolean ? "yes" : "no"); + else if (StructuredData::Array *array = value_sp->GetAsArray()) + dump(*array, s); + s << '\n'; + + return true; + }); +} + void CommandObjectVersion::DoExecute(Args &args, CommandReturnObject &result) { result.AppendMessageWithFormat("%s\n", lldb_private::GetVersion()); + + if (m_options.verbose) + dump(*Debugger::GetBuildConfiguration(), result.GetOutputStream()); + result.SetStatus(eReturnStatusSuccessFinishResult); } diff --git a/lldb/source/Commands/CommandObjectVersion.h b/lldb/source/Commands/CommandObjectVersion.h index 4ba081b..ea2741c 100644 --- a/lldb/source/Commands/CommandObjectVersion.h +++ b/lldb/source/Commands/CommandObjectVersion.h @@ -9,20 +9,56 @@ #ifndef LLDB_SOURCE_COMMANDS_COMMANDOBJECTVERSION_H #define LLDB_SOURCE_COMMANDS_COMMANDOBJECTVERSION_H +#include "lldb/Host/OptionParser.h" #include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/Options.h" namespace lldb_private { -// CommandObjectVersion - class CommandObjectVersion : public CommandObjectParsed { public: CommandObjectVersion(CommandInterpreter &interpreter); ~CommandObjectVersion() override; + class CommandOptions : public Options { + public: + CommandOptions() = default; + + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) { + case 'v': + verbose = true; + break; + default: + llvm_unreachable("Unimplemented option"); + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + verbose = false; + } + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override; + + bool verbose; + }; + + Options *GetOptions() override { return &m_options; } + protected: void DoExecute(Args &args, CommandReturnObject &result) override; + +private: + CommandOptions m_options; }; } // namespace lldb_private diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index ed06131..d96354a 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -173,6 +173,14 @@ let Command = "breakpoint dummy" in { "is provided, which prime new targets.">; } +let Command = "breakpoint names" in { + def breakpoint_name_option_names : + Option<"breakpoint-name", "N">, Group<1>, + Arg<"BreakpointName">, + Desc<"Adds this name to the list of names for this breakpoint. " + "Can be specified more than once.">; +} + let Command = "breakpoint set" in { def breakpoint_set_shlib : Option<"shlib", "s">, @@ -357,6 +365,124 @@ let Command = "breakpoint set" in { */ } +let Command = "breakpoint add address" in { + def breakpoint_add_address_shlib : Option<"shlib", "s">, Arg<"ShlibName">, + Completion<"Module">, + Desc<"Set the breakpoint at an address relative to sections in this shared" + " library.">; + def breakpoint_add_address_hardware : Option<"hardware", "H">, + Desc<"Require the breakpoint to use hardware breakpoints.">; +} + +let Command = "breakpoint add exception" in { + def breakpoint_add_exception_hardware : Option<"hardware", "H">, + Desc<"Require the breakpoint to use hardware breakpoints.">; + def breakpoint_add_exception_typename : Option<"exception-typename", "O">, + Arg<"TypeName">, Desc<"The breakpoint will only stop if an " + "exception Object of this type is thrown. Can be repeated multiple times " + "to stop for multiple object types">; + def breakpoint_add_exception_stage : Option<"exception-stage", "E">, + Arg<"ExceptionStage">, + Desc<"Stop only at the specified exception stage. Can be specified more " + "than once to create a mask of stages.">; +} + +let Command = "breakpoint add file" in { + def breakpoint_add_file_hardware : Option<"hardware", "H">, + Desc<"Require the breakpoint to use hardware breakpoints.">; + def breakpoint_add_file_line : Option<"line", "l">, Group<2>, Arg<"LineNum">, + Required, + Desc<"Specifies the line number on which to set this breakpoint.">; + def breakpoint_add_file_filename : Option<"filename", "f">, Group<2>, + Arg<"Filename">, Completion<"SourceFile">, Desc<"The file in which to seek " + "the specified source line.">; + def breakpoint_add_file_column : Option<"column", "u">, Arg<"ColumnNum">, Group<2>, + Desc<"Specifies the column number on which to set this breakpoint.">; + def breakpoint_add_file_shlib : Option<"shlib", "s">, Arg<"ShlibName">, + Completion<"Module">, + Desc<"Set the breakpoint only in this shared library. Can repeat this " + "option multiple times to specify multiple shared libraries.">; + def breakpoint_add_file_move_to_nearest_code : Option<"move-to-nearest-code", "m">, + Arg<"Boolean">, + Desc<"Move breakpoints to nearest code. If not set the " + "target.move-to-nearest-code setting is used.">; + def breakpoint_add_file_address_slide : Option<"address-slide", "S">, + Arg<"Offset">, + Desc<"Add the specified offset to whatever address(es) the breakpoint " + "resolves to. At present this applies the offset directly as given, and " + "doesn't try to align it to instruction boundaries.">; + def breakpoint_add_file_skip_prologue : Option<"skip-prologue", "K">, + Arg<"Boolean">, + Desc<"Skip the prologue if the breakpoint is at the beginning of a " + "function. If not set the target.skip-prologue setting is used.">; +} + +let Command = "breakpoint add name" in { + def breakpoint_add_name_hardware : Option<"hardware", "H">, + Desc<"Require the breakpoint to use hardware breakpoints.">; + def breakpoint_add_name_address_slide : Option<"address-slide", "S">, + Arg<"Offset">, + Desc<"Add the specified offset to whatever address(es) the breakpoint " + "resolves to. At present this applies the offset directly as given, and " + "doesn't try to align it to instruction boundaries.">; + def breakpoint_add_name_shlib : Option<"shlib", "s">, Arg<"ShlibName">, + Completion<"Module">, + Desc<"Search for names only in this shared library. Can repeat this " + "option multiple times to specify multiple shared libraries.">; + def breakpoint_add_name_filename : Option<"filename", "f">, + Arg<"Filename">, Completion<"SourceFile">, Desc<"Only search for functions " + "defined in the given source file. Can be specified more than once.">; + def breakpoint_add_name_skip_prologue : Option<"skip-prologue", "K">, + Arg<"Boolean">, + Desc<"Skip the prologue if the breakpoint is at the beginning of a " + "function. If not set the target.skip-prologue setting is used.">; + def breakpoint_add_name_match_style : Option<"match-style", "m">, + Arg<"NameMatchStyle">, + Desc<"The style of matching to do when looking for candidate symbols - " + "auto if not specified.">; + def breakpoint_add_name_language : Option<"name-language", "L">, + Arg<"Language">, + Desc<"Only consider indentifiers from the given language when looking for " + "match candidates.">; +} + +let Command = "breakpoint add pattern" in { + def breakpoint_add_pattern_hardware : Option<"hardware", "H">, + Desc<"Require the breakpoint to use hardware breakpoints.">; + def breakpoint_add_pattern_move_to_nearest_code : Option<"move-to-nearest-code", "m">, + Arg<"Boolean">, + Desc<"Move breakpoints to nearest code. If not set the " + "target.move-to-nearest-code setting is used.">; + def breakpoint_add_pattern_shlib : Option<"shlib", "s">, Arg<"ShlibName">, + Completion<"Module">, + Desc<"Search for pattern matches only in this shared library. Can repeat this " + "option multiple times to specify multiple shared libraries.">; + def breakpoint_add_pattern_filename : Option<"filename", "f">, Group<1>, + Arg<"Filename">, Completion<"SourceFile">, Desc<"Limit the pattern search " + "to the specified source file. Can be specified more than once.">; + def breakpoint_add_pattern_name : Option<"name", "n">, Arg<"FunctionName">, + Completion<"Symbol">, Group<1>, + Desc<"Search for pattern matches only in functions matching the specified " + "function name using the 'auto' match style. Can be specified more than " + "once, and composes with the filename option.">; + def breakpoint_add_pattern_all_files : Option<"all-files", "a">, Group<2>, + Desc<"All files are searched for source pattern matches, limited by the " + "shlib argument.">; +} + +let Command = "breakpoint add scripted" in { + def breakpoint_add_scripted_hardware : Option<"hardware", "H">, + Desc<"Require the breakpoint to use hardware breakpoints.">; + def breakpoint_add_scripted_filename : Option<"filename", "f">, + Arg<"Filename">, Completion<"SourceFile">, Desc<"The files in which to apply " + "the scripted resolver callback. Can repeat the option multiple times.">; + def breakpoint_add_scripted_shlib : Option<"shlib", "s">, Arg<"ShlibName">, + Completion<"Module">, + Desc<"The module in which to apply the scripted resolver callback. Can " + "repeat this option multiple times to specify multiple shared libraries.">; +} + + let Command = "breakpoint clear" in { def breakpoint_clear_file : Option<"file", "f">, Group<1>, @@ -2432,3 +2558,8 @@ let Command = "statistics dump" in { "enabled state. Defaults to true for both summary and default " "mode.">; } + +let Command = "version" in { + def version_verbose : Option<"verbose", "v">, + Desc<"Include build configuration in version output.">; +} diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td index 1be911c..a54d553 100644 --- a/lldb/source/Core/CoreProperties.td +++ b/lldb/source/Core/CoreProperties.td @@ -59,7 +59,7 @@ let Definition = "debugger" in { Desc<"The default disassembly format string to use when disassembling instruction sequences.">; def FrameFormat: Property<"frame-format", "FormatEntity">, Global, - DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">, + DefaultStringValue<"frame #${frame.index}: {${ansi.fg.cyan}${frame.pc}${ansi.normal}}{ ${module.file.basename}{`}}{${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">, Desc<"The default frame format string to use when displaying stack frame information for threads.">; def NotiftVoid: Property<"notify-void", "Boolean">, Global, @@ -235,7 +235,7 @@ let Definition = "debugger" in { Desc<"If true, LLDB will automatically escape non-printable and escape characters when formatting strings.">; def FrameFormatUnique: Property<"frame-format-unique", "FormatEntity">, Global, - DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">, + DefaultStringValue<"frame #${frame.index}: {${ansi.fg.cyan}${frame.pc}${ansi.normal}}{ ${module.file.basename}{`}}{${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">, Desc<"The default frame format string to use when displaying stack frame information for threads from thread backtrace unique.">; def ShowAutosuggestion: Property<"show-autosuggestion", "Boolean">, Global, diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index b37d9d3..99f4a72 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -21,12 +21,14 @@ #include "lldb/Core/Telemetry.h" #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/Expression/REPL.h" +#include "lldb/Host/Config.h" #include "lldb/Host/File.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/StreamFile.h" #include "lldb/Host/Terminal.h" #include "lldb/Host/ThreadLauncher.h" +#include "lldb/Host/XML.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/OptionValue.h" @@ -965,7 +967,8 @@ llvm::StringRef Debugger::GetStaticBroadcasterClass() { Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton) : UserID(g_unique_id++), Properties(std::make_shared<OptionValueProperties>()), - m_input_file_sp(std::make_shared<NativeFile>(stdin, NativeFile::Unowned)), + m_input_file_sp(std::make_shared<NativeFile>( + stdin, File::eOpenOptionReadOnly, NativeFile::Unowned)), m_output_stream_sp(std::make_shared<LockableStreamFile>( stdout, NativeFile::Unowned, m_output_mutex)), m_error_stream_sp(std::make_shared<LockableStreamFile>( @@ -1172,7 +1175,8 @@ Status Debugger::SetInputString(const char *data) { return result; } - SetInputFile((FileSP)std::make_shared<NativeFile>(commands_file, true)); + SetInputFile((FileSP)std::make_shared<NativeFile>( + commands_file, File::eOpenOptionReadOnly, true)); return result; } @@ -1378,7 +1382,8 @@ void Debugger::AdoptTopIOHandlerFilesIfInvalid(FileSP &in, in = GetInputFileSP(); // If there is nothing, use stdin if (!in) - in = std::make_shared<NativeFile>(stdin, NativeFile::Unowned); + in = std::make_shared<NativeFile>(stdin, File::eOpenOptionReadOnly, + NativeFile::Unowned); } // If no STDOUT has been set, then set it appropriately if (!out || !out->GetUnlockedFile().IsValid()) { @@ -2439,3 +2444,56 @@ llvm::ThreadPoolInterface &Debugger::GetThreadPool() { "Debugger::GetThreadPool called before Debugger::Initialize"); return *g_thread_pool; } + +static void AddBoolConfigEntry(StructuredData::Dictionary &dict, + llvm::StringRef name, bool value, + llvm::StringRef description) { + auto entry_up = std::make_unique<StructuredData::Dictionary>(); + entry_up->AddBooleanItem("value", value); + entry_up->AddStringItem("description", description); + dict.AddItem(name, std::move(entry_up)); +} + +static void AddLLVMTargets(StructuredData::Dictionary &dict) { + auto array_up = std::make_unique<StructuredData::Array>(); +#define LLVM_TARGET(target) \ + array_up->AddItem(std::make_unique<StructuredData::String>(#target)); +#include "llvm/Config/Targets.def" + auto entry_up = std::make_unique<StructuredData::Dictionary>(); + entry_up->AddItem("value", std::move(array_up)); + entry_up->AddStringItem("description", "A list of configured LLVM targets."); + dict.AddItem("targets", std::move(entry_up)); +} + +StructuredData::DictionarySP Debugger::GetBuildConfiguration() { + auto config_up = std::make_unique<StructuredData::Dictionary>(); + AddBoolConfigEntry( + *config_up, "xml", XMLDocument::XMLEnabled(), + "A boolean value that indicates if XML support is enabled in LLDB"); + AddBoolConfigEntry( + *config_up, "curl", LLVM_ENABLE_CURL, + "A boolean value that indicates if CURL support is enabled in LLDB"); + AddBoolConfigEntry( + *config_up, "curses", LLDB_ENABLE_CURSES, + "A boolean value that indicates if curses support is enabled in LLDB"); + AddBoolConfigEntry( + *config_up, "editline", LLDB_ENABLE_LIBEDIT, + "A boolean value that indicates if editline support is enabled in LLDB"); + AddBoolConfigEntry(*config_up, "editline_wchar", LLDB_EDITLINE_USE_WCHAR, + "A boolean value that indicates if editline wide " + "characters support is enabled in LLDB"); + AddBoolConfigEntry( + *config_up, "lzma", LLDB_ENABLE_LZMA, + "A boolean value that indicates if lzma support is enabled in LLDB"); + AddBoolConfigEntry( + *config_up, "python", LLDB_ENABLE_PYTHON, + "A boolean value that indicates if python support is enabled in LLDB"); + AddBoolConfigEntry( + *config_up, "lua", LLDB_ENABLE_LUA, + "A boolean value that indicates if lua support is enabled in LLDB"); + AddBoolConfigEntry(*config_up, "fbsdvmcore", LLDB_ENABLE_FBSDVMCORE, + "A boolean value that indicates if fbsdvmcore support is " + "enabled in LLDB"); + AddLLVMTargets(*config_up); + return config_up; +} diff --git a/lldb/source/Core/DemangledNameInfo.cpp b/lldb/source/Core/DemangledNameInfo.cpp index 76f8987..16fbfda 100644 --- a/lldb/source/Core/DemangledNameInfo.cpp +++ b/lldb/source/Core/DemangledNameInfo.cpp @@ -16,7 +16,7 @@ bool TrackingOutputBuffer::shouldTrack() const { if (!isPrintingTopLevelFunctionType()) return false; - if (isGtInsideTemplateArgs()) + if (isInsideTemplateArgs()) return false; if (NameInfo.ArgumentsRange.first > 0) @@ -29,7 +29,7 @@ bool TrackingOutputBuffer::canFinalize() const { if (!isPrintingTopLevelFunctionType()) return false; - if (isGtInsideTemplateArgs()) + if (isInsideTemplateArgs()) return false; if (NameInfo.ArgumentsRange.first == 0) diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index f2ed1f7..2d73df1 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -208,7 +208,7 @@ Disassembler::GetFunctionDeclLineEntry(const SymbolContext &sc) { return {}; LineEntry prologue_end_line = sc.line_entry; - SupportFileSP func_decl_file_sp; + SupportFileNSP func_decl_file_sp = std::make_shared<SupportFile>(); uint32_t func_decl_line; sc.function->GetStartLineSourceInfo(func_decl_file_sp, func_decl_line); @@ -286,6 +286,18 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( return false; } +static constexpr const llvm::StringLiteral kUndefLocation = "undef"; +static constexpr const llvm::StringLiteral kUndefLocationFormatted = "<undef>"; +static void +AddVariableAnnotationToVector(std::vector<VariableAnnotation> &annotations, + VariableAnnotation annotation_entity, + const bool is_live) { + annotation_entity.is_live = is_live; + if (!is_live) + annotation_entity.location_description = kUndefLocation; + annotations.push_back(std::move(annotation_entity)); +} + // For each instruction, this block attempts to resolve in-scope variables // and determine if the current PC falls within their // DWARF location entry. If so, it prints a simplified annotation using the @@ -299,17 +311,38 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( // The goal is to give users helpful live variable hints alongside the // disassembled instruction stream, similar to how debug information // enhances source-level debugging. -std::vector<std::string> -VariableAnnotator::annotate(Instruction &inst, Target &target, - const lldb::ModuleSP &module_sp) { +std::vector<std::string> VariableAnnotator::Annotate(Instruction &inst) { + std::vector<VariableAnnotation> structured_annotations = + AnnotateStructured(inst); + std::vector<std::string> events; + events.reserve(structured_annotations.size()); + + for (const VariableAnnotation &annotation : structured_annotations) { + const llvm::StringRef location = + (annotation.location_description == kUndefLocation + ? llvm::StringRef(kUndefLocationFormatted) + : llvm::StringRef(annotation.location_description)); + + events.push_back( + llvm::formatv("{0} = {1}", annotation.variable_name, location).str()); + } + + return events; +} + +std::vector<VariableAnnotation> +VariableAnnotator::AnnotateStructured(Instruction &inst) { + std::vector<VariableAnnotation> annotations; + + auto module_sp = inst.GetAddress().GetModule(); - // If we lost module context, everything becomes <undef>. + // If we lost module context, mark all live variables as UndefLocation. if (!module_sp) { - for (const auto &KV : Live_) - events.emplace_back(llvm::formatv("{0} = <undef>", KV.second.name).str()); - Live_.clear(); - return events; + for (const auto &KV : m_live_vars) + AddVariableAnnotationToVector(annotations, KV.second, false); + m_live_vars.clear(); + return annotations; } // Resolve function/block at this *file* address. @@ -319,13 +352,13 @@ VariableAnnotator::annotate(Instruction &inst, Target &target, if (!module_sp->ResolveSymbolContextForAddress(iaddr, mask, sc) || !sc.function) { // No function context: everything dies here. - for (const auto &KV : Live_) - events.emplace_back(llvm::formatv("{0} = <undef>", KV.second.name).str()); - Live_.clear(); - return events; + for (const auto &KV : m_live_vars) + AddVariableAnnotationToVector(annotations, KV.second, false); + m_live_vars.clear(); + return annotations; } - // Collect in-scope variables for this instruction into Current. + // Collect in-scope variables for this instruction into current_vars. VariableList var_list; // Innermost block containing iaddr. if (Block *B = sc.block) { @@ -341,7 +374,7 @@ VariableAnnotator::annotate(Instruction &inst, Target &target, const lldb::addr_t func_file = sc.function->GetAddress().GetFileAddress(); // ABI from Target (pretty reg names if plugin exists). Safe to be null. - lldb::ABISP abi_sp = ABI::FindPlugin(nullptr, target.GetArchitecture()); + lldb::ABISP abi_sp = ABI::FindPlugin(nullptr, module_sp->GetArchitecture()); ABI *abi = abi_sp.get(); llvm::DIDumpOptions opts; @@ -349,7 +382,7 @@ VariableAnnotator::annotate(Instruction &inst, Target &target, // Prefer "register-only" output when we have an ABI. opts.PrintRegisterOnly = static_cast<bool>(abi_sp); - llvm::DenseMap<lldb::user_id_t, VarState> Current; + llvm::DenseMap<lldb::user_id_t, VariableAnnotation> current_vars; for (size_t i = 0, e = var_list.GetSize(); i != e; ++i) { lldb::VariableSP v = var_list.GetVariableAtIndex(i); @@ -376,35 +409,50 @@ VariableAnnotator::annotate(Instruction &inst, Target &target, if (loc.empty()) continue; - Current.try_emplace(v->GetID(), - VarState{std::string(name), std::string(loc)}); + std::optional<std::string> decl_file; + std::optional<uint32_t> decl_line; + std::optional<std::string> type_name; + + const Declaration &decl = v->GetDeclaration(); + if (decl.GetFile()) { + decl_file = decl.GetFile().GetFilename().AsCString(); + if (decl.GetLine() > 0) + decl_line = decl.GetLine(); + } + + if (Type *type = v->GetType()) + if (const char *type_str = type->GetName().AsCString()) + type_name = type_str; + + current_vars.try_emplace( + v->GetID(), + VariableAnnotation{std::string(name), std::string(loc), true, + entry.expr->GetRegisterKind(), entry.file_range, + decl_file, decl_line, type_name}); } - // Diff Live_ → Current. + // Diff m_live_vars → current_vars. - // 1) Starts/changes: iterate Current and compare with Live_. - for (const auto &KV : Current) { - auto it = Live_.find(KV.first); - if (it == Live_.end()) { + // 1) Starts/changes: iterate current_vars and compare with m_live_vars. + for (const auto &KV : current_vars) { + auto it = m_live_vars.find(KV.first); + if (it == m_live_vars.end()) // Newly live. - events.emplace_back( - llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str()); - } else if (it->second.last_loc != KV.second.last_loc) { + AddVariableAnnotationToVector(annotations, KV.second, true); + else if (it->second.location_description != KV.second.location_description) // Location changed. - events.emplace_back( - llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str()); - } + AddVariableAnnotationToVector(annotations, KV.second, true); } - // 2) Ends: anything that was live but is not in Current becomes <undef>. - for (const auto &KV : Live_) { - if (!Current.count(KV.first)) - events.emplace_back(llvm::formatv("{0} = <undef>", KV.second.name).str()); - } + // 2) Ends: anything that was live but is not in current_vars becomes + // UndefLocation. + for (const auto &KV : m_live_vars) + if (!current_vars.count(KV.first)) + AddVariableAnnotationToVector(annotations, KV.second, false); // Commit new state. - Live_ = std::move(Current); - return events; + m_live_vars = std::move(current_vars); + return annotations; } void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, @@ -539,7 +587,8 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, LineEntry prologue_end_line = sc.line_entry; if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, prologue_end_line)) { - SupportFileSP func_decl_file_sp; + SupportFileNSP func_decl_file_sp = + std::make_shared<SupportFile>(); uint32_t func_decl_line; sc.function->GetStartLineSourceInfo(func_decl_file_sp, func_decl_line); @@ -676,7 +725,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, address_text_size); if ((options & eOptionVariableAnnotations) && target_sp) { - auto annotations = annot.annotate(*inst, *target_sp, module_sp); + auto annotations = annot.Annotate(*inst); if (!annotations.empty()) { const size_t annotation_column = 100; inst_line.FillLastLineToColumn(annotation_column, ' '); @@ -1340,6 +1389,11 @@ bool PseudoInstruction::DoesBranch() { return false; } +bool PseudoInstruction::IsBarrier() { + // This is NOT a valid question for a pseudo instruction. + return false; +} + bool PseudoInstruction::HasDelaySlot() { // This is NOT a valid question for a pseudo instruction. return false; diff --git a/lldb/source/Core/DynamicLoader.cpp b/lldb/source/Core/DynamicLoader.cpp index 7580b15..31d277b 100644 --- a/lldb/source/Core/DynamicLoader.cpp +++ b/lldb/source/Core/DynamicLoader.cpp @@ -165,7 +165,8 @@ ModuleSP DynamicLoader::FindModuleViaTarget(const FileSpec &file) { if (ModuleSP module_sp = target.GetImages().FindFirstModule(module_spec)) return module_sp; - if (ModuleSP module_sp = target.GetOrCreateModule(module_spec, false)) + if (ModuleSP module_sp = + target.GetOrCreateModule(module_spec, /*notify=*/false)) return module_sp; return nullptr; @@ -227,6 +228,7 @@ ModuleSP DynamicLoader::LoadBinaryWithUUIDAndAddress( } } ModuleSpec module_spec; + module_spec.SetTarget(target.shared_from_this()); module_spec.GetUUID() = uuid; FileSpec name_filespec(name); if (FileSystem::Instance().Exists(name_filespec)) @@ -238,8 +240,8 @@ ModuleSP DynamicLoader::LoadBinaryWithUUIDAndAddress( // Has lldb already seen a module with this UUID? // Or have external lookup enabled in DebugSymbols on macOS. if (!module_sp) - error = ModuleList::GetSharedModule(module_spec, module_sp, nullptr, - nullptr, nullptr); + error = + ModuleList::GetSharedModule(module_spec, module_sp, nullptr, nullptr); // Can lldb's symbol/executable location schemes // find an executable and symbol file. diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 491f5c6..faafb5e 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -27,6 +27,7 @@ #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/VariableList.h" +#include "lldb/Target/BorrowedStackFrame.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/ExecutionContextScope.h" #include "lldb/Target/Language.h" @@ -109,6 +110,7 @@ constexpr Definition g_frame_child_entries[] = { g_string_entry), Definition("is-artificial", EntryType::FrameIsArtificial), Definition("kind", EntryType::FrameKind), + Definition("borrowed-info", EntryType::FrameBorrowedInfo), }; constexpr Definition g_function_child_entries[] = { @@ -382,6 +384,7 @@ const char *FormatEntity::Entry::TypeToCString(Type t) { ENUM_TO_CSTR(FrameRegisterByName); ENUM_TO_CSTR(FrameIsArtificial); ENUM_TO_CSTR(FrameKind); + ENUM_TO_CSTR(FrameBorrowedInfo); ENUM_TO_CSTR(ScriptFrame); ENUM_TO_CSTR(FunctionID); ENUM_TO_CSTR(FunctionDidChange); @@ -1681,10 +1684,9 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { const Address &pc_addr = frame->GetFrameCodeAddress(); - if (pc_addr.IsValid() || frame->IsSynthetic()) { + if (pc_addr.IsValid()) if (DumpAddressAndContent(s, sc, exe_ctx, pc_addr, false)) return true; - } } } return false; @@ -1761,6 +1763,22 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, return false; } + case Entry::Type::FrameBorrowedInfo: { + if (exe_ctx) + if (StackFrame *frame = exe_ctx->GetFramePtr()) { + if (BorrowedStackFrame *borrowed_frame = + llvm::dyn_cast<BorrowedStackFrame>(frame)) { + if (lldb::StackFrameSP borrowed_from_sp = + borrowed_frame->GetBorrowedFrame()) { + s.Printf(" [borrowed from frame #%u]", + borrowed_from_sp->GetFrameIndex()); + return true; + } + } + } + return false; + } + case Entry::Type::ScriptFrame: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); @@ -1789,70 +1807,91 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, return initial_function; case Entry::Type::FunctionName: { - if (!sc) - return false; + if (sc) { + Language *language_plugin = nullptr; + bool language_plugin_handled = false; + StreamString ss; - Language *language_plugin = nullptr; - bool language_plugin_handled = false; - StreamString ss; + if (sc->function) + language_plugin = Language::FindPlugin(sc->function->GetLanguage()); + else if (sc->symbol) + language_plugin = Language::FindPlugin(sc->symbol->GetLanguage()); - if (sc->function) - language_plugin = Language::FindPlugin(sc->function->GetLanguage()); - else if (sc->symbol) - language_plugin = Language::FindPlugin(sc->symbol->GetLanguage()); + if (language_plugin) + language_plugin_handled = language_plugin->GetFunctionDisplayName( + *sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss); - if (language_plugin) - language_plugin_handled = language_plugin->GetFunctionDisplayName( - *sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss); + if (language_plugin_handled) { + s << ss.GetString(); + return true; + } - if (language_plugin_handled) { - s << ss.GetString(); - return true; + const char *name = sc->GetPossiblyInlinedFunctionName() + .GetName(Mangled::NamePreference::ePreferDemangled) + .AsCString(); + if (name) { + s.PutCString(name); + return true; + } } - const char *name = sc->GetPossiblyInlinedFunctionName() - .GetName(Mangled::NamePreference::ePreferDemangled) - .AsCString(); - if (!name) - return false; - - s.PutCString(name); - - return true; + // Fallback to frame methods if available. + if (exe_ctx) { + StackFrame *frame = exe_ctx->GetFramePtr(); + if (frame) { + const char *name = frame->GetFunctionName(); + if (name) { + s.PutCString(name); + return true; + } + } + } + return false; } case Entry::Type::FunctionNameNoArgs: { - if (!sc) - return false; - - Language *language_plugin = nullptr; - bool language_plugin_handled = false; - StreamString ss; - if (sc->function) - language_plugin = Language::FindPlugin(sc->function->GetLanguage()); - else if (sc->symbol) - language_plugin = Language::FindPlugin(sc->symbol->GetLanguage()); - - if (language_plugin) - language_plugin_handled = language_plugin->GetFunctionDisplayName( - *sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs, - ss); + if (sc) { + Language *language_plugin = nullptr; + bool language_plugin_handled = false; + StreamString ss; + if (sc->function) + language_plugin = Language::FindPlugin(sc->function->GetLanguage()); + else if (sc->symbol) + language_plugin = Language::FindPlugin(sc->symbol->GetLanguage()); + + if (language_plugin) + language_plugin_handled = language_plugin->GetFunctionDisplayName( + *sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs, + ss); + + if (language_plugin_handled) { + s << ss.GetString(); + return true; + } - if (language_plugin_handled) { - s << ss.GetString(); - return true; + const char *name = + sc->GetPossiblyInlinedFunctionName() + .GetName( + Mangled::NamePreference::ePreferDemangledWithoutArguments) + .AsCString(); + if (name) { + s.PutCString(name); + return true; + } } - const char *name = - sc->GetPossiblyInlinedFunctionName() - .GetName(Mangled::NamePreference::ePreferDemangledWithoutArguments) - .AsCString(); - if (!name) - return false; - - s.PutCString(name); - - return true; + // Fallback to frame methods if available. + if (exe_ctx) { + StackFrame *frame = exe_ctx->GetFramePtr(); + if (frame) { + const char *name = frame->GetFunctionName(); + if (name) { + s.PutCString(name); + return true; + } + } + } + return false; } case Entry::Type::FunctionPrefix: @@ -1879,13 +1918,26 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, } case Entry::Type::FunctionNameWithArgs: { - if (!sc) - return false; + if (sc) { + if (FormatFunctionNameForLanguage(s, exe_ctx, sc)) + return true; - if (FormatFunctionNameForLanguage(s, exe_ctx, sc)) - return true; + if (HandleFunctionNameWithArgs(s, exe_ctx, *sc)) + return true; + } - return HandleFunctionNameWithArgs(s, exe_ctx, *sc); + // Fallback to frame methods if available. + if (exe_ctx) { + StackFrame *frame = exe_ctx->GetFramePtr(); + if (frame) { + const char *name = frame->GetDisplayFunctionName(); + if (name) { + s.PutCString(name); + return true; + } + } + } + return false; } case Entry::Type::FunctionMangledName: { if (!sc) @@ -1927,12 +1979,11 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, case Entry::Type::FunctionPCOffset: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); - if (frame) { + if (frame) if (DumpAddressOffsetFromFunction(s, sc, exe_ctx, frame->GetFrameCodeAddress(), false, false, false)) return true; - } } return false; @@ -1956,11 +2007,8 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, case Entry::Type::LineEntryFile: if (sc && sc->line_entry.IsValid()) { - Module *module = sc->module_sp.get(); - if (module) { - if (DumpFile(s, sc->line_entry.GetFile(), (FileKind)entry.number)) - return true; - } + if (DumpFile(s, sc->line_entry.GetFile(), (FileKind)entry.number)) + return true; } return false; diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp index f27a95d..da2c188 100644 --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -52,9 +52,6 @@ #include "lldb/Host/windows/PosixApi.h" #endif -#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" -#include "Plugins/Language/ObjC/ObjCLanguage.h" - #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/DJB.h" @@ -646,26 +643,13 @@ void Module::FindCompileUnits(const FileSpec &path, Module::LookupInfo::LookupInfo(ConstString name, FunctionNameType name_type_mask, - LanguageType language) - : m_name(name), m_lookup_name(name), m_language(language) { + LanguageType lang_type) + : m_name(name), m_lookup_name(name), m_language(lang_type) { std::optional<ConstString> basename; - - std::vector<Language *> languages; - { - std::vector<LanguageType> lang_types; - if (language != eLanguageTypeUnknown) - lang_types.push_back(language); - else - lang_types = {eLanguageTypeObjC, eLanguageTypeC_plus_plus}; - - for (LanguageType lang_type : lang_types) { - if (Language *lang = Language::FindPlugin(lang_type)) - languages.push_back(lang); - } - } + Language *lang = Language::FindPlugin(lang_type); if (name_type_mask & eFunctionNameTypeAuto) { - for (Language *lang : languages) { + if (lang) { auto info = lang->GetFunctionNameInfo(name); if (info.first != eFunctionNameTypeNone) { m_name_type_mask |= info.first; @@ -682,7 +666,7 @@ Module::LookupInfo::LookupInfo(ConstString name, } else { m_name_type_mask = name_type_mask; - for (Language *lang : languages) { + if (lang) { auto info = lang->GetFunctionNameInfo(name); if (info.first & m_name_type_mask) { // If the user asked for FunctionNameTypes that aren't possible, @@ -691,14 +675,12 @@ Module::LookupInfo::LookupInfo(ConstString name, // ObjC) m_name_type_mask &= info.first; basename = info.second; - break; - } - // Still try and get a basename in case someone specifies a name type mask - // of eFunctionNameTypeFull and a name like "A::func" - if (name_type_mask & eFunctionNameTypeFull && - info.first != eFunctionNameTypeNone && !basename && info.second) { + } else if (name_type_mask & eFunctionNameTypeFull && + info.first != eFunctionNameTypeNone && !basename && + info.second) { + // Still try and get a basename in case someone specifies a name type + // mask of eFunctionNameTypeFull and a name like "A::func" basename = info.second; - break; } } } @@ -714,6 +696,36 @@ Module::LookupInfo::LookupInfo(ConstString name, } } +std::vector<Module::LookupInfo> +Module::LookupInfo::MakeLookupInfos(ConstString name, + lldb::FunctionNameType name_type_mask, + lldb::LanguageType lang_type) { + std::vector<LanguageType> lang_types; + if (lang_type != eLanguageTypeUnknown) { + lang_types.push_back(lang_type); + } else { + // If the language type was not specified, look up in every language + // available. + Language::ForEach([&](Language *lang) { + auto lang_type = lang->GetLanguageType(); + if (!llvm::is_contained(lang_types, lang_type)) + lang_types.push_back(lang_type); + return IterationAction::Continue; + }); + + if (lang_types.empty()) + lang_types = {eLanguageTypeObjC, eLanguageTypeC_plus_plus}; + } + + std::vector<Module::LookupInfo> infos; + infos.reserve(lang_types.size()); + for (LanguageType lang_type : lang_types) { + Module::LookupInfo info(name, name_type_mask, lang_type); + infos.push_back(info); + } + return infos; +} + bool Module::LookupInfo::NameMatchesLookupInfo( ConstString function_name, LanguageType language_type) const { // We always keep unnamed symbols @@ -808,22 +820,21 @@ void Module::LookupInfo::Prune(SymbolContextList &sc_list, } } -void Module::FindFunctions(const Module::LookupInfo &lookup_info, +void Module::FindFunctions(llvm::ArrayRef<Module::LookupInfo> lookup_infos, const CompilerDeclContext &parent_decl_ctx, const ModuleFunctionSearchOptions &options, SymbolContextList &sc_list) { - // Find all the functions (not symbols, but debug information functions... - if (SymbolFile *symbols = GetSymbolFile()) { + for (auto &lookup_info : lookup_infos) { + SymbolFile *symbols = GetSymbolFile(); + if (!symbols) + continue; + symbols->FindFunctions(lookup_info, parent_decl_ctx, options.include_inlines, sc_list); - // Now check our symbol table for symbols that are code symbols if - // requested - if (options.include_symbols) { - if (Symtab *symtab = symbols->GetSymtab()) { + if (options.include_symbols) + if (Symtab *symtab = symbols->GetSymtab()) symtab->FindFunctionSymbols(lookup_info.GetLookupName(), lookup_info.GetNameTypeMask(), sc_list); - } - } } } @@ -832,13 +843,16 @@ void Module::FindFunctions(ConstString name, FunctionNameType name_type_mask, const ModuleFunctionSearchOptions &options, SymbolContextList &sc_list) { - const size_t old_size = sc_list.GetSize(); - LookupInfo lookup_info(name, name_type_mask, eLanguageTypeUnknown); - FindFunctions(lookup_info, parent_decl_ctx, options, sc_list); - if (name_type_mask & eFunctionNameTypeAuto) { - const size_t new_size = sc_list.GetSize(); - if (old_size < new_size) - lookup_info.Prune(sc_list, old_size); + std::vector<LookupInfo> lookup_infos = + LookupInfo::MakeLookupInfos(name, name_type_mask, eLanguageTypeUnknown); + for (auto &lookup_info : lookup_infos) { + const size_t old_size = sc_list.GetSize(); + FindFunctions(lookup_info, parent_decl_ctx, options, sc_list); + if (name_type_mask & eFunctionNameTypeAuto) { + const size_t new_size = sc_list.GetSize(); + if (old_size < new_size) + lookup_info.Prune(sc_list, old_size); + } } } diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp index c40612c..be6ff72 100644 --- a/lldb/source/Core/ModuleList.cpp +++ b/lldb/source/Core/ModuleList.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "lldb/Core/ModuleList.h" +#include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" @@ -19,6 +20,8 @@ #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Target.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/FileSpecList.h" @@ -26,6 +29,7 @@ #include "lldb/Utility/Log.h" #include "lldb/Utility/UUID.h" #include "lldb/lldb-defines.h" +#include "llvm/Support/ThreadPool.h" #if defined(_WIN32) #include "lldb/Host/windows/PosixApi.h" @@ -449,21 +453,22 @@ void ModuleList::FindFunctions(ConstString name, FunctionNameType name_type_mask, const ModuleFunctionSearchOptions &options, SymbolContextList &sc_list) const { - const size_t old_size = sc_list.GetSize(); - if (name_type_mask & eFunctionNameTypeAuto) { - Module::LookupInfo lookup_info(name, name_type_mask, eLanguageTypeUnknown); - + std::vector<Module::LookupInfo> lookup_infos = + Module::LookupInfo::MakeLookupInfos(name, name_type_mask, + eLanguageTypeUnknown); std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); - for (const ModuleSP &module_sp : m_modules) { - module_sp->FindFunctions(lookup_info, CompilerDeclContext(), options, - sc_list); - } - - const size_t new_size = sc_list.GetSize(); + for (const auto &lookup_info : lookup_infos) { + const size_t old_size = sc_list.GetSize(); + for (const ModuleSP &module_sp : m_modules) { + module_sp->FindFunctions(lookup_info, CompilerDeclContext(), options, + sc_list); + } - if (old_size < new_size) - lookup_info.Prune(sc_list, old_size); + const size_t new_size = sc_list.GetSize(); + if (old_size < new_size) + lookup_info.Prune(sc_list, old_size); + } } else { std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); for (const ModuleSP &module_sp : m_modules) { @@ -476,21 +481,24 @@ void ModuleList::FindFunctions(ConstString name, void ModuleList::FindFunctionSymbols(ConstString name, lldb::FunctionNameType name_type_mask, SymbolContextList &sc_list) { - const size_t old_size = sc_list.GetSize(); - if (name_type_mask & eFunctionNameTypeAuto) { - Module::LookupInfo lookup_info(name, name_type_mask, eLanguageTypeUnknown); + std::vector<Module::LookupInfo> lookup_infos = + Module::LookupInfo::MakeLookupInfos(name, name_type_mask, + eLanguageTypeUnknown); std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); - for (const ModuleSP &module_sp : m_modules) { - module_sp->FindFunctionSymbols(lookup_info.GetLookupName(), - lookup_info.GetNameTypeMask(), sc_list); - } + for (const auto &lookup_info : lookup_infos) { + const size_t old_size = sc_list.GetSize(); + for (const ModuleSP &module_sp : m_modules) { + module_sp->FindFunctionSymbols(lookup_info.GetLookupName(), + lookup_info.GetNameTypeMask(), sc_list); + } - const size_t new_size = sc_list.GetSize(); + const size_t new_size = sc_list.GetSize(); - if (old_size < new_size) - lookup_info.Prune(sc_list, old_size); + if (old_size < new_size) + lookup_info.Prune(sc_list, old_size); + } } else { std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); for (const ModuleSP &module_sp : m_modules) { @@ -1038,9 +1046,9 @@ size_t ModuleList::RemoveOrphanSharedModules(bool mandatory) { Status ModuleList::GetSharedModule(const ModuleSpec &module_spec, ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, - bool *did_create_ptr, bool always_create) { + bool *did_create_ptr, bool always_create, + bool invoke_locate_callback) { SharedModuleList &shared_module_list = GetSharedModuleList(); std::lock_guard<std::recursive_mutex> guard(shared_module_list.GetMutex()); char path[PATH_MAX]; @@ -1095,6 +1103,22 @@ ModuleList::GetSharedModule(const ModuleSpec &module_spec, ModuleSP &module_sp, if (module_sp) return error; + // Try target's platform locate module callback before second attempt. + if (invoke_locate_callback) { + TargetSP target_sp = module_spec.GetTargetSP(); + if (target_sp && target_sp->IsValid()) { + if (PlatformSP platform_sp = target_sp->GetPlatform()) { + FileSpec symbol_file_spec; + platform_sp->CallLocateModuleCallbackIfSet( + module_spec, module_sp, symbol_file_spec, did_create_ptr); + if (module_sp) { + // The callback found a module. + return error; + } + } + } + } + module_sp = std::make_shared<Module>(module_spec); // Make sure there are a module and an object file since we can specify a // valid file path with an architecture that might not be in that file. By @@ -1122,10 +1146,16 @@ ModuleList::GetSharedModule(const ModuleSpec &module_spec, ModuleSP &module_sp, module_sp.reset(); } - if (module_search_paths_ptr) { - const auto num_directories = module_search_paths_ptr->GetSize(); + // Get module search paths from the target if available. + lldb::TargetSP target_sp = module_spec.GetTargetSP(); + FileSpecList module_search_paths; + if (target_sp) + module_search_paths = target_sp->GetExecutableSearchPaths(); + + if (!module_search_paths.IsEmpty()) { + const auto num_directories = module_search_paths.GetSize(); for (size_t idx = 0; idx < num_directories; ++idx) { - auto search_path_spec = module_search_paths_ptr->GetFileSpecAtIndex(idx); + auto search_path_spec = module_search_paths.GetFileSpecAtIndex(idx); FileSystem::Instance().Resolve(search_path_spec); namespace fs = llvm::sys::fs; if (!FileSystem::Instance().IsDirectory(search_path_spec)) @@ -1357,3 +1387,21 @@ void ModuleList::Swap(ModuleList &other) { m_modules_mutex, other.m_modules_mutex); m_modules.swap(other.m_modules); } + +void ModuleList::PreloadSymbols(bool parallelize) const { + std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); + + if (!parallelize) { + for (const ModuleSP &module_sp : m_modules) + module_sp->PreloadSymbols(); + return; + } + + llvm::ThreadPoolTaskGroup task_group(Debugger::GetThreadPool()); + for (const ModuleSP &module_sp : m_modules) + task_group.async([module_sp] { + if (module_sp) + module_sp->PreloadSymbols(); + }); + task_group.wait(); +} diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp index 5887367..4e3563c 100644 --- a/lldb/source/Core/PluginManager.cpp +++ b/lldb/source/Core/PluginManager.cpp @@ -1300,6 +1300,61 @@ PluginManager::GetScriptInterpreterForLanguage(lldb::ScriptLanguage script_lang, return none_instance(debugger); } +#pragma mark SyntheticFrameProvider + +typedef PluginInstance<SyntheticFrameProviderCreateInstance> + SyntheticFrameProviderInstance; +typedef PluginInstance<ScriptedFrameProviderCreateInstance> + ScriptedFrameProviderInstance; +typedef PluginInstances<SyntheticFrameProviderInstance> + SyntheticFrameProviderInstances; +typedef PluginInstances<ScriptedFrameProviderInstance> + ScriptedFrameProviderInstances; + +static SyntheticFrameProviderInstances &GetSyntheticFrameProviderInstances() { + static SyntheticFrameProviderInstances g_instances; + return g_instances; +} + +static ScriptedFrameProviderInstances &GetScriptedFrameProviderInstances() { + static ScriptedFrameProviderInstances g_instances; + return g_instances; +} + +bool PluginManager::RegisterPlugin( + llvm::StringRef name, llvm::StringRef description, + SyntheticFrameProviderCreateInstance create_native_callback, + ScriptedFrameProviderCreateInstance create_scripted_callback) { + if (create_native_callback) + return GetSyntheticFrameProviderInstances().RegisterPlugin( + name, description, create_native_callback); + else if (create_scripted_callback) + return GetScriptedFrameProviderInstances().RegisterPlugin( + name, description, create_scripted_callback); + return false; +} + +bool PluginManager::UnregisterPlugin( + SyntheticFrameProviderCreateInstance create_callback) { + return GetSyntheticFrameProviderInstances().UnregisterPlugin(create_callback); +} + +bool PluginManager::UnregisterPlugin( + ScriptedFrameProviderCreateInstance create_callback) { + return GetScriptedFrameProviderInstances().UnregisterPlugin(create_callback); +} + +SyntheticFrameProviderCreateInstance +PluginManager::GetSyntheticFrameProviderCreateCallbackForPluginName( + llvm::StringRef name) { + return GetSyntheticFrameProviderInstances().GetCallbackForName(name); +} + +ScriptedFrameProviderCreateInstance +PluginManager::GetScriptedFrameProviderCreateCallbackAtIndex(uint32_t idx) { + return GetScriptedFrameProviderInstances().GetCallbackAtIndex(idx); +} + #pragma mark StructuredDataPlugin struct StructuredDataPluginInstance diff --git a/lldb/source/Core/Section.cpp b/lldb/source/Core/Section.cpp index 02d9d86..f16035b 100644 --- a/lldb/source/Core/Section.cpp +++ b/lldb/source/Core/Section.cpp @@ -471,8 +471,14 @@ bool Section::ContainsOnlyDebugInfo() const { return false; } +bool Section::IsGOTSection() const { + return GetObjectFile()->IsGOTSection(*this); +} + #pragma mark SectionList +SectionList::SectionList(const SectionList &rhs) : m_sections(rhs.m_sections) {} + SectionList &SectionList::operator=(const SectionList &rhs) { if (this != &rhs) m_sections = rhs.m_sections; @@ -683,6 +689,33 @@ uint64_t SectionList::GetDebugInfoSize() const { return debug_info_size; } +SectionList SectionList::Merge(SectionList &lhs, SectionList &rhs, + MergeCallback filter) { + SectionList output_list; + + // Iterate through all the sections in lhs and see if we have matches in + // the rhs list. + for (const auto &lhs_section : lhs) { + auto rhs_section = rhs.FindSectionByName(lhs_section->GetName()); + if (rhs_section) + output_list.AddSection(filter(lhs_section, rhs_section)); + else + output_list.AddSection(lhs_section); + } + + // Now that we've visited all possible duplicates, we can iterate over + // the rhs and take any values not in lhs. + for (const auto &rhs_section : rhs) { + auto lhs_section = lhs.FindSectionByName(rhs_section->GetName()); + // Because we already visited everything overlapping between rhs + // and lhs, any section not in lhs is unique and can be output. + if (!lhs_section) + output_list.AddSection(rhs_section); + } + + return output_list; +} + namespace llvm { namespace json { diff --git a/lldb/source/Core/SourceManager.cpp b/lldb/source/Core/SourceManager.cpp index 097173f..c60288c 100644 --- a/lldb/source/Core/SourceManager.cpp +++ b/lldb/source/Core/SourceManager.cpp @@ -30,6 +30,7 @@ #include "lldb/Utility/Log.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Stream.h" +#include "lldb/Utility/SupportFile.h" #include "lldb/lldb-enumerations.h" #include "llvm/ADT/Twine.h" @@ -69,22 +70,20 @@ static std::string toString(const Checksum &checksum) { // SourceManager constructor SourceManager::SourceManager(const TargetSP &target_sp) - : m_last_support_file_sp(std::make_shared<SupportFile>()), m_last_line(0), + : m_last_support_file_nsp(std::make_shared<SupportFile>()), m_last_line(0), m_last_count(0), m_default_set(false), m_target_wp(target_sp), m_debugger_wp(target_sp->GetDebugger().shared_from_this()) {} SourceManager::SourceManager(const DebuggerSP &debugger_sp) - : m_last_support_file_sp(std::make_shared<SupportFile>()), m_last_line(0), + : m_last_support_file_nsp(std::make_shared<SupportFile>()), m_last_line(0), m_last_count(0), m_default_set(false), m_target_wp(), m_debugger_wp(debugger_sp) {} // Destructor SourceManager::~SourceManager() = default; -SourceManager::FileSP SourceManager::GetFile(SupportFileSP support_file_sp) { - assert(support_file_sp && "SupportFileSP must be valid"); - - FileSpec file_spec = support_file_sp->GetSpecOnly(); +SourceManager::FileSP SourceManager::GetFile(SupportFileNSP support_file_nsp) { + FileSpec file_spec = support_file_nsp->GetSpecOnly(); if (!file_spec) return {}; @@ -97,8 +96,8 @@ SourceManager::FileSP SourceManager::GetFile(SupportFileSP support_file_sp) { LLDB_LOG(log, "Source file caching disabled: creating new source file: {0}", file_spec); if (target_sp) - return std::make_shared<File>(support_file_sp, target_sp); - return std::make_shared<File>(support_file_sp, debugger_sp); + return std::make_shared<File>(support_file_nsp, target_sp); + return std::make_shared<File>(support_file_nsp, debugger_sp); } ProcessSP process_sp = target_sp ? target_sp->GetProcessSP() : ProcessSP(); @@ -159,9 +158,9 @@ SourceManager::FileSP SourceManager::GetFile(SupportFileSP support_file_sp) { // (Re)create the file. if (target_sp) - file_sp = std::make_shared<File>(support_file_sp, target_sp); + file_sp = std::make_shared<File>(support_file_nsp, target_sp); else - file_sp = std::make_shared<File>(support_file_sp, debugger_sp); + file_sp = std::make_shared<File>(support_file_nsp, debugger_sp); // Add the file to the debugger and process cache. If the file was // invalidated, this will overwrite it. @@ -325,12 +324,12 @@ size_t SourceManager::DisplaySourceLinesWithLineNumbersUsingLastFile( } size_t SourceManager::DisplaySourceLinesWithLineNumbers( - lldb::SupportFileSP support_file_sp, uint32_t line, uint32_t column, + SupportFileNSP support_file_nsp, uint32_t line, uint32_t column, uint32_t context_before, uint32_t context_after, const char *current_line_cstr, Stream *s, const SymbolContextList *bp_locs) { - assert(support_file_sp && "SupportFile must be valid"); - FileSP file_sp(GetFile(support_file_sp)); + assert(support_file_nsp && "SupportFile must be valid"); + FileSP file_sp(GetFile(support_file_nsp)); uint32_t start_line; uint32_t count = context_before + context_after + 1; @@ -343,7 +342,7 @@ size_t SourceManager::DisplaySourceLinesWithLineNumbers( if (last_file_sp.get() != file_sp.get()) { if (line == 0) m_last_line = 0; - m_last_support_file_sp = support_file_sp; + m_last_support_file_nsp = support_file_nsp; } return DisplaySourceLinesWithLineNumbersUsingLastFile( @@ -389,15 +388,15 @@ size_t SourceManager::DisplayMoreWithLineNumbers( return 0; } -bool SourceManager::SetDefaultFileAndLine(lldb::SupportFileSP support_file_sp, +bool SourceManager::SetDefaultFileAndLine(SupportFileNSP support_file_nsp, uint32_t line) { - assert(support_file_sp && "SupportFile must be valid"); + assert(support_file_nsp && "SupportFile must be valid"); m_default_set = true; - if (FileSP file_sp = GetFile(support_file_sp)) { + if (FileSP file_sp = GetFile(support_file_nsp)) { m_last_line = line; - m_last_support_file_sp = support_file_sp; + m_last_support_file_nsp = support_file_nsp; return true; } @@ -407,7 +406,7 @@ bool SourceManager::SetDefaultFileAndLine(lldb::SupportFileSP support_file_sp, std::optional<SourceManager::SupportFileAndLine> SourceManager::GetDefaultFileAndLine() { if (FileSP last_file_sp = GetLastFile()) - return SupportFileAndLine(m_last_support_file_sp, m_last_line); + return SupportFileAndLine(m_last_support_file_nsp, m_last_line); if (!m_default_set) { TargetSP target_sp(m_target_wp.lock()); @@ -446,36 +445,36 @@ SourceManager::GetDefaultFileAndLine() { return std::nullopt; } -void SourceManager::FindLinesMatchingRegex(SupportFileSP support_file_sp, +void SourceManager::FindLinesMatchingRegex(SupportFileNSP support_file_nsp, RegularExpression ®ex, uint32_t start_line, uint32_t end_line, std::vector<uint32_t> &match_lines) { match_lines.clear(); - FileSP file_sp = GetFile(support_file_sp); + FileSP file_sp = GetFile(support_file_nsp); if (!file_sp) return; return file_sp->FindLinesMatchingRegex(regex, start_line, end_line, match_lines); } -SourceManager::File::File(SupportFileSP support_file_sp, +SourceManager::File::File(SupportFileNSP support_file_nsp, lldb::DebuggerSP debugger_sp) - : m_support_file_sp(std::make_shared<SupportFile>()), m_checksum(), + : m_support_file_nsp(std::make_shared<SupportFile>()), m_checksum(), m_mod_time(), m_debugger_wp(debugger_sp), m_target_wp(TargetSP()) { - CommonInitializer(support_file_sp, {}); + CommonInitializer(support_file_nsp, {}); } -SourceManager::File::File(SupportFileSP support_file_sp, TargetSP target_sp) - : m_support_file_sp(std::make_shared<SupportFile>()), m_checksum(), +SourceManager::File::File(SupportFileNSP support_file_nsp, TargetSP target_sp) + : m_support_file_nsp(std::make_shared<SupportFile>()), m_checksum(), m_mod_time(), m_debugger_wp(target_sp ? target_sp->GetDebugger().shared_from_this() : DebuggerSP()), m_target_wp(target_sp) { - CommonInitializer(support_file_sp, target_sp); + CommonInitializer(support_file_nsp, target_sp); } -void SourceManager::File::CommonInitializer(SupportFileSP support_file_sp, +void SourceManager::File::CommonInitializer(SupportFileNSP support_file_nsp, TargetSP target_sp) { // It might take a while to read a source file, for example because it's // coming from a virtual file system that's fetching the data on demand. When @@ -484,23 +483,23 @@ void SourceManager::File::CommonInitializer(SupportFileSP support_file_sp, static constexpr auto g_progress_delay = std::chrono::milliseconds(500); std::future<void> future = std::async(std::launch::async, [=]() { - CommonInitializerImpl(support_file_sp, target_sp); + CommonInitializerImpl(support_file_nsp, target_sp); }); std::optional<Progress> progress; if (future.wait_for(g_progress_delay) == std::future_status::timeout) { Debugger *debugger = target_sp ? &target_sp->GetDebugger() : nullptr; progress.emplace("Loading source file", - support_file_sp->GetSpecOnly().GetFilename().GetString(), + support_file_nsp->GetSpecOnly().GetFilename().GetString(), 1, debugger); } future.wait(); } -void SourceManager::File::CommonInitializerImpl(SupportFileSP support_file_sp, +void SourceManager::File::CommonInitializerImpl(SupportFileNSP support_file_nsp, TargetSP target_sp) { // Set the file and update the modification time. - SetSupportFile(support_file_sp); + SetSupportFile(support_file_nsp); // Always update the source map modification ID if we have a target. if (target_sp) @@ -511,7 +510,7 @@ void SourceManager::File::CommonInitializerImpl(SupportFileSP support_file_sp, if (target_sp) { // If this is just a file name, try finding it in the target. { - FileSpec file_spec = support_file_sp->GetSpecOnly(); + FileSpec file_spec = support_file_nsp->GetSpecOnly(); if (!file_spec.GetDirectory() && file_spec.GetFilename()) { bool check_inlines = false; SymbolContextList sc_list; @@ -548,7 +547,7 @@ void SourceManager::File::CommonInitializerImpl(SupportFileSP support_file_sp, // Try remapping the file if it doesn't exist. { - FileSpec file_spec = support_file_sp->GetSpecOnly(); + FileSpec file_spec = support_file_nsp->GetSpecOnly(); if (!FileSystem::Instance().Exists(file_spec)) { // Check target specific source remappings (i.e., the // target.source-map setting), then fall back to the module @@ -561,7 +560,7 @@ void SourceManager::File::CommonInitializerImpl(SupportFileSP support_file_sp, } if (remapped) SetSupportFile(std::make_shared<SupportFile>( - *remapped, support_file_sp->GetChecksum())); + *remapped, support_file_nsp->GetChecksum())); } } } @@ -570,16 +569,16 @@ void SourceManager::File::CommonInitializerImpl(SupportFileSP support_file_sp, // If the file exists, read in the data. if (m_mod_time != llvm::sys::TimePoint<>()) { m_data_sp = FileSystem::Instance().CreateDataBuffer( - m_support_file_sp->GetSpecOnly()); + m_support_file_nsp->GetSpecOnly()); m_checksum = llvm::MD5::hash(m_data_sp->GetData()); } } -void SourceManager::File::SetSupportFile(lldb::SupportFileSP support_file_sp) { - FileSpec file_spec = support_file_sp->GetSpecOnly(); +void SourceManager::File::SetSupportFile(SupportFileNSP support_file_nsp) { + FileSpec file_spec = support_file_nsp->GetSpecOnly(); resolve_tilde(file_spec); - m_support_file_sp = - std::make_shared<SupportFile>(file_spec, support_file_sp->GetChecksum()); + m_support_file_nsp = + std::make_shared<SupportFile>(file_spec, support_file_nsp->GetChecksum()); m_mod_time = FileSystem::Instance().GetModificationTime(file_spec); } @@ -654,7 +653,7 @@ bool SourceManager::File::ModificationTimeIsStale() const { // source cache and only update when we determine a file has been updated. // For now we check each time we want to display info for the file. auto curr_mod_time = FileSystem::Instance().GetModificationTime( - m_support_file_sp->GetSpecOnly()); + m_support_file_nsp->GetSpecOnly()); return curr_mod_time != llvm::sys::TimePoint<>() && m_mod_time != curr_mod_time; } diff --git a/lldb/source/Core/Statusline.cpp b/lldb/source/Core/Statusline.cpp index bfbd190..922aada 100644 --- a/lldb/source/Core/Statusline.cpp +++ b/lldb/source/Core/Statusline.cpp @@ -91,7 +91,7 @@ void Statusline::UpdateScrollWindow(ScrollWindowMode mode) { if (!stream_sp) return; - const unsigned reduced_scroll_window = m_terminal_height - 1; + const unsigned reduced_scroll_rows = m_terminal_height - 1; LockedStreamFile locked_stream = stream_sp->Lock(); switch (mode) { @@ -101,13 +101,14 @@ void Statusline::UpdateScrollWindow(ScrollWindowMode mode) { locked_stream.Printf(ANSI_UP_ROWS, 1); // Reduce the scroll window. locked_stream << ANSI_SAVE_CURSOR; - locked_stream.Printf(ANSI_SET_SCROLL_ROWS, reduced_scroll_window); + locked_stream.Printf(ANSI_SET_SCROLL_ROWS, reduced_scroll_rows); locked_stream << ANSI_RESTORE_CURSOR; break; case DisableStatusline: // Reset the scroll window. locked_stream << ANSI_SAVE_CURSOR; - locked_stream.Printf(ANSI_SET_SCROLL_ROWS, 0); + locked_stream.Printf(ANSI_SET_SCROLL_ROWS, + static_cast<unsigned>(m_terminal_height)); locked_stream << ANSI_RESTORE_CURSOR; // Clear the screen below to hide the old statusline. locked_stream << ANSI_CLEAR_BELOW; @@ -116,7 +117,7 @@ void Statusline::UpdateScrollWindow(ScrollWindowMode mode) { // Clear the screen and update the scroll window. // FIXME: Find a better solution (#146919). locked_stream << ANSI_CLEAR_SCREEN; - locked_stream.Printf(ANSI_SET_SCROLL_ROWS, reduced_scroll_window); + locked_stream.Printf(ANSI_SET_SCROLL_ROWS, reduced_scroll_rows); break; } diff --git a/lldb/source/Expression/CMakeLists.txt b/lldb/source/Expression/CMakeLists.txt index 08dc536..515289c 100644 --- a/lldb/source/Expression/CMakeLists.txt +++ b/lldb/source/Expression/CMakeLists.txt @@ -24,6 +24,7 @@ add_lldb_library(lldbExpression NO_PLUGIN_DEPENDENCIES LINK_COMPONENTS Core + DebugInfoDWARF ExecutionEngine Support LINK_LIBS diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index 4f9d6eb..364b2ec 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -861,32 +861,130 @@ ResolveLoadAddress(ExecutionContext *exe_ctx, lldb::ModuleSP &module_sp, return load_addr; } -static llvm::Error Evaluate_DW_OP_deref(DWARFExpression::Stack &stack, - ExecutionContext *exe_ctx, - lldb::ModuleSP module_sp, - Process *process) { +/// @brief Helper function to load sized data from a uint8_t buffer. +/// +/// @param addr_bytes The buffer containing raw data. +/// @param size_addr_bytes How large is the underlying raw data. +/// @param byte_order What is the byte order of the underlying data. +/// @param size How much of the underlying data we want to use. +/// @return The underlying data converted into a Scalar. +static Scalar DerefSizeExtractDataHelper(uint8_t *addr_bytes, + size_t size_addr_bytes, + ByteOrder byte_order, size_t size) { + DataExtractor addr_data(addr_bytes, size_addr_bytes, byte_order, size); + + lldb::offset_t addr_data_offset = 0; + if (size <= 8) + return addr_data.GetMaxU64(&addr_data_offset, size); + return addr_data.GetAddress(&addr_data_offset); +} + +static llvm::Error Evaluate_DW_OP_deref_size( + DWARFExpression::Stack &stack, ExecutionContext *exe_ctx, + lldb::ModuleSP module_sp, Process *process, Target *target, uint8_t size, + size_t size_addr_bytes, + LocationDescriptionKind &dwarf4_location_description_kind) { if (stack.empty()) - return llvm::createStringError("expression stack empty for DW_OP_deref"); + return llvm::createStringError( + "expression stack empty for DW_OP_deref_size"); - const Value::ValueType value_type = stack.back().GetValueType(); + if (size > 8) + return llvm::createStringError( + "Invalid address size for DW_OP_deref_size: %d\n", size); + + // Deref a register or implicit location and truncate the value to `size` + // bytes. See the corresponding comment in DW_OP_deref for more details on + // why we deref these locations this way. + if (dwarf4_location_description_kind == Register || + dwarf4_location_description_kind == Implicit) { + // Reset context to default values. + dwarf4_location_description_kind = Memory; + stack.back().ClearContext(); + + // Truncate the value on top of the stack to *size* bytes then + // extend to the size of an address (e.g. generic type). + Scalar scalar = stack.back().GetScalar(); + scalar.TruncOrExtendTo(size * 8, /*sign=*/false); + scalar.TruncOrExtendTo(size_addr_bytes * 8, + /*sign=*/false); + stack.back().GetScalar() = scalar; + return llvm::Error::success(); + } + + Value::ValueType value_type = stack.back().GetValueType(); switch (value_type) { case Value::ValueType::HostAddress: { void *src = (void *)stack.back().GetScalar().ULongLong(); intptr_t ptr; ::memcpy(&ptr, src, sizeof(void *)); + // I can't decide whether the size operand should apply to the bytes in + // their lldb-host endianness or the target endianness.. I doubt this'll + // ever come up but I'll opt for assuming big endian regardless. + switch (size) { + case 1: + ptr = ptr & 0xff; + break; + case 2: + ptr = ptr & 0xffff; + break; + case 3: + ptr = ptr & 0xffffff; + break; + case 4: + ptr = ptr & 0xffffffff; + break; + // The casts are added to work around the case where intptr_t is a 32-bit + // quantity. Presumably we won't hit the 5..7 cases if (void*) is 32-bits in + // this program. + case 5: + ptr = (intptr_t)ptr & 0xffffffffffULL; + break; + case 6: + ptr = (intptr_t)ptr & 0xffffffffffffULL; + break; + case 7: + ptr = (intptr_t)ptr & 0xffffffffffffffULL; + break; + default: + break; + } stack.back().GetScalar() = ptr; stack.back().ClearContext(); } break; case Value::ValueType::FileAddress: { auto file_addr = stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); Address so_addr; - auto maybe_load_addr = ResolveLoadAddress(exe_ctx, module_sp, "DW_OP_deref", - file_addr, so_addr); + auto maybe_load_addr = ResolveLoadAddress( + exe_ctx, module_sp, "DW_OP_deref_size", file_addr, so_addr, + /*check_sectionoffset=*/true); + if (!maybe_load_addr) return maybe_load_addr.takeError(); - stack.back().GetScalar() = *maybe_load_addr; + + addr_t load_addr = *maybe_load_addr; + + if (load_addr == LLDB_INVALID_ADDRESS && so_addr.IsSectionOffset()) { + uint8_t addr_bytes[8]; + Status error; + + if (!target || target->ReadMemory(so_addr, &addr_bytes, size, error, + /*force_live_memory=*/false) != size) + return llvm::createStringError( + "failed to dereference pointer for DW_OP_deref_size: " + "%s\n", + error.AsCString()); + + ObjectFile *objfile = module_sp->GetObjectFile(); + + stack.back().GetScalar() = DerefSizeExtractDataHelper( + addr_bytes, size, objfile->GetByteOrder(), size); + stack.back().ClearContext(); + break; + } + stack.back().GetScalar() = load_addr; // Fall through to load address promotion code below. } + [[fallthrough]]; case Value::ValueType::Scalar: // Promote Scalar to LoadAddress and fall through. @@ -894,51 +992,34 @@ static llvm::Error Evaluate_DW_OP_deref(DWARFExpression::Stack &stack, [[fallthrough]]; case Value::ValueType::LoadAddress: { if (!exe_ctx) - return llvm::createStringError("NULL execution context for DW_OP_deref"); + return llvm::createStringError( + "no execution context for DW_OP_deref_size"); if (!process) - return llvm::createStringError("NULL process for DW_OP_deref"); + return llvm::createStringError("no process for DW_OP_deref_size"); + lldb::addr_t pointer_addr = stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + uint8_t addr_bytes[sizeof(lldb::addr_t)]; Status error; - lldb::addr_t pointer_value = - process->ReadPointerFromMemory(pointer_addr, error); - if (pointer_value == LLDB_INVALID_ADDRESS) - return llvm::joinErrors( - llvm::createStringError( - "Failed to dereference pointer from 0x%" PRIx64 - " for DW_OP_deref", - pointer_addr), - error.takeError()); - stack.back().GetScalar() = pointer_value; + + if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) != size) + return llvm::createStringError( + "failed to dereference pointer from 0x%" PRIx64 + " for DW_OP_deref_size: %s\n", + pointer_addr, error.AsCString()); + + stack.back().GetScalar() = DerefSizeExtractDataHelper( + addr_bytes, sizeof(addr_bytes), process->GetByteOrder(), size); stack.back().ClearContext(); } break; + case Value::ValueType::Invalid: - return llvm::createStringError("invalid value type for DW_OP_deref"); + return llvm::createStringError("invalid value for DW_OP_deref_size"); } return llvm::Error::success(); } -/// Helper function to move common code used to load sized data from a uint8_t -/// buffer. -/// -/// \param addr_bytes uint8_t buffer containg raw data -/// \param size_addr_bytes how large is the underlying raw data -/// \param byte_order what is the byter order of the underlyig data -/// \param size How much of the underlying data we want to use -/// \return The underlying data converted into a Scalar -static Scalar DerefSizeExtractDataHelper(uint8_t *addr_bytes, - size_t size_addr_bytes, - ByteOrder byte_order, size_t size) { - DataExtractor addr_data(addr_bytes, size_addr_bytes, byte_order, size); - - lldb::offset_t addr_data_offset = 0; - if (size <= 8) - return addr_data.GetMaxU64(&addr_data_offset, size); - else - return addr_data.GetAddress(&addr_data_offset); -} - llvm::Expected<Value> DWARFExpression::Evaluate( ExecutionContext *exe_ctx, RegisterContext *reg_ctx, lldb::ModuleSP module_sp, const DataExtractor &opcodes, @@ -1079,8 +1160,10 @@ llvm::Expected<Value> DWARFExpression::Evaluate( // retrieved from the dereferenced address is the size of an address on the // target machine. case DW_OP_deref: { - if (llvm::Error err = - Evaluate_DW_OP_deref(stack, exe_ctx, module_sp, process)) + size_t size = opcodes.GetAddressByteSize(); + if (llvm::Error err = Evaluate_DW_OP_deref_size( + stack, exe_ctx, module_sp, process, target, size, size, + dwarf4_location_description_kind)) return err; } break; @@ -1097,131 +1180,11 @@ llvm::Expected<Value> DWARFExpression::Evaluate( // the size of an address on the target machine before being pushed on the // expression stack. case DW_OP_deref_size: { - if (stack.empty()) { - return llvm::createStringError( - "expression stack empty for DW_OP_deref_size"); - } - uint8_t size = opcodes.GetU8(&offset); - if (size > 8) { - return llvm::createStringError( - "Invalid address size for DW_OP_deref_size: %d\n", size); - } - Value::ValueType value_type = stack.back().GetValueType(); - switch (value_type) { - case Value::ValueType::HostAddress: { - void *src = (void *)stack.back().GetScalar().ULongLong(); - intptr_t ptr; - ::memcpy(&ptr, src, sizeof(void *)); - // I can't decide whether the size operand should apply to the bytes in - // their - // lldb-host endianness or the target endianness.. I doubt this'll ever - // come up but I'll opt for assuming big endian regardless. - switch (size) { - case 1: - ptr = ptr & 0xff; - break; - case 2: - ptr = ptr & 0xffff; - break; - case 3: - ptr = ptr & 0xffffff; - break; - case 4: - ptr = ptr & 0xffffffff; - break; - // the casts are added to work around the case where intptr_t is a 32 - // bit quantity; - // presumably we won't hit the 5..7 cases if (void*) is 32-bits in this - // program. - case 5: - ptr = (intptr_t)ptr & 0xffffffffffULL; - break; - case 6: - ptr = (intptr_t)ptr & 0xffffffffffffULL; - break; - case 7: - ptr = (intptr_t)ptr & 0xffffffffffffffULL; - break; - default: - break; - } - stack.back().GetScalar() = ptr; - stack.back().ClearContext(); - } break; - case Value::ValueType::FileAddress: { - auto file_addr = - stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); - Address so_addr; - auto maybe_load_addr = ResolveLoadAddress( - exe_ctx, module_sp, "DW_OP_deref_size", file_addr, so_addr, - /*check_sectionoffset=*/true); - - if (!maybe_load_addr) - return maybe_load_addr.takeError(); - - addr_t load_addr = *maybe_load_addr; - - if (load_addr == LLDB_INVALID_ADDRESS && so_addr.IsSectionOffset()) { - uint8_t addr_bytes[8]; - Status error; - - if (target && - target->ReadMemory(so_addr, &addr_bytes, size, error, - /*force_live_memory=*/false) == size) { - ObjectFile *objfile = module_sp->GetObjectFile(); - - stack.back().GetScalar() = DerefSizeExtractDataHelper( - addr_bytes, size, objfile->GetByteOrder(), size); - stack.back().ClearContext(); - break; - } else { - return llvm::createStringError( - "Failed to dereference pointer for DW_OP_deref_size: " - "%s\n", - error.AsCString()); - } - } - stack.back().GetScalar() = load_addr; - // Fall through to load address promotion code below. - } - - [[fallthrough]]; - case Value::ValueType::Scalar: - case Value::ValueType::LoadAddress: - if (exe_ctx) { - if (process) { - lldb::addr_t pointer_addr = - stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); - uint8_t addr_bytes[sizeof(lldb::addr_t)]; - Status error; - if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) == - size) { - - stack.back().GetScalar() = - DerefSizeExtractDataHelper(addr_bytes, sizeof(addr_bytes), - process->GetByteOrder(), size); - stack.back().ClearContext(); - } else { - return llvm::createStringError( - "Failed to dereference pointer from 0x%" PRIx64 - " for DW_OP_deref: %s\n", - pointer_addr, error.AsCString()); - } - } else { - - return llvm::createStringError("NULL process for DW_OP_deref_size"); - } - } else { - return llvm::createStringError( - "NULL execution context for DW_OP_deref_size"); - } - break; - - case Value::ValueType::Invalid: - - return llvm::createStringError("invalid value for DW_OP_deref_size"); - } - + size_t size = opcodes.GetU8(&offset); + if (llvm::Error err = Evaluate_DW_OP_deref_size( + stack, exe_ctx, module_sp, process, target, size, + opcodes.GetAddressByteSize(), dwarf4_location_description_kind)) + return err; } break; // OPCODE: DW_OP_xderef_size diff --git a/lldb/source/Expression/ObjectFileJIT.cpp b/lldb/source/Expression/ObjectFileJIT.cpp index e4a6135..46ceb75 100644 --- a/lldb/source/Expression/ObjectFileJIT.cpp +++ b/lldb/source/Expression/ObjectFileJIT.cpp @@ -73,8 +73,8 @@ ObjectFileJIT::ObjectFileJIT(const lldb::ModuleSP &module_sp, : ObjectFile(module_sp, nullptr, 0, 0, DataBufferSP(), 0), m_delegate_wp() { if (delegate_sp) { m_delegate_wp = delegate_sp; - m_data.SetByteOrder(delegate_sp->GetByteOrder()); - m_data.SetAddressByteSize(delegate_sp->GetAddressByteSize()); + m_data_nsp->SetByteOrder(delegate_sp->GetByteOrder()); + m_data_nsp->SetAddressByteSize(delegate_sp->GetAddressByteSize()); } } @@ -85,12 +85,14 @@ bool ObjectFileJIT::ParseHeader() { return false; } -ByteOrder ObjectFileJIT::GetByteOrder() const { return m_data.GetByteOrder(); } +ByteOrder ObjectFileJIT::GetByteOrder() const { + return m_data_nsp->GetByteOrder(); +} bool ObjectFileJIT::IsExecutable() const { return false; } uint32_t ObjectFileJIT::GetAddressByteSize() const { - return m_data.GetAddressByteSize(); + return m_data_nsp->GetAddressByteSize(); } void ObjectFileJIT::ParseSymtab(Symtab &symtab) { diff --git a/lldb/source/Expression/REPL.cpp b/lldb/source/Expression/REPL.cpp index 92017d2..c6cf6d8 100644 --- a/lldb/source/Expression/REPL.cpp +++ b/lldb/source/Expression/REPL.cpp @@ -615,6 +615,6 @@ Status REPL::RunLoop() { // Restore the default file and line if (default_file_line) m_target.GetSourceManager().SetDefaultFileAndLine( - default_file_line->support_file_sp, default_file_line->line); + default_file_line->support_file_nsp, default_file_line->line); return error; } diff --git a/lldb/source/Expression/UserExpression.cpp b/lldb/source/Expression/UserExpression.cpp index af4b477..5563eba 100644 --- a/lldb/source/Expression/UserExpression.cpp +++ b/lldb/source/Expression/UserExpression.cpp @@ -246,7 +246,7 @@ UserExpression::Evaluate(ExecutionContext &exe_ctx, // language in the target's properties if specified, else default to the // langage for the frame. if (!language) { - if (target->GetLanguage() != lldb::eLanguageTypeUnknown) + if (target->GetLanguage()) language = target->GetLanguage(); else if (StackFrame *frame = exe_ctx.GetFramePtr()) language = frame->GetLanguage(); diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt index c9e8afe..3184d3a 100644 --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -17,6 +17,7 @@ macro(add_host_subdirectory group) endmacro() add_host_subdirectory(common + common/DiagnosticsRendering.cpp common/FileAction.cpp common/FileCache.cpp common/File.cpp diff --git a/lldb/source/Utility/DiagnosticsRendering.cpp b/lldb/source/Host/common/DiagnosticsRendering.cpp index 8c21e661..f2cd396 100644 --- a/lldb/source/Utility/DiagnosticsRendering.cpp +++ b/lldb/source/Host/common/DiagnosticsRendering.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "lldb/Utility/DiagnosticsRendering.h" +#include "lldb/Host/common/DiagnosticsRendering.h" #include <cstdint> using namespace lldb_private; diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp index 1b1922e..e2995b3 100644 --- a/lldb/source/Host/common/Editline.cpp +++ b/lldb/source/Host/common/Editline.cpp @@ -1626,6 +1626,9 @@ bool Editline::GetLine(std::string &line, bool &interrupted) { m_editor_status = EditorStatus::Editing; m_revert_cursor_index = -1; + lldbassert(m_output_stream_sp); + fprintf(m_locked_output->GetFile().GetStream(), "\r" ANSI_CLEAR_RIGHT); + int count; auto input = el_wgets(m_editline, &count); diff --git a/lldb/source/Host/common/File.cpp b/lldb/source/Host/common/File.cpp index 65b75bd..4fad93f 100644 --- a/lldb/source/Host/common/File.cpp +++ b/lldb/source/Host/common/File.cpp @@ -249,8 +249,8 @@ uint32_t File::GetPermissions(Status &error) const { NativeFile::NativeFile() = default; -NativeFile::NativeFile(FILE *fh, bool transfer_ownership) - : m_stream(fh), m_own_stream(transfer_ownership) { +NativeFile::NativeFile(FILE *fh, OpenOptions options, bool transfer_ownership) + : m_stream(fh), m_options(options), m_own_stream(transfer_ownership) { #ifdef _WIN32 // In order to properly display non ASCII characters in Windows, we need to // use Windows APIs to print to the console. This is only required if the @@ -258,6 +258,26 @@ NativeFile::NativeFile(FILE *fh, bool transfer_ownership) int fd = _fileno(fh); is_windows_console = ::GetFileType((HANDLE)::_get_osfhandle(fd)) == FILE_TYPE_CHAR; +#else +#ifndef NDEBUG + int fd = fileno(fh); + if (fd != -1) { + int required_mode = ConvertOpenOptionsForPOSIXOpen(options) & O_ACCMODE; + int mode = fcntl(fd, F_GETFL); + if (mode != -1) { + mode &= O_ACCMODE; + // Check that the file is open with a valid subset of the requested file + // access mode, e.g. if we expected the file to be writable then ensure it + // was opened with O_WRONLY or O_RDWR. + assert( + (required_mode == O_RDWR && mode == O_RDWR) || + (required_mode == O_RDONLY && (mode == O_RDWR || mode == O_RDONLY) || + (required_mode == O_WRONLY && + (mode == O_RDWR || mode == O_WRONLY))) && + "invalid file access mode"); + } + } +#endif #endif } @@ -274,7 +294,8 @@ NativeFile::NativeFile(int fd, OpenOptions options, bool transfer_ownership) } bool NativeFile::IsValid() const { - std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, m_stream_mutex); + std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, + m_stream_mutex); return DescriptorIsValidUnlocked() || StreamIsValidUnlocked(); } @@ -343,7 +364,8 @@ FILE *NativeFile::GetStream() { } Status NativeFile::Close() { - std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, m_stream_mutex); + std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, + m_stream_mutex); Status error; @@ -548,6 +570,10 @@ Status NativeFile::Sync() { Status NativeFile::Read(void *buf, size_t &num_bytes) { Status error; + // Ensure the file is open for reading. + if ((m_options & File::OpenOptionsModeMask) == eOpenOptionWriteOnly) + return Status(std::make_error_code(std::errc::bad_file_descriptor)); + #if defined(MAX_READ_SIZE) if (num_bytes > MAX_READ_SIZE) { uint8_t *p = (uint8_t *)buf; @@ -612,6 +638,10 @@ Status NativeFile::Read(void *buf, size_t &num_bytes) { Status NativeFile::Write(const void *buf, size_t &num_bytes) { Status error; + // Ensure the file is open for writing. + if ((m_options & File::OpenOptionsModeMask) == File::eOpenOptionReadOnly) + return Status(std::make_error_code(std::errc::bad_file_descriptor)); + #if defined(MAX_WRITE_SIZE) if (num_bytes > MAX_WRITE_SIZE) { const uint8_t *p = (const uint8_t *)buf; @@ -776,8 +806,8 @@ Status NativeFile::Write(const void *buf, size_t &num_bytes, off_t &offset) { int fd = GetDescriptor(); if (fd != kInvalidDescriptor) { #ifndef _WIN32 - ssize_t bytes_written = - llvm::sys::RetryAfterSignal(-1, ::pwrite, m_descriptor, buf, num_bytes, offset); + ssize_t bytes_written = llvm::sys::RetryAfterSignal( + -1, ::pwrite, m_descriptor, buf, num_bytes, offset); if (bytes_written < 0) { num_bytes = 0; error = Status::FromErrno(); diff --git a/lldb/source/Host/common/FileAction.cpp b/lldb/source/Host/common/FileAction.cpp index e1c3e14..ec271f7 100644 --- a/lldb/source/Host/common/FileAction.cpp +++ b/lldb/source/Host/common/FileAction.cpp @@ -25,10 +25,6 @@ void FileAction::Clear() { m_file_spec.Clear(); } -llvm::StringRef FileAction::GetPath() const { - return m_file_spec.GetPathAsConstString().AsCString(); -} - const FileSpec &FileAction::GetFileSpec() const { return m_file_spec; } bool FileAction::Open(int fd, const FileSpec &file_spec, bool read, diff --git a/lldb/source/Host/common/StreamFile.cpp b/lldb/source/Host/common/StreamFile.cpp index 099980a..131412d 100644 --- a/lldb/source/Host/common/StreamFile.cpp +++ b/lldb/source/Host/common/StreamFile.cpp @@ -27,7 +27,8 @@ StreamFile::StreamFile(int fd, bool transfer_ownership) : Stream() { } StreamFile::StreamFile(FILE *fh, bool transfer_ownership) : Stream() { - m_file_sp = std::make_shared<NativeFile>(fh, transfer_ownership); + m_file_sp = std::make_shared<NativeFile>(fh, File::eOpenOptionWriteOnly, + transfer_ownership); } StreamFile::StreamFile(const char *path, File::OpenOptions options, diff --git a/lldb/source/Host/macosx/objcxx/Host.mm b/lldb/source/Host/macosx/objcxx/Host.mm index 96a282c..16bca0f1 100644 --- a/lldb/source/Host/macosx/objcxx/Host.mm +++ b/lldb/source/Host/macosx/objcxx/Host.mm @@ -1013,20 +1013,29 @@ static Status LaunchProcessXPC(const char *exe_path, xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey, GetPosixspawnFlags(launch_info)); const FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO); - if (file_action && !file_action->GetPath().empty()) { + std::string file_action_path; + if (file_action) + file_action_path = file_action->GetFileSpec().GetPath(); + + if (!file_action_path.empty()) xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey, - file_action->GetPath().str().c_str()); - } + file_action_path.c_str()); + file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); - if (file_action && !file_action->GetPath().empty()) { + if (file_action) + file_action_path = file_action->GetFileSpec().GetPath(); + + if (!file_action_path.empty()) xpc_dictionary_set_string(message, LauncherXPCServiceStdOutPathKeyKey, - file_action->GetPath().str().c_str()); - } + file_action_path.c_str()); + file_action = launch_info.GetFileActionForFD(STDERR_FILENO); - if (file_action && !file_action->GetPath().empty()) { + if (file_action) + file_action_path = file_action->GetFileSpec().GetPath(); + + if (!file_action_path.empty()) xpc_dictionary_set_string(message, LauncherXPCServiceStdErrPathKeyKey, - file_action->GetPath().str().c_str()); - } + file_action_path.c_str()); xpc_object_t reply = xpc_connection_send_message_with_reply_sync(conn, message); @@ -1135,16 +1144,16 @@ static bool AddPosixSpawnFileAction(void *_file_actions, const FileAction *info, if (oflag & O_CREAT) mode = 0640; - error = Status(::posix_spawn_file_actions_addopen( - file_actions, info->GetFD(), - info->GetPath().str().c_str(), oflag, mode), - eErrorTypePOSIX); + const std::string file_path(info->GetFileSpec().GetPath()); + error = Status( + ::posix_spawn_file_actions_addopen(file_actions, info->GetFD(), + file_path.c_str(), oflag, mode), + eErrorTypePOSIX); if (error.Fail()) LLDB_LOG(log, "error: {0}, posix_spawn_file_actions_addopen (action={1}, " "fd={2}, path='{3}', oflag={4}, mode={5})", - error, file_actions, info->GetFD(), info->GetPath(), oflag, - mode); + error, file_actions, info->GetFD(), file_path, oflag, mode); } break; } diff --git a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp index 15a8097..a5f5cc5 100644 --- a/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp +++ b/lldb/source/Host/posix/ProcessLauncherPosixFork.cpp @@ -229,8 +229,8 @@ struct ForkLaunchInfo { // End of code running in the child process. ForkFileAction::ForkFileAction(const FileAction &act) - : action(act.GetAction()), fd(act.GetFD()), path(act.GetPath().str()), - arg(act.GetActionArgument()) {} + : action(act.GetAction()), fd(act.GetFD()), + path(act.GetFileSpec().GetPath()), arg(act.GetActionArgument()) {} static std::vector<ForkFileAction> MakeForkActions(const ProcessLaunchInfo &info) { diff --git a/lldb/source/Host/windows/ProcessLauncherWindows.cpp b/lldb/source/Host/windows/ProcessLauncherWindows.cpp index f5adada..e983c52 100644 --- a/lldb/source/Host/windows/ProcessLauncherWindows.cpp +++ b/lldb/source/Host/windows/ProcessLauncherWindows.cpp @@ -14,6 +14,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Program.h" +#include "llvm/Support/WindowsError.h" #include <string> #include <vector> @@ -21,42 +22,63 @@ using namespace lldb; using namespace lldb_private; -static void CreateEnvironmentBuffer(const Environment &env, - std::vector<char> &buffer) { - // The buffer is a list of null-terminated UTF-16 strings, followed by an - // extra L'\0' (two bytes of 0). An empty environment must have one - // empty string, followed by an extra L'\0'. +/// Create a UTF-16 environment block to use with CreateProcessW. +/// +/// The buffer is a sequence of null-terminated UTF-16 strings, followed by an +/// extra L'\0' (two bytes of 0). An empty environment must have one +/// empty string, followed by an extra L'\0'. +/// +/// The keys are sorted to comply with the CreateProcess API calling convention. +/// +/// Ensure that the resulting buffer is used in conjunction with +/// CreateProcessW and be sure that dwCreationFlags includes +/// CREATE_UNICODE_ENVIRONMENT. +/// +/// \param env The Environment object to convert. +/// \returns The sorted sequence of environment variables and their values, +/// separated by null terminators. The vector is guaranteed to never be empty. +static std::vector<wchar_t> CreateEnvironmentBufferW(const Environment &env) { + std::vector<std::wstring> env_entries; for (const auto &KV : env) { - std::wstring warg; - if (llvm::ConvertUTF8toWide(Environment::compose(KV), warg)) { - buffer.insert( - buffer.end(), reinterpret_cast<const char *>(warg.c_str()), - reinterpret_cast<const char *>(warg.c_str() + warg.size() + 1)); - } + std::wstring wentry; + if (llvm::ConvertUTF8toWide(Environment::compose(KV), wentry)) + env_entries.push_back(std::move(wentry)); } - // One null wchar_t (to end the block) is two null bytes - buffer.push_back(0); - buffer.push_back(0); - // Insert extra two bytes, just in case the environment was empty. - buffer.push_back(0); - buffer.push_back(0); + std::sort(env_entries.begin(), env_entries.end(), + [](const std::wstring &a, const std::wstring &b) { + return _wcsicmp(a.c_str(), b.c_str()) < 0; + }); + + std::vector<wchar_t> buffer; + for (const auto &env_entry : env_entries) { + buffer.insert(buffer.end(), env_entry.begin(), env_entry.end()); + buffer.push_back(L'\0'); + } + + if (buffer.empty()) + buffer.push_back(L'\0'); // If there are no environment variables, we have + // to ensure there are 4 zero bytes in the buffer. + buffer.push_back(L'\0'); + + return buffer; } -static bool GetFlattenedWindowsCommandString(Args args, std::wstring &command) { +/// Flattens an Args object into a Windows command-line wide string. +/// +/// Returns an empty string if args is empty. +/// +/// \param args The Args object to flatten. +/// \returns A wide string containing the flattened command line. +static llvm::ErrorOr<std::wstring> +GetFlattenedWindowsCommandStringW(Args args) { if (args.empty()) - return false; + return L""; std::vector<llvm::StringRef> args_ref; for (auto &entry : args.entries()) args_ref.push_back(entry.ref()); - llvm::ErrorOr<std::wstring> result = - llvm::sys::flattenWindowsCommandLine(args_ref); - if (result.getError()) - return false; - - command = *result; - return true; + return llvm::sys::flattenWindowsCommandLine(args_ref); } HostProcess @@ -64,11 +86,9 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, Status &error) { error.Clear(); - std::string executable; - std::vector<char> environment; - STARTUPINFOEX startupinfoex = {}; - STARTUPINFO &startupinfo = startupinfoex.StartupInfo; - PROCESS_INFORMATION pi = {}; + STARTUPINFOEXW startupinfoex = {}; + startupinfoex.StartupInfo.cb = sizeof(startupinfoex); + startupinfoex.StartupInfo.dwFlags |= STARTF_USESTDHANDLES; HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO); HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO); @@ -82,23 +102,6 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, ::CloseHandle(stderr_handle); }); - startupinfo.cb = sizeof(startupinfoex); - startupinfo.dwFlags |= STARTF_USESTDHANDLES; - startupinfo.hStdError = - stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE); - startupinfo.hStdInput = - stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE); - startupinfo.hStdOutput = - stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE); - - std::vector<HANDLE> inherited_handles; - if (startupinfo.hStdError) - inherited_handles.push_back(startupinfo.hStdError); - if (startupinfo.hStdInput) - inherited_handles.push_back(startupinfo.hStdInput); - if (startupinfo.hStdOutput) - inherited_handles.push_back(startupinfo.hStdOutput); - SIZE_T attributelist_size = 0; InitializeProcThreadAttributeList(/*lpAttributeList=*/nullptr, /*dwAttributeCount=*/1, /*dwFlags=*/0, @@ -116,29 +119,21 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, } auto delete_attributelist = llvm::make_scope_exit( [&] { DeleteProcThreadAttributeList(startupinfoex.lpAttributeList); }); - for (size_t i = 0; i < launch_info.GetNumFileActions(); ++i) { - const FileAction *act = launch_info.GetFileActionAtIndex(i); - if (act->GetAction() == FileAction::eFileActionDuplicate && - act->GetFD() == act->GetActionArgument()) - inherited_handles.push_back(reinterpret_cast<HANDLE>(act->GetFD())); - } - if (!inherited_handles.empty()) { - if (!UpdateProcThreadAttribute( - startupinfoex.lpAttributeList, /*dwFlags=*/0, - PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited_handles.data(), - inherited_handles.size() * sizeof(HANDLE), - /*lpPreviousValue=*/nullptr, /*lpReturnSize=*/nullptr)) { - error = Status(::GetLastError(), eErrorTypeWin32); - return HostProcess(); - } + + auto inherited_handles_or_err = GetInheritedHandles( + launch_info, startupinfoex, stdout_handle, stderr_handle, stdin_handle); + if (!inherited_handles_or_err) { + error = Status(inherited_handles_or_err.getError()); + return HostProcess(); } + std::vector<HANDLE> inherited_handles = *inherited_handles_or_err; const char *hide_console_var = getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE"); if (hide_console_var && llvm::StringRef(hide_console_var).equals_insensitive("true")) { - startupinfo.dwFlags |= STARTF_USESHOWWINDOW; - startupinfo.wShowWindow = SW_HIDE; + startupinfoex.StartupInfo.dwFlags |= STARTF_USESHOWWINDOW; + startupinfoex.StartupInfo.wShowWindow = SW_HIDE; } DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT | @@ -149,28 +144,34 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, if (launch_info.GetFlags().Test(eLaunchFlagDisableSTDIO)) flags &= ~CREATE_NEW_CONSOLE; - LPVOID env_block = nullptr; - ::CreateEnvironmentBuffer(launch_info.GetEnvironment(), environment); - env_block = environment.data(); + std::vector<wchar_t> environment = + CreateEnvironmentBufferW(launch_info.GetEnvironment()); - executable = launch_info.GetExecutableFile().GetPath(); - std::wstring wcommandLine; - GetFlattenedWindowsCommandString(launch_info.GetArguments(), wcommandLine); - - std::wstring wexecutable, wworkingDirectory; - llvm::ConvertUTF8toWide(executable, wexecutable); - llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetPath(), - wworkingDirectory); + auto wcommandLineOrErr = + GetFlattenedWindowsCommandStringW(launch_info.GetArguments()); + if (!wcommandLineOrErr) { + error = Status(wcommandLineOrErr.getError()); + return HostProcess(); + } + std::wstring wcommandLine = *wcommandLineOrErr; // If the command line is empty, it's best to pass a null pointer to tell // CreateProcessW to use the executable name as the command line. If the // command line is not empty, its contents may be modified by CreateProcessW. WCHAR *pwcommandLine = wcommandLine.empty() ? nullptr : &wcommandLine[0]; + std::wstring wexecutable, wworkingDirectory; + llvm::ConvertUTF8toWide(launch_info.GetExecutableFile().GetPath(), + wexecutable); + llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetPath(), + wworkingDirectory); + + PROCESS_INFORMATION pi = {}; + BOOL result = ::CreateProcessW( wexecutable.c_str(), pwcommandLine, NULL, NULL, - /*bInheritHandles=*/!inherited_handles.empty(), flags, env_block, + /*bInheritHandles=*/!inherited_handles.empty(), flags, environment.data(), wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(), - reinterpret_cast<STARTUPINFO *>(&startupinfoex), &pi); + reinterpret_cast<STARTUPINFOW *>(&startupinfoex), &pi); if (!result) { // Call GetLastError before we make any other system calls. @@ -191,6 +192,45 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, return HostProcess(pi.hProcess); } +llvm::ErrorOr<std::vector<HANDLE>> ProcessLauncherWindows::GetInheritedHandles( + const ProcessLaunchInfo &launch_info, STARTUPINFOEXW &startupinfoex, + HANDLE stdout_handle, HANDLE stderr_handle, HANDLE stdin_handle) { + std::vector<HANDLE> inherited_handles; + + startupinfoex.StartupInfo.hStdError = + stderr_handle ? stderr_handle : GetStdHandle(STD_ERROR_HANDLE); + startupinfoex.StartupInfo.hStdInput = + stdin_handle ? stdin_handle : GetStdHandle(STD_INPUT_HANDLE); + startupinfoex.StartupInfo.hStdOutput = + stdout_handle ? stdout_handle : GetStdHandle(STD_OUTPUT_HANDLE); + + if (startupinfoex.StartupInfo.hStdError) + inherited_handles.push_back(startupinfoex.StartupInfo.hStdError); + if (startupinfoex.StartupInfo.hStdInput) + inherited_handles.push_back(startupinfoex.StartupInfo.hStdInput); + if (startupinfoex.StartupInfo.hStdOutput) + inherited_handles.push_back(startupinfoex.StartupInfo.hStdOutput); + + for (size_t i = 0; i < launch_info.GetNumFileActions(); ++i) { + const FileAction *act = launch_info.GetFileActionAtIndex(i); + if (act->GetAction() == FileAction::eFileActionDuplicate && + act->GetFD() == act->GetActionArgument()) + inherited_handles.push_back(reinterpret_cast<HANDLE>(act->GetFD())); + } + + if (inherited_handles.empty()) + return inherited_handles; + + if (!UpdateProcThreadAttribute( + startupinfoex.lpAttributeList, /*dwFlags=*/0, + PROC_THREAD_ATTRIBUTE_HANDLE_LIST, inherited_handles.data(), + inherited_handles.size() * sizeof(HANDLE), + /*lpPreviousValue=*/nullptr, /*lpReturnSize=*/nullptr)) + return llvm::mapWindowsError(::GetLastError()); + + return inherited_handles; +} + HANDLE ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info, int fd) { @@ -201,7 +241,6 @@ ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info, secattr.nLength = sizeof(SECURITY_ATTRIBUTES); secattr.bInheritHandle = TRUE; - llvm::StringRef path = action->GetPath(); DWORD access = 0; DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; DWORD create = 0; @@ -218,6 +257,7 @@ ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info, flags = FILE_FLAG_WRITE_THROUGH; } + const std::string path = action->GetFileSpec().GetPath(); std::wstring wpath; llvm::ConvertUTF8toWide(path, wpath); HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create, diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index ffcc9ce..cb6acfc 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -3708,7 +3708,7 @@ CommandInterpreter::ResolveCommandImpl(std::string &command_line, for (uint32_t i = 0; i < num_matches; ++i) { error_msg.Printf("\t%s\n", matches.GetStringAtIndex(i)); } - result.AppendRawError(error_msg.GetString()); + result.AppendError(error_msg.GetString()); } } else { // We didn't have only one match, otherwise we wouldn't get here. diff --git a/lldb/source/Interpreter/CommandReturnObject.cpp b/lldb/source/Interpreter/CommandReturnObject.cpp index 0a2948e..85b058e 100644 --- a/lldb/source/Interpreter/CommandReturnObject.cpp +++ b/lldb/source/Interpreter/CommandReturnObject.cpp @@ -8,7 +8,7 @@ #include "lldb/Interpreter/CommandReturnObject.h" -#include "lldb/Utility/DiagnosticsRendering.h" +#include "lldb/Host/common/DiagnosticsRendering.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StreamString.h" @@ -173,15 +173,6 @@ StructuredData::ObjectSP CommandReturnObject::GetErrorData() { return Serialize(m_diagnostics); } -// Similar to AppendError, but do not prepend 'Status: ' to message, and don't -// append "\n" to the end of it. - -void CommandReturnObject::AppendRawError(llvm::StringRef in_string) { - SetStatus(eReturnStatusFailed); - assert(!in_string.empty() && "Expected a non-empty error message"); - GetErrorStream() << in_string; -} - void CommandReturnObject::SetStatus(ReturnStatus status) { m_status = status; } ReturnStatus CommandReturnObject::GetStatus() const { return m_status; } diff --git a/lldb/source/Interpreter/Options.cpp b/lldb/source/Interpreter/Options.cpp index cae61781..eab452c 100644 --- a/lldb/source/Interpreter/Options.cpp +++ b/lldb/source/Interpreter/Options.cpp @@ -14,13 +14,13 @@ #include <set> #include "lldb/Host/OptionParser.h" +#include "lldb/Host/common/DiagnosticsRendering.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Target/Target.h" #include "lldb/Utility/AnsiTerminal.h" -#include "lldb/Utility/DiagnosticsRendering.h" #include "lldb/Utility/StreamString.h" #include "llvm/ADT/STLExtras.h" @@ -1410,7 +1410,9 @@ llvm::Error lldb_private::CreateOptionParsingError( llvm::StringRef long_option, llvm::StringRef additional_context) { std::string buffer; llvm::raw_string_ostream stream(buffer); - stream << "Invalid value ('" << option_arg << "') for -" << short_option; + stream << "invalid value ('" << option_arg << "')"; + if (short_option) + stream << " for -" << short_option; if (!long_option.empty()) stream << " (" << long_option << ")"; if (!additional_context.empty()) diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp index ca768db..7bad10f 100644 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -106,6 +106,13 @@ ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const { return Status(); } +lldb::ThreadSP ScriptInterpreter::GetOpaqueTypeFromSBThread( + const lldb::SBThread &thread) const { + if (thread.m_opaque_sp) + return thread.m_opaque_sp->GetThreadSP(); + return nullptr; +} + lldb::StackFrameSP ScriptInterpreter::GetOpaqueTypeFromSBFrame(const lldb::SBFrame &frame) const { if (frame.m_opaque_sp) @@ -136,7 +143,7 @@ SymbolContext ScriptInterpreter::GetOpaqueTypeFromSBSymbolContext( return {}; } -std::optional<MemoryRegionInfo> +std::optional<lldb_private::MemoryRegionInfo> ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo( const lldb::SBMemoryRegionInfo &mem_region) const { if (!mem_region.m_opaque_up) @@ -150,6 +157,11 @@ ScriptInterpreter::GetOpaqueTypeFromSBExecutionContext( return exe_ctx.m_exe_ctx_sp; } +lldb::StackFrameListSP ScriptInterpreter::GetOpaqueTypeFromSBFrameList( + const lldb::SBFrameList &frame_list) const { + return frame_list.m_opaque_sp; +} + lldb::ScriptLanguage ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) { if (language.equals_insensitive(LanguageToString(eScriptLanguageNone))) diff --git a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp index ff37b48..a5547a4 100644 --- a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp +++ b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp @@ -798,6 +798,8 @@ bool ABISysV_riscv::RegisterIsCalleeSaved(const RegisterInfo *reg_info) { .Cases({"f8", "f9", "f18", "f19", "f20", "f21", "f22", "f23"}, is_hw_fp) .Cases({"f24", "f25", "f26", "f27"}, is_hw_fp) + // vlenb is constant and needed for vector unwinding. + .Case("vlenb", true) .Default(false); return is_callee_saved; diff --git a/lldb/source/Plugins/CMakeLists.txt b/lldb/source/Plugins/CMakeLists.txt index 08f444e..b6878b2 100644 --- a/lldb/source/Plugins/CMakeLists.txt +++ b/lldb/source/Plugins/CMakeLists.txt @@ -22,6 +22,7 @@ add_subdirectory(SymbolFile) add_subdirectory(SystemRuntime) add_subdirectory(SymbolLocator) add_subdirectory(SymbolVendor) +add_subdirectory(SyntheticFrameProvider) add_subdirectory(Trace) add_subdirectory(TraceExporter) add_subdirectory(TypeSystem) diff --git a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp index 66d0a50..e8bb706 100644 --- a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp +++ b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp @@ -70,6 +70,7 @@ public: bool HasDelaySlot(llvm::MCInst &mc_inst) const; bool IsCall(llvm::MCInst &mc_inst) const; bool IsLoad(llvm::MCInst &mc_inst) const; + bool IsBarrier(llvm::MCInst &mc_inst) const; bool IsAuthenticated(llvm::MCInst &mc_inst) const; private: @@ -436,6 +437,11 @@ public: return m_is_load; } + bool IsBarrier() override { + VisitInstruction(); + return m_is_barrier; + } + bool IsAuthenticated() override { VisitInstruction(); return m_is_authenticated; @@ -1195,6 +1201,7 @@ protected: bool m_is_call = false; bool m_is_load = false; bool m_is_authenticated = false; + bool m_is_barrier = false; void VisitInstruction() { if (m_has_visited_instruction) @@ -1227,6 +1234,7 @@ protected: m_is_call = mc_disasm_ptr->IsCall(inst); m_is_load = mc_disasm_ptr->IsLoad(inst); m_is_authenticated = mc_disasm_ptr->IsAuthenticated(inst); + m_is_barrier = mc_disasm_ptr->IsBarrier(inst); } private: @@ -1432,6 +1440,11 @@ bool DisassemblerLLVMC::MCDisasmInstance::IsLoad(llvm::MCInst &mc_inst) const { return m_instr_info_up->get(mc_inst.getOpcode()).mayLoad(); } +bool DisassemblerLLVMC::MCDisasmInstance::IsBarrier( + llvm::MCInst &mc_inst) const { + return m_instr_info_up->get(mc_inst.getOpcode()).isBarrier(); +} + bool DisassemblerLLVMC::MCDisasmInstance::IsAuthenticated( llvm::MCInst &mc_inst) const { const auto &InstrDesc = m_instr_info_up->get(mc_inst.getOpcode()); diff --git a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp index 1d210ea..2d0a4f67 100644 --- a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp +++ b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp @@ -789,6 +789,7 @@ bool DynamicLoaderDarwinKernel::KextImageInfo::LoadImageUsingMemoryModule( // Search for the kext on the local filesystem via the UUID if (!m_module_sp && m_uuid.IsValid()) { ModuleSpec module_spec; + module_spec.SetTarget(target.shared_from_this()); module_spec.GetUUID() = m_uuid; if (!m_uuid.IsValid()) module_spec.GetArchitecture() = target.GetArchitecture(); @@ -801,9 +802,8 @@ bool DynamicLoaderDarwinKernel::KextImageInfo::LoadImageUsingMemoryModule( // system. PlatformSP platform_sp(target.GetPlatform()); if (platform_sp) { - FileSpecList search_paths = target.GetExecutableSearchPaths(); - platform_sp->GetSharedModule(module_spec, process, m_module_sp, - &search_paths, nullptr, nullptr); + platform_sp->GetSharedModule(module_spec, process, m_module_sp, nullptr, + nullptr); } // Ask the Target to find this file on the local system, if possible. diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp index 326b691..3605e7b2 100644 --- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -469,7 +469,8 @@ void DynamicLoaderPOSIXDYLD::RefreshModules() { } ModuleSP module_sp = LoadModuleAtAddress( - so_entry.file_spec, so_entry.link_addr, so_entry.base_addr, true); + so_entry.file_spec, so_entry.link_addr, so_entry.base_addr, + /*base_addr_is_offset=*/true); if (!module_sp.get()) return; @@ -726,9 +727,8 @@ void DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() { task_group.async(load_module_fn, *I); task_group.wait(); } else { - for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) { + for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) load_module_fn(*I); - } } m_process->GetTarget().ModulesDidLoad(module_list); @@ -901,10 +901,9 @@ void DynamicLoaderPOSIXDYLD::ResolveExecutableModule( if (module_sp && module_sp->MatchesModuleSpec(module_spec)) return; + module_spec.SetTarget(target.shared_from_this()); const auto executable_search_paths(Target::GetDefaultExecutableSearchPaths()); - auto error = platform_sp->ResolveExecutable( - module_spec, module_sp, - !executable_search_paths.IsEmpty() ? &executable_search_paths : nullptr); + auto error = platform_sp->ResolveExecutable(module_spec, module_sp); if (error.Fail()) { StreamString stream; module_spec.Dump(stream); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt b/lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt index 01d588f..759a7c4 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt +++ b/lldb/source/Plugins/ExpressionParser/Clang/CMakeLists.txt @@ -51,10 +51,10 @@ add_lldb_library(lldbPluginExpressionParserClang CLANG_LIBS clangAST clangCodeGen - clangDriver clangEdit clangFrontend clangLex + clangOptions clangParse clangRewrite clangRewriteFrontend diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp index 9900745..bae3c44 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -12,6 +12,7 @@ #include "clang/AST/PrettyPrinter.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/DarwinSDKInfo.h" +#include "clang/Basic/DiagnosticFrontend.h" #include "clang/Basic/DiagnosticIDs.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/SourceLocation.h" @@ -25,7 +26,6 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendActions.h" -#include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/FrontendPluginRegistry.h" #include "clang/Frontend/TextDiagnostic.h" #include "clang/Frontend/TextDiagnosticBuffer.h" @@ -115,6 +115,7 @@ class ClangExpressionParser::LLDBPreprocessorCallbacks : public PPCallbacks { ClangModulesDeclVendor &m_decl_vendor; ClangPersistentVariables &m_persistent_vars; clang::SourceManager &m_source_mgr; + /// Accumulates error messages across all moduleImport calls. StreamString m_error_stream; bool m_has_errors = false; @@ -140,11 +141,12 @@ public: module.path.push_back( ConstString(component.getIdentifierInfo()->getName())); - StreamString error_stream; - ClangModulesDeclVendor::ModuleVector exported_modules; - if (!m_decl_vendor.AddModule(module, &exported_modules, m_error_stream)) + if (auto err = m_decl_vendor.AddModule(module, &exported_modules)) { m_has_errors = true; + m_error_stream.PutCString(llvm::toString(std::move(err))); + m_error_stream.PutChar('\n'); + } for (ClangModulesDeclVendor::ModuleID module : exported_modules) m_persistent_vars.AddHandLoadedClangModule(module); @@ -169,9 +171,9 @@ public: : m_options(opts), m_filename(filename) { m_options.ShowPresumedLoc = true; m_options.ShowLevel = false; - m_os = std::make_shared<llvm::raw_string_ostream>(m_output); + m_os = std::make_unique<llvm::raw_string_ostream>(m_output); m_passthrough = - std::make_shared<clang::TextDiagnosticPrinter>(*m_os, m_options); + std::make_unique<clang::TextDiagnosticPrinter>(*m_os, m_options); } void ResetManager(DiagnosticManager *manager = nullptr) { @@ -313,11 +315,11 @@ public: private: DiagnosticManager *m_manager = nullptr; DiagnosticOptions m_options; - std::shared_ptr<clang::TextDiagnosticPrinter> m_passthrough; - /// Output stream of m_passthrough. - std::shared_ptr<llvm::raw_string_ostream> m_os; /// Output string filled by m_os. std::string m_output; + /// Output stream of m_passthrough. + std::unique_ptr<llvm::raw_string_ostream> m_os; + std::unique_ptr<clang::TextDiagnosticPrinter> m_passthrough; StringRef m_filename; }; @@ -1502,7 +1504,7 @@ lldb_private::Status ClangExpressionParser::DoPrepareForExecution( LLDB_LOGF(log, "%s - Current expression language is %s\n", __FUNCTION__, lang.GetDescription().data()); lldb::ProcessSP process_sp = exe_ctx.GetProcessSP(); - if (process_sp && lang != lldb::eLanguageTypeUnknown) { + if (process_sp && lang) { auto runtime = process_sp->GetLanguageRuntime(lang.AsLanguageType()); if (runtime) runtime->GetIRPasses(custom_passes); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp index ff9ed9c..ad48d29 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp @@ -383,10 +383,11 @@ bool ClangExpressionSourceCode::GetText( block->CalculateSymbolContext(&sc); if (sc.comp_unit) { - StreamString error_stream; - - decl_vendor->AddModulesForCompileUnit( - *sc.comp_unit, modules_for_macros, error_stream); + if (auto err = decl_vendor->AddModulesForCompileUnit( + *sc.comp_unit, modules_for_macros)) + LLDB_LOG_ERROR( + GetLog(LLDBLog::Expressions), std::move(err), + "Error while loading hand-imported modules:\n{0}"); } } } diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.cpp index 6de8510..660a21e 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangHost.cpp @@ -10,7 +10,7 @@ #include "clang/Basic/Version.h" #include "clang/Config/config.h" -#include "clang/Driver/Driver.h" +#include "clang/Options/OptionUtils.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" @@ -53,7 +53,7 @@ static bool DefaultComputeClangResourceDirectory(FileSpec &lldb_shlib_spec, std::string raw_path = lldb_shlib_spec.GetPath(); llvm::StringRef parent_dir = llvm::sys::path::parent_path(raw_path); static const std::string clang_resource_path = - clang::driver::Driver::GetResourcesPath("bin/lldb"); + clang::GetResourcesPath("bin/lldb"); static const llvm::StringRef kResourceDirSuffixes[] = { // LLVM.org's build of LLDB uses the clang resource directory placed diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp index b77e269..ce8dc50b 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp @@ -10,6 +10,7 @@ #include "clang/Basic/DiagnosticFrontend.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Driver/CreateInvocationFromArgs.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Frontend/TextDiagnosticPrinter.h" @@ -67,13 +68,13 @@ private: IDAndDiagnostic; std::vector<IDAndDiagnostic> m_diagnostics; std::unique_ptr<clang::DiagnosticOptions> m_diag_opts; + /// Output string filled by m_os. Will be reused for different diagnostics. + std::string m_output; + /// Output stream of m_diag_printer. + std::unique_ptr<llvm::raw_string_ostream> m_os; /// The DiagnosticPrinter used for creating the full diagnostic messages /// that are stored in m_diagnostics. std::unique_ptr<clang::TextDiagnosticPrinter> m_diag_printer; - /// Output stream of m_diag_printer. - std::unique_ptr<llvm::raw_string_ostream> m_os; - /// Output string filled by m_os. Will be reused for different diagnostics. - std::string m_output; /// A Progress with explicitly managed lifetime. std::unique_ptr<Progress> m_current_progress_up; std::vector<std::string> m_module_build_stack; @@ -92,11 +93,11 @@ public: ~ClangModulesDeclVendorImpl() override = default; - bool AddModule(const SourceModule &module, ModuleVector *exported_modules, - Stream &error_stream) override; + llvm::Error AddModule(const SourceModule &module, + ModuleVector *exported_modules) override; - bool AddModulesForCompileUnit(CompileUnit &cu, ModuleVector &exported_modules, - Stream &error_stream) override; + llvm::Error AddModulesForCompileUnit(CompileUnit &cu, + ModuleVector &exported_modules) override; uint32_t FindDecls(ConstString name, bool append, uint32_t max_matches, std::vector<CompilerDecl> &decls) override; @@ -273,16 +274,14 @@ void ClangModulesDeclVendorImpl::ReportModuleExports( exports.push_back(module); } -bool ClangModulesDeclVendorImpl::AddModule(const SourceModule &module, - ModuleVector *exported_modules, - Stream &error_stream) { +llvm::Error +ClangModulesDeclVendorImpl::AddModule(const SourceModule &module, + ModuleVector *exported_modules) { // 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->hadModuleLoaderFatalFailure()) + return llvm::createStringError( + "couldn't load a module because the module loader is in a fatal state"); // Check if we've already imported this module. @@ -297,7 +296,7 @@ bool ClangModulesDeclVendorImpl::AddModule(const SourceModule &module, if (mi != m_imported_modules.end()) { if (exported_modules) ReportModuleExports(*exported_modules, mi->second); - return true; + return llvm::Error::success(); } } @@ -315,30 +314,30 @@ bool ClangModulesDeclVendorImpl::AddModule(const SourceModule &module, std::equal(sysroot_begin, sysroot_end, path_begin); // No need to inject search paths to modules in the sysroot. if (!is_system_module) { - auto error = [&]() { - error_stream.Printf("error: No module map file in %s\n", - module.search_path.AsCString()); - return false; - }; - bool is_system = true; bool is_framework = false; auto dir = HS.getFileMgr().getOptionalDirectoryRef( module.search_path.GetStringRef()); if (!dir) - return error(); + return llvm::createStringError( + "couldn't find module search path directory %s", + module.search_path.GetCString()); + auto file = HS.lookupModuleMapFile(*dir, is_framework); if (!file) - return error(); + return llvm::createStringError("couldn't find modulemap file in %s", + module.search_path.GetCString()); + if (HS.parseAndLoadModuleMapFile(*file, is_system)) - return error(); + return llvm::createStringError( + "failed to parse and load modulemap file in %s", + module.search_path.GetCString()); } } - if (!HS.lookupModule(module.path.front().GetStringRef())) { - error_stream.Printf("error: Header search couldn't locate module '%s'\n", - module.path.front().AsCString()); - return false; - } + + if (!HS.lookupModule(module.path.front().GetStringRef())) + return llvm::createStringError("header search couldn't locate module '%s'", + module.path.front().AsCString()); llvm::SmallVector<clang::IdentifierLoc, 4> clang_path; @@ -364,22 +363,29 @@ bool ClangModulesDeclVendorImpl::AddModule(const SourceModule &module, clang::Module *top_level_module = DoGetModule(clang_path.front(), false); if (!top_level_module) { + lldb_private::StreamString error_stream; diagnostic_consumer->DumpDiagnostics(error_stream); - error_stream.Printf("error: Couldn't load top-level module %s\n", - module.path.front().AsCString()); - return false; + + return llvm::createStringError(llvm::formatv( + "couldn't load top-level module {0}:\n{1}", + module.path.front().GetStringRef(), error_stream.GetString())); } clang::Module *submodule = top_level_module; for (auto &component : llvm::ArrayRef<ConstString>(module.path).drop_front()) { - submodule = submodule->findSubmodule(component.GetStringRef()); - if (!submodule) { + clang::Module *found = submodule->findSubmodule(component.GetStringRef()); + if (!found) { + lldb_private::StreamString error_stream; diagnostic_consumer->DumpDiagnostics(error_stream); - error_stream.Printf("error: Couldn't load submodule %s\n", - component.GetCString()); - return false; + + return llvm::createStringError(llvm::formatv( + "couldn't load submodule '{0}' of module '{1}':\n{2}", + component.GetStringRef(), submodule->getFullModuleName(), + error_stream.GetString())); } + + submodule = found; } // If we didn't make the submodule visible here, Clang wouldn't allow LLDB to @@ -399,10 +405,12 @@ bool ClangModulesDeclVendorImpl::AddModule(const SourceModule &module, m_enabled = true; - return true; + return llvm::Error::success(); } - return false; + return llvm::createStringError( + llvm::formatv("unknown error while loading module {0}\n", + module.path.front().GetStringRef())); } bool ClangModulesDeclVendor::LanguageSupportsClangModules( @@ -424,15 +432,18 @@ bool ClangModulesDeclVendor::LanguageSupportsClangModules( } } -bool ClangModulesDeclVendorImpl::AddModulesForCompileUnit( - CompileUnit &cu, ClangModulesDeclVendor::ModuleVector &exported_modules, - Stream &error_stream) { - if (LanguageSupportsClangModules(cu.GetLanguage())) { - for (auto &imported_module : cu.GetImportedModules()) - if (!AddModule(imported_module, &exported_modules, error_stream)) - return false; - } - return true; +llvm::Error ClangModulesDeclVendorImpl::AddModulesForCompileUnit( + CompileUnit &cu, ClangModulesDeclVendor::ModuleVector &exported_modules) { + if (!LanguageSupportsClangModules(cu.GetLanguage())) + return llvm::Error::success(); + + llvm::Error errors = llvm::Error::success(); + + for (auto &imported_module : cu.GetImportedModules()) + if (auto err = AddModule(imported_module, &exported_modules)) + errors = llvm::joinErrors(std::move(errors), std::move(err)); + + return errors; } // ClangImporter::lookupValue diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h index ad4d060..0436320 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h @@ -41,21 +41,16 @@ public: /// The path to the exact module to be loaded. E.g., if the desired /// module is std.io, then this should be { "std", "io" }. /// - /// \param[in] exported_modules + /// \param[out] exported_modules /// If non-NULL, a pointer to a vector to populate with the ID of every /// module that is re-exported by the specified module. /// - /// \param[in] error_stream - /// A stream to populate with the output of the Clang parser when - /// it tries to load the module. - /// /// \return /// True if the module could be loaded; false if not. If the /// compiler encountered a fatal error during a previous module /// load, then this will always return false for this ModuleImporter. - virtual bool AddModule(const SourceModule &module, - ModuleVector *exported_modules, - Stream &error_stream) = 0; + virtual llvm::Error AddModule(const SourceModule &module, + ModuleVector *exported_modules) = 0; /// Add all modules referred to in a given compilation unit to the list /// of modules to search. @@ -63,22 +58,17 @@ public: /// \param[in] cu /// The compilation unit to scan for imported modules. /// - /// \param[in] exported_modules + /// \param[out] exported_modules /// A vector to populate with the ID of each module loaded (directly /// and via re-exports) in this way. /// - /// \param[in] error_stream - /// A stream to populate with the output of the Clang parser when - /// it tries to load the modules. - /// /// \return /// True if all modules referred to by the compilation unit could be /// loaded; false if one could not be loaded. If the compiler /// encountered a fatal error during a previous module /// load, then this will always return false for this ModuleImporter. - virtual bool AddModulesForCompileUnit(CompileUnit &cu, - ModuleVector &exported_modules, - Stream &error_stream) = 0; + virtual llvm::Error + AddModulesForCompileUnit(CompileUnit &cu, ModuleVector &exported_modules) = 0; /// Enumerate all the macros that are defined by a given set of modules /// that are already imported. diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp index e8d5ec3..d1feda1 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp @@ -371,26 +371,20 @@ static void SetupDeclVendor(ExecutionContext &exe_ctx, Target *target, if (!sc.comp_unit) return; - StreamString error_stream; - ClangModulesDeclVendor::ModuleVector modules_for_macros = persistent_state->GetHandLoadedClangModules(); - if (decl_vendor->AddModulesForCompileUnit(*sc.comp_unit, modules_for_macros, - error_stream)) - return; - // Failed to load some modules, so emit the error stream as a diagnostic. - if (!error_stream.Empty()) { - // The error stream already contains several Clang diagnostics that might - // be either errors or warnings, so just print them all as one remark - // diagnostic to prevent that the message starts with "error: error:". - diagnostic_manager.PutString(lldb::eSeverityInfo, error_stream.GetString()); + auto err = + decl_vendor->AddModulesForCompileUnit(*sc.comp_unit, modules_for_macros); + if (!err) return; - } - diagnostic_manager.PutString(lldb::eSeverityError, - "Unknown error while loading modules needed for " - "current compilation unit."); + // Module load errors aren't fatal to the expression evaluator. Printing + // them as diagnostics to the console would be too noisy and misleading + // Hence just print them to the expression log. + llvm::handleAllErrors(std::move(err), [](const llvm::StringError &e) { + LLDB_LOG(GetLog(LLDBLog::Expressions), "{0}", e.getMessage()); + }); } ClangExpressionSourceCode::WrapKind ClangUserExpression::GetWrapKind() const { diff --git a/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp b/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp index a8901be..f124424 100644 --- a/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp +++ b/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp @@ -346,6 +346,16 @@ EmulateInstructionARM64::GetOpcodeForInstruction(const uint32_t opcode) { &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_OFF>, "LDR <Xt>, [<Xn|SP>{, #<pimm>}]"}, + {0x3f200c00, 0x3c000400, No_VFP, + &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_POST>, + "LDR|STR <Bt|Ht|St|Dt|Qt>, [<Xn|SP>], #<simm>"}, + {0x3f200c00, 0x3c000c00, No_VFP, + &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_PRE>, + "LDR|STR <Bt|Ht|St|Dt|Qt>, [<Xn|SP>, #<simm>]!"}, + {0x3f000000, 0x3d000000, No_VFP, + &EmulateInstructionARM64::EmulateLDRSTRImm<AddrMode_OFF>, + "LDR|STR <Bt|Ht|St|Dt|Qt>, [<Xn|SP>{, #<pimm>}]"}, + {0xfc000000, 0x14000000, No_VFP, &EmulateInstructionARM64::EmulateB, "B <label>"}, {0xff000010, 0x54000000, No_VFP, &EmulateInstructionARM64::EmulateBcond, @@ -930,9 +940,29 @@ template <EmulateInstructionARM64::AddrMode a_mode> bool EmulateInstructionARM64::EmulateLDRSTRImm(const uint32_t opcode) { uint32_t size = Bits32(opcode, 31, 30); uint32_t opc = Bits32(opcode, 23, 22); + uint32_t vr = Bit32(opcode, 26); uint32_t n = Bits32(opcode, 9, 5); uint32_t t = Bits32(opcode, 4, 0); + MemOp memop; + if (vr) { + // opc<1> == 1 && size != 0 is an undefined encoding. + if (Bit32(opc, 1) == 1 && size != 0) + return false; + // opc<1> == 1 && size == 0 encode the 128-bit variant. + if (Bit32(opc, 1) == 1) + size = 4; + memop = Bit32(opc, 0) == 1 ? MemOp_LOAD : MemOp_STORE; + } else { + if (Bit32(opc, 1) == 0) { + memop = Bit32(opc, 0) == 1 ? MemOp_LOAD : MemOp_STORE; + } else { + memop = MemOp_LOAD; + if (size == 2 && Bit32(opc, 0) == 1) + return false; + } + } + bool wback; bool postindex; uint64_t offset; @@ -955,16 +985,6 @@ bool EmulateInstructionARM64::EmulateLDRSTRImm(const uint32_t opcode) { break; } - MemOp memop; - - if (Bit32(opc, 1) == 0) { - memop = Bit32(opc, 0) == 1 ? MemOp_LOAD : MemOp_STORE; - } else { - memop = MemOp_LOAD; - if (size == 2 && Bit32(opc, 0) == 1) - return false; - } - Status error; bool success = false; uint64_t address; @@ -989,7 +1009,8 @@ bool EmulateInstructionARM64::EmulateLDRSTRImm(const uint32_t opcode) { return false; std::optional<RegisterInfo> reg_info_Rt = - GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + t); + vr ? GetRegisterInfo(eRegisterKindLLDB, fpu_d0_arm64 + t) + : GetRegisterInfo(eRegisterKindLLDB, gpr_x0_arm64 + t); if (!reg_info_Rt) return false; diff --git a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp index 5c1b7d4..2957cb71 100644 --- a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp +++ b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp @@ -1328,32 +1328,36 @@ public: m_emu, inst, 8, ZextD, [](uint64_t a, uint64_t b) { return std::max(a, b); }); } - template <typename T> - bool F_Load(T inst, const fltSemantics &(*semantics)(), - unsigned int numBits) { + template <typename I, typename T> + bool F_Load(I inst, const fltSemantics &(*semantics)()) { return transformOptional(inst.rs1.Read(m_emu), [&](auto &&rs1) { - uint64_t addr = rs1 + uint64_t(inst.imm); - uint64_t bits = *m_emu.ReadMem<uint64_t>(addr); + uint64_t addr = + rs1 + uint64_t(SignExt(inst.imm)); + uint64_t bits = *m_emu.ReadMem<T>(addr); + unsigned numBits = sizeof(T) * 8; APFloat f(semantics(), APInt(numBits, bits)); return inst.rd.WriteAPFloat(m_emu, f); }) .value_or(false); } - bool operator()(FLW inst) { return F_Load(inst, &APFloat::IEEEsingle, 32); } - template <typename T> bool F_Store(T inst, bool isDouble) { + bool operator()(FLW inst) { + return F_Load<FLW, uint32_t>(inst, &APFloat::IEEEsingle); + } + template <typename I, typename T> bool F_Store(I inst, bool isDouble) { return transformOptional(zipOpt(inst.rs1.Read(m_emu), inst.rs2.ReadAPFloat(m_emu, isDouble)), [&](auto &&tup) { auto [rs1, rs2] = tup; - uint64_t addr = rs1 + uint64_t(inst.imm); + uint64_t addr = + rs1 + uint64_t(SignExt(inst.imm)); uint64_t bits = rs2.bitcastToAPInt().getZExtValue(); - return m_emu.WriteMem<uint64_t>(addr, bits); + return m_emu.WriteMem<T>(addr, bits); }) .value_or(false); } - bool operator()(FSW inst) { return F_Store(inst, false); } + bool operator()(FSW inst) { return F_Store<FSW, uint32_t>(inst, false); } std::tuple<bool, APFloat> FusedMultiplyAdd(APFloat rs1, APFloat rs2, APFloat rs3) { auto opStatus = rs1.fusedMultiplyAdd(rs2, rs3, m_emu.GetRoundingMode()); @@ -1616,8 +1620,10 @@ public: bool operator()(FCVT_S_LU inst) { return FCVT_f2i(inst, &Rs::Read, APFloat::IEEEsingle()); } - bool operator()(FLD inst) { return F_Load(inst, &APFloat::IEEEdouble, 64); } - bool operator()(FSD inst) { return F_Store(inst, true); } + bool operator()(FLD inst) { + return F_Load<FLD, uint64_t>(inst, &APFloat::IEEEdouble); + } + bool operator()(FSD inst) { return F_Store<FSD, uint64_t>(inst, true); } bool operator()(FMADD_D inst) { return FMA(inst, true, 1.0f, 1.0f); } bool operator()(FMSUB_D inst) { return FMA(inst, true, 1.0f, -1.0f); } bool operator()(FNMSUB_D inst) { return FMA(inst, true, -1.0f, 1.0f); } diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/CMakeLists.txt b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/CMakeLists.txt new file mode 100644 index 0000000..adbd6c4 --- /dev/null +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_library(lldbPluginInstrumentationRuntimeBoundsSafety PLUGIN + InstrumentationRuntimeBoundsSafety.cpp + + LINK_LIBS + lldbBreakpoint + lldbCore + lldbSymbol + lldbTarget + lldbPluginInstrumentationRuntimeUtility + + CLANG_LIBS + clangCodeGen + ) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp new file mode 100644 index 0000000..db9b213 --- /dev/null +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp @@ -0,0 +1,481 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "InstrumentationRuntimeBoundsSafety.h" + +#include "Plugins/Process/Utility/HistoryThread.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/InstrumentationRuntimeStopInfo.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/RegularExpression.h" +#include "clang/CodeGen/ModuleBuilder.h" + +#include <memory> +#include <type_traits> + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(InstrumentationRuntimeBoundsSafety) + +constexpr llvm::StringLiteral + BoundsSafetySoftTrapMinimal("__bounds_safety_soft_trap"); +constexpr llvm::StringLiteral + BoundsSafetySoftTrapStr("__bounds_safety_soft_trap_s"); + +constexpr std::array<llvm::StringLiteral, 2> +getBoundsSafetySoftTrapRuntimeFuncs() { + return {BoundsSafetySoftTrapMinimal, BoundsSafetySoftTrapStr}; +} + +#define SOFT_TRAP_CATEGORY_PREFIX "Soft " +#define SOFT_TRAP_FALLBACK_CATEGORY \ + SOFT_TRAP_CATEGORY_PREFIX "Bounds check failed" + +using ComputedStopInfo = + std::pair<std::optional<std::string>, std::optional<uint32_t>>; + +class InstrumentationBoundsSafetyStopInfo : public StopInfo { +public: + ~InstrumentationBoundsSafetyStopInfo() override = default; + + lldb::StopReason GetStopReason() const override { + return lldb::eStopReasonInstrumentation; + } + + std::optional<uint32_t> + GetSuggestedStackFrameIndex(bool inlined_stack) override { + return m_value; + } + + const char *GetDescription() override { return m_description.c_str(); } + + bool DoShouldNotify(Event *event_ptr) override { return true; } + + static lldb::StopInfoSP + CreateInstrumentationBoundsSafetyStopInfo(Thread &thread) { + return StopInfoSP(new InstrumentationBoundsSafetyStopInfo(thread)); + } + +private: + InstrumentationBoundsSafetyStopInfo(Thread &thread); + + ComputedStopInfo + ComputeStopReasonAndSuggestedStackFrame(bool &warning_emitted_for_failure); + + ComputedStopInfo ComputeStopReasonAndSuggestedStackFrameWithDebugInfo( + lldb::StackFrameSP parent_sf, lldb::user_id_t debugger_id, + bool &warning_emitted_for_failure); + + ComputedStopInfo ComputeStopReasonAndSuggestedStackFrameWithoutDebugInfo( + ThreadSP thread_sp, lldb::user_id_t debugger_id, + bool &warning_emitted_for_failure); +}; + +InstrumentationBoundsSafetyStopInfo::InstrumentationBoundsSafetyStopInfo( + Thread &thread) + : StopInfo(thread, 0) { + // No additional data describing the reason for stopping. + m_extended_info = nullptr; + m_description = SOFT_TRAP_FALLBACK_CATEGORY; + + bool warning_emitted_for_failure = false; + auto [MaybeDescription, MaybeSuggestedStackIndex] = + ComputeStopReasonAndSuggestedStackFrame(warning_emitted_for_failure); + if (MaybeDescription) + m_description = MaybeDescription.value(); + if (MaybeSuggestedStackIndex) + m_value = MaybeSuggestedStackIndex.value(); + + // Emit warning about the failure to compute the stop info if one wasn't + // already emitted. + if ((!MaybeDescription.has_value()) && !warning_emitted_for_failure) { + if (ThreadSP thread_sp = GetThread()) { + lldb::user_id_t debugger_id = + thread_sp->GetProcess()->GetTarget().GetDebugger().GetID(); + Debugger::ReportWarning( + "specific BoundsSafety trap reason could not be computed", + debugger_id); + } + } +} + +// Helper functions to make it convenient to log a failure and then return. +template <typename T, typename... ArgTys> +[[nodiscard]] T LogBeforeReturn(ArgTys &&...Args) { + LLDB_LOG(GetLog(LLDBLog::InstrumentationRuntime), Args...); + return T(); +} + +template <typename... ArgTys> +[[nodiscard]] ComputedStopInfo LogFailedCSI(ArgTys &&...Args) { + return LogBeforeReturn<ComputedStopInfo>(Args...); +} + +ComputedStopInfo +InstrumentationBoundsSafetyStopInfo::ComputeStopReasonAndSuggestedStackFrame( + bool &warning_emitted_for_failure) { + ThreadSP thread_sp = GetThread(); + if (!thread_sp) + return LogFailedCSI("failed to get thread while stopped"); + + lldb::user_id_t debugger_id = + thread_sp->GetProcess()->GetTarget().GetDebugger().GetID(); + + StackFrameSP parent_sf = thread_sp->GetStackFrameAtIndex(1); + if (!parent_sf) + return LogFailedCSI("got nullptr when fetching stackframe at index 1"); + + if (parent_sf->HasDebugInformation()) + return ComputeStopReasonAndSuggestedStackFrameWithDebugInfo( + parent_sf, debugger_id, warning_emitted_for_failure); + + // If the debug info is missing we can still get some information + // from the parameter in the soft trap runtime call. + return ComputeStopReasonAndSuggestedStackFrameWithoutDebugInfo( + thread_sp, debugger_id, warning_emitted_for_failure); +} + +ComputedStopInfo InstrumentationBoundsSafetyStopInfo:: + ComputeStopReasonAndSuggestedStackFrameWithDebugInfo( + lldb::StackFrameSP parent_sf, lldb::user_id_t debugger_id, + bool &warning_emitted_for_failure) { + // First try to use debug info to understand the reason for trapping. The + // call stack will look something like this: + // + // ``` + // frame #0: `__bounds_safety_soft_trap_s(reason="") + // frame #1: `__clang_trap_msg$Bounds check failed$<reason>' + // frame #2: `bad_read(index=10) + // ``` + // .... + const char *TrapReasonFuncName = parent_sf->GetFunctionName(); + + auto MaybeTrapReason = + clang::CodeGen::DemangleTrapReasonInDebugInfo(TrapReasonFuncName); + if (!MaybeTrapReason.has_value()) + return LogFailedCSI( + "clang::CodeGen::DemangleTrapReasonInDebugInfo(\"{0}\") call failed", + TrapReasonFuncName); + + llvm::StringRef category = MaybeTrapReason.value().first; + llvm::StringRef message = MaybeTrapReason.value().second; + + // TODO: Clang should probably be changed to emit the "Soft " prefix itself + std::string stop_reason; + llvm::raw_string_ostream ss(stop_reason); + ss << SOFT_TRAP_CATEGORY_PREFIX; + if (category.empty()) + ss << "<empty category>"; + else + ss << category; + if (message.empty()) { + // This is not a failure so leave `warning_emitted_for_failure` untouched. + Debugger::ReportWarning( + "specific BoundsSafety trap reason is not " + "available because the compiler omitted it from the debug info", + debugger_id); + } else { + ss << ": " << message; + } + // Use computed stop-reason and assume the parent of `parent_sf` is the + // the place in the user's code where the call to the soft trap runtime + // originated. + return std::make_pair(stop_reason, parent_sf->GetFrameIndex() + 1); +} + +ComputedStopInfo InstrumentationBoundsSafetyStopInfo:: + ComputeStopReasonAndSuggestedStackFrameWithoutDebugInfo( + ThreadSP thread_sp, lldb::user_id_t debugger_id, + bool &warning_emitted_for_failure) { + + StackFrameSP softtrap_sf = thread_sp->GetStackFrameAtIndex(0); + if (!softtrap_sf) + return LogFailedCSI("got nullptr when fetching stackframe at index 0"); + llvm::StringRef trap_reason_func_name = softtrap_sf->GetFunctionName(); + + if (trap_reason_func_name == BoundsSafetySoftTrapMinimal) { + // This function has no arguments so there's no additional information + // that would allow us to identify the trap reason. + // + // Use the fallback stop reason and the current frame. + // While we "could" set the suggested frame to our parent (where the + // bounds check failed), doing this leads to very misleading output in + // LLDB. E.g.: + // + // ``` + // 0x100003b40 <+104>: bl 0x100003d64 ; __bounds_safety_soft_trap + // -> 0x100003b44 <+108>: b 0x100003b48 ; <+112> + // ``` + // + // This makes it look we stopped after finishing the call to + // `__bounds_safety_soft_trap` but actually we are in the middle of the + // call. To avoid this confusion just use the current frame. + std::string warning; + llvm::raw_string_ostream ss(warning); + ss << "specific BoundsSafety trap reason is not available because debug " + "info is missing on the caller of '" + << BoundsSafetySoftTrapMinimal << "'"; + Debugger::ReportWarning(warning.c_str(), debugger_id); + warning_emitted_for_failure = true; + return {}; + } + + // __bounds_safety_soft_trap_s has one argument which is a pointer to a string + // describing the trap or a nullptr. + if (trap_reason_func_name != BoundsSafetySoftTrapStr) { + assert(0 && "hit breakpoint for unexpected function name"); + return LogFailedCSI( + "unexpected function name. Expected \"{0}\" but got \"{1}\"", + BoundsSafetySoftTrapStr.data(), trap_reason_func_name.data()); + } + + RegisterContextSP rc = thread_sp->GetRegisterContext(); + if (!rc) + return LogFailedCSI("failed to get register context"); + + // FIXME: LLDB should have an API that tells us for the current target if + // `LLDB_REGNUM_GENERIC_ARG1` can be used. + // https://github.com/llvm/llvm-project/issues/168602 + // Don't try for architectures where examining the first register won't + // work. + ProcessSP process = thread_sp->GetProcess(); + if (!process) + return LogFailedCSI("failed to get process"); + + switch (process->GetTarget().GetArchitecture().GetCore()) { + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_32_i486: + case ArchSpec::eCore_x86_32_i486sx: + case ArchSpec::eCore_x86_32_i686: { + // Technically some x86 calling conventions do use a register for + // passing the first argument but let's ignore that for now. + std::string warning; + llvm::raw_string_ostream ss(warning); + ss << "specific BoundsSafety trap reason cannot be inferred on x86 when " + "the caller of '" + << BoundsSafetySoftTrapStr << "' is missing debug info"; + Debugger::ReportWarning(warning.c_str(), debugger_id); + warning_emitted_for_failure = true; + return {}; + } + default: { + } + }; + + // Examine the register for the first argument. + const RegisterInfo *arg0_info = rc->GetRegisterInfo( + lldb::RegisterKind::eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); + if (!arg0_info) + return LogFailedCSI( + "failed to get register info for LLDB_REGNUM_GENERIC_ARG1"); + RegisterValue reg_value; + if (!rc->ReadRegister(arg0_info, reg_value)) + return LogFailedCSI("failed to read register {0}", arg0_info->name); + uint64_t reg_value_as_int = reg_value.GetAsUInt64(UINT64_MAX); + if (reg_value_as_int == UINT64_MAX) + return LogFailedCSI("failed to read register {0} as a UInt64", + arg0_info->name); + + if (reg_value_as_int == 0) { + // nullptr arg. The compiler will pass that if no trap reason string was + // available. + Debugger::ReportWarning( + "specific BoundsSafety trap reason cannot be inferred because the " + "compiler omitted the reason", + debugger_id); + warning_emitted_for_failure = true; + return {}; + } + + // The first argument to the call is a pointer to a global C string + // containing the trap reason. + std::string out_string; + Status error_status; + thread_sp->GetProcess()->ReadCStringFromMemory(reg_value_as_int, out_string, + error_status); + if (error_status.Fail()) + return LogFailedCSI("failed to read C string from address {0}", + (void *)reg_value_as_int); + + LLDB_LOG(GetLog(LLDBLog::InstrumentationRuntime), + "read C string from {0} found in register {1}: \"{2}\"", + (void *)reg_value_as_int, arg0_info->name, out_string.c_str()); + std::string stop_reason; + llvm::raw_string_ostream SS(stop_reason); + SS << SOFT_TRAP_FALLBACK_CATEGORY; + if (!stop_reason.empty()) { + SS << ": " << out_string; + } + // Use the current frame as the suggested frame for the same reason as for + // `__bounds_safety_soft_trap`. + return {stop_reason, 0}; +} + +InstrumentationRuntimeBoundsSafety::~InstrumentationRuntimeBoundsSafety() { + Deactivate(); +} + +lldb::InstrumentationRuntimeSP +InstrumentationRuntimeBoundsSafety::CreateInstance( + const lldb::ProcessSP &process_sp) { + return InstrumentationRuntimeSP( + new InstrumentationRuntimeBoundsSafety(process_sp)); +} + +void InstrumentationRuntimeBoundsSafety::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + "BoundsSafety instrumentation runtime plugin.", + CreateInstance, GetTypeStatic); +} + +void InstrumentationRuntimeBoundsSafety::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb::InstrumentationRuntimeType +InstrumentationRuntimeBoundsSafety::GetTypeStatic() { + return lldb::eInstrumentationRuntimeTypeBoundsSafety; +} + +const RegularExpression & +InstrumentationRuntimeBoundsSafety::GetPatternForRuntimeLibrary() { + static RegularExpression regex; + return regex; +} + +bool InstrumentationRuntimeBoundsSafety::CheckIfRuntimeIsValid( + const lldb::ModuleSP module_sp) { + Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); + for (const auto &SoftTrapFunc : getBoundsSafetySoftTrapRuntimeFuncs()) { + ConstString test_sym(SoftTrapFunc); + + if (module_sp->FindFirstSymbolWithNameAndType(test_sym, + lldb::eSymbolTypeAny)) { + LLDB_LOG(log_category, "found \"{0}\" in {1}", + test_sym.AsCString("<unknown symbol>"), + module_sp->GetObjectName().AsCString("<unknown module>")); + return true; + } + } + LLDB_LOG(log_category, + "did not find BoundsSafety soft trap functions in module {0}", + module_sp->GetObjectName().AsCString("<unknown module>")); + return false; +} + +bool InstrumentationRuntimeBoundsSafety::NotifyBreakpointHit( + void *baton, StoppointCallbackContext *context, user_id_t break_id, + user_id_t break_loc_id) { + assert(baton && "null baton"); + if (!baton) + return false; ///< false => resume execution. + + InstrumentationRuntimeBoundsSafety *const instance = + static_cast<InstrumentationRuntimeBoundsSafety *>(baton); + + ProcessSP process_sp = instance->GetProcessSP(); + if (!process_sp) + return LogBeforeReturn<bool>("failed to get process from baton"); + ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); + if (!thread_sp) + return LogBeforeReturn<bool>( + "failed to get thread from StoppointCallbackContext"); + + if (process_sp != context->exe_ctx_ref.GetProcessSP()) + return LogBeforeReturn<bool>( + "process from baton ({0}) and StoppointCallbackContext ({1}) do " + "not match", + (void *)process_sp.get(), + (void *)context->exe_ctx_ref.GetProcessSP().get()); + + if (process_sp->GetModIDRef().IsLastResumeForUserExpression()) + return LogBeforeReturn<bool>("IsLastResumeForUserExpression is true"); + + // Maybe the stop reason and stackframe selection should be done by + // a stackframe recognizer instead? + thread_sp->SetStopInfo( + InstrumentationBoundsSafetyStopInfo:: + CreateInstrumentationBoundsSafetyStopInfo(*thread_sp)); + return true; +} + +void InstrumentationRuntimeBoundsSafety::Activate() { + if (IsActive()) + return; + + ProcessSP process_sp = GetProcessSP(); + if (!process_sp) + return LogBeforeReturn<void>("could not get process during Activate()"); + + std::vector<std::string> breakpoints; + for (auto &breakpoint_func : getBoundsSafetySoftTrapRuntimeFuncs()) + breakpoints.emplace_back(breakpoint_func); + + BreakpointSP breakpoint = process_sp->GetTarget().CreateBreakpoint( + /*containingModules=*/nullptr, + /*containingSourceFiles=*/nullptr, breakpoints, eFunctionNameTypeFull, + eLanguageTypeUnknown, + /*m_offset=*/0, + /*skip_prologue*/ eLazyBoolNo, + /*internal=*/true, + /*request_hardware*/ false); + + if (!breakpoint) + return LogBeforeReturn<void>("failed to create breakpoint"); + + if (!breakpoint->HasResolvedLocations()) { + assert(0 && "breakpoint has no resolved locations"); + process_sp->GetTarget().RemoveBreakpointByID(breakpoint->GetID()); + return LogBeforeReturn<void>( + "breakpoint {0} for BoundsSafety soft traps did not resolve to " + "any locations", + breakpoint->GetID()); + } + + // Note: When `sync=true` the suggested stackframe is completely ignored. So + // we use `sync=false`. Is that a bug? + breakpoint->SetCallback( + InstrumentationRuntimeBoundsSafety::NotifyBreakpointHit, this, + /*sync=*/false); + breakpoint->SetBreakpointKind("bounds-safety-soft-trap"); + SetBreakpointID(breakpoint->GetID()); + LLDB_LOG(GetLog(LLDBLog::InstrumentationRuntime), + "created breakpoint {0} for BoundsSafety soft traps", + breakpoint->GetID()); + SetActive(true); +} + +void InstrumentationRuntimeBoundsSafety::Deactivate() { + SetActive(false); + Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); + if (ProcessSP process_sp = GetProcessSP()) { + bool success = + process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID()); + LLDB_LOG(log_category, + "{0}removed breakpoint {1} for BoundsSafety soft traps", + success ? "" : "failed to ", GetBreakpointID()); + } else { + LLDB_LOG(log_category, "no process available during Deactivate()"); + } + + SetBreakpointID(LLDB_INVALID_BREAK_ID); +} diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.h b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.h new file mode 100644 index 0000000..06c30f8 --- /dev/null +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.h @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_INSTRUMENTATIONRUNTIME_BOUNDS_SAFETY_SOFT_TRAP_H +#define LLDB_SOURCE_PLUGINS_INSTRUMENTATIONRUNTIME_BOUNDS_SAFETY_SOFT_TRAP_H + +#include "lldb/Target/ABI.h" +#include "lldb/Target/InstrumentationRuntime.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class InstrumentationRuntimeBoundsSafety + : public lldb_private::InstrumentationRuntime { +public: + ~InstrumentationRuntimeBoundsSafety() override; + + static lldb::InstrumentationRuntimeSP + CreateInstance(const lldb::ProcessSP &process_sp); + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "BoundsSafety"; } + + static lldb::InstrumentationRuntimeType GetTypeStatic(); + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + virtual lldb::InstrumentationRuntimeType GetType() { return GetTypeStatic(); } + +private: + InstrumentationRuntimeBoundsSafety(const lldb::ProcessSP &process_sp) + : lldb_private::InstrumentationRuntime(process_sp) {} + + const RegularExpression &GetPatternForRuntimeLibrary() override; + + bool CheckIfRuntimeIsValid(const lldb::ModuleSP module_sp) override; + + void Activate() override; + + void Deactivate(); + + static bool NotifyBreakpointHit(void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + bool MatchAllModules() override { return true; } +}; + +} // namespace lldb_private + +#endif diff --git a/lldb/source/Plugins/InstrumentationRuntime/CMakeLists.txt b/lldb/source/Plugins/InstrumentationRuntime/CMakeLists.txt index 2a6cf93..b7e1a60 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/CMakeLists.txt +++ b/lldb/source/Plugins/InstrumentationRuntime/CMakeLists.txt @@ -2,6 +2,7 @@ set_property(DIRECTORY PROPERTY LLDB_PLUGIN_KIND InstrumentationRuntime) add_subdirectory(ASan) add_subdirectory(ASanLibsanitizers) +add_subdirectory(BoundsSafety) add_subdirectory(MainThreadChecker) add_subdirectory(TSan) add_subdirectory(UBSan) diff --git a/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp b/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp index 38c334b..3642cb1 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp @@ -207,8 +207,11 @@ bool ReportRetriever::NotifyBreakpointHit(ProcessSP process_sp, return false; StructuredData::ObjectSP report = RetrieveReportData(process_sp); - if (!report || report->GetType() != lldb::eStructuredDataTypeDictionary) + if (!report || report->GetType() != lldb::eStructuredDataTypeDictionary) { + LLDB_LOGF(GetLog(LLDBLog::InstrumentationRuntime), + "ReportRetriever::RetrieveReportData() failed"); return false; + } std::string description = FormatDescription(report); diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt index cbc6f14..c52d3bd 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -14,11 +14,11 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN CxxStringTypes.cpp Generic.cpp GenericBitset.cpp + GenericInitializerList.cpp GenericList.cpp GenericOptional.cpp LibCxx.cpp LibCxxAtomic.cpp - LibCxxInitializerList.cpp LibCxxMap.cpp LibCxxQueue.cpp LibCxxRangesRefView.cpp @@ -31,6 +31,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN LibCxxValarray.cpp LibCxxVector.cpp LibStdcpp.cpp + LibStdcppSpan.cpp LibStdcppTuple.cpp LibStdcppUniquePointer.cpp MsvcStl.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index a2199cb..ae6086f 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -84,7 +84,7 @@ CPlusPlusLanguage::GetFunctionNameInfo(ConstString name) const { if (basename.empty()) { llvm::StringRef context; func_name_type |= - (ExtractContextAndIdentifier(name.GetCString(), context, basename) + (ExtractContextAndIdentifier(name.GetStringRef(), context, basename) ? (eFunctionNameTypeMethod | eFunctionNameTypeBase) : eFunctionNameTypeFull); } else { @@ -103,7 +103,7 @@ CPlusPlusLanguage::GetFunctionNameInfo(ConstString name) const { return {func_name_type, ConstString(basename)}; } -bool CPlusPlusLanguage::SymbolNameFitsToLanguage(Mangled mangled) const { +bool CPlusPlusLanguage::SymbolNameFitsToLanguage(const Mangled &mangled) const { auto mangling_scheme = Mangled::GetManglingScheme(mangled.GetMangledName().GetStringRef()); return mangling_scheme == Mangled::eManglingSchemeItanium || @@ -208,6 +208,20 @@ static bool IsTrivialBasename(const llvm::StringRef &basename) { return idx == basename.size(); } +/// A context is trivial if an only if it matches this pattern. +/// "^\s*([A-Za-z_:]*)\s*$". for example function `foo::bar::func()` +/// has a trivial context but. but `foo<int>::bar::func()` doesn't. +static bool IsTrivialContext(llvm::StringRef context) { + // remove trailing or leading whitespace. + context = context.trim(); + + const auto iter = context.find_if_not([](char current) { + return std::isalnum(static_cast<unsigned char>(current)) || + current == '_' || current == ':'; + }); + return iter == llvm::StringRef::npos; +} + /// Writes out the function name in 'full_name' to 'out_stream' /// but replaces each argument type with the variable name /// and the corresponding pretty-printed value @@ -481,18 +495,17 @@ bool CPlusPlusLanguage::CxxMethodName::TrySimplifiedParse() { m_basename = full.substr(basename_begin, basename_end - basename_begin); } - if (IsTrivialBasename(m_basename)) { + if (IsTrivialBasename(m_basename) && IsTrivialContext(m_context)) { return true; - } else { - // 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_return_type = llvm::StringRef(); - return false; } + // 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_return_type = llvm::StringRef(); + return false; } return false; } @@ -546,9 +559,8 @@ bool CPlusPlusLanguage::CxxMethodName::ContainsPath(llvm::StringRef path) { llvm::StringRef identifier; llvm::StringRef context; - std::string path_str = path.str(); - bool success = CPlusPlusLanguage::ExtractContextAndIdentifier( - path_str.c_str(), context, identifier); + const bool success = + CPlusPlusLanguage::ExtractContextAndIdentifier(path, context, identifier); if (!success) return m_full.GetStringRef().contains(path); @@ -592,7 +604,8 @@ bool CPlusPlusLanguage::DemangledNameContainsPath(llvm::StringRef path, } bool CPlusPlusLanguage::ExtractContextAndIdentifier( - const char *name, llvm::StringRef &context, llvm::StringRef &identifier) { + llvm::StringRef name, llvm::StringRef &context, + llvm::StringRef &identifier) { if (MSVCUndecoratedNameParser::IsMSVCUndecoratedName(name)) return MSVCUndecoratedNameParser::ExtractContextAndIdentifier(name, context, identifier); @@ -899,11 +912,6 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "libc++ std::unordered containers synthetic children", "^std::__[[:alnum:]]+::unordered_(multi)?(map|set)<.+> >$", stl_synth_flags, true); - AddCXXSynthetic( - cpp_category_sp, - lldb_private::formatters::LibcxxInitializerListSyntheticFrontEndCreator, - "libc++ std::initializer_list synthetic children", - "^std::initializer_list<.+>$", stl_synth_flags, true); AddCXXSynthetic(cpp_category_sp, LibcxxQueueFrontEndCreator, "libc++ std::queue synthetic children", "^std::__[[:alnum:]]+::queue<.+>$", stl_synth_flags, true); @@ -1416,6 +1424,10 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { stl_synth_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider"))); + AddCXXSynthetic(cpp_category_sp, LibStdcppSpanSyntheticFrontEndCreator, + "libstdc++ std::span synthetic children", "^std::span<.+>$", + stl_deref_flags, true); + stl_summary_flags.SetDontShowChildren(false); stl_summary_flags.SetSkipPointers(false); @@ -1506,6 +1518,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { lldb_private::formatters::StdlibCoroutineHandleSummaryProvider, "libstdc++ std::coroutine_handle summary provider", libstdcpp_std_coroutine_handle_regex, stl_summary_flags, true); + + AddCXXSummary(cpp_category_sp, + lldb_private::formatters::ContainerSizeSummaryProvider, + "libstdc++ std::span summary provider", "^std::span<.+>$", + stl_summary_flags, true); } static lldb_private::SyntheticChildrenFrontEnd * @@ -1705,6 +1722,14 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { }, "MSVC STL/libstdc++ std::wstring summary provider")); + // NOTE: it is loaded as a common formatter because the libc++ version is not + // in the `__1` namespace, hence we need to dispatch based on the class + // layout. + AddCXXSynthetic(cpp_category_sp, + GenericInitializerListSyntheticFrontEndCreator, + "std::initializer_list synthetic children", + "^std::initializer_list<.+>$", stl_synth_flags, true); + stl_summary_flags.SetDontShowChildren(false); stl_summary_flags.SetSkipPointers(false); @@ -1748,6 +1773,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "^std::(multi)?(map|set)<.+>(( )?&)?$", stl_synth_flags, true); + AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider, + "std::initializer_list summary provider", + "^std::initializer_list<.+>$", stl_summary_flags, true); AddCXXSummary(cpp_category_sp, GenericSmartPointerSummaryProvider, "MSVC STL/libstdc++ std::shared_ptr summary provider", "^std::shared_ptr<.+>(( )?&)?$", stl_summary_flags, true); diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h index 9a528ca..b547234 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -92,7 +92,7 @@ public: static llvm::StringRef GetPluginNameStatic() { return "cplusplus"; } - bool SymbolNameFitsToLanguage(Mangled mangled) const override; + bool SymbolNameFitsToLanguage(const Mangled &mangled) const override; bool DemangledNameContainsPath(llvm::StringRef path, ConstString demangled) const override; @@ -154,7 +154,7 @@ public: // C/C++ identifier, then it will return false // and identifier and context will be unchanged. - static bool ExtractContextAndIdentifier(const char *name, + static bool ExtractContextAndIdentifier(llvm::StringRef name, llvm::StringRef &context, llvm::StringRef &identifier); diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp index d8c095d..4d283bb 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp @@ -315,7 +315,7 @@ bool CPlusPlusNameParser::ConsumeAbiTag() { // Consume the actual tag string (and allow some special characters) while (ConsumeToken(tok::raw_identifier, tok::comma, tok::period, - tok::numeric_constant)) + tok::numeric_constant, tok::kw_operator)) ; if (!ConsumeToken(tok::r_square)) @@ -420,10 +420,11 @@ bool CPlusPlusNameParser::ConsumeOperator() { // Make sure we have more tokens before attempting to look ahead one more. if (m_next_token_index + 1 < m_tokens.size()) { // Look ahead two tokens. - clang::Token n_token = m_tokens[m_next_token_index + 1]; - // If we find ( or < then this is indeed operator<< no need for fix. - if (n_token.getKind() != tok::l_paren && n_token.getKind() != tok::less) { - clang::Token tmp_tok; + const clang::Token n_token = m_tokens[m_next_token_index + 1]; + // If we find `(`, `<` or `[` then this is indeed operator<< no need for + // fix. + if (!n_token.isOneOf(tok::l_paren, tok::less, tok::l_square)) { + clang::Token tmp_tok{}; tmp_tok.startToken(); tmp_tok.setLength(1); tmp_tok.setLocation(token.getLocation().getLocWithOffset(1)); diff --git a/lldb/source/Plugins/Language/CPlusPlus/Generic.h b/lldb/source/Plugins/Language/CPlusPlus/Generic.h index f394622..539eddd 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/Generic.h +++ b/lldb/source/Plugins/Language/CPlusPlus/Generic.h @@ -24,6 +24,9 @@ bool GenericOptionalSummaryProvider(ValueObject &valobj, Stream &stream, lldb::ValueObjectSP GetDesugaredSmartPointerValue(ValueObject &ptr, ValueObject &container); +SyntheticChildrenFrontEnd * +GenericInitializerListSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); } // namespace formatters } // namespace lldb_private diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericInitializerList.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericInitializerList.cpp new file mode 100644 index 0000000..7f012b7 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/GenericInitializerList.cpp @@ -0,0 +1,145 @@ +//===-- GenericInitializerList.cpp ----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/ValueObject/ValueObject.h" +#include <cstddef> +#include <optional> +#include <type_traits> + +using namespace lldb; +using namespace lldb_private; + +namespace generic_check { +template <class T> +using size_func = decltype(T::GetSizeMember(std::declval<ValueObject &>())); +template <class T> +using start_func = decltype(T::GetStartMember(std::declval<ValueObject &>())); +namespace { +template <typename...> struct check_func : std::true_type {}; +} // namespace + +template <typename T> +using has_functions = check_func<size_func<T>, start_func<T>>; +} // namespace generic_check + +struct LibCxx { + static ValueObjectSP GetStartMember(ValueObject &backend) { + return backend.GetChildMemberWithName("__begin_"); + } + + static ValueObjectSP GetSizeMember(ValueObject &backend) { + return backend.GetChildMemberWithName("__size_"); + } +}; + +struct LibStdcpp { + static ValueObjectSP GetStartMember(ValueObject &backend) { + return backend.GetChildMemberWithName("_M_array"); + } + + static ValueObjectSP GetSizeMember(ValueObject &backend) { + return backend.GetChildMemberWithName("_M_len"); + } +}; + +namespace lldb_private::formatters { + +template <class StandardImpl> +class GenericInitializerListSyntheticFrontEnd + : public SyntheticChildrenFrontEnd { +public: + static_assert(generic_check::has_functions<StandardImpl>::value, + "Missing Required Functions."); + + GenericInitializerListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() { + if (valobj_sp) + Update(); + } + + ~GenericInitializerListSyntheticFrontEnd() override { + // this needs to stay around because it's a child object who will follow its + // parent's life cycle + // delete m_start; + } + + llvm::Expected<uint32_t> CalculateNumChildren() override { + m_num_elements = 0; + + const ValueObjectSP size_sp(StandardImpl::GetSizeMember(m_backend)); + if (size_sp) + m_num_elements = size_sp->GetValueAsUnsigned(0); + return m_num_elements; + } + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { + if (!m_start) + return {}; + + uint64_t offset = static_cast<uint64_t>(idx) * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(name.GetString(), offset, + m_backend.GetExecutionContextRef(), + m_element_type); + } + + lldb::ChildCacheState Update() override { + m_start = nullptr; + m_num_elements = 0; + m_element_type = m_backend.GetCompilerType().GetTypeTemplateArgument(0); + if (!m_element_type.IsValid()) + return lldb::ChildCacheState::eRefetch; + + llvm::Expected<uint64_t> size_or_err = m_element_type.GetByteSize(nullptr); + if (!size_or_err) + LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), size_or_err.takeError(), + "{0}"); + else { + m_element_size = *size_or_err; + // Store raw pointers or end up with a circular dependency. + m_start = StandardImpl::GetStartMember(m_backend).get(); + } + + return lldb::ChildCacheState::eRefetch; + } + + llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override { + if (!m_start) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + return *optional_idx; + } + +private: + ValueObject *m_start = nullptr; + CompilerType m_element_type; + uint32_t m_element_size = 0; + size_t m_num_elements = 0; +}; + +SyntheticChildrenFrontEnd *GenericInitializerListSyntheticFrontEndCreator( + CXXSyntheticChildren * /*unused*/, lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + + if (LibCxx::GetStartMember(*valobj_sp) != nullptr) + return new GenericInitializerListSyntheticFrontEnd<LibCxx>(valobj_sp); + + return new GenericInitializerListSyntheticFrontEnd<LibStdcpp>(valobj_sp); +} +} // namespace lldb_private::formatters diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp index 5289027..8c5ac31 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp @@ -203,6 +203,16 @@ private: ValueObject *m_tail = nullptr; }; +/// Gets the (forward-)list element type from the head node instead of the +/// template arguments. This is needed with PDB as it doesn't have info about +/// the template arguments. +CompilerType GetMsvcStlElementTypeFromHead(ValueObject &head) { + auto val_sp = head.GetChildMemberWithName("_Myval"); + if (val_sp) + return val_sp->GetCompilerType(); + return CompilerType(); +} + } // end anonymous namespace template <StlType Stl> @@ -530,6 +540,10 @@ lldb::ChildCacheState MsvcStlForwardListFrontEnd::Update() { m_backend.GetChildAtNamePath({"_Mypair", "_Myval2", "_Myhead"})) m_head = head_sp.get(); + // With PDB, we can't get the element type from the template arguments + if (!m_element_type && m_head) + m_element_type = GetMsvcStlElementTypeFromHead(*m_head); + return ChildCacheState::eRefetch; } @@ -606,6 +620,10 @@ lldb::ChildCacheState MsvcStlListFrontEnd::Update() { m_head = first.get(); m_tail = last.get(); + // With PDB, we can't get the element type from the template arguments + if (!m_element_type && m_head) + m_element_type = GetMsvcStlElementTypeFromHead(*m_head); + return lldb::ChildCacheState::eRefetch; } diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h index 819f8a9..8fd2928 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h @@ -194,10 +194,6 @@ SyntheticChildrenFrontEnd * LibCxxUnorderedMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); -SyntheticChildrenFrontEnd * -LibcxxInitializerListSyntheticFrontEndCreator(CXXSyntheticChildren *, - lldb::ValueObjectSP); - SyntheticChildrenFrontEnd *LibcxxQueueFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp deleted file mode 100644 index d952688..0000000 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxInitializerList.cpp +++ /dev/null @@ -1,124 +0,0 @@ -//===-- LibCxxInitializerList.cpp -----------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "LibCxx.h" - -#include "lldb/DataFormatters/FormattersHelpers.h" -#include "lldb/Utility/ConstString.h" -#include "lldb/ValueObject/ValueObject.h" -#include <optional> - -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); - - ~LibcxxInitializerListSyntheticFrontEnd() override; - - llvm::Expected<uint32_t> CalculateNumChildren() override; - - lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; - - lldb::ChildCacheState Update() override; - - llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override; - -private: - ValueObject *m_start = nullptr; - CompilerType m_element_type; - uint32_t m_element_size = 0; - size_t m_num_elements = 0; -}; -} // namespace formatters -} // namespace lldb_private - -lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: - LibcxxInitializerListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) - : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() { - if (valobj_sp) - Update(); -} - -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; -} - -llvm::Expected<uint32_t> lldb_private::formatters:: - LibcxxInitializerListSyntheticFrontEnd::CalculateNumChildren() { - m_num_elements = 0; - ValueObjectSP size_sp(m_backend.GetChildMemberWithName("__size_")); - if (size_sp) - m_num_elements = size_sp->GetValueAsUnsigned(0); - return m_num_elements; -} - -lldb::ValueObjectSP lldb_private::formatters:: - LibcxxInitializerListSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { - if (!m_start) - return lldb::ValueObjectSP(); - - uint64_t offset = idx * m_element_size; - offset = offset + m_start->GetValueAsUnsigned(0); - StreamString name; - name.Printf("[%" PRIu64 "]", (uint64_t)idx); - return CreateValueObjectFromAddress(name.GetString(), offset, - m_backend.GetExecutionContextRef(), - m_element_type); -} - -lldb::ChildCacheState -lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd::Update() { - m_start = nullptr; - m_num_elements = 0; - m_element_type = m_backend.GetCompilerType().GetTypeTemplateArgument(0); - if (!m_element_type.IsValid()) - return lldb::ChildCacheState::eRefetch; - - llvm::Expected<uint64_t> size_or_err = m_element_type.GetByteSize(nullptr); - if (!size_or_err) - LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), size_or_err.takeError(), - "{0}"); - else { - m_element_size = *size_or_err; - // Store raw pointers or end up with a circular dependency. - m_start = m_backend.GetChildMemberWithName("__begin_").get(); - } - - return lldb::ChildCacheState::eRefetch; -} - -llvm::Expected<size_t> -lldb_private::formatters::LibcxxInitializerListSyntheticFrontEnd:: - GetIndexOfChildWithName(ConstString name) { - if (!m_start) { - return llvm::createStringError("Type has no child named '%s'", - name.AsCString()); - } - auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); - if (!optional_idx) { - return llvm::createStringError("Type has no child named '%s'", - name.AsCString()); - } - return *optional_idx; -} - -lldb_private::SyntheticChildrenFrontEnd * -lldb_private::formatters::LibcxxInitializerListSyntheticFrontEndCreator( - CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { - return (valobj_sp ? new LibcxxInitializerListSyntheticFrontEnd(valobj_sp) - : nullptr); -} diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp index f4a695e..86f0a5a 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp @@ -199,9 +199,6 @@ lldb::ChildCacheState VectorIteratorSyntheticFrontEnd::Update() { if (!valobj_sp) return lldb::ChildCacheState::eRefetch; - if (!valobj_sp) - return lldb::ChildCacheState::eRefetch; - ValueObjectSP item_ptr = formatters::GetChildMemberWithName(*valobj_sp, m_item_names); if (!item_ptr) diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h index 429142f6..8d2c81f 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h @@ -38,6 +38,10 @@ LibstdcppMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); SyntheticChildrenFrontEnd * +LibStdcppSpanSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd * LibStdcppTupleSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppSpan.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppSpan.cpp new file mode 100644 index 0000000..5e69792 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppSpan.cpp @@ -0,0 +1,112 @@ +//===---------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "LibStdcpp.h" + +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/ValueObject/ValueObject.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/Support/Error.h" +#include <cstddef> +#include <optional> + +using namespace lldb; + +namespace lldb_private::formatters { + +class LibStdcppSpanSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + LibStdcppSpanSyntheticFrontEnd(const lldb::ValueObjectSP &valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); + } + + ~LibStdcppSpanSyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override { + return m_num_elements; + } + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override { + if (!m_start) + return {}; + + uint64_t offset = (static_cast<uint64_t>(idx) * m_element_size); + offset += m_start->GetValueAsUnsigned(0); + const std::string name = llvm::formatv("[{0}]", idx); + return CreateValueObjectFromAddress( + name, offset, m_backend.GetExecutionContextRef(), m_element_type); + } + + lldb::ChildCacheState Update() override { + const ValueObjectSP data_ptr = m_backend.GetChildMemberWithName("_M_ptr"); + if (!data_ptr) + return lldb::ChildCacheState::eRefetch; + + m_element_type = data_ptr->GetCompilerType().GetPointeeType(); + + // Get element size. + llvm::Expected<uint64_t> size_or_err = m_element_type.GetByteSize(nullptr); + if (!size_or_err) { + LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), size_or_err.takeError(), + "{0}"); + return lldb::ChildCacheState::eReuse; + } + + m_element_size = *size_or_err; + if (m_element_size > 0) { + m_start = data_ptr.get(); + } + + // Get number of elements. + if (const ValueObjectSP size_sp = + m_backend.GetChildAtNamePath({"_M_extent", "_M_extent_value"})) { + m_num_elements = size_sp->GetValueAsUnsigned(0); + } else if (const auto arg = + m_backend.GetCompilerType().GetIntegralTemplateArgument(1)) { + + m_num_elements = arg->value.GetAPSInt().getLimitedValue(); + } + + return lldb::ChildCacheState::eReuse; + } + + llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override { + if (!m_start) + return llvm::createStringError( + llvm::formatv("Type has no child named {0}", name.GetStringRef())); + + auto optional_idx = formatters::ExtractIndexFromString(name.GetCString()); + if (!optional_idx) { + return llvm::createStringError( + llvm::formatv("Type has no child named {0}", name.GetStringRef())); + } + return *optional_idx; + } + +private: + ValueObject *m_start = nullptr; ///< First element of span. Held, not owned. + CompilerType m_element_type; ///< Type of span elements. + size_t m_num_elements = 0; ///< Number of elements in span. + uint32_t m_element_size = 0; ///< Size in bytes of each span element. +}; + +SyntheticChildrenFrontEnd * +LibStdcppSpanSyntheticFrontEndCreator(CXXSyntheticChildren * /*unused*/, + lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + const CompilerType type = valobj_sp->GetCompilerType(); + if (!type || type.GetNumTemplateArguments() != 2) + return nullptr; + return new LibStdcppSpanSyntheticFrontEnd(valobj_sp); +} + +} // namespace lldb_private::formatters diff --git a/lldb/source/Plugins/Language/ObjC/NSSet.cpp b/lldb/source/Plugins/Language/ObjC/NSSet.cpp index 7d814e65..150b233 100644 --- a/lldb/source/Plugins/Language/ObjC/NSSet.cpp +++ b/lldb/source/Plugins/Language/ObjC/NSSet.cpp @@ -419,8 +419,6 @@ lldb_private::formatters::NSSetISyntheticFrontEnd::Update() { ValueObjectSP valobj_sp = m_backend.GetSP(); if (!valobj_sp) return lldb::ChildCacheState::eRefetch; - if (!valobj_sp) - return lldb::ChildCacheState::eRefetch; m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); lldb::ProcessSP process_sp(valobj_sp->GetProcessSP()); if (!process_sp) diff --git a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp index 3b8e21c..c0dcb95 100644 --- a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp +++ b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp @@ -235,7 +235,7 @@ ObjCLanguage::GetFunctionNameInfo(ConstString name) const { return {func_name_type, std::nullopt}; } -bool ObjCLanguage::SymbolNameFitsToLanguage(Mangled mangled) const { +bool ObjCLanguage::SymbolNameFitsToLanguage(const Mangled &mangled) const { ConstString demangled_name = mangled.GetDemangledName(); if (!demangled_name) return false; @@ -1065,3 +1065,10 @@ ObjCLanguage::GetBooleanFromString(llvm::StringRef str) const { .Case("NO", {false}) .Default({}); } + +bool ObjCLanguage::IsPossibleObjCMethodName(llvm::StringRef name) { + if (!name.starts_with("-[") && !name.starts_with("+[")) + return false; + + return name.ends_with("]"); +} diff --git a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h index a68ea41..ced6bd3 100644 --- a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h +++ b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h @@ -145,7 +145,7 @@ public: std::pair<lldb::FunctionNameType, std::optional<ConstString>> GetFunctionNameInfo(ConstString name) const override; - bool SymbolNameFitsToLanguage(Mangled mangled) const override; + bool SymbolNameFitsToLanguage(const Mangled &mangled) const override; lldb::TypeCategoryImplSP GetFormatters() override; @@ -175,13 +175,7 @@ public: static llvm::StringRef GetPluginNameStatic() { return "objc"; } - static bool IsPossibleObjCMethodName(const char *name) { - if (!name) - return false; - bool starts_right = (name[0] == '+' || name[0] == '-') && name[1] == '['; - bool ends_right = (name[strlen(name) - 1] == ']'); - return (starts_right && ends_right); - } + static bool IsPossibleObjCMethodName(llvm::StringRef name); static bool IsPossibleObjCSelector(const char *name) { if (!name) diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt index 1717b0a..727c829 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CMakeLists.txt @@ -1,10 +1,13 @@ add_lldb_library(lldbPluginCPPRuntime CPPLanguageRuntime.cpp + VerboseTrapFrameRecognizer.cpp LINK_LIBS lldbCore lldbSymbol lldbTarget + CLANG_LIBS + clangCodeGen ) add_subdirectory(ItaniumABI) diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp index 21a5ebe..913678b 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp @@ -12,6 +12,7 @@ #include <memory> #include "CPPLanguageRuntime.h" +#include "VerboseTrapFrameRecognizer.h" #include "llvm/ADT/StringRef.h" @@ -107,12 +108,15 @@ public: CPPLanguageRuntime::CPPLanguageRuntime(Process *process) : LanguageRuntime(process) { - if (process) + if (process) { process->GetTarget().GetFrameRecognizerManager().AddRecognizer( StackFrameRecognizerSP(new LibCXXFrameRecognizer()), {}, std::make_shared<RegularExpression>("^std::__[^:]*::"), /*mangling_preference=*/Mangled::ePreferDemangledWithoutArguments, /*first_instruction_only=*/false); + + RegisterVerboseTrapFrameRecognizer(*process); + } } bool CPPLanguageRuntime::IsAllowedRuntimeValue(ConstString name) { diff --git a/lldb/source/Target/VerboseTrapFrameRecognizer.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/VerboseTrapFrameRecognizer.cpp index 03ab58b..2b6bf2c 100644 --- a/lldb/source/Target/VerboseTrapFrameRecognizer.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/VerboseTrapFrameRecognizer.cpp @@ -1,4 +1,4 @@ -#include "lldb/Target/VerboseTrapFrameRecognizer.h" +#include "VerboseTrapFrameRecognizer.h" #include "lldb/Core/Module.h" #include "lldb/Symbol/Function.h" @@ -95,33 +95,14 @@ VerboseTrapFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) { if (func_name.empty()) return {}; - static auto trap_regex = - llvm::Regex(llvm::formatv("^{0}\\$(.*)\\$(.*)$", ClangTrapPrefix).str()); - SmallVector<llvm::StringRef, 3> matches; - std::string regex_err_msg; - if (!trap_regex.match(func_name, &matches, ®ex_err_msg)) { - LLDB_LOGF(GetLog(LLDBLog::Unwind), - "Failed to parse match trap regex for '%s': %s", func_name.data(), - regex_err_msg.c_str()); - - return {}; - } - - // For `__clang_trap_msg$category$message$` we expect 3 matches: - // 1. entire string - // 2. category - // 3. message - if (matches.size() != 3) { - LLDB_LOGF(GetLog(LLDBLog::Unwind), - "Unexpected function name format. Expected '<trap prefix>$<trap " - "category>$<trap message>'$ but got: '%s'.", - func_name.data()); - + auto maybe_trap_reason = + clang::CodeGen::DemangleTrapReasonInDebugInfo(func_name); + if (!maybe_trap_reason.has_value()) { + LLDB_LOGF(GetLog(LLDBLog::Unwind), "Failed to demangle '%s' as trap reason", + func_name.str().c_str()); return {}; } - - auto category = matches[1]; - auto message = matches[2]; + auto [category, message] = maybe_trap_reason.value(); std::string stop_reason = category.empty() ? "<empty category>" : category.str(); diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/VerboseTrapFrameRecognizer.h b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/VerboseTrapFrameRecognizer.h new file mode 100644 index 0000000..7d7020f --- /dev/null +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/VerboseTrapFrameRecognizer.h @@ -0,0 +1,47 @@ +//===-- VerboseTrapFrameRecognizer.h --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_C_PLUS_PLUS_VERBOSETRAPFRAMERECOGNIZER_H +#define LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_C_PLUS_PLUS_VERBOSETRAPFRAMERECOGNIZER_H + +#include "lldb/Target/StackFrameRecognizer.h" + +namespace lldb_private { + +void RegisterVerboseTrapFrameRecognizer(Process &process); + +/// Holds the stack frame that caused the Verbose trap and the inlined stop +/// reason message. +class VerboseTrapRecognizedStackFrame : public RecognizedStackFrame { +public: + VerboseTrapRecognizedStackFrame(lldb::StackFrameSP most_relevant_frame_sp, + std::string stop_desc); + + lldb::StackFrameSP GetMostRelevantFrame() override; + +private: + lldb::StackFrameSP m_most_relevant_frame; +}; + +/// When a thread stops, it checks the current frame contains a +/// Verbose Trap diagnostic. If so, it returns a \a +/// VerboseTrapRecognizedStackFrame holding the diagnostic a stop reason +/// description with and the parent frame as the most relavant frame. +class VerboseTrapFrameRecognizer : public StackFrameRecognizer { +public: + std::string GetName() override { + return "Verbose Trap StackFrame Recognizer"; + } + + lldb::RecognizedStackFrameSP + RecognizeFrame(lldb::StackFrameSP frame) override; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGERUNTIME_C_PLUS_PLUS_VERBOSETRAPFRAMERECOGNIZER_H diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp index 954f269..ebde889 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp @@ -111,7 +111,6 @@ bool ClassDescriptorV2::class_rw_t::Read(Process *process, lldb::addr_t addr) { process->GetAddressByteSize()); lldb::offset_t cursor = 0; - m_flags = extractor.GetU32_unchecked(&cursor); m_version = extractor.GetU32_unchecked(&cursor); m_ro_ptr = extractor.GetAddress_unchecked(&cursor); @@ -119,18 +118,16 @@ bool ClassDescriptorV2::class_rw_t::Read(Process *process, lldb::addr_t addr) { m_ro_ptr = abi_sp->FixCodeAddress(m_ro_ptr); m_method_list_ptr = extractor.GetAddress_unchecked(&cursor); m_properties_ptr = extractor.GetAddress_unchecked(&cursor); - m_firstSubclass = extractor.GetAddress_unchecked(&cursor); - m_nextSiblingClass = extractor.GetAddress_unchecked(&cursor); if (m_ro_ptr & 1) { DataBufferHeap buffer(ptr_size, '\0'); process->ReadMemory(m_ro_ptr ^ 1, buffer.GetBytes(), ptr_size, error); if (error.Fail()) return false; - cursor = 0; DataExtractor extractor(buffer.GetBytes(), ptr_size, process->GetByteOrder(), process->GetAddressByteSize()); + lldb::offset_t cursor = 0; m_ro_ptr = extractor.GetAddress_unchecked(&cursor); if (ABISP abi_sp = process->GetABI()) m_ro_ptr = abi_sp->FixCodeAddress(m_ro_ptr); diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h index 0fff9af..8d19b00 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h @@ -133,9 +133,6 @@ private: lldb::addr_t m_properties_ptr; lldb::addr_t m_protocols_ptr; - ObjCLanguageRuntime::ObjCISA m_firstSubclass; - ObjCLanguageRuntime::ObjCISA m_nextSiblingClass; - bool Read(Process *process, lldb::addr_t addr); }; diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp index 9beb133..83e39f3 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -320,7 +320,7 @@ extern "C" static const char *g_get_shared_cache_class_info_name = "__lldb_apple_objc_v2_get_shared_cache_class_info"; -static const char *g_get_shared_cache_class_info_body = R"( +static const char *g_get_shared_cache_class_info_definitions = R"( extern "C" { @@ -411,6 +411,9 @@ struct ClassInfo Class isa; uint32_t hash; } __attribute__((__packed__)); +)"; + +static const char *g_get_shared_cache_class_info_body = R"( uint32_t __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr, @@ -418,6 +421,7 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr, void *class_infos_ptr, uint64_t *relative_selector_offset, uint32_t class_infos_byte_size, + uint32_t *start_idx, uint32_t should_log) { *relative_selector_offset = 0; @@ -426,6 +430,7 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr, DEBUG_PRINTF ("shared_cache_base_ptr = %p\n", shared_cache_base_ptr); DEBUG_PRINTF ("class_infos_ptr = %p\n", class_infos_ptr); DEBUG_PRINTF ("class_infos_byte_size = %u (%llu class infos)\n", class_infos_byte_size, (uint64_t)(class_infos_byte_size/sizeof(ClassInfo))); + DEBUG_PRINTF ("start_idx = %u\n", *start_idx); if (objc_opt_ro_ptr) { const objc_opt_t *objc_opt = (objc_opt_t *)objc_opt_ro_ptr; @@ -480,7 +485,11 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr, DEBUG_PRINTF ("clsopt->mask = 0x%8.8x\n", clsopt->mask); DEBUG_PRINTF ("classOffsets = %p\n", classOffsets); - for (uint32_t i=0; i<clsopt->capacity; ++i) + const uint32_t original_start_idx = *start_idx; + + // Always start at the start_idx here. If it's greater than the capacity, + // it will skip the loop entirely and go to the duplicate handling below. + for (uint32_t i=*start_idx; i<clsopt->capacity; ++i) { const uint64_t objectCacheOffset = classOffsets[i].objectCacheOffset; DEBUG_PRINTF("objectCacheOffset[%u] = %u\n", i, objectCacheOffset); @@ -524,59 +533,78 @@ __lldb_apple_objc_v2_get_shared_cache_class_info (void *objc_opt_ro_ptr, else { DEBUG_PRINTF("not(class_infos && idx < max_class_infos)\n"); + *start_idx = i; + break; } ++idx; } - const uint32_t *duplicate_count_ptr = (uint32_t *)&classOffsets[clsopt->capacity]; - const uint32_t duplicate_count = *duplicate_count_ptr; - const objc_classheader_v16_t *duplicateClassOffsets = (const objc_classheader_v16_t *)(&duplicate_count_ptr[1]); - - DEBUG_PRINTF ("duplicate_count = %u\n", duplicate_count); - DEBUG_PRINTF ("duplicateClassOffsets = %p\n", duplicateClassOffsets); - - for (uint32_t i=0; i<duplicate_count; ++i) - { - const uint64_t objectCacheOffset = classOffsets[i].objectCacheOffset; - DEBUG_PRINTF("objectCacheOffset[%u] = %u\n", i, objectCacheOffset); + if (idx < max_class_infos) { + const uint32_t *duplicate_count_ptr = (uint32_t *)&classOffsets[clsopt->capacity]; + const uint32_t duplicate_count = *duplicate_count_ptr; + const objc_classheader_v16_t *duplicateClassOffsets = (const objc_classheader_v16_t *)(&duplicate_count_ptr[1]); - if (classOffsets[i].isDuplicate) { - DEBUG_PRINTF("isDuplicate = true\n"); - continue; // duplicate - } + DEBUG_PRINTF ("duplicate_count = %u\n", duplicate_count); + DEBUG_PRINTF ("duplicateClassOffsets = %p\n", duplicateClassOffsets); - if (objectCacheOffset == 0) { - DEBUG_PRINTF("objectCacheOffset == invalidEntryOffset\n"); - continue; // invalid offset - } + const uint32_t duplicate_start_idx = + *start_idx < clsopt->capacity ? + 0 : + *start_idx - clsopt->capacity; - if (class_infos && idx < max_class_infos) + for (uint32_t i=duplicate_start_idx; i<duplicate_count; ++i) { - class_infos[idx].isa = (Class)((uint8_t *)shared_cache_base_ptr + objectCacheOffset); + const uint64_t objectCacheOffset = duplicateClassOffsets[i].objectCacheOffset; + DEBUG_PRINTF("objectCacheOffset[%u] = %u\n", i, objectCacheOffset); - // Lookup the class name. - const char *name = class_name_lookup_func(class_infos[idx].isa); - DEBUG_PRINTF("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name); + if (duplicateClassOffsets[i].isDuplicate) { + DEBUG_PRINTF("isDuplicate = true\n"); + continue; // duplicate + } - // Hash the class name so we don't have to read it. - const char *s = name; - uint32_t h = 5381; - for (unsigned char c = *s; c; c = *++s) + if (objectCacheOffset == 0) { + DEBUG_PRINTF("objectCacheOffset == invalidEntryOffset\n"); + continue; // invalid offset + } + + if (class_infos && idx < max_class_infos) { - // class_getName demangles swift names and the hash must - // be calculated on the mangled name. hash==0 means lldb - // will fetch the mangled name and compute the hash in - // ParseClassInfoArray. - if (c == '.') + class_infos[idx].isa = (Class)((uint8_t *)shared_cache_base_ptr + objectCacheOffset); + + // Lookup the class name. + const char *name = class_name_lookup_func(class_infos[idx].isa); + DEBUG_PRINTF("[%u] isa = %8p %s\n", idx, class_infos[idx].isa, name); + + // Hash the class name so we don't have to read it. + const char *s = name; + uint32_t h = 5381; + for (unsigned char c = *s; c; c = *++s) { - h = 0; - break; + // class_getName demangles swift names and the hash must + // be calculated on the mangled name. hash==0 means lldb + // will fetch the mangled name and compute the hash in + // ParseClassInfoArray. + if (c == '.') + { + h = 0; + break; + } + h = ((h << 5) + h) + c; } - h = ((h << 5) + h) + c; + class_infos[idx].hash = h; + } else { + DEBUG_PRINTF("not(class_infos && idx < max_class_infos)\n"); + *start_idx = i; + break; } - class_infos[idx].hash = h; + ++idx; } } + // Always make sure start_idx gets updated. Otherwise we have an infinite + // loop if there are exactly max_class_infos number of classes. + if (*start_idx == original_start_idx) { + *start_idx = idx; + } } else if (objc_opt->version >= 12 && objc_opt->version <= 15) { @@ -1937,6 +1965,7 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor:: class_name_getter_function_name.AsCString(), class_name_getter_function_name.AsCString()); + shared_class_expression += g_get_shared_cache_class_info_definitions; shared_class_expression += g_get_shared_cache_class_info_body; auto utility_fn_or_error = exe_ctx.GetTargetRef().CreateUtilityFunction( @@ -1958,6 +1987,9 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor:: CompilerType clang_uint64_t_pointer_type = scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 64) .GetPointerType(); + CompilerType clang_uint32_t_pointer_type = + scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 32) + .GetPointerType(); // Next make the function caller for our implementation utility function. ValueList arguments; @@ -1975,6 +2007,13 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor:: value.SetValueType(Value::ValueType::Scalar); value.SetCompilerType(clang_uint32_t_type); arguments.PushValue(value); + + value.SetValueType(Value::ValueType::Scalar); + value.SetCompilerType(clang_uint32_t_pointer_type); + arguments.PushValue(value); + + value.SetValueType(Value::ValueType::Scalar); + value.SetCompilerType(clang_uint32_t_type); arguments.PushValue(value); std::unique_ptr<UtilityFunction> utility_fn = std::move(*utility_fn_or_error); @@ -2312,10 +2351,7 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() { // The number of entries to pre-allocate room for. // Each entry is (addrsize + 4) bytes - // FIXME: It is not sustainable to continue incrementing this value every time - // the shared cache grows. This is because it requires allocating memory in - // the inferior process and some inferior processes have small memory limits. - const uint32_t max_num_classes = 212992; + const uint32_t max_num_classes_in_buffer = 212992; UtilityFunction *get_class_info_code = GetClassInfoUtilityFunction(exe_ctx); if (!get_class_info_code) { @@ -2337,15 +2373,22 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() { DiagnosticManager diagnostics; const uint32_t class_info_byte_size = addr_size + 4; - const uint32_t class_infos_byte_size = max_num_classes * class_info_byte_size; + const uint32_t class_infos_byte_size = + max_num_classes_in_buffer * class_info_byte_size; lldb::addr_t class_infos_addr = process->AllocateMemory( class_infos_byte_size, ePermissionsReadable | ePermissionsWritable, err); const uint32_t relative_selector_offset_addr_size = 64; lldb::addr_t relative_selector_offset_addr = process->AllocateMemory(relative_selector_offset_addr_size, ePermissionsReadable | ePermissionsWritable, err); + constexpr uint32_t class_info_start_idx_byte_size = sizeof(uint32_t); + lldb::addr_t class_info_start_idx_addr = + process->AllocateMemory(class_info_start_idx_byte_size, + ePermissionsReadable | ePermissionsWritable, err); - if (class_infos_addr == LLDB_INVALID_ADDRESS) { + if (class_infos_addr == LLDB_INVALID_ADDRESS || + relative_selector_offset_addr == LLDB_INVALID_ADDRESS || + class_info_start_idx_addr == LLDB_INVALID_ADDRESS) { LLDB_LOGF(log, "unable to allocate %" PRIu32 " bytes in process for shared cache read", @@ -2353,6 +2396,17 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() { return DescriptorMapUpdateResult::Fail(); } + const uint32_t start_idx_init_value = 0; + size_t bytes_written = process->WriteMemory( + class_info_start_idx_addr, &start_idx_init_value, sizeof(uint32_t), err); + if (bytes_written != sizeof(uint32_t)) { + LLDB_LOGF(log, + "unable to write %" PRIu32 + " bytes in process for shared cache read", + class_infos_byte_size); + return DescriptorMapUpdateResult::Fail(); + } + std::lock_guard<std::mutex> guard(m_mutex); // Fill in our function argument values @@ -2361,12 +2415,13 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() { arguments.GetValueAtIndex(2)->GetScalar() = class_infos_addr; arguments.GetValueAtIndex(3)->GetScalar() = relative_selector_offset_addr; arguments.GetValueAtIndex(4)->GetScalar() = class_infos_byte_size; + arguments.GetValueAtIndex(5)->GetScalar() = class_info_start_idx_addr; // Only dump the runtime classes from the expression evaluation if the log is // verbose: Log *type_log = GetLog(LLDBLog::Types); bool dump_log = type_log && type_log->GetVerbose(); - arguments.GetValueAtIndex(5)->GetScalar() = dump_log ? 1 : 0; + arguments.GetValueAtIndex(6)->GetScalar() = dump_log ? 1 : 0; bool success = false; @@ -2393,78 +2448,80 @@ AppleObjCRuntimeV2::SharedCacheClassInfoExtractor::UpdateISAToDescriptorMap() { diagnostics.Clear(); - // Run the function - ExpressionResults results = - get_shared_cache_class_info_function->ExecuteFunction( - exe_ctx, &m_args, options, diagnostics, return_value); - - if (results == eExpressionCompleted) { - // The result is the number of ClassInfo structures that were filled in - num_class_infos = return_value.GetScalar().ULong(); - LLDB_LOG(log, "Discovered {0} Objective-C classes in the shared cache", - num_class_infos); - // Assert if there were more classes than we pre-allocated - // room for. - assert(num_class_infos <= max_num_classes); - if (num_class_infos > 0) { - if (num_class_infos > max_num_classes) { - num_class_infos = max_num_classes; - - success = false; - } else { + uint32_t num_class_infos_read = 0; + bool already_read_relative_selector_offset = false; + + do { + // Run the function. + ExpressionResults results = + get_shared_cache_class_info_function->ExecuteFunction( + exe_ctx, &m_args, options, diagnostics, return_value); + + if (results == eExpressionCompleted) { + // The result is the number of ClassInfo structures that were filled in. + num_class_infos_read = return_value.GetScalar().ULong(); + num_class_infos += num_class_infos_read; + LLDB_LOG(log, "Discovered {0} Objective-C classes in the shared cache", + num_class_infos_read); + if (num_class_infos_read > 0) { success = true; - } - // Read the relative selector offset. - DataBufferHeap relative_selector_offset_buffer(64, 0); - if (process->ReadMemory(relative_selector_offset_addr, - relative_selector_offset_buffer.GetBytes(), - relative_selector_offset_buffer.GetByteSize(), - err) == - relative_selector_offset_buffer.GetByteSize()) { - DataExtractor relative_selector_offset_data( - relative_selector_offset_buffer.GetBytes(), - relative_selector_offset_buffer.GetByteSize(), - process->GetByteOrder(), addr_size); - lldb::offset_t offset = 0; - uint64_t relative_selector_offset = - relative_selector_offset_data.GetU64(&offset); - if (relative_selector_offset > 0) { - // The offset is relative to the objc_opt struct. - m_runtime.SetRelativeSelectorBaseAddr(objc_opt_ptr + - relative_selector_offset); + // Read the relative selector offset. This only needs to occur once no + // matter how many times the function is called. + if (!already_read_relative_selector_offset) { + DataBufferHeap relative_selector_offset_buffer(64, 0); + if (process->ReadMemory( + relative_selector_offset_addr, + relative_selector_offset_buffer.GetBytes(), + relative_selector_offset_buffer.GetByteSize(), + err) == relative_selector_offset_buffer.GetByteSize()) { + DataExtractor relative_selector_offset_data( + relative_selector_offset_buffer.GetBytes(), + relative_selector_offset_buffer.GetByteSize(), + process->GetByteOrder(), addr_size); + lldb::offset_t offset = 0; + uint64_t relative_selector_offset = + relative_selector_offset_data.GetU64(&offset); + if (relative_selector_offset > 0) { + // The offset is relative to the objc_opt struct. + m_runtime.SetRelativeSelectorBaseAddr(objc_opt_ptr + + relative_selector_offset); + } + } + already_read_relative_selector_offset = true; } - } - - // Read the ClassInfo structures - DataBufferHeap class_infos_buffer( - num_class_infos * class_info_byte_size, 0); - if (process->ReadMemory(class_infos_addr, class_infos_buffer.GetBytes(), - class_infos_buffer.GetByteSize(), - err) == class_infos_buffer.GetByteSize()) { - DataExtractor class_infos_data(class_infos_buffer.GetBytes(), - class_infos_buffer.GetByteSize(), - process->GetByteOrder(), addr_size); - m_runtime.ParseClassInfoArray(class_infos_data, num_class_infos); + // Read the ClassInfo structures + DataBufferHeap class_infos_buffer( + num_class_infos_read * class_info_byte_size, 0); + if (process->ReadMemory(class_infos_addr, + class_infos_buffer.GetBytes(), + class_infos_buffer.GetByteSize(), + err) == class_infos_buffer.GetByteSize()) { + DataExtractor class_infos_data(class_infos_buffer.GetBytes(), + class_infos_buffer.GetByteSize(), + process->GetByteOrder(), addr_size); + + m_runtime.ParseClassInfoArray(class_infos_data, + num_class_infos_read); + } } - } else { - success = true; - } - } else { - if (log) { + } else if (log) { LLDB_LOGF(log, "Error evaluating our find class name function."); diagnostics.Dump(log); + break; } - } - } else { - if (log) { - LLDB_LOGF(log, "Error writing function arguments."); - diagnostics.Dump(log); - } + } while (num_class_infos_read == max_num_classes_in_buffer); + } else if (log) { + LLDB_LOGF(log, "Error writing function arguments."); + diagnostics.Dump(log); } - // Deallocate the memory we allocated for the ClassInfo array + LLDB_LOG(log, "Processed {0} Objective-C classes total from the shared cache", + num_class_infos); + // Cleanup memory we allocated in the process. + process->DeallocateMemory(relative_selector_offset_addr); + process->DeallocateMemory(class_info_start_idx_addr); process->DeallocateMemory(class_infos_addr); return DescriptorMapUpdateResult(success, false, num_class_infos); diff --git a/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp b/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp index 33673f1..53ff6ef6 100644 --- a/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp +++ b/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp @@ -130,13 +130,13 @@ void ObjectFileBreakpad::CreateSections(SectionList &unified_section_list) { std::optional<Record::Kind> current_section; offset_t section_start; - llvm::StringRef text = toStringRef(m_data.GetData()); + llvm::StringRef text = toStringRef(m_data_nsp->GetData()); uint32_t next_section_id = 1; auto maybe_add_section = [&](const uint8_t *end_ptr) { if (!current_section) return; // We have been called before parsing the first line. - offset_t end_offset = end_ptr - m_data.GetDataStart(); + offset_t end_offset = end_ptr - m_data_nsp->GetDataStart(); auto section_sp = std::make_shared<Section>( GetModule(), this, next_section_id++, ConstString(toString(*current_section)), eSectionTypeOther, @@ -162,8 +162,8 @@ void ObjectFileBreakpad::CreateSections(SectionList &unified_section_list) { maybe_add_section(line.bytes_begin()); // And start a new one. current_section = next_section; - section_start = line.bytes_begin() - m_data.GetDataStart(); + section_start = line.bytes_begin() - m_data_nsp->GetDataStart(); } // Finally, add the last section. - maybe_add_section(m_data.GetDataEnd()); + maybe_add_section(m_data_nsp->GetDataEnd()); } diff --git a/lldb/source/Plugins/ObjectFile/COFF/ObjectFileCOFF.cpp b/lldb/source/Plugins/ObjectFile/COFF/ObjectFileCOFF.cpp index 1121f69..e78f4f0 100644 --- a/lldb/source/Plugins/ObjectFile/COFF/ObjectFileCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/COFF/ObjectFileCOFF.cpp @@ -300,8 +300,8 @@ bool ObjectFileCOFF::ParseHeader() { std::lock_guard<std::recursive_mutex> guard(module->GetMutex()); - m_data.SetByteOrder(eByteOrderLittle); - m_data.SetAddressByteSize(GetAddressByteSize()); + m_data_nsp->SetByteOrder(eByteOrderLittle); + m_data_nsp->SetAddressByteSize(GetAddressByteSize()); return true; } diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index 49841e7..5d81b11 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -130,6 +130,29 @@ private: RelocUnion reloc; }; + +lldb::SectionSP MergeSections(lldb::SectionSP lhs, lldb::SectionSP rhs) { + assert(lhs && rhs); + + lldb::ModuleSP lhs_module_parent = lhs->GetModule(); + lldb::ModuleSP rhs_module_parent = rhs->GetModule(); + assert(lhs_module_parent && rhs_module_parent); + + // Do a sanity check, these should be the same. + if (lhs->GetFileAddress() != rhs->GetFileAddress()) + lhs_module_parent->ReportWarning( + "Mismatch addresses for section {0} when " + "merging with {1}, expected: {2:x}, " + "actual: {3:x}", + lhs->GetTypeAsCString(), + rhs_module_parent->GetFileSpec().GetPathAsConstString().GetCString(), + lhs->GetByteSize(), rhs->GetByteSize()); + + // We want to take the greater of two sections. If LHS and RHS are both + // SHT_NOBITS, we should default to LHS. If RHS has a bigger section, + // indicating it has data that wasn't stripped, we should take that instead. + return rhs->GetFileSize() > lhs->GetFileSize() ? rhs : lhs; +} } // end anonymous namespace ELFRelocation::ELFRelocation(unsigned type) { @@ -781,7 +804,7 @@ ByteOrder ObjectFileELF::GetByteOrder() const { } uint32_t ObjectFileELF::GetAddressByteSize() const { - return m_data.GetAddressByteSize(); + return m_data_nsp->GetAddressByteSize(); } AddressClass ObjectFileELF::GetAddressClass(addr_t file_addr) { @@ -822,7 +845,7 @@ size_t ObjectFileELF::SectionIndex(const SectionHeaderCollConstIter &I) const { bool ObjectFileELF::ParseHeader() { lldb::offset_t offset = 0; - return m_header.Parse(m_data, &offset); + return m_header.Parse(*m_data_nsp.get(), &offset); } UUID ObjectFileELF::GetUUID() { @@ -858,7 +881,7 @@ UUID ObjectFileELF::GetUUID() { return UUID(); core_notes_crc = - CalculateELFNotesSegmentsCRC32(m_program_headers, m_data); + CalculateELFNotesSegmentsCRC32(m_program_headers, *m_data_nsp.get()); if (core_notes_crc) { // Use 8 bytes - first 4 bytes for *magic* prefix, mainly to make it @@ -869,7 +892,7 @@ UUID ObjectFileELF::GetUUID() { } } else { if (!m_gnu_debuglink_crc) - m_gnu_debuglink_crc = calc_crc32(0, m_data); + m_gnu_debuglink_crc = calc_crc32(0, *m_data_nsp.get()); if (m_gnu_debuglink_crc) { // Use 4 bytes of crc from the .gnu_debuglink section. u32le data(m_gnu_debuglink_crc); @@ -1055,7 +1078,8 @@ size_t ObjectFileELF::GetProgramHeaderInfo(ProgramHeaderColl &program_headers, // ParseProgramHeaders bool ObjectFileELF::ParseProgramHeaders() { - return GetProgramHeaderInfo(m_program_headers, m_data, m_header) != 0; + return GetProgramHeaderInfo(m_program_headers, *m_data_nsp.get(), m_header) != + 0; } lldb_private::Status @@ -1645,8 +1669,8 @@ ObjectFileELF::StripLinkerSymbolAnnotations(llvm::StringRef symbol_name) const { // ParseSectionHeaders size_t ObjectFileELF::ParseSectionHeaders() { - return GetSectionHeaderInfo(m_section_headers, m_data, m_header, m_uuid, - m_gnu_debuglink_file, m_gnu_debuglink_crc, + return GetSectionHeaderInfo(m_section_headers, *m_data_nsp.get(), m_header, + m_uuid, m_gnu_debuglink_file, m_gnu_debuglink_crc, m_arch_spec); } @@ -1967,10 +1991,10 @@ void ObjectFileELF::CreateSections(SectionList &unified_section_list) { provider.AddSection(std::move(*InfoOr), std::move(section_sp)); } - // For eTypeDebugInfo files, the Symbol Vendor will take care of updating the - // unified section list. - if (GetType() != eTypeDebugInfo) - unified_section_list = *m_sections_up; + // Merge the two adding any new sections, and overwriting any existing + // sections that are SHT_NOBITS + unified_section_list = + SectionList::Merge(unified_section_list, *m_sections_up, MergeSections); // If there's a .gnu_debugdata section, we'll try to read the .symtab that's // embedded in there and replace the one in the original object file (if any). @@ -2735,9 +2759,8 @@ static void ApplyELF64ABS64Relocation(Symtab *symtab, ELFRelocation &rel, // ObjectFileELF creates a WritableDataBuffer in CreateInstance. WritableDataBuffer *data_buffer = llvm::cast<WritableDataBuffer>(data_buffer_sp.get()); - uint64_t *dst = reinterpret_cast<uint64_t *>( - data_buffer->GetBytes() + rel_section->GetFileOffset() + - ELFRelocation::RelocOffset64(rel)); + void *const dst = data_buffer->GetBytes() + rel_section->GetFileOffset() + + ELFRelocation::RelocOffset64(rel); uint64_t val_offset = value + ELFRelocation::RelocAddend64(rel); memcpy(dst, &val_offset, sizeof(uint64_t)); } @@ -2762,9 +2785,8 @@ static void ApplyELF64ABS32Relocation(Symtab *symtab, ELFRelocation &rel, // ObjectFileELF creates a WritableDataBuffer in CreateInstance. WritableDataBuffer *data_buffer = llvm::cast<WritableDataBuffer>(data_buffer_sp.get()); - uint32_t *dst = reinterpret_cast<uint32_t *>( - data_buffer->GetBytes() + rel_section->GetFileOffset() + - ELFRelocation::RelocOffset32(rel)); + void *const dst = data_buffer->GetBytes() + rel_section->GetFileOffset() + + ELFRelocation::RelocOffset32(rel); memcpy(dst, &truncated_addr, sizeof(uint32_t)); } } @@ -3657,7 +3679,8 @@ ArchSpec ObjectFileELF::GetArchitecture() { if (H.p_type != PT_NOTE || H.p_offset == 0 || H.p_filesz == 0) continue; DataExtractor data; - if (data.SetData(m_data, H.p_offset, H.p_filesz) == H.p_filesz) { + if (data.SetData(*m_data_nsp.get(), H.p_offset, H.p_filesz) == + H.p_filesz) { UUID uuid; RefineModuleDetailsFromNote(data, m_arch_spec, uuid); } @@ -3812,10 +3835,10 @@ llvm::ArrayRef<ELFProgramHeader> ObjectFileELF::ProgramHeaders() { } DataExtractor ObjectFileELF::GetSegmentData(const ELFProgramHeader &H) { - // Try and read the program header from our cached m_data which can come from - // the file on disk being mmap'ed or from the initial part of the ELF file we - // read from memory and cached. - DataExtractor data = DataExtractor(m_data, H.p_offset, H.p_filesz); + // Try and read the program header from our cached m_data_nsp which can come + // from the file on disk being mmap'ed or from the initial part of the ELF + // file we read from memory and cached. + DataExtractor data = DataExtractor(*m_data_nsp.get(), H.p_offset, H.p_filesz); if (data.GetByteSize() == H.p_filesz) return data; if (IsInMemory()) { diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index c8e520d..dff9eab 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -1012,35 +1012,35 @@ bool ObjectFileMachO::ParseHeader() { std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex()); bool can_parse = false; lldb::offset_t offset = 0; - m_data.SetByteOrder(endian::InlHostByteOrder()); + m_data_nsp->SetByteOrder(endian::InlHostByteOrder()); // Leave magic in the original byte order - m_header.magic = m_data.GetU32(&offset); + m_header.magic = m_data_nsp->GetU32(&offset); switch (m_header.magic) { case MH_MAGIC: - m_data.SetByteOrder(endian::InlHostByteOrder()); - m_data.SetAddressByteSize(4); + m_data_nsp->SetByteOrder(endian::InlHostByteOrder()); + m_data_nsp->SetAddressByteSize(4); can_parse = true; break; case MH_MAGIC_64: - m_data.SetByteOrder(endian::InlHostByteOrder()); - m_data.SetAddressByteSize(8); + m_data_nsp->SetByteOrder(endian::InlHostByteOrder()); + m_data_nsp->SetAddressByteSize(8); can_parse = true; break; case MH_CIGAM: - m_data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig - ? eByteOrderLittle - : eByteOrderBig); - m_data.SetAddressByteSize(4); + m_data_nsp->SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig + ? eByteOrderLittle + : eByteOrderBig); + m_data_nsp->SetAddressByteSize(4); can_parse = true; break; case MH_CIGAM_64: - m_data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig - ? eByteOrderLittle - : eByteOrderBig); - m_data.SetAddressByteSize(8); + m_data_nsp->SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig + ? eByteOrderLittle + : eByteOrderBig); + m_data_nsp->SetAddressByteSize(8); can_parse = true; break; @@ -1049,12 +1049,13 @@ bool ObjectFileMachO::ParseHeader() { } if (can_parse) { - m_data.GetU32(&offset, &m_header.cputype, 6); + m_data_nsp->GetU32(&offset, &m_header.cputype, 6); ModuleSpecList all_specs; ModuleSpec base_spec; - GetAllArchSpecs(m_header, m_data, MachHeaderSizeFromMagic(m_header.magic), - base_spec, all_specs); + GetAllArchSpecs(m_header, *m_data_nsp.get(), + MachHeaderSizeFromMagic(m_header.magic), base_spec, + all_specs); for (unsigned i = 0, e = all_specs.GetSize(); i != e; ++i) { ArchSpec mach_arch = @@ -1068,7 +1069,7 @@ bool ObjectFileMachO::ParseHeader() { if (SetModulesArchitecture(mach_arch)) { const size_t header_and_lc_size = m_header.sizeofcmds + MachHeaderSizeFromMagic(m_header.magic); - if (m_data.GetByteSize() < header_and_lc_size) { + if (m_data_nsp->GetByteSize() < header_and_lc_size) { DataBufferSP data_sp; ProcessSP process_sp(m_process_wp.lock()); if (process_sp) { @@ -1080,7 +1081,7 @@ bool ObjectFileMachO::ParseHeader() { continue; } if (data_sp) - m_data.SetData(data_sp); + m_data_nsp->SetData(data_sp); } } return true; @@ -1094,7 +1095,7 @@ bool ObjectFileMachO::ParseHeader() { } ByteOrder ObjectFileMachO::GetByteOrder() const { - return m_data.GetByteOrder(); + return m_data_nsp->GetByteOrder(); } bool ObjectFileMachO::IsExecutable() const { @@ -1114,7 +1115,7 @@ bool ObjectFileMachO::IsKext() const { } uint32_t ObjectFileMachO::GetAddressByteSize() const { - return m_data.GetAddressByteSize(); + return m_data_nsp->GetAddressByteSize(); } AddressClass ObjectFileMachO::GetAddressClass(lldb::addr_t file_addr) { @@ -1297,13 +1298,13 @@ bool ObjectFileMachO::IsStripped() { const lldb::offset_t load_cmd_offset = offset; llvm::MachO::load_command lc = {}; - if (m_data.GetU32(&offset, &lc.cmd, 2) == nullptr) + if (m_data_nsp->GetU32(&offset, &lc.cmd, 2) == nullptr) break; if (lc.cmd == LC_DYSYMTAB) { m_dysymtab.cmd = lc.cmd; m_dysymtab.cmdsize = lc.cmdsize; - if (m_data.GetU32(&offset, &m_dysymtab.ilocalsym, - (sizeof(m_dysymtab) / sizeof(uint32_t)) - 2) == + if (m_data_nsp->GetU32(&offset, &m_dysymtab.ilocalsym, + (sizeof(m_dysymtab) / sizeof(uint32_t)) - 2) == nullptr) { // Clear m_dysymtab if we were unable to read all items from the // load command @@ -1326,14 +1327,14 @@ ObjectFileMachO::EncryptedFileRanges ObjectFileMachO::GetEncryptedFileRanges() { llvm::MachO::encryption_info_command encryption_cmd; for (uint32_t i = 0; i < m_header.ncmds; ++i) { const lldb::offset_t load_cmd_offset = offset; - if (m_data.GetU32(&offset, &encryption_cmd, 2) == nullptr) + if (m_data_nsp->GetU32(&offset, &encryption_cmd, 2) == nullptr) break; // LC_ENCRYPTION_INFO and LC_ENCRYPTION_INFO_64 have the same sizes for the // 3 fields we care about, so treat them the same. if (encryption_cmd.cmd == LC_ENCRYPTION_INFO || encryption_cmd.cmd == LC_ENCRYPTION_INFO_64) { - if (m_data.GetU32(&offset, &encryption_cmd.cryptoff, 3)) { + if (m_data_nsp->GetU32(&offset, &encryption_cmd.cryptoff, 3)) { if (encryption_cmd.cryptid != 0) { EncryptedFileRanges::Entry entry; entry.SetRangeBase(encryption_cmd.cryptoff); @@ -1562,7 +1563,7 @@ void ObjectFileMachO::ProcessSegmentCommand( llvm::MachO::segment_command_64 load_cmd; memcpy(&load_cmd, &load_cmd_, sizeof(load_cmd_)); - if (!m_data.GetU8(&offset, (uint8_t *)load_cmd.segname, 16)) + if (!m_data_nsp->GetU8(&offset, (uint8_t *)load_cmd.segname, 16)) return; ModuleSP module_sp = GetModule(); @@ -1586,11 +1587,11 @@ void ObjectFileMachO::ProcessSegmentCommand( add_section = false; } } - load_cmd.vmaddr = m_data.GetAddress(&offset); - load_cmd.vmsize = m_data.GetAddress(&offset); - load_cmd.fileoff = m_data.GetAddress(&offset); - load_cmd.filesize = m_data.GetAddress(&offset); - if (!m_data.GetU32(&offset, &load_cmd.maxprot, 4)) + load_cmd.vmaddr = m_data_nsp->GetAddress(&offset); + load_cmd.vmsize = m_data_nsp->GetAddress(&offset); + load_cmd.fileoff = m_data_nsp->GetAddress(&offset); + load_cmd.filesize = m_data_nsp->GetAddress(&offset); + if (!m_data_nsp->GetU32(&offset, &load_cmd.maxprot, 4)) return; SanitizeSegmentCommand(load_cmd, cmd_idx); @@ -1681,16 +1682,16 @@ void ObjectFileMachO::ProcessSegmentCommand( const uint32_t num_u32s = load_cmd.cmd == LC_SEGMENT ? 7 : 8; for (segment_sect_idx = 0; segment_sect_idx < load_cmd.nsects; ++segment_sect_idx) { - if (m_data.GetU8(&offset, (uint8_t *)sect64.sectname, - sizeof(sect64.sectname)) == nullptr) + if (m_data_nsp->GetU8(&offset, (uint8_t *)sect64.sectname, + sizeof(sect64.sectname)) == nullptr) break; - if (m_data.GetU8(&offset, (uint8_t *)sect64.segname, - sizeof(sect64.segname)) == nullptr) + if (m_data_nsp->GetU8(&offset, (uint8_t *)sect64.segname, + sizeof(sect64.segname)) == nullptr) break; - sect64.addr = m_data.GetAddress(&offset); - sect64.size = m_data.GetAddress(&offset); + sect64.addr = m_data_nsp->GetAddress(&offset); + sect64.size = m_data_nsp->GetAddress(&offset); - if (m_data.GetU32(&offset, §64.offset, num_u32s) == nullptr) + if (m_data_nsp->GetU32(&offset, §64.offset, num_u32s) == nullptr) break; if (IsSharedCacheBinary() && !IsInMemory()) { @@ -1855,8 +1856,8 @@ void ObjectFileMachO::ProcessDysymtabCommand( const llvm::MachO::load_command &load_cmd, lldb::offset_t offset) { m_dysymtab.cmd = load_cmd.cmd; m_dysymtab.cmdsize = load_cmd.cmdsize; - m_data.GetU32(&offset, &m_dysymtab.ilocalsym, - (sizeof(m_dysymtab) / sizeof(uint32_t)) - 2); + m_data_nsp->GetU32(&offset, &m_dysymtab.ilocalsym, + (sizeof(m_dysymtab) / sizeof(uint32_t)) - 2); } void ObjectFileMachO::CreateSections(SectionList &unified_section_list) { @@ -1875,7 +1876,7 @@ void ObjectFileMachO::CreateSections(SectionList &unified_section_list) { llvm::MachO::load_command load_cmd; for (uint32_t i = 0; i < m_header.ncmds; ++i) { const lldb::offset_t load_cmd_offset = offset; - if (m_data.GetU32(&offset, &load_cmd, 2) == nullptr) + if (m_data_nsp->GetU32(&offset, &load_cmd, 2) == nullptr) break; if (load_cmd.cmd == LC_SEGMENT || load_cmd.cmd == LC_SEGMENT_64) @@ -2240,13 +2241,13 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { const lldb::offset_t cmd_offset = offset; // Read in the load command and load command size llvm::MachO::load_command lc; - if (m_data.GetU32(&offset, &lc, 2) == nullptr) + if (m_data_nsp->GetU32(&offset, &lc, 2) == nullptr) break; // Watch for the symbol table load command switch (lc.cmd) { case LC_SYMTAB: { llvm::MachO::symtab_command lc_obj; - if (m_data.GetU32(&offset, &lc_obj.symoff, 4)) { + if (m_data_nsp->GetU32(&offset, &lc_obj.symoff, 4)) { lc_obj.cmd = lc.cmd; lc_obj.cmdsize = lc.cmdsize; symtab_load_command = lc_obj; @@ -2256,7 +2257,7 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { case LC_DYLD_INFO: case LC_DYLD_INFO_ONLY: { llvm::MachO::dyld_info_command lc_obj; - if (m_data.GetU32(&offset, &lc_obj.rebase_off, 10)) { + if (m_data_nsp->GetU32(&offset, &lc_obj.rebase_off, 10)) { lc_obj.cmd = lc.cmd; lc_obj.cmdsize = lc.cmdsize; dyld_info = lc_obj; @@ -2268,8 +2269,8 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { case LC_REEXPORT_DYLIB: case LC_LOADFVMLIB: case LC_LOAD_UPWARD_DYLIB: { - uint32_t name_offset = cmd_offset + m_data.GetU32(&offset); - const char *path = m_data.PeekCStr(name_offset); + uint32_t name_offset = cmd_offset + m_data_nsp->GetU32(&offset); + const char *path = m_data_nsp->PeekCStr(name_offset); if (path) { FileSpec file_spec(path); // Strip the path if there is @rpath, @executable, etc so we just use @@ -2289,19 +2290,19 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { llvm::MachO::linkedit_data_command lc_obj; lc_obj.cmd = lc.cmd; lc_obj.cmdsize = lc.cmdsize; - if (m_data.GetU32(&offset, &lc_obj.dataoff, 2)) + if (m_data_nsp->GetU32(&offset, &lc_obj.dataoff, 2)) exports_trie_load_command = lc_obj; } break; case LC_FUNCTION_STARTS: { llvm::MachO::linkedit_data_command lc_obj; lc_obj.cmd = lc.cmd; lc_obj.cmdsize = lc.cmdsize; - if (m_data.GetU32(&offset, &lc_obj.dataoff, 2)) + if (m_data_nsp->GetU32(&offset, &lc_obj.dataoff, 2)) function_starts_load_command = lc_obj; } break; case LC_UUID: { - const uint8_t *uuid_bytes = m_data.PeekData(offset, 16); + const uint8_t *uuid_bytes = m_data_nsp->PeekData(offset, 16); if (uuid_bytes) image_uuid = UUID(uuid_bytes, 16); @@ -2321,8 +2322,8 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { if (section_list == nullptr) return; - const uint32_t addr_byte_size = m_data.GetAddressByteSize(); - const ByteOrder byte_order = m_data.GetByteOrder(); + const uint32_t addr_byte_size = m_data_nsp->GetAddressByteSize(); + const ByteOrder byte_order = m_data_nsp->GetByteOrder(); bool bit_width_32 = addr_byte_size == 4; const size_t nlist_byte_size = bit_width_32 ? sizeof(struct nlist) : sizeof(struct nlist_64); @@ -2487,9 +2488,9 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { exports_trie_load_command.dataoff += linkedit_slide; } - nlist_data.SetData(m_data, symtab_load_command.symoff, + nlist_data.SetData(*m_data_nsp.get(), symtab_load_command.symoff, nlist_data_byte_size); - strtab_data.SetData(m_data, symtab_load_command.stroff, + strtab_data.SetData(*m_data_nsp.get(), symtab_load_command.stroff, strtab_data_byte_size); // We shouldn't have exports data from both the LC_DYLD_INFO command @@ -2497,19 +2498,22 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { lldbassert(!((dyld_info.export_size > 0) && (exports_trie_load_command.datasize > 0))); if (dyld_info.export_size > 0) { - dyld_trie_data.SetData(m_data, dyld_info.export_off, + dyld_trie_data.SetData(*m_data_nsp.get(), dyld_info.export_off, dyld_info.export_size); } else if (exports_trie_load_command.datasize > 0) { - dyld_trie_data.SetData(m_data, exports_trie_load_command.dataoff, + dyld_trie_data.SetData(*m_data_nsp.get(), + exports_trie_load_command.dataoff, exports_trie_load_command.datasize); } if (dysymtab.nindirectsyms != 0) { - indirect_symbol_index_data.SetData(m_data, dysymtab.indirectsymoff, + indirect_symbol_index_data.SetData(*m_data_nsp.get(), + dysymtab.indirectsymoff, dysymtab.nindirectsyms * 4); } if (function_starts_load_command.cmd) { - function_starts_data.SetData(m_data, function_starts_load_command.dataoff, + function_starts_data.SetData(*m_data_nsp.get(), + function_starts_load_command.dataoff, function_starts_load_command.datasize); } } @@ -4561,8 +4565,9 @@ void ObjectFileMachO::Dump(Stream *s) { *s << ", file = '" << m_file; ModuleSpecList all_specs; ModuleSpec base_spec; - GetAllArchSpecs(m_header, m_data, MachHeaderSizeFromMagic(m_header.magic), - base_spec, all_specs); + GetAllArchSpecs(m_header, *m_data_nsp.get(), + MachHeaderSizeFromMagic(m_header.magic), base_spec, + all_specs); for (unsigned i = 0, e = all_specs.GetSize(); i != e; ++i) { *s << "', triple"; if (e) @@ -4868,7 +4873,7 @@ UUID ObjectFileMachO::GetUUID() { if (module_sp) { std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex()); lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic); - return GetUUID(m_header, m_data, offset); + return GetUUID(m_header, *m_data_nsp.get(), offset); } return UUID(); } @@ -4888,7 +4893,7 @@ uint32_t ObjectFileMachO::GetDependentModules(FileSpecList &files) { uint32_t i; for (i = 0; i < m_header.ncmds; ++i) { const uint32_t cmd_offset = offset; - if (m_data.GetU32(&offset, &load_cmd, 2) == nullptr) + if (m_data_nsp->GetU32(&offset, &load_cmd, 2) == nullptr) break; switch (load_cmd.cmd) { @@ -4899,17 +4904,17 @@ uint32_t ObjectFileMachO::GetDependentModules(FileSpecList &files) { case LC_LOAD_DYLINKER: case LC_LOADFVMLIB: case LC_LOAD_UPWARD_DYLIB: { - uint32_t name_offset = cmd_offset + m_data.GetU32(&offset); + uint32_t name_offset = cmd_offset + m_data_nsp->GetU32(&offset); // For LC_LOAD_DYLIB there is an alternate encoding // which adds a uint32_t `flags` field for `DYLD_USE_*` // flags. This can be detected by a timestamp field with // the `DYLIB_USE_MARKER` constant value. bool is_delayed_init = false; - uint32_t use_command_marker = m_data.GetU32(&offset); + uint32_t use_command_marker = m_data_nsp->GetU32(&offset); if (use_command_marker == 0x1a741800 /* DYLIB_USE_MARKER */) { offset += 4; /* uint32_t current_version */ offset += 4; /* uint32_t compat_version */ - uint32_t flags = m_data.GetU32(&offset); + uint32_t flags = m_data_nsp->GetU32(&offset); // If this LC_LOAD_DYLIB is marked delay-init, // don't report it as a dependent library -- it // may be loaded in the process at some point, @@ -4917,7 +4922,7 @@ uint32_t ObjectFileMachO::GetDependentModules(FileSpecList &files) { if (flags & 0x08 /* DYLIB_USE_DELAYED_INIT */) is_delayed_init = true; } - const char *path = m_data.PeekCStr(name_offset); + const char *path = m_data_nsp->PeekCStr(name_offset); if (path && !is_delayed_init) { if (load_cmd.cmd == LC_RPATH) rpath_paths.push_back(path); @@ -5037,15 +5042,15 @@ lldb_private::Address ObjectFileMachO::GetEntryPointAddress() { for (i = 0; i < m_header.ncmds; ++i) { const lldb::offset_t cmd_offset = offset; - if (m_data.GetU32(&offset, &load_cmd, 2) == nullptr) + if (m_data_nsp->GetU32(&offset, &load_cmd, 2) == nullptr) break; switch (load_cmd.cmd) { case LC_UNIXTHREAD: case LC_THREAD: { while (offset < cmd_offset + load_cmd.cmdsize) { - uint32_t flavor = m_data.GetU32(&offset); - uint32_t count = m_data.GetU32(&offset); + uint32_t flavor = m_data_nsp->GetU32(&offset); + uint32_t count = m_data_nsp->GetU32(&offset); if (count == 0) { // We've gotten off somehow, log and exit; return m_entry_point_address; @@ -5059,7 +5064,7 @@ lldb_private::Address ObjectFileMachO::GetEntryPointAddress() { { offset += 60; // This is the offset of pc in the GPR thread state // data structure. - start_address = m_data.GetU32(&offset); + start_address = m_data_nsp->GetU32(&offset); done = true; } break; @@ -5069,7 +5074,7 @@ lldb_private::Address ObjectFileMachO::GetEntryPointAddress() { { offset += 256; // This is the offset of pc in the GPR thread state // data structure. - start_address = m_data.GetU64(&offset); + start_address = m_data_nsp->GetU64(&offset); done = true; } break; @@ -5079,7 +5084,7 @@ lldb_private::Address ObjectFileMachO::GetEntryPointAddress() { { offset += 16 * 8; // This is the offset of rip in the GPR thread // state data structure. - start_address = m_data.GetU64(&offset); + start_address = m_data_nsp->GetU64(&offset); done = true; } break; @@ -5094,7 +5099,7 @@ lldb_private::Address ObjectFileMachO::GetEntryPointAddress() { } } break; case LC_MAIN: { - uint64_t entryoffset = m_data.GetU64(&offset); + uint64_t entryoffset = m_data_nsp->GetU64(&offset); SectionSP text_segment_sp = GetSectionList()->FindSectionByName(GetSegmentNameTEXT()); if (text_segment_sp) { @@ -5178,7 +5183,7 @@ uint32_t ObjectFileMachO::GetNumThreadContexts() { llvm::MachO::thread_command thread_cmd; for (uint32_t i = 0; i < m_header.ncmds; ++i) { const uint32_t cmd_offset = offset; - if (m_data.GetU32(&offset, &thread_cmd, 2) == nullptr) + if (m_data_nsp->GetU32(&offset, &thread_cmd, 2) == nullptr) break; if (thread_cmd.cmd == LC_THREAD) { @@ -5204,17 +5209,17 @@ ObjectFileMachO::FindLC_NOTEByName(std::string name) { for (uint32_t i = 0; i < m_header.ncmds; ++i) { const uint32_t cmd_offset = offset; llvm::MachO::load_command lc = {}; - if (m_data.GetU32(&offset, &lc.cmd, 2) == nullptr) + if (m_data_nsp->GetU32(&offset, &lc.cmd, 2) == nullptr) break; if (lc.cmd == LC_NOTE) { char data_owner[17]; - m_data.CopyData(offset, 16, data_owner); + m_data_nsp->CopyData(offset, 16, data_owner); data_owner[16] = '\0'; offset += 16; if (name == data_owner) { - offset_t payload_offset = m_data.GetU64_unchecked(&offset); - offset_t payload_size = m_data.GetU64_unchecked(&offset); + offset_t payload_offset = m_data_nsp->GetU64_unchecked(&offset); + offset_t payload_size = m_data_nsp->GetU64_unchecked(&offset); results.push_back({payload_offset, payload_size}); } } @@ -5236,11 +5241,11 @@ std::string ObjectFileMachO::GetIdentifierString() { offset_t payload_offset = std::get<0>(lc_note); offset_t payload_size = std::get<1>(lc_note); uint32_t version; - if (m_data.GetU32(&payload_offset, &version, 1) != nullptr) { + if (m_data_nsp->GetU32(&payload_offset, &version, 1) != nullptr) { if (version == 1) { uint32_t strsize = payload_size - sizeof(uint32_t); std::string result(strsize, '\0'); - m_data.CopyData(payload_offset, strsize, result.data()); + m_data_nsp->CopyData(payload_offset, strsize, result.data()); LLDB_LOGF(log, "LC_NOTE 'kern ver str' found with text '%s'", result.c_str()); return result; @@ -5254,12 +5259,12 @@ std::string ObjectFileMachO::GetIdentifierString() { for (uint32_t i = 0; i < m_header.ncmds; ++i) { const uint32_t cmd_offset = offset; llvm::MachO::ident_command ident_command; - if (m_data.GetU32(&offset, &ident_command, 2) == nullptr) + if (m_data_nsp->GetU32(&offset, &ident_command, 2) == nullptr) break; if (ident_command.cmd == LC_IDENT && ident_command.cmdsize != 0) { std::string result(ident_command.cmdsize, '\0'); - if (m_data.CopyData(offset, ident_command.cmdsize, result.data()) == - ident_command.cmdsize) { + if (m_data_nsp->CopyData(offset, ident_command.cmdsize, + result.data()) == ident_command.cmdsize) { LLDB_LOGF(log, "LC_IDENT found with text '%s'", result.c_str()); return result; } @@ -5281,9 +5286,10 @@ AddressableBits ObjectFileMachO::GetAddressableBits() { for (auto lc_note : lc_notes) { offset_t payload_offset = std::get<0>(lc_note); uint32_t version; - if (m_data.GetU32(&payload_offset, &version, 1) != nullptr) { + if (m_data_nsp->GetU32(&payload_offset, &version, 1) != nullptr) { if (version == 3) { - uint32_t num_addr_bits = m_data.GetU32_unchecked(&payload_offset); + uint32_t num_addr_bits = + m_data_nsp->GetU32_unchecked(&payload_offset); addressable_bits.SetAddressableBits(num_addr_bits); LLDB_LOGF(log, "LC_NOTE 'addrable bits' v3 found, value %d " @@ -5291,8 +5297,8 @@ AddressableBits ObjectFileMachO::GetAddressableBits() { num_addr_bits); } if (version == 4) { - uint32_t lo_addr_bits = m_data.GetU32_unchecked(&payload_offset); - uint32_t hi_addr_bits = m_data.GetU32_unchecked(&payload_offset); + uint32_t lo_addr_bits = m_data_nsp->GetU32_unchecked(&payload_offset); + uint32_t hi_addr_bits = m_data_nsp->GetU32_unchecked(&payload_offset); if (lo_addr_bits == hi_addr_bits) addressable_bits.SetAddressableBits(lo_addr_bits); @@ -5363,25 +5369,26 @@ bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &value, // uint32_t unused [ for alignment ] uint32_t version; - if (m_data.GetU32(&payload_offset, &version, 1) != nullptr && + if (m_data_nsp->GetU32(&payload_offset, &version, 1) != nullptr && version <= 2) { uint32_t binspec_type = 0; uuid_t raw_uuid; memset(raw_uuid, 0, sizeof(uuid_t)); - if (!m_data.GetU32(&payload_offset, &binspec_type, 1)) + if (!m_data_nsp->GetU32(&payload_offset, &binspec_type, 1)) return false; - if (!m_data.GetU64(&payload_offset, &value, 1)) + if (!m_data_nsp->GetU64(&payload_offset, &value, 1)) return false; uint64_t slide = LLDB_INVALID_ADDRESS; - if (version > 1 && !m_data.GetU64(&payload_offset, &slide, 1)) + if (version > 1 && !m_data_nsp->GetU64(&payload_offset, &slide, 1)) return false; if (value == LLDB_INVALID_ADDRESS && slide != LLDB_INVALID_ADDRESS) { value = slide; value_is_offset = true; } - if (m_data.CopyData(payload_offset, sizeof(uuid_t), raw_uuid) != 0) { + if (m_data_nsp->CopyData(payload_offset, sizeof(uuid_t), raw_uuid) != + 0) { uuid = UUID(raw_uuid, sizeof(uuid_t)); // convert the "main bin spec" type into our // ObjectFile::BinaryType enum @@ -5415,9 +5422,9 @@ bool ObjectFileMachO::GetCorefileMainBinaryInfo(addr_t &value, version, type, typestr, value, value_is_offset ? "true" : "false", uuid.GetAsString().c_str()); - if (!m_data.GetU32(&payload_offset, &log2_pagesize, 1)) + if (!m_data_nsp->GetU32(&payload_offset, &log2_pagesize, 1)) return false; - if (version > 1 && !m_data.GetU32(&payload_offset, &platform, 1)) + if (version > 1 && !m_data_nsp->GetU32(&payload_offset, &platform, 1)) return false; return true; } @@ -5497,7 +5504,7 @@ StructuredData::ObjectSP ObjectFileMachO::GetCorefileProcessMetadata() { auto [payload_offset, strsize] = lc_notes[0]; std::string buf(strsize, '\0'); - if (m_data.CopyData(payload_offset, strsize, buf.data()) != strsize) { + if (m_data_nsp->CopyData(payload_offset, strsize, buf.data()) != strsize) { LLDB_LOGF(log, "Unable to read %" PRIu64 " bytes of 'process metadata' LC_NOTE JSON contents", @@ -5537,7 +5544,8 @@ ObjectFileMachO::GetThreadContextAtIndex(uint32_t idx, m_thread_context_offsets.GetEntryAtIndex(idx); if (thread_context_file_range) { - DataExtractor data(m_data, thread_context_file_range->GetRangeBase(), + DataExtractor data(*m_data_nsp.get(), + thread_context_file_range->GetRangeBase(), thread_context_file_range->GetByteSize()); switch (m_header.cputype) { @@ -5677,13 +5685,13 @@ llvm::VersionTuple ObjectFileMachO::GetVersion() { uint32_t i; for (i = 0; i < m_header.ncmds; ++i) { const lldb::offset_t cmd_offset = offset; - if (m_data.GetU32(&offset, &load_cmd, 2) == nullptr) + if (m_data_nsp->GetU32(&offset, &load_cmd, 2) == nullptr) break; if (load_cmd.cmd == LC_ID_DYLIB) { if (version_cmd == 0) { version_cmd = load_cmd.cmd; - if (m_data.GetU32(&offset, &load_cmd.dylib, 4) == nullptr) + if (m_data_nsp->GetU32(&offset, &load_cmd.dylib, 4) == nullptr) break; version = load_cmd.dylib.current_version; } @@ -5709,7 +5717,7 @@ ArchSpec ObjectFileMachO::GetArchitecture() { if (module_sp) { std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex()); - return GetArchitecture(module_sp, m_header, m_data, + return GetArchitecture(module_sp, m_header, *m_data_nsp.get(), MachHeaderSizeFromMagic(m_header.magic)); } return arch; @@ -5880,14 +5888,16 @@ static llvm::VersionTuple FindMinimumVersionInfo(DataExtractor &data, llvm::VersionTuple ObjectFileMachO::GetMinimumOSVersion() { if (!m_min_os_version) m_min_os_version = FindMinimumVersionInfo( - m_data, MachHeaderSizeFromMagic(m_header.magic), m_header.ncmds); + *m_data_nsp.get(), MachHeaderSizeFromMagic(m_header.magic), + m_header.ncmds); return *m_min_os_version; } llvm::VersionTuple ObjectFileMachO::GetSDKVersion() { if (!m_sdk_versions) m_sdk_versions = FindMinimumVersionInfo( - m_data, MachHeaderSizeFromMagic(m_header.magic), m_header.ncmds); + *m_data_nsp.get(), MachHeaderSizeFromMagic(m_header.magic), + m_header.ncmds); return *m_sdk_versions; } @@ -5936,6 +5946,20 @@ Section *ObjectFileMachO::GetMachHeaderSection() { return nullptr; } +bool ObjectFileMachO::IsGOTSection(const lldb_private::Section §ion) const { + assert(section.GetObjectFile() == this && "Wrong object file!"); + SectionSP segment = section.GetParent(); + if (!segment) + return false; + + const bool is_data_const_got = + segment->GetName() == "__DATA_CONST" && section.GetName() == "__got"; + const bool is_auth_const_ptr = + segment->GetName() == "__AUTH_CONST" && + (section.GetName() == "__auth_got" || section.GetName() == "__auth_ptr"); + return is_data_const_got || is_auth_const_ptr; +} + bool ObjectFileMachO::SectionIsLoadable(const Section *section) { if (!section) return false; @@ -6688,12 +6712,12 @@ ObjectFileMachO::GetCorefileAllImageInfos() { for (auto lc_note : lc_notes) { offset_t payload_offset = std::get<0>(lc_note); // Read the struct all_image_infos_header. - uint32_t version = m_data.GetU32(&payload_offset); + uint32_t version = m_data_nsp->GetU32(&payload_offset); if (version != 1) { return image_infos; } - uint32_t imgcount = m_data.GetU32(&payload_offset); - uint64_t entries_fileoff = m_data.GetU64(&payload_offset); + uint32_t imgcount = m_data_nsp->GetU32(&payload_offset); + uint64_t entries_fileoff = m_data_nsp->GetU64(&payload_offset); // 'entries_size' is not used, nor is the 'unused' entry. // offset += 4; // uint32_t entries_size; // offset += 4; // uint32_t unused; @@ -6703,17 +6727,18 @@ ObjectFileMachO::GetCorefileAllImageInfos() { payload_offset = entries_fileoff; for (uint32_t i = 0; i < imgcount; i++) { // Read the struct image_entry. - offset_t filepath_offset = m_data.GetU64(&payload_offset); + offset_t filepath_offset = m_data_nsp->GetU64(&payload_offset); uuid_t uuid; - memcpy(&uuid, m_data.GetData(&payload_offset, sizeof(uuid_t)), + memcpy(&uuid, m_data_nsp->GetData(&payload_offset, sizeof(uuid_t)), sizeof(uuid_t)); - uint64_t load_address = m_data.GetU64(&payload_offset); - offset_t seg_addrs_offset = m_data.GetU64(&payload_offset); - uint32_t segment_count = m_data.GetU32(&payload_offset); - uint32_t currently_executing = m_data.GetU32(&payload_offset); + uint64_t load_address = m_data_nsp->GetU64(&payload_offset); + offset_t seg_addrs_offset = m_data_nsp->GetU64(&payload_offset); + uint32_t segment_count = m_data_nsp->GetU32(&payload_offset); + uint32_t currently_executing = m_data_nsp->GetU32(&payload_offset); MachOCorefileImageEntry image_entry; - image_entry.filename = (const char *)m_data.GetCStr(&filepath_offset); + image_entry.filename = + (const char *)m_data_nsp->GetCStr(&filepath_offset); image_entry.uuid = UUID(uuid, sizeof(uuid_t)); image_entry.load_address = load_address; image_entry.currently_executing = currently_executing; @@ -6721,10 +6746,10 @@ ObjectFileMachO::GetCorefileAllImageInfos() { offset_t seg_vmaddrs_offset = seg_addrs_offset; for (uint32_t j = 0; j < segment_count; j++) { char segname[17]; - m_data.CopyData(seg_vmaddrs_offset, 16, segname); + m_data_nsp->CopyData(seg_vmaddrs_offset, 16, segname); segname[16] = '\0'; seg_vmaddrs_offset += 16; - uint64_t vmaddr = m_data.GetU64(&seg_vmaddrs_offset); + uint64_t vmaddr = m_data_nsp->GetU64(&seg_vmaddrs_offset); seg_vmaddrs_offset += 8; /* unused */ std::tuple<ConstString, addr_t> new_seg{ConstString(segname), vmaddr}; @@ -6743,14 +6768,14 @@ ObjectFileMachO::GetCorefileAllImageInfos() { lc_notes = FindLC_NOTEByName("load binary"); for (auto lc_note : lc_notes) { offset_t payload_offset = std::get<0>(lc_note); - uint32_t version = m_data.GetU32(&payload_offset); + uint32_t version = m_data_nsp->GetU32(&payload_offset); if (version == 1) { uuid_t uuid; - memcpy(&uuid, m_data.GetData(&payload_offset, sizeof(uuid_t)), + memcpy(&uuid, m_data_nsp->GetData(&payload_offset, sizeof(uuid_t)), sizeof(uuid_t)); - uint64_t load_address = m_data.GetU64(&payload_offset); - uint64_t slide = m_data.GetU64(&payload_offset); - std::string filename = m_data.GetCStr(&payload_offset); + uint64_t load_address = m_data_nsp->GetU64(&payload_offset); + uint64_t slide = m_data_nsp->GetU64(&payload_offset); + std::string filename = m_data_nsp->GetCStr(&payload_offset); MachOCorefileImageEntry image_entry; image_entry.filename = filename; diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h index 25643aa..5456f03 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h @@ -162,6 +162,8 @@ public: lldb_private::Section *GetMachHeaderSection(); + bool IsGOTSection(const lldb_private::Section §ion) const override; + // PluginInterface protocol llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp index 244489a..f25ed51 100644 --- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -396,7 +396,7 @@ bool ObjectFilePECOFF::CreateBinary() { Log *log = GetLog(LLDBLog::Object); auto binary = llvm::object::createBinary(llvm::MemoryBufferRef( - toStringRef(m_data.GetData()), m_file.GetFilename().GetStringRef())); + toStringRef(m_data_nsp->GetData()), m_file.GetFilename().GetStringRef())); if (!binary) { LLDB_LOG_ERROR(log, binary.takeError(), "Failed to create binary for file ({1}): {0}", m_file); @@ -442,20 +442,20 @@ bool ObjectFilePECOFF::ParseHeader() { if (module_sp) { std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex()); m_sect_headers.clear(); - m_data.SetByteOrder(eByteOrderLittle); + m_data_nsp->SetByteOrder(eByteOrderLittle); lldb::offset_t offset = 0; - if (ParseDOSHeader(m_data, m_dos_header)) { + if (ParseDOSHeader(*m_data_nsp.get(), m_dos_header)) { offset = m_dos_header.e_lfanew; - uint32_t pe_signature = m_data.GetU32(&offset); + uint32_t pe_signature = m_data_nsp->GetU32(&offset); if (pe_signature != IMAGE_NT_SIGNATURE) return false; - if (ParseCOFFHeader(m_data, &offset, m_coff_header)) { + if (ParseCOFFHeader(*m_data_nsp.get(), &offset, m_coff_header)) { if (m_coff_header.hdrsize > 0) ParseCOFFOptionalHeader(&offset); ParseSectionHeaders(offset); } - m_data.SetAddressByteSize(GetAddressByteSize()); + m_data_nsp->SetAddressByteSize(GetAddressByteSize()); return true; } } @@ -602,57 +602,63 @@ bool ObjectFilePECOFF::ParseCOFFOptionalHeader(lldb::offset_t *offset_ptr) { const lldb::offset_t end_offset = *offset_ptr + m_coff_header.hdrsize; if (*offset_ptr < end_offset) { success = true; - m_coff_header_opt.magic = m_data.GetU16(offset_ptr); - m_coff_header_opt.major_linker_version = m_data.GetU8(offset_ptr); - m_coff_header_opt.minor_linker_version = m_data.GetU8(offset_ptr); - m_coff_header_opt.code_size = m_data.GetU32(offset_ptr); - m_coff_header_opt.data_size = m_data.GetU32(offset_ptr); - m_coff_header_opt.bss_size = m_data.GetU32(offset_ptr); - m_coff_header_opt.entry = m_data.GetU32(offset_ptr); - m_coff_header_opt.code_offset = m_data.GetU32(offset_ptr); + m_coff_header_opt.magic = m_data_nsp->GetU16(offset_ptr); + m_coff_header_opt.major_linker_version = m_data_nsp->GetU8(offset_ptr); + m_coff_header_opt.minor_linker_version = m_data_nsp->GetU8(offset_ptr); + m_coff_header_opt.code_size = m_data_nsp->GetU32(offset_ptr); + m_coff_header_opt.data_size = m_data_nsp->GetU32(offset_ptr); + m_coff_header_opt.bss_size = m_data_nsp->GetU32(offset_ptr); + m_coff_header_opt.entry = m_data_nsp->GetU32(offset_ptr); + m_coff_header_opt.code_offset = m_data_nsp->GetU32(offset_ptr); const uint32_t addr_byte_size = GetAddressByteSize(); if (*offset_ptr < end_offset) { if (m_coff_header_opt.magic == OPT_HEADER_MAGIC_PE32) { // PE32 only - m_coff_header_opt.data_offset = m_data.GetU32(offset_ptr); + m_coff_header_opt.data_offset = m_data_nsp->GetU32(offset_ptr); } else m_coff_header_opt.data_offset = 0; if (*offset_ptr < end_offset) { m_coff_header_opt.image_base = - m_data.GetMaxU64(offset_ptr, addr_byte_size); - m_coff_header_opt.sect_alignment = m_data.GetU32(offset_ptr); - m_coff_header_opt.file_alignment = m_data.GetU32(offset_ptr); - m_coff_header_opt.major_os_system_version = m_data.GetU16(offset_ptr); - m_coff_header_opt.minor_os_system_version = m_data.GetU16(offset_ptr); - m_coff_header_opt.major_image_version = m_data.GetU16(offset_ptr); - m_coff_header_opt.minor_image_version = m_data.GetU16(offset_ptr); - m_coff_header_opt.major_subsystem_version = m_data.GetU16(offset_ptr); - m_coff_header_opt.minor_subsystem_version = m_data.GetU16(offset_ptr); - m_coff_header_opt.reserved1 = m_data.GetU32(offset_ptr); - m_coff_header_opt.image_size = m_data.GetU32(offset_ptr); - m_coff_header_opt.header_size = m_data.GetU32(offset_ptr); - m_coff_header_opt.checksum = m_data.GetU32(offset_ptr); - m_coff_header_opt.subsystem = m_data.GetU16(offset_ptr); - m_coff_header_opt.dll_flags = m_data.GetU16(offset_ptr); + m_data_nsp->GetMaxU64(offset_ptr, addr_byte_size); + m_coff_header_opt.sect_alignment = m_data_nsp->GetU32(offset_ptr); + m_coff_header_opt.file_alignment = m_data_nsp->GetU32(offset_ptr); + m_coff_header_opt.major_os_system_version = + m_data_nsp->GetU16(offset_ptr); + m_coff_header_opt.minor_os_system_version = + m_data_nsp->GetU16(offset_ptr); + m_coff_header_opt.major_image_version = m_data_nsp->GetU16(offset_ptr); + m_coff_header_opt.minor_image_version = m_data_nsp->GetU16(offset_ptr); + m_coff_header_opt.major_subsystem_version = + m_data_nsp->GetU16(offset_ptr); + m_coff_header_opt.minor_subsystem_version = + m_data_nsp->GetU16(offset_ptr); + m_coff_header_opt.reserved1 = m_data_nsp->GetU32(offset_ptr); + m_coff_header_opt.image_size = m_data_nsp->GetU32(offset_ptr); + m_coff_header_opt.header_size = m_data_nsp->GetU32(offset_ptr); + m_coff_header_opt.checksum = m_data_nsp->GetU32(offset_ptr); + m_coff_header_opt.subsystem = m_data_nsp->GetU16(offset_ptr); + m_coff_header_opt.dll_flags = m_data_nsp->GetU16(offset_ptr); m_coff_header_opt.stack_reserve_size = - m_data.GetMaxU64(offset_ptr, addr_byte_size); + m_data_nsp->GetMaxU64(offset_ptr, addr_byte_size); m_coff_header_opt.stack_commit_size = - m_data.GetMaxU64(offset_ptr, addr_byte_size); + m_data_nsp->GetMaxU64(offset_ptr, addr_byte_size); m_coff_header_opt.heap_reserve_size = - m_data.GetMaxU64(offset_ptr, addr_byte_size); + m_data_nsp->GetMaxU64(offset_ptr, addr_byte_size); m_coff_header_opt.heap_commit_size = - m_data.GetMaxU64(offset_ptr, addr_byte_size); - m_coff_header_opt.loader_flags = m_data.GetU32(offset_ptr); - uint32_t num_data_dir_entries = m_data.GetU32(offset_ptr); + m_data_nsp->GetMaxU64(offset_ptr, addr_byte_size); + m_coff_header_opt.loader_flags = m_data_nsp->GetU32(offset_ptr); + uint32_t num_data_dir_entries = m_data_nsp->GetU32(offset_ptr); m_coff_header_opt.data_dirs.clear(); m_coff_header_opt.data_dirs.resize(num_data_dir_entries); uint32_t i; for (i = 0; i < num_data_dir_entries; i++) { - m_coff_header_opt.data_dirs[i].vmaddr = m_data.GetU32(offset_ptr); - m_coff_header_opt.data_dirs[i].vmsize = m_data.GetU32(offset_ptr); + m_coff_header_opt.data_dirs[i].vmaddr = + m_data_nsp->GetU32(offset_ptr); + m_coff_header_opt.data_dirs[i].vmsize = + m_data_nsp->GetU32(offset_ptr); } m_image_base = m_coff_header_opt.image_base; @@ -684,8 +690,8 @@ DataExtractor ObjectFilePECOFF::ReadImageData(uint32_t offset, size_t size) { if (!size) return {}; - if (m_data.ValidOffsetForDataOfSize(offset, size)) - return DataExtractor(m_data, offset, size); + if (m_data_nsp->ValidOffsetForDataOfSize(offset, size)) + return DataExtractor(*m_data_nsp.get(), offset, size); ProcessSP process_sp(m_process_wp.lock()); DataExtractor data; @@ -759,7 +765,7 @@ llvm::StringRef ObjectFilePECOFF::GetSectionName(const section_header_t §) { return ""; lldb::offset_t string_file_offset = m_coff_header.symoff + (m_coff_header.nsyms * 18) + stroff; - if (const char *name = m_data.GetCStr(&string_file_offset)) + if (const char *name = m_data_nsp->GetCStr(&string_file_offset)) return name; return ""; } diff --git a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp index d2c46ed..bfe1556 100644 --- a/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/XCOFF/ObjectFileXCOFF.cpp @@ -94,7 +94,7 @@ bool ObjectFileXCOFF::CreateBinary() { Log *log = GetLog(LLDBLog::Object); - auto memory_ref = llvm::MemoryBufferRef(toStringRef(m_data.GetData()), + auto memory_ref = llvm::MemoryBufferRef(toStringRef(m_data_nsp->GetData()), m_file.GetFilename().GetStringRef()); llvm::file_magic magic = llvm::identify_magic(memory_ref.getBuffer()); diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp index 492b441..0bedb5e 100644 --- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp +++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp @@ -287,7 +287,7 @@ ObjectFileWasm::ObjectFileWasm(const ModuleSP &module_sp, DataBufferSP data_sp, offset_t offset, offset_t length) : ObjectFile(module_sp, file, offset, length, data_sp, data_offset), m_arch("wasm32-unknown-unknown-wasm") { - m_data.SetAddressByteSize(4); + m_data_nsp->SetAddressByteSize(4); } ObjectFileWasm::ObjectFileWasm(const lldb::ModuleSP &module_sp, @@ -719,11 +719,11 @@ DataExtractor ObjectFileWasm::ReadImageData(offset_t offset, uint32_t size) { DataBufferSP buffer_sp(data_up.release()); data.SetData(buffer_sp, 0, buffer_sp->GetByteSize()); } - } else if (offset < m_data.GetByteSize()) { - size = - std::min(static_cast<uint64_t>(size), m_data.GetByteSize() - offset); - return DataExtractor(m_data.GetDataStart() + offset, size, GetByteOrder(), - GetAddressByteSize()); + } else if (offset < m_data_nsp->GetByteSize()) { + size = std::min(static_cast<uint64_t>(size), + m_data_nsp->GetByteSize() - offset); + return DataExtractor(m_data_nsp->GetDataStart() + offset, size, + GetByteOrder(), GetAddressByteSize()); } } data.SetByteOrder(GetByteOrder()); diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp index 57d88f6..22b9711 100644 --- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp +++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp @@ -15,6 +15,8 @@ #include "lldb/Utility/UriParser.h" #include "lldb/ValueObject/ValueObject.h" +#include "llvm/ADT/DenseMap.h" + #include "AdbClient.h" #include "PlatformAndroid.h" #include "PlatformAndroidRemoteGDBServer.h" @@ -479,136 +481,90 @@ std::string PlatformAndroid::GetRunAs() { return run_as.str(); } -// Helper function to populate process status information from -// /proc/[pid]/status -void PlatformAndroid::PopulateProcessStatusInfo( - lldb::pid_t pid, ProcessInstanceInfo &process_info) { - // Read /proc/[pid]/status to get parent PID, UIDs, and GIDs - Status error; - AdbClientUP status_adb = GetAdbClient(error); - if (error.Fail()) - return; - - std::string status_output; - StreamString status_cmd; - status_cmd.Printf( - "cat /proc/%llu/status 2>/dev/null | grep -E '^(PPid|Uid|Gid):'", - static_cast<unsigned long long>(pid)); - Status status_error = - status_adb->Shell(status_cmd.GetData(), seconds(5), &status_output); +static bool NeedsCmdlineSupplement(const ProcessInstanceInfo &proc_info) { + llvm::StringRef name = + proc_info.GetExecutableFile().GetFilename().GetStringRef(); + return name.contains("app_process") || name.contains("zygote"); +} - if (status_error.Fail() || status_output.empty()) +// Fetch /proc/PID/cmdline for processes to get actual package names. +// Android apps often show as "zygote" or "app_process" without this. +static void SupplementWithCmdlineInfo(ProcessInstanceInfoList &proc_infos, + AdbClient *adb) { + if (proc_infos.empty()) return; - llvm::SmallVector<llvm::StringRef, 16> lines; - llvm::StringRef(status_output).split(lines, '\n'); - - for (llvm::StringRef line : lines) { - line = line.trim(); - if (line.starts_with("PPid:")) { - llvm::StringRef ppid_str = line.substr(5).trim(); - lldb::pid_t ppid; - if (llvm::to_integer(ppid_str, ppid)) - process_info.SetParentProcessID(ppid); - } else if (line.starts_with("Uid:")) { - llvm::SmallVector<llvm::StringRef, 4> uid_parts; - line.substr(4).trim().split(uid_parts, '\t', -1, false); - if (uid_parts.size() >= 2) { - uint32_t uid, euid; - if (llvm::to_integer(uid_parts[0].trim(), uid)) - process_info.SetUserID(uid); - if (llvm::to_integer(uid_parts[1].trim(), euid)) - process_info.SetEffectiveUserID(euid); - } - } else if (line.starts_with("Gid:")) { - llvm::SmallVector<llvm::StringRef, 4> gid_parts; - line.substr(4).trim().split(gid_parts, '\t', -1, false); - if (gid_parts.size() >= 2) { - uint32_t gid, egid; - if (llvm::to_integer(gid_parts[0].trim(), gid)) - process_info.SetGroupID(gid); - if (llvm::to_integer(gid_parts[1].trim(), egid)) - process_info.SetEffectiveGroupID(egid); - } + llvm::DenseMap<lldb::pid_t, ProcessInstanceInfo *> pid_map; + std::string pid_list; + for (auto &proc_info : proc_infos) { + if (NeedsCmdlineSupplement(proc_info)) { + lldb::pid_t pid = proc_info.GetProcessID(); + pid_map[pid] = &proc_info; + if (!pid_list.empty()) + pid_list += " "; + pid_list += std::to_string(pid); } } -} -// Helper function to populate command line arguments from /proc/[pid]/cmdline -void PlatformAndroid::PopulateProcessCommandLine( - lldb::pid_t pid, ProcessInstanceInfo &process_info) { - // Read /proc/[pid]/cmdline to get command line arguments - Status error; - AdbClientUP cmdline_adb = GetAdbClient(error); - if (error.Fail()) + if (pid_list.empty()) return; + Log *log = GetLog(LLDBLog::Platform); + + // Use xargs -P to parallelize cmdline fetching (up to 8 concurrent reads) + StreamString cmd; + cmd.Printf( + "echo '%s' | xargs -n 1 -P 8 sh -c " + "'echo \"$1:$(cat /proc/$1/cmdline 2>/dev/null | tr \"\\0\" \" \")\"' sh", + pid_list.c_str()); + std::string cmdline_output; - StreamString cmdline_cmd; - cmdline_cmd.Printf("cat /proc/%llu/cmdline 2>/dev/null | tr '\\000' ' '", - static_cast<unsigned long long>(pid)); - Status cmdline_error = - cmdline_adb->Shell(cmdline_cmd.GetData(), seconds(5), &cmdline_output); + Status error = adb->Shell(cmd.GetData(), seconds(5), &cmdline_output); - if (cmdline_error.Fail() || cmdline_output.empty()) + if (error.Fail() || cmdline_output.empty()) return; - cmdline_output = llvm::StringRef(cmdline_output).trim().str(); - if (cmdline_output.empty()) - return; + llvm::SmallVector<llvm::StringRef, 256> lines; + llvm::StringRef(cmdline_output).split(lines, '\n', -1, false); - llvm::SmallVector<llvm::StringRef, 16> args; - llvm::StringRef(cmdline_output).split(args, ' ', -1, false); - if (args.empty()) - return; + for (llvm::StringRef line : lines) { + line = line.trim(); + auto [pid_str, cmdline] = line.split(':'); + if (pid_str.empty() || cmdline.empty()) + continue; - process_info.SetArg0(args[0]); - Args process_args; - for (size_t i = 1; i < args.size(); i++) { - if (!args[i].empty()) - process_args.AppendArgument(args[i]); - } - process_info.SetArguments(process_args, false); -} + cmdline = cmdline.trim(); -// Helper function to populate architecture from /proc/[pid]/exe -void PlatformAndroid::PopulateProcessArchitecture( - lldb::pid_t pid, ProcessInstanceInfo &process_info) { - // Read /proc/[pid]/exe to get executable path for architecture detection - Status error; - AdbClientUP exe_adb = GetAdbClient(error); - if (error.Fail()) - return; + lldb::pid_t pid; + if (!llvm::to_integer(pid_str, pid) || cmdline.empty()) + continue; - std::string exe_output; - StreamString exe_cmd; - exe_cmd.Printf("readlink /proc/%llu/exe 2>/dev/null", - static_cast<unsigned long long>(pid)); - Status exe_error = exe_adb->Shell(exe_cmd.GetData(), seconds(5), &exe_output); + auto it = pid_map.find(pid); + if (it == pid_map.end()) + continue; - if (exe_error.Fail() || exe_output.empty()) - return; + ProcessInstanceInfo *proc_info = it->second; + llvm::SmallVector<llvm::StringRef, 16> args; + cmdline.split(args, ' ', -1, false); - exe_output = llvm::StringRef(exe_output).trim().str(); - - // Determine architecture from exe path - ArchSpec arch; - if (exe_output.find("64") != std::string::npos || - exe_output.find("arm64") != std::string::npos || - exe_output.find("aarch64") != std::string::npos) { - arch.SetTriple("aarch64-unknown-linux-android"); - } else if (exe_output.find("x86_64") != std::string::npos) { - arch.SetTriple("x86_64-unknown-linux-android"); - } else if (exe_output.find("x86") != std::string::npos || - exe_output.find("i686") != std::string::npos) { - arch.SetTriple("i686-unknown-linux-android"); - } else { - // Default to armv7 for 32-bit ARM (most common on Android) - arch.SetTriple("armv7-unknown-linux-android"); - } + if (!args.empty()) { + proc_info->GetExecutableFile().SetFile(args[0], FileSpec::Style::posix); + + if (args.size() > 1) { + Args process_args; + for (size_t i = 1; i < args.size(); ++i) { + if (!args[i].empty()) + process_args.AppendArgument(args[i]); + } + proc_info->SetArguments(process_args, false); + } - if (arch.IsValid()) - process_info.SetArchitecture(arch); + LLDB_LOGF(log, + "PlatformAndroid::%s supplemented PID %llu with cmdline: %s", + __FUNCTION__, static_cast<unsigned long long>(pid), + cmdline.str().c_str()); + } + } } uint32_t @@ -616,109 +572,39 @@ PlatformAndroid::FindProcesses(const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &proc_infos) { proc_infos.clear(); - // When LLDB is running natively on an Android device (IsHost() == true), - // use the parent class's standard Linux /proc enumeration. IsHost() is only - // true when compiled for Android (#if defined(__ANDROID__)), so calling - // PlatformLinux methods is safe (Android is Linux-based). if (IsHost()) return PlatformLinux::FindProcesses(match_info, proc_infos); - // Remote Android platform: implement process name lookup using 'pidof' over - // adb. - - // LLDB stores the search name in GetExecutableFile() (even though it's - // actually a process name like "com.android.chrome" rather than an - // executable path). If no search name is provided, we can't use - // 'pidof', so return early with no results. - const ProcessInstanceInfo &match_process_info = match_info.GetProcessInfo(); - if (!match_process_info.GetExecutableFile() || - match_info.GetNameMatchType() == NameMatch::Ignore) { - return 0; - } - - // Extract the process name to search for (typically an Android package name - // like "com.example.app" or binary name like "app_process64") - std::string process_name = match_process_info.GetExecutableFile().GetPath(); - if (process_name.empty()) - return 0; - - // Use adb to find the process by name - Status error; - AdbClientUP adb(GetAdbClient(error)); - if (error.Fail()) { - Log *log = GetLog(LLDBLog::Platform); - LLDB_LOGF(log, "PlatformAndroid::%s failed to get ADB client: %s", - __FUNCTION__, error.AsCString()); - return 0; - } - - // Use 'pidof' command to get PIDs for the process name. - // Quote the process name to handle special characters (spaces, etc.) - std::string pidof_output; - StreamString command; - command.Printf("pidof '%s'", process_name.c_str()); - error = adb->Shell(command.GetData(), seconds(5), &pidof_output); - - if (error.Fail()) { - Log *log = GetLog(LLDBLog::Platform); - LLDB_LOG(log, "PlatformAndroid::{} 'pidof {}' failed: {}", __FUNCTION__, - process_name.c_str(), error.AsCString()); - return 0; - } - - // Parse PIDs from pidof output. - // Note: pidof can return multiple PIDs (space-separated) if multiple - // instances of the same executable are running. - pidof_output = llvm::StringRef(pidof_output).trim().str(); - if (pidof_output.empty()) { - Log *log = GetLog(LLDBLog::Platform); - LLDB_LOGF(log, "PlatformAndroid::%s no process found with name '%s'", - __FUNCTION__, process_name.c_str()); + if (!m_remote_platform_sp) return 0; - } - - // Split the output by whitespace to handle multiple PIDs - llvm::SmallVector<llvm::StringRef, 8> pid_strings; - llvm::StringRef(pidof_output).split(pid_strings, ' ', -1, false); - - Log *log = GetLog(LLDBLog::Platform); - - // Process each PID and gather information - uint32_t num_matches = 0; - for (llvm::StringRef pid_str : pid_strings) { - pid_str = pid_str.trim(); - if (pid_str.empty()) - continue; - - lldb::pid_t pid; - if (!llvm::to_integer(pid_str, pid)) { - LLDB_LOGF(log, "PlatformAndroid::%s failed to parse PID from: '%s'", - __FUNCTION__, pid_str.str().c_str()); - continue; - } - - ProcessInstanceInfo process_info; - process_info.SetProcessID(pid); - process_info.GetExecutableFile().SetFile(process_name, - FileSpec::Style::posix); - - // Populate additional process information - PopulateProcessStatusInfo(pid, process_info); - PopulateProcessCommandLine(pid, process_info); - PopulateProcessArchitecture(pid, process_info); - - // Check if this process matches the criteria - if (match_info.Matches(process_info)) { - proc_infos.push_back(process_info); - num_matches++; - LLDB_LOGF(log, "PlatformAndroid::%s found process '%s' with PID %llu", - __FUNCTION__, process_name.c_str(), - static_cast<unsigned long long>(pid)); + // Android-specific process name handling: + // Apps spawned from zygote initially appear as "app_process" or "zygote" + // in the process list, but their actual package names (e.g., + // "com.example.app") are only available in /proc/PID/cmdline. To support + // name-based matching, we must first fetch cmdline info for all processes, + // then apply the original name filter. + ProcessInstanceInfoMatch broad_match_info = match_info; + broad_match_info.SetNameMatchType(NameMatch::Ignore); + + ProcessInstanceInfoList all_procs; + uint32_t count = + m_remote_platform_sp->FindProcesses(broad_match_info, all_procs); + + if (count > 0) { + Status error; + AdbClientUP adb(GetAdbClient(error)); + if (error.Success()) + SupplementWithCmdlineInfo(all_procs, adb.get()); + + // Apply the original name matching against supplemented process info. + for (auto &proc_info : all_procs) { + if (match_info.Matches(proc_info)) + proc_infos.push_back(proc_info); } } - return num_matches; + return proc_infos.size(); } std::unique_ptr<AdbSyncService> PlatformAndroid::GetSyncService(Status &error) { diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h index e771c6a..c6a412b 100644 --- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h +++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h @@ -60,7 +60,7 @@ public: uint32_t GetDefaultMemoryCacheLineSize() override; uint32_t FindProcesses(const ProcessInstanceInfoMatch &match_info, - ProcessInstanceInfoList &proc_infos) override; + ProcessInstanceInfoList &process_infos) override; protected: const char *GetCacheHostname() override; @@ -86,17 +86,8 @@ public: protected: virtual std::unique_ptr<AdbSyncService> GetSyncService(Status &error); -private: std::string m_device_id; uint32_t m_sdk_version; - - // Helper functions for process information gathering - void PopulateProcessStatusInfo(lldb::pid_t pid, - ProcessInstanceInfo &process_info); - void PopulateProcessCommandLine(lldb::pid_t pid, - ProcessInstanceInfo &process_info); - void PopulateProcessArchitecture(lldb::pid_t pid, - ProcessInstanceInfo &process_info); }; } // namespace platform_android diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp index 4cfb0a8..47111c9 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp @@ -90,7 +90,7 @@ void PlatformAppleSimulator::GetStatus(Stream &strm) { if (!sdk.empty()) strm << " SDK Path: \"" << sdk << "\"\n"; else - strm << " SDK Path: error: unable to locate SDK\n"; + strm << " SDK Path: <unable to locate SDK>\n"; #if defined(__APPLE__) // This will get called by subclasses, so just output status on the current @@ -420,7 +420,6 @@ Status PlatformAppleSimulator::GetSymbolFile(const FileSpec &platform_file, Status PlatformAppleSimulator::GetSharedModule( const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr) { // For iOS/tvOS/watchOS, the SDK files are all cached locally on the // host system. So first we ask for the file in the cached SDK, then @@ -432,12 +431,10 @@ Status PlatformAppleSimulator::GetSharedModule( error = GetSymbolFile(platform_file, module_spec.GetUUIDPtr(), platform_module_spec.GetFileSpec()); if (error.Success()) { - error = ResolveExecutable(platform_module_spec, module_sp, - module_search_paths_ptr); + error = ResolveExecutable(platform_module_spec, module_sp); } else { const bool always_create = false; - error = ModuleList::GetSharedModule(module_spec, module_sp, - module_search_paths_ptr, old_modules, + error = ModuleList::GetSharedModule(module_spec, module_sp, old_modules, did_create_ptr, always_create); } if (module_sp) @@ -660,4 +657,3 @@ void PlatformAppleSimulator::Terminate() { PlatformDarwin::Terminate(); } } - diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.h b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.h index 7fcf2c5..77d2a3b 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.h @@ -89,7 +89,6 @@ public: Status GetSharedModule(const ModuleSpec &module_spec, Process *process, lldb::ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr) override; diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp index 5aad447..bfbd85e 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp @@ -56,7 +56,7 @@ using namespace lldb; using namespace lldb_private; #define OPTTABLE_STR_TABLE_CODE -#include "clang/Driver/Options.inc" +#include "clang/Options/Options.inc" #undef OPTTABLE_STR_TABLE_CODE static Status ExceptionMaskValidator(const char *string, void *unused) { @@ -331,7 +331,6 @@ Status PlatformDarwin::ResolveSymbolFile(Target &target, Status PlatformDarwin::GetSharedModule( const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<ModuleSP> *old_modules, bool *did_create_ptr) { Status error; module_sp.reset(); @@ -341,19 +340,22 @@ Status PlatformDarwin::GetSharedModule( // module first. if (m_remote_platform_sp) { error = m_remote_platform_sp->GetSharedModule( - module_spec, process, module_sp, module_search_paths_ptr, old_modules, - did_create_ptr); + module_spec, process, module_sp, old_modules, did_create_ptr); } } if (!module_sp) { // Fall back to the local platform and find the file locally error = Platform::GetSharedModule(module_spec, process, module_sp, - module_search_paths_ptr, old_modules, - did_create_ptr); + old_modules, did_create_ptr); const FileSpec &platform_file = module_spec.GetFileSpec(); - if (!module_sp && module_search_paths_ptr && platform_file) { + // Get module search paths from the target if available. + TargetSP target_sp = module_spec.GetTargetSP(); + FileSpecList module_search_paths; + if (target_sp) + module_search_paths = target_sp->GetExecutableSearchPaths(); + if (!module_sp && !module_search_paths.IsEmpty() && platform_file) { // We can try to pull off part of the file path up to the bundle // directory level and try any module search paths... FileSpec bundle_directory; @@ -362,9 +364,9 @@ Status PlatformDarwin::GetSharedModule( ModuleSpec new_module_spec(module_spec); new_module_spec.GetFileSpec() = bundle_directory; if (Host::ResolveExecutableInBundle(new_module_spec.GetFileSpec())) { - Status new_error(Platform::GetSharedModule( - new_module_spec, process, module_sp, nullptr, old_modules, - did_create_ptr)); + Status new_error(Platform::GetSharedModule(new_module_spec, process, + module_sp, old_modules, + did_create_ptr)); if (module_sp) return new_error; @@ -376,10 +378,10 @@ Status PlatformDarwin::GetSharedModule( const size_t bundle_directory_len = bundle_directory.GetPath(bundle_dir, sizeof(bundle_dir)); char new_path[PATH_MAX]; - size_t num_module_search_paths = module_search_paths_ptr->GetSize(); + size_t num_module_search_paths = module_search_paths.GetSize(); for (size_t i = 0; i < num_module_search_paths; ++i) { const size_t search_path_len = - module_search_paths_ptr->GetFileSpecAtIndex(i).GetPath( + module_search_paths.GetFileSpecAtIndex(i).GetPath( new_path, sizeof(new_path)); if (search_path_len < sizeof(new_path)) { snprintf(new_path + search_path_len, @@ -390,7 +392,7 @@ Status PlatformDarwin::GetSharedModule( ModuleSpec new_module_spec(module_spec); new_module_spec.GetFileSpec() = new_file_spec; Status new_error(Platform::GetSharedModule( - new_module_spec, process, module_sp, nullptr, old_modules, + new_module_spec, process, module_sp, old_modules, did_create_ptr)); if (module_sp) { @@ -1122,7 +1124,7 @@ void PlatformDarwin::AddClangModuleCompilationOptionsForSDKType( #define OPTION(PREFIX_OFFSET, NAME_OFFSET, VAR, ...) \ llvm::StringRef opt_##VAR = OptionStrTable[NAME_OFFSET]; \ (void)opt_##VAR; -#include "clang/Driver/Options.inc" +#include "clang/Options/Options.inc" #undef OPTION minimum_version_option << '-'; switch (sdk_type) { @@ -1303,12 +1305,15 @@ PlatformDarwin::LaunchProcess(lldb_private::ProcessLaunchInfo &launch_info) { lldb_private::Status PlatformDarwin::FindBundleBinaryInExecSearchPaths( const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<ModuleSP> *old_modules, bool *did_create_ptr) { const FileSpec &platform_file = module_spec.GetFileSpec(); - // See if the file is present in any of the module_search_paths_ptr + TargetSP target_sp = module_spec.GetTargetSP(); + FileSpecList module_search_paths; + if (target_sp) + module_search_paths = target_sp->GetExecutableSearchPaths(); + // See if the file is present in any of the module_search_paths // directories. - if (!module_sp && module_search_paths_ptr && platform_file) { + if (!module_sp && !module_search_paths.IsEmpty() && platform_file) { // create a vector of all the file / directory names in platform_file e.g. // this might be // /System/Library/PrivateFrameworks/UIFoundation.framework/UIFoundation @@ -1322,21 +1327,21 @@ lldb_private::Status PlatformDarwin::FindBundleBinaryInExecSearchPaths( std::reverse(path_parts.begin(), path_parts.end()); const size_t path_parts_size = path_parts.size(); - size_t num_module_search_paths = module_search_paths_ptr->GetSize(); + size_t num_module_search_paths = module_search_paths.GetSize(); for (size_t i = 0; i < num_module_search_paths; ++i) { Log *log_verbose = GetLog(LLDBLog::Host); LLDB_LOGF( log_verbose, "PlatformRemoteDarwinDevice::GetSharedModule searching for binary in " "search-path %s", - module_search_paths_ptr->GetFileSpecAtIndex(i).GetPath().c_str()); + module_search_paths.GetFileSpecAtIndex(i).GetPath().c_str()); // Create a new FileSpec with this module_search_paths_ptr plus just the // filename ("UIFoundation"), then the parent dir plus filename // ("UIFoundation.framework/UIFoundation") etc - up to four names (to // handle "Foo.framework/Contents/MacOS/Foo") for (size_t j = 0; j < 4 && j < path_parts_size - 1; ++j) { - FileSpec path_to_try(module_search_paths_ptr->GetFileSpecAtIndex(i)); + FileSpec path_to_try(module_search_paths.GetFileSpecAtIndex(i)); // Add the components backwards. For // .../PrivateFrameworks/UIFoundation.framework/UIFoundation path_parts @@ -1356,9 +1361,9 @@ lldb_private::Status PlatformDarwin::FindBundleBinaryInExecSearchPaths( if (FileSystem::Instance().Exists(path_to_try)) { ModuleSpec new_module_spec(module_spec); new_module_spec.GetFileSpec() = path_to_try; - Status new_error( - Platform::GetSharedModule(new_module_spec, process, module_sp, - nullptr, old_modules, did_create_ptr)); + Status new_error(Platform::GetSharedModule(new_module_spec, process, + module_sp, old_modules, + did_create_ptr)); if (module_sp) { module_sp->SetPlatformFileSpec(path_to_try); diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h index f8a62ce..82e69e3 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h @@ -73,7 +73,6 @@ public: Status GetSharedModule(const ModuleSpec &module_spec, Process *process, lldb::ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr) override; @@ -189,7 +188,7 @@ protected: Status FindBundleBinaryInExecSearchPaths( const ModuleSpec &module_spec, Process *process, - lldb::ModuleSP &module_sp, const FileSpecList *module_search_paths_ptr, + lldb::ModuleSP &module_sp, llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr); // The OSType where lldb is running. diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinDevice.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinDevice.cpp index 68ef817..a72d94e 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinDevice.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinDevice.cpp @@ -295,7 +295,6 @@ BringInRemoteFile(Platform *platform, lldb_private::Status PlatformDarwinDevice::GetSharedModuleWithLocalCache( const lldb_private::ModuleSpec &module_spec, lldb::ModuleSP &module_sp, - const lldb_private::FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr) { Log *log = GetLog(LLDBLog::Platform); @@ -329,8 +328,7 @@ lldb_private::Status PlatformDarwinDevice::GetSharedModuleWithLocalCache( ModuleSpec shared_cache_spec(module_spec.GetFileSpec(), image_info.uuid, image_info.data_sp); err = ModuleList::GetSharedModule(shared_cache_spec, module_sp, - module_search_paths_ptr, old_modules, - did_create_ptr); + old_modules, did_create_ptr); if (module_sp) { LLDB_LOGF(log, "[%s] module %s was found in the in-memory shared cache", (IsHost() ? "host" : "remote"), @@ -348,8 +346,7 @@ lldb_private::Status PlatformDarwinDevice::GetSharedModuleWithLocalCache( FileSystem::Instance().Resolve(device_support_spec); if (FileSystem::Instance().Exists(device_support_spec)) { ModuleSpec local_spec(device_support_spec, module_spec.GetUUID()); - err = ModuleList::GetSharedModule(local_spec, module_sp, - module_search_paths_ptr, old_modules, + err = ModuleList::GetSharedModule(local_spec, module_sp, old_modules, did_create_ptr); if (module_sp) { LLDB_LOGF(log, @@ -363,8 +360,7 @@ lldb_private::Status PlatformDarwinDevice::GetSharedModuleWithLocalCache( } } - err = ModuleList::GetSharedModule(module_spec, module_sp, - module_search_paths_ptr, old_modules, + err = ModuleList::GetSharedModule(module_spec, module_sp, old_modules, did_create_ptr); if (module_sp) return err; diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinDevice.h b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinDevice.h index e1eba08f..e0142ab 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinDevice.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinDevice.h @@ -26,7 +26,6 @@ public: protected: virtual Status GetSharedModuleWithLocalCache( const ModuleSpec &module_spec, lldb::ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr); struct SDKDirectoryInfo { diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp index 07c5a52..04e87b9d 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp @@ -719,7 +719,6 @@ void PlatformDarwinKernel::UpdateKextandKernelsLocalScan() { Status PlatformDarwinKernel::GetSharedModule( const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<ModuleSP> *old_modules, bool *did_create_ptr) { Status error; module_sp.reset(); @@ -734,14 +733,12 @@ Status PlatformDarwinKernel::GetSharedModule( // UUID search can get here with no name - and it may be a kernel. if (kext_bundle_id == "mach_kernel" || kext_bundle_id.empty()) { error = GetSharedModuleKernel(module_spec, process, module_sp, - module_search_paths_ptr, old_modules, - did_create_ptr); + old_modules, did_create_ptr); if (error.Success() && module_sp) { return error; } } else { - return GetSharedModuleKext(module_spec, process, module_sp, - module_search_paths_ptr, old_modules, + return GetSharedModuleKext(module_spec, process, module_sp, old_modules, did_create_ptr); } } @@ -749,13 +746,11 @@ Status PlatformDarwinKernel::GetSharedModule( // Give the generic methods, including possibly calling into DebugSymbols // framework on macOS systems, a chance. return PlatformDarwin::GetSharedModule(module_spec, process, module_sp, - module_search_paths_ptr, old_modules, - did_create_ptr); + old_modules, did_create_ptr); } Status PlatformDarwinKernel::GetSharedModuleKext( const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<ModuleSP> *old_modules, bool *did_create_ptr) { Status error; module_sp.reset(); @@ -782,8 +777,7 @@ Status PlatformDarwinKernel::GetSharedModuleKext( // Give the generic methods, including possibly calling into DebugSymbols // framework on macOS systems, a chance. error = PlatformDarwin::GetSharedModule(module_spec, process, module_sp, - module_search_paths_ptr, old_modules, - did_create_ptr); + old_modules, did_create_ptr); if (error.Success() && module_sp.get()) { return error; } @@ -793,7 +787,6 @@ Status PlatformDarwinKernel::GetSharedModuleKext( Status PlatformDarwinKernel::GetSharedModuleKernel( const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<ModuleSP> *old_modules, bool *did_create_ptr) { assert(module_sp.get() == nullptr); UpdateKextandKernelsLocalScan(); @@ -848,8 +841,7 @@ Status PlatformDarwinKernel::GetSharedModuleKernel( // Give the generic methods, including possibly calling into DebugSymbols // framework on macOS systems, a chance. return PlatformDarwin::GetSharedModule(module_spec, process, module_sp, - module_search_paths_ptr, old_modules, - did_create_ptr); + old_modules, did_create_ptr); } std::vector<lldb_private::FileSpec> @@ -888,8 +880,8 @@ Status PlatformDarwinKernel::ExamineKextForMatchingUUID( ModuleSP module_sp(new Module(exe_spec)); if (module_sp && module_sp->GetObjectFile() && module_sp->MatchesModuleSpec(exe_spec)) { - Status error = ModuleList::GetSharedModule(exe_spec, exe_module_sp, - NULL, NULL, NULL); + Status error = + ModuleList::GetSharedModule(exe_spec, exe_module_sp, NULL, NULL); if (exe_module_sp && exe_module_sp->GetObjectFile()) { return error; } diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h index 9db9c00..b5cf701 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h @@ -60,7 +60,6 @@ public: Status GetSharedModule(const ModuleSpec &module_spec, Process *process, lldb::ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr) override; @@ -142,14 +141,14 @@ protected: Status GetSharedModuleKext(const ModuleSpec &module_spec, Process *process, lldb::ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr); - Status GetSharedModuleKernel( - const ModuleSpec &module_spec, Process *process, - lldb::ModuleSP &module_sp, const FileSpecList *module_search_paths_ptr, - llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr); + Status + GetSharedModuleKernel(const ModuleSpec &module_spec, Process *process, + lldb::ModuleSP &module_sp, + llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, + bool *did_create_ptr); Status ExamineKextForMatchingUUID(const FileSpec &kext_bundle_path, const UUID &uuid, const ArchSpec &arch, diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp index dad6dcd..e6ea75a 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.cpp @@ -182,10 +182,8 @@ PlatformMacOSX::GetSupportedArchitectures(const ArchSpec &process_host_arch) { lldb_private::Status PlatformMacOSX::GetSharedModule( const lldb_private::ModuleSpec &module_spec, Process *process, lldb::ModuleSP &module_sp, - const lldb_private::FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr) { Status error = GetSharedModuleWithLocalCache(module_spec, module_sp, - module_search_paths_ptr, old_modules, did_create_ptr); if (module_sp) { @@ -199,9 +197,9 @@ lldb_private::Status PlatformMacOSX::GetSharedModule( lldb::ModuleSP x86_64_module_sp; llvm::SmallVector<lldb::ModuleSP, 1> old_x86_64_modules; bool did_create = false; - Status x86_64_error = GetSharedModuleWithLocalCache( - module_spec_x86_64, x86_64_module_sp, module_search_paths_ptr, - &old_x86_64_modules, &did_create); + Status x86_64_error = + GetSharedModuleWithLocalCache(module_spec_x86_64, x86_64_module_sp, + &old_x86_64_modules, &did_create); if (x86_64_module_sp && x86_64_module_sp->GetObjectFile()) { module_sp = x86_64_module_sp; if (old_modules) @@ -217,7 +215,6 @@ lldb_private::Status PlatformMacOSX::GetSharedModule( if (!module_sp) { error = FindBundleBinaryInExecSearchPaths(module_spec, process, module_sp, - module_search_paths_ptr, old_modules, did_create_ptr); } return error; diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.h b/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.h index be84485..9555b16 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformMacOSX.h @@ -48,7 +48,6 @@ public: Status GetSharedModule(const ModuleSpec &module_spec, Process *process, lldb::ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr) override; diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp index b83d07b..53fab93 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp @@ -53,7 +53,7 @@ void PlatformRemoteDarwinDevice::GetStatus(Stream &strm) { if (sdk_directory) strm.Printf(" SDK Path: \"%s\"\n", sdk_directory); else - strm.PutCString(" SDK Path: error: unable to locate SDK\n"); + strm.PutCString(" SDK Path: <unable to locate SDK>\n"); const uint32_t num_sdk_infos = m_sdk_directory_infos.size(); for (uint32_t i = 0; i < num_sdk_infos; ++i) { @@ -158,7 +158,6 @@ Status PlatformRemoteDarwinDevice::GetSymbolFile(const FileSpec &platform_file, Status PlatformRemoteDarwinDevice::GetSharedModule( const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<ModuleSP> *old_modules, bool *did_create_ptr) { // For iOS, the SDK files are all cached locally on the host system. So first // we ask for the file in the cached SDK, then we attempt to get a shared @@ -185,7 +184,7 @@ Status PlatformRemoteDarwinDevice::GetSharedModule( if (GetFileInSDK(platform_file_path, connected_sdk_idx, platform_module_spec.GetFileSpec())) { module_sp.reset(); - error = ResolveExecutable(platform_module_spec, module_sp, nullptr); + error = ResolveExecutable(platform_module_spec, module_sp); if (module_sp) { m_last_module_sdk_idx = connected_sdk_idx; error.Clear(); @@ -202,7 +201,7 @@ Status PlatformRemoteDarwinDevice::GetSharedModule( if (GetFileInSDK(platform_file_path, m_last_module_sdk_idx, platform_module_spec.GetFileSpec())) { module_sp.reset(); - error = ResolveExecutable(platform_module_spec, module_sp, nullptr); + error = ResolveExecutable(platform_module_spec, module_sp); if (module_sp) { error.Clear(); return error; @@ -224,7 +223,7 @@ Status PlatformRemoteDarwinDevice::GetSharedModule( if (GetFileInSDK(platform_file_path, current_sdk_idx, platform_module_spec.GetFileSpec())) { module_sp.reset(); - error = ResolveExecutable(platform_module_spec, module_sp, nullptr); + error = ResolveExecutable(platform_module_spec, module_sp); if (module_sp) { m_last_module_sdk_idx = current_sdk_idx; error.Clear(); @@ -245,7 +244,7 @@ Status PlatformRemoteDarwinDevice::GetSharedModule( platform_module_spec.GetFileSpec())) { // printf ("sdk[%u]: '%s'\n", sdk_idx, local_file.GetPath().c_str()); - error = ResolveExecutable(platform_module_spec, module_sp, nullptr); + error = ResolveExecutable(platform_module_spec, module_sp); if (module_sp) { // Remember the index of the last SDK that we found a file in in case // the wrong SDK was selected. @@ -261,8 +260,7 @@ Status PlatformRemoteDarwinDevice::GetSharedModule( // This may not be an SDK-related module. Try whether we can bring in the // thing to our local cache. - error = GetSharedModuleWithLocalCache(module_spec, module_sp, - module_search_paths_ptr, old_modules, + error = GetSharedModuleWithLocalCache(module_spec, module_sp, old_modules, did_create_ptr); if (error.Success()) return error; @@ -271,15 +269,13 @@ Status PlatformRemoteDarwinDevice::GetSharedModule( // directories. if (!module_sp) error = PlatformDarwin::FindBundleBinaryInExecSearchPaths( - module_spec, process, module_sp, module_search_paths_ptr, old_modules, - did_create_ptr); + module_spec, process, module_sp, old_modules, did_create_ptr); if (error.Success()) return error; const bool always_create = false; - error = ModuleList::GetSharedModule(module_spec, module_sp, - module_search_paths_ptr, old_modules, + error = ModuleList::GetSharedModule(module_spec, module_sp, old_modules, did_create_ptr, always_create); if (module_sp) diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.h b/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.h index 557f487..4abd74e 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.h @@ -47,7 +47,6 @@ public: Status GetSharedModule(const ModuleSpec &module_spec, Process *process, lldb::ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr) override; diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index 0fecefe..4cc39f9 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -79,8 +79,10 @@ namespace lldb_private { ProcessSP ProcessWindows::CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp, - const FileSpec *, + const FileSpec *crash_file_path, bool can_connect) { + if (crash_file_path) + return nullptr; // Cannot create a Windows process from a crash_file. return ProcessSP(new ProcessWindows(target_sp, listener_sp)); } diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp index b7029fb..f8e33ea 100644 --- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp +++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp @@ -84,8 +84,9 @@ bool ProcessElfCore::CanDebug(lldb::TargetSP target_sp, // For now we are just making sure the file exists for a given module if (!m_core_module_sp && FileSystem::Instance().Exists(m_core_file)) { ModuleSpec core_module_spec(m_core_file, target_sp->GetArchitecture()); + core_module_spec.SetTarget(target_sp); Status error(ModuleList::GetSharedModule(core_module_spec, m_core_module_sp, - nullptr, nullptr, nullptr)); + nullptr, nullptr)); if (m_core_module_sp) { ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); if (core_objfile && core_objfile->GetType() == ObjectFile::eTypeCoreFile) diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 3c4d9a1..cde68b5 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -210,11 +210,9 @@ void ProcessGDBRemote::Terminate() { lldb::ProcessSP ProcessGDBRemote::CreateInstance( lldb::TargetSP target_sp, ListenerSP listener_sp, const FileSpec *crash_file_path, bool can_connect) { - lldb::ProcessSP process_sp; - if (crash_file_path == nullptr) - process_sp = std::shared_ptr<ProcessGDBRemote>( - new ProcessGDBRemote(target_sp, listener_sp)); - return process_sp; + if (crash_file_path) + return nullptr; // Cannot create a GDBRemote process from a crash_file. + return lldb::ProcessSP(new ProcessGDBRemote(target_sp, listener_sp)); } void ProcessGDBRemote::DumpPluginHistory(Stream &s) { @@ -441,8 +439,16 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { if (!arch_to_use.IsValid()) arch_to_use = target_arch; - if (GetGDBServerRegisterInfo(arch_to_use)) + llvm::Error register_info_err = GetGDBServerRegisterInfo(arch_to_use); + if (!register_info_err) { + // We got the registers from target XML. return; + } + + Log *log = GetLog(GDBRLog::Process); + LLDB_LOG_ERROR(log, std::move(register_info_err), + "Failed to read register information from target XML: {0}"); + LLDB_LOG(log, "Now trying to use qRegisterInfo instead."); char packet[128]; std::vector<DynamicRegisterInfo::Register> registers; @@ -541,7 +547,23 @@ void ProcessGDBRemote::BuildDynamicRegisterInfo(bool force) { assert(reg_info.byte_size != 0); registers.push_back(reg_info); } else { - break; // ensure exit before reg_num is incremented + // Only warn if we were offered Target XML and could not use it, and + // the qRegisterInfo fallback failed. This is something a user could + // take action on by getting an lldb with libxml2. + // + // It's possible we weren't offered Target XML and qRegisterInfo failed, + // but there's no much a user can do about that. It may be the intended + // way the debug stub works, so we do not warn for that case. + if (response_type == StringExtractorGDBRemote::eUnsupported && + m_gdb_comm.GetQXferFeaturesReadSupported() && + !XMLDocument::XMLEnabled()) { + Debugger::ReportWarning( + "the debug server supports Target Description XML but LLDB does " + "not have XML parsing enabled. Using \"qRegisterInfo\" was also " + "not possible. Register information may be incorrect or missing.", + GetTarget().GetDebugger().GetID()); + } + break; } } else { break; @@ -5137,14 +5159,19 @@ void ProcessGDBRemote::AddRemoteRegisters( // query the target of gdb-remote for extended target information returns // true on success (got register definitions), false on failure (did not). -bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { - // Make sure LLDB has an XML parser it can use first - if (!XMLDocument::XMLEnabled()) - return false; - - // check that we have extended feature read support +llvm::Error ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { + // If the remote does not offer XML, does not matter if we would have been + // able to parse it. if (!m_gdb_comm.GetQXferFeaturesReadSupported()) - return false; + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "the debug server does not support \"qXfer:features:read\""); + + if (!XMLDocument::XMLEnabled()) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "the debug server supports \"qXfer:features:read\", but LLDB does not " + "have XML parsing enabled (check LLLDB_ENABLE_LIBXML2)"); // These hold register type information for the whole of target.xml. // target.xml may include further documents that @@ -5161,7 +5188,11 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { !registers.empty()) AddRemoteRegisters(registers, arch_to_use); - return m_register_info_sp->GetNumRegisters() > 0; + return m_register_info_sp->GetNumRegisters() > 0 + ? llvm::ErrorSuccess() + : llvm::createStringError( + llvm::inconvertibleErrorCode(), + "the debug server did not describe any registers"); } llvm::Expected<LoadedModuleInfoList> ProcessGDBRemote::GetLoadedModuleList() { diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index eb33b52..b7e8777 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -416,7 +416,7 @@ protected: void AddRemoteRegisters(std::vector<DynamicRegisterInfo::Register> ®isters, const ArchSpec &arch_to_use); // Query remote GDBServer for register information - bool GetGDBServerRegisterInfo(ArchSpec &arch); + llvm::Error GetGDBServerRegisterInfo(ArchSpec &arch); lldb::ModuleSP LoadModuleAtAddress(const FileSpec &file, lldb::addr_t link_map, diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp index a780b3f..83d684e 100644 --- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp +++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp @@ -95,8 +95,9 @@ bool ProcessMachCore::CanDebug(lldb::TargetSP target_sp, // header but we should still try to use it - // ModuleSpecList::FindMatchingModuleSpec enforces a strict arch mach. ModuleSpec core_module_spec(m_core_file); + core_module_spec.SetTarget(target_sp); Status error(ModuleList::GetSharedModule(core_module_spec, m_core_module_sp, - nullptr, nullptr, nullptr)); + nullptr, nullptr)); if (m_core_module_sp) { ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp index 6519df9..70ce101 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp @@ -7,42 +7,77 @@ //===----------------------------------------------------------------------===// #include "ScriptedFrame.h" - +#include "Plugins/Process/Utility/RegisterContextMemory.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h" +#include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolFile.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" #include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StructuredData.h" using namespace lldb; using namespace lldb_private; +char ScriptedFrame::ID; + void ScriptedFrame::CheckInterpreterAndScriptObject() const { lldbassert(m_script_object_sp && "Invalid Script Object."); lldbassert(GetInterface() && "Invalid Scripted Frame Interface."); } llvm::Expected<std::shared_ptr<ScriptedFrame>> -ScriptedFrame::Create(ScriptedThread &thread, +ScriptedFrame::Create(ThreadSP thread_sp, + ScriptedThreadInterfaceSP scripted_thread_interface_sp, StructuredData::DictionarySP args_sp, StructuredData::Generic *script_object) { - if (!thread.IsValid()) - return llvm::createStringError("Invalid scripted thread."); + if (!thread_sp || !thread_sp->IsValid()) + return llvm::createStringError("invalid thread"); + + ProcessSP process_sp = thread_sp->GetProcess(); + if (!process_sp || !process_sp->IsValid()) + return llvm::createStringError("invalid process"); - thread.CheckInterpreterAndScriptObject(); + ScriptInterpreter *script_interp = + process_sp->GetTarget().GetDebugger().GetScriptInterpreter(); + if (!script_interp) + return llvm::createStringError("no script interpreter"); - auto scripted_frame_interface = - thread.GetInterface()->CreateScriptedFrameInterface(); + auto scripted_frame_interface = script_interp->CreateScriptedFrameInterface(); if (!scripted_frame_interface) return llvm::createStringError("failed to create scripted frame interface"); llvm::StringRef frame_class_name; if (!script_object) { - std::optional<std::string> class_name = - thread.GetInterface()->GetScriptedFramePluginName(); - if (!class_name || class_name->empty()) + // If no script object is provided and we have a scripted thread interface, + // try to get the frame class name from it. + if (scripted_thread_interface_sp) { + std::optional<std::string> class_name = + scripted_thread_interface_sp->GetScriptedFramePluginName(); + if (!class_name || class_name->empty()) + return llvm::createStringError( + "failed to get scripted frame class name"); + frame_class_name = *class_name; + } else { return llvm::createStringError( - "failed to get scripted thread class name"); - frame_class_name = *class_name; + "no script object provided and no scripted thread interface"); + } } - ExecutionContext exe_ctx(thread); + ExecutionContext exe_ctx(thread_sp); auto obj_or_err = scripted_frame_interface->CreatePluginObject( frame_class_name, exe_ctx, args_sp, script_object); @@ -62,66 +97,43 @@ ScriptedFrame::Create(ScriptedThread &thread, SymbolContext sc; Address symbol_addr; if (pc != LLDB_INVALID_ADDRESS) { - symbol_addr.SetLoadAddress(pc, &thread.GetProcess()->GetTarget()); + symbol_addr.SetLoadAddress(pc, &process_sp->GetTarget()); symbol_addr.CalculateSymbolContext(&sc); } std::optional<SymbolContext> maybe_sym_ctx = scripted_frame_interface->GetSymbolContext(); - if (maybe_sym_ctx) { + if (maybe_sym_ctx) sc = *maybe_sym_ctx; - } - - StructuredData::DictionarySP reg_info = - scripted_frame_interface->GetRegisterInfo(); - - if (!reg_info) - return llvm::createStringError( - "failed to get scripted thread registers info"); - - std::shared_ptr<DynamicRegisterInfo> register_info_sp = - DynamicRegisterInfo::Create( - *reg_info, thread.GetProcess()->GetTarget().GetArchitecture()); lldb::RegisterContextSP reg_ctx_sp; - - std::optional<std::string> reg_data = - scripted_frame_interface->GetRegisterContext(); - if (reg_data) { - DataBufferSP data_sp( - std::make_shared<DataBufferHeap>(reg_data->c_str(), reg_data->size())); - - if (!data_sp->GetByteSize()) - return llvm::createStringError("failed to copy raw registers data"); - - std::shared_ptr<RegisterContextMemory> reg_ctx_memory = - std::make_shared<RegisterContextMemory>( - thread, frame_id, *register_info_sp, LLDB_INVALID_ADDRESS); - if (!reg_ctx_memory) - return llvm::createStringError("failed to create a register context."); - - reg_ctx_memory->SetAllRegisterData(data_sp); - reg_ctx_sp = reg_ctx_memory; - } - - return std::make_shared<ScriptedFrame>( - thread, scripted_frame_interface, frame_id, pc, sc, reg_ctx_sp, - register_info_sp, owned_script_object_sp); + auto regs_or_err = + CreateRegisterContext(*scripted_frame_interface, *thread_sp, frame_id); + if (!regs_or_err) + LLDB_LOG_ERROR(GetLog(LLDBLog::Thread), regs_or_err.takeError(), "{0}"); + else + reg_ctx_sp = *regs_or_err; + + return std::make_shared<ScriptedFrame>(thread_sp, scripted_frame_interface, + frame_id, pc, sc, reg_ctx_sp, + owned_script_object_sp); } -ScriptedFrame::ScriptedFrame(ScriptedThread &thread, +ScriptedFrame::ScriptedFrame(ThreadSP thread_sp, ScriptedFrameInterfaceSP interface_sp, lldb::user_id_t id, lldb::addr_t pc, SymbolContext &sym_ctx, lldb::RegisterContextSP reg_ctx_sp, - std::shared_ptr<DynamicRegisterInfo> reg_info_sp, StructuredData::GenericSP script_object_sp) - : StackFrame(thread.shared_from_this(), /*frame_idx=*/id, + : StackFrame(thread_sp, /*frame_idx=*/id, /*concrete_frame_idx=*/id, /*reg_context_sp=*/reg_ctx_sp, /*cfa=*/0, /*pc=*/pc, /*behaves_like_zeroth_frame=*/!id, /*symbol_ctx=*/&sym_ctx), m_scripted_frame_interface_sp(interface_sp), - m_script_object_sp(script_object_sp), m_register_info_sp(reg_info_sp) {} + m_script_object_sp(script_object_sp) { + // FIXME: This should be part of the base class constructor. + m_stack_frame_kind = StackFrame::Kind::Synthetic; +} ScriptedFrame::~ScriptedFrame() {} @@ -129,7 +141,7 @@ const char *ScriptedFrame::GetFunctionName() { CheckInterpreterAndScriptObject(); std::optional<std::string> function_name = GetInterface()->GetFunctionName(); if (!function_name) - return nullptr; + return StackFrame::GetFunctionName(); return ConstString(function_name->c_str()).AsCString(); } @@ -138,7 +150,7 @@ const char *ScriptedFrame::GetDisplayFunctionName() { std::optional<std::string> function_name = GetInterface()->GetDisplayFunctionName(); if (!function_name) - return nullptr; + return StackFrame::GetDisplayFunctionName(); return ConstString(function_name->c_str()).AsCString(); } @@ -157,35 +169,99 @@ lldb::ScriptedFrameInterfaceSP ScriptedFrame::GetInterface() const { std::shared_ptr<DynamicRegisterInfo> ScriptedFrame::GetDynamicRegisterInfo() { CheckInterpreterAndScriptObject(); - if (!m_register_info_sp) { - StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo(); + StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo(); + + Status error; + if (!reg_info) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr<DynamicRegisterInfo>>( + LLVM_PRETTY_FUNCTION, "failed to get scripted frame registers info", + error, LLDBLog::Thread); + + ThreadSP thread_sp = m_thread_wp.lock(); + if (!thread_sp || !thread_sp->IsValid()) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr<DynamicRegisterInfo>>( + LLVM_PRETTY_FUNCTION, + "failed to get scripted frame registers info: invalid thread", error, + LLDBLog::Thread); + + ProcessSP process_sp = thread_sp->GetProcess(); + if (!process_sp || !process_sp->IsValid()) + return ScriptedInterface::ErrorWithMessage< + std::shared_ptr<DynamicRegisterInfo>>( + LLVM_PRETTY_FUNCTION, + "failed to get scripted frame registers info: invalid process", error, + LLDBLog::Thread); + + return DynamicRegisterInfo::Create(*reg_info, + process_sp->GetTarget().GetArchitecture()); +} + +llvm::Expected<lldb::RegisterContextSP> +ScriptedFrame::CreateRegisterContext(ScriptedFrameInterface &interface, + Thread &thread, lldb::user_id_t frame_id) { + StructuredData::DictionarySP reg_info = interface.GetRegisterInfo(); + if (!reg_info) + return llvm::createStringError( + "failed to get scripted frame registers info"); + + std::shared_ptr<DynamicRegisterInfo> register_info_sp = + DynamicRegisterInfo::Create( + *reg_info, thread.GetProcess()->GetTarget().GetArchitecture()); + + lldb::RegisterContextSP reg_ctx_sp; + + std::optional<std::string> reg_data = interface.GetRegisterContext(); + if (!reg_data) + return llvm::createStringError( + "failed to get scripted frame registers data"); + + DataBufferSP data_sp( + std::make_shared<DataBufferHeap>(reg_data->c_str(), reg_data->size())); + + if (!data_sp->GetByteSize()) + return llvm::createStringError("failed to copy raw registers data"); + + std::shared_ptr<RegisterContextMemory> reg_ctx_memory = + std::make_shared<RegisterContextMemory>( + thread, frame_id, *register_info_sp, LLDB_INVALID_ADDRESS); + + reg_ctx_memory->SetAllRegisterData(data_sp); + reg_ctx_sp = reg_ctx_memory; + + return reg_ctx_sp; +} + +lldb::RegisterContextSP ScriptedFrame::GetRegisterContext() { + if (!m_reg_context_sp) { Status error; - if (!reg_info) - return ScriptedInterface::ErrorWithMessage< - std::shared_ptr<DynamicRegisterInfo>>( - LLVM_PRETTY_FUNCTION, "Failed to get scripted frame registers info.", + if (!m_scripted_frame_interface_sp) + return ScriptedInterface::ErrorWithMessage<RegisterContextSP>( + LLVM_PRETTY_FUNCTION, + "failed to get scripted frame registers context: invalid interface", error, LLDBLog::Thread); - ThreadSP thread_sp = m_thread_wp.lock(); - if (!thread_sp || !thread_sp->IsValid()) - return ScriptedInterface::ErrorWithMessage< - std::shared_ptr<DynamicRegisterInfo>>( + ThreadSP thread_sp = GetThread(); + if (!thread_sp) + return ScriptedInterface::ErrorWithMessage<RegisterContextSP>( LLVM_PRETTY_FUNCTION, - "Failed to get scripted frame registers info: invalid thread.", error, - LLDBLog::Thread); + "failed to get scripted frame registers context: invalid thread", + error, LLDBLog::Thread); - ProcessSP process_sp = thread_sp->GetProcess(); - if (!process_sp || !process_sp->IsValid()) - return ScriptedInterface::ErrorWithMessage< - std::shared_ptr<DynamicRegisterInfo>>( + auto regs_or_err = CreateRegisterContext(*m_scripted_frame_interface_sp, + *thread_sp, GetFrameIndex()); + if (!regs_or_err) { + error = Status::FromError(regs_or_err.takeError()); + return ScriptedInterface::ErrorWithMessage<RegisterContextSP>( LLVM_PRETTY_FUNCTION, - "Failed to get scripted frame registers info: invalid process.", - error, LLDBLog::Thread); + "failed to get scripted frame registers context", error, + LLDBLog::Thread); + } - m_register_info_sp = DynamicRegisterInfo::Create( - *reg_info, process_sp->GetTarget().GetArchitecture()); + m_reg_context_sp = *regs_or_err; } - return m_register_info_sp; + return m_reg_context_sp; } diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.h b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h index 6e01e2f..0545548 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedFrame.h +++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h @@ -9,33 +9,50 @@ #ifndef LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H #define LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H -#include "Plugins/Process/Utility/RegisterContextMemory.h" #include "ScriptedThread.h" -#include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Target/DynamicRegisterInfo.h" #include "lldb/Target/StackFrame.h" +#include "lldb/lldb-forward.h" +#include "llvm/Support/Error.h" +#include <memory> #include <string> namespace lldb_private { -class ScriptedThread; -} - -namespace lldb_private { class ScriptedFrame : public lldb_private::StackFrame { public: - ScriptedFrame(ScriptedThread &thread, + ScriptedFrame(lldb::ThreadSP thread_sp, lldb::ScriptedFrameInterfaceSP interface_sp, lldb::user_id_t frame_idx, lldb::addr_t pc, SymbolContext &sym_ctx, lldb::RegisterContextSP reg_ctx_sp, - std::shared_ptr<DynamicRegisterInfo> reg_info_sp, StructuredData::GenericSP script_object_sp = nullptr); ~ScriptedFrame() override; + /// Create a ScriptedFrame from a object instanciated in the script + /// interpreter. + /// + /// \param[in] thread_sp + /// The thread this frame belongs to. + /// + /// \param[in] scripted_thread_interface_sp + /// The scripted thread interface (needed for ScriptedThread + /// compatibility). Can be nullptr for frames on real threads. + /// + /// \param[in] args_sp + /// Arguments to pass to the frame creation. + /// + /// \param[in] script_object + /// The optional script object representing this frame. + /// + /// \return + /// An Expected containing the ScriptedFrame shared pointer if successful, + /// otherwise an error. static llvm::Expected<std::shared_ptr<ScriptedFrame>> - Create(ScriptedThread &thread, StructuredData::DictionarySP args_sp, + Create(lldb::ThreadSP thread_sp, + lldb::ScriptedThreadInterfaceSP scripted_thread_interface_sp, + StructuredData::DictionarySP args_sp, StructuredData::Generic *script_object = nullptr); bool IsInlined() override; @@ -44,9 +61,19 @@ public: const char *GetFunctionName() override; const char *GetDisplayFunctionName() override; + lldb::RegisterContextSP GetRegisterContext() override; + + bool isA(const void *ClassID) const override { + return ClassID == &ID || StackFrame::isA(ClassID); + } + static bool classof(const StackFrame *obj) { return obj->isA(&ID); } + private: void CheckInterpreterAndScriptObject() const; lldb::ScriptedFrameInterfaceSP GetInterface() const; + static llvm::Expected<lldb::RegisterContextSP> + CreateRegisterContext(ScriptedFrameInterface &interface, Thread &thread, + lldb::user_id_t frame_id); ScriptedFrame(const ScriptedFrame &) = delete; const ScriptedFrame &operator=(const ScriptedFrame &) = delete; @@ -55,7 +82,8 @@ private: lldb::ScriptedFrameInterfaceSP m_scripted_frame_interface_sp; lldb_private::StructuredData::GenericSP m_script_object_sp; - std::shared_ptr<DynamicRegisterInfo> m_register_info_sp; + + static char ID; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp index 491efac..1dd9c48 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp @@ -210,7 +210,7 @@ bool ScriptedThread::LoadArtificialStackFrames() { SymbolContext sc; symbol_addr.CalculateSymbolContext(&sc); - return std::make_shared<StackFrame>(this->shared_from_this(), idx, idx, cfa, + return std::make_shared<StackFrame>(shared_from_this(), idx, idx, cfa, cfa_is_valid, pc, StackFrame::Kind::Synthetic, artificial, behaves_like_zeroth_frame, &sc); @@ -231,8 +231,8 @@ bool ScriptedThread::LoadArtificialStackFrames() { return error.ToError(); } - auto frame_or_error = - ScriptedFrame::Create(*this, nullptr, object_sp->GetAsGeneric()); + auto frame_or_error = ScriptedFrame::Create( + shared_from_this(), GetInterface(), nullptr, object_sp->GetAsGeneric()); if (!frame_or_error) { ScriptedInterface::ErrorWithMessage<bool>( diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt index 0910357..50569cd 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt @@ -23,6 +23,7 @@ add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN OperatingSystemPythonInterface.cpp ScriptInterpreterPythonInterfaces.cpp ScriptedFramePythonInterface.cpp + ScriptedFrameProviderPythonInterface.cpp ScriptedPlatformPythonInterface.cpp ScriptedProcessPythonInterface.cpp ScriptedPythonInterface.cpp diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp index d43036d..f6c707b 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp @@ -31,6 +31,7 @@ void ScriptInterpreterPythonInterfaces::Initialize() { ScriptedStopHookPythonInterface::Initialize(); ScriptedBreakpointPythonInterface::Initialize(); ScriptedThreadPlanPythonInterface::Initialize(); + ScriptedFrameProviderPythonInterface::Initialize(); } void ScriptInterpreterPythonInterfaces::Terminate() { @@ -40,6 +41,7 @@ void ScriptInterpreterPythonInterfaces::Terminate() { ScriptedStopHookPythonInterface::Terminate(); ScriptedBreakpointPythonInterface::Terminate(); ScriptedThreadPlanPythonInterface::Terminate(); + ScriptedFrameProviderPythonInterface::Terminate(); } #endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h index 3814f46..b2a3479 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h @@ -17,6 +17,7 @@ #include "OperatingSystemPythonInterface.h" #include "ScriptedBreakpointPythonInterface.h" +#include "ScriptedFrameProviderPythonInterface.h" #include "ScriptedFramePythonInterface.h" #include "ScriptedPlatformPythonInterface.h" #include "ScriptedProcessPythonInterface.h" diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp new file mode 100644 index 0000000..3dde503 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.cpp @@ -0,0 +1,113 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/Config.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Log.h" +#include "lldb/lldb-enumerations.h" + +#if LLDB_ENABLE_PYTHON + +// LLDB Python header must be included first +#include "../lldb-python.h" + +#include "../SWIGPythonBridge.h" +#include "../ScriptInterpreterPythonImpl.h" +#include "ScriptedFrameProviderPythonInterface.h" +#include <optional> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::python; +using Locker = ScriptInterpreterPythonImpl::Locker; + +ScriptedFrameProviderPythonInterface::ScriptedFrameProviderPythonInterface( + ScriptInterpreterPythonImpl &interpreter) + : ScriptedFrameProviderInterface(), ScriptedPythonInterface(interpreter) {} + +bool ScriptedFrameProviderPythonInterface::AppliesToThread( + llvm::StringRef class_name, lldb::ThreadSP thread_sp) { + // If there is any issue with this method, we will just assume it also applies + // to this thread which is the default behavior. + constexpr bool fail_value = true; + Status error; + StructuredData::ObjectSP obj = + CallStaticMethod(class_name, "applies_to_thread", error, thread_sp); + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return fail_value; + + return obj->GetBooleanValue(fail_value); +} + +llvm::Expected<StructuredData::GenericSP> +ScriptedFrameProviderPythonInterface::CreatePluginObject( + const llvm::StringRef class_name, lldb::StackFrameListSP input_frames, + StructuredData::DictionarySP args_sp) { + if (!input_frames) + return llvm::createStringError("invalid frame list"); + + StructuredDataImpl sd_impl(args_sp); + return ScriptedPythonInterface::CreatePluginObject(class_name, nullptr, + input_frames, sd_impl); +} + +std::string ScriptedFrameProviderPythonInterface::GetDescription( + llvm::StringRef class_name) { + Status error; + StructuredData::ObjectSP obj = + CallStaticMethod(class_name, "get_description", error); + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj->GetStringValue().str(); +} + +StructuredData::ObjectSP +ScriptedFrameProviderPythonInterface::GetFrameAtIndex(uint32_t index) { + Status error; + StructuredData::ObjectSP obj = Dispatch("get_frame_at_index", error, index); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return {}; + + return obj; +} + +bool ScriptedFrameProviderPythonInterface::CreateInstance( + lldb::ScriptLanguage language, ScriptedInterfaceUsages usages) { + if (language != eScriptLanguagePython) + return false; + + return true; +} + +void ScriptedFrameProviderPythonInterface::Initialize() { + const std::vector<llvm::StringRef> ci_usages = { + "target frame-provider register -C <script-name> [-k key -v value ...]", + "target frame-provider list", + "target frame-provider remove <provider-name>", + "target frame-provider clear"}; + const std::vector<llvm::StringRef> api_usages = { + "SBTarget.RegisterScriptedFrameProvider", + "SBTarget.RemoveScriptedFrameProvider", + "SBTarget.ClearScriptedFrameProvider"}; + PluginManager::RegisterPlugin( + GetPluginNameStatic(), + llvm::StringRef("Provide scripted stack frames for threads"), + CreateInstance, eScriptLanguagePython, {ci_usages, api_usages}); +} + +void ScriptedFrameProviderPythonInterface::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +#endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h new file mode 100644 index 0000000..97a5cc7 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFrameProviderPythonInterface.h @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPROVIDERPYTHONINTERFACE_H +#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPROVIDERPYTHONINTERFACE_H + +#include "lldb/Host/Config.h" + +#if LLDB_ENABLE_PYTHON + +#include "ScriptedPythonInterface.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h" +#include <optional> + +namespace lldb_private { +class ScriptedFrameProviderPythonInterface + : public ScriptedFrameProviderInterface, + public ScriptedPythonInterface, + public PluginInterface { +public: + ScriptedFrameProviderPythonInterface( + ScriptInterpreterPythonImpl &interpreter); + + bool AppliesToThread(llvm::StringRef class_name, + lldb::ThreadSP thread_sp) override; + + llvm::Expected<StructuredData::GenericSP> + CreatePluginObject(llvm::StringRef class_name, + lldb::StackFrameListSP input_frames, + StructuredData::DictionarySP args_sp) override; + + llvm::SmallVector<AbstractMethodRequirement> + GetAbstractMethodRequirements() const override { + return llvm::SmallVector<AbstractMethodRequirement>( + {{"get_description"}, {"get_frame_at_index"}}); + } + + std::string GetDescription(llvm::StringRef class_name) override; + + StructuredData::ObjectSP GetFrameAtIndex(uint32_t index) override; + + static void Initialize(); + static void Terminate(); + + static bool CreateInstance(lldb::ScriptLanguage language, + ScriptedInterfaceUsages usages); + + static llvm::StringRef GetPluginNameStatic() { + return "ScriptedFrameProviderPythonInterface"; + } + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } +}; +} // namespace lldb_private + +#endif // LLDB_ENABLE_PYTHON +#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPROVIDERPYTHONINTERFACE_H diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp index 4fdf2b1..ba4473c 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp @@ -94,6 +94,19 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StackFrameSP>( } template <> +lldb::ThreadSP +ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ThreadSP>( + python::PythonObject &p, Status &error) { + if (lldb::SBThread *sb_thread = reinterpret_cast<lldb::SBThread *>( + python::LLDBSWIGPython_CastPyObjectToSBThread(p.get()))) + return m_interpreter.GetOpaqueTypeFromSBThread(*sb_thread); + error = Status::FromErrorString( + "Couldn't cast lldb::SBThread to lldb_private::Thread."); + + return nullptr; +} + +template <> SymbolContext ScriptedPythonInterface::ExtractValueFromPythonObject<SymbolContext>( python::PythonObject &p, Status &error) { @@ -243,4 +256,21 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DescriptionLevel>( return static_cast<lldb::DescriptionLevel>(unsigned_val); } +template <> +lldb::StackFrameListSP +ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StackFrameListSP>( + python::PythonObject &p, Status &error) { + + lldb::SBFrameList *sb_frame_list = reinterpret_cast<lldb::SBFrameList *>( + python::LLDBSWIGPython_CastPyObjectToSBFrameList(p.get())); + + if (!sb_frame_list) { + error = Status::FromErrorStringWithFormat( + "couldn't cast lldb::SBFrameList to lldb::StackFrameListSP."); + return {}; + } + + return m_interpreter.GetOpaqueTypeFromSBFrameList(*sb_frame_list); +} + #endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h index 2335b2e..b737f94 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h @@ -41,7 +41,7 @@ public: eValid }; - struct AbstrackMethodCheckerPayload { + struct AbstractMethodCheckerPayload { struct InvalidArgumentCountPayload { InvalidArgumentCountPayload(size_t required, size_t actual) @@ -55,13 +55,69 @@ public: std::variant<std::monostate, InvalidArgumentCountPayload> payload; }; - llvm::Expected<std::map<llvm::StringLiteral, AbstrackMethodCheckerPayload>> + llvm::Expected<FileSpec> GetScriptedModulePath() override { + using namespace python; + using Locker = ScriptInterpreterPythonImpl::Locker; + + Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, + Locker::FreeLock); + + if (!m_object_instance_sp) + return llvm::createStringError("scripted Interface has invalid object"); + + PythonObject py_obj = + PythonObject(PyRefType::Borrowed, + static_cast<PyObject *>(m_object_instance_sp->GetValue())); + + if (!py_obj.IsAllocated()) + return llvm::createStringError( + "scripted Interface has invalid python object"); + + PythonObject py_obj_class = py_obj.GetAttributeValue("__class__"); + if (!py_obj_class.IsValid()) + return llvm::createStringError( + "scripted Interface python object is missing '__class__' attribute"); + + PythonObject py_obj_module = py_obj_class.GetAttributeValue("__module__"); + if (!py_obj_module.IsValid()) + return llvm::createStringError( + "scripted Interface python object '__class__' is missing " + "'__module__' attribute"); + + PythonString py_obj_module_str = py_obj_module.Str(); + if (!py_obj_module_str.IsValid()) + return llvm::createStringError( + "scripted Interface python object '__class__.__module__' attribute " + "is not a string"); + + llvm::StringRef py_obj_module_str_ref = py_obj_module_str.GetString(); + PythonModule py_module = PythonModule::AddModule(py_obj_module_str_ref); + if (!py_module.IsValid()) + return llvm::createStringError("failed to import '%s' module", + py_obj_module_str_ref.data()); + + PythonObject py_module_file = py_module.GetAttributeValue("__file__"); + if (!py_module_file.IsValid()) + return llvm::createStringError( + "module '%s' is missing '__file__' attribute", + py_obj_module_str_ref.data()); + + PythonString py_module_file_str = py_module_file.Str(); + if (!py_module_file_str.IsValid()) + return llvm::createStringError( + "module '%s.__file__' attribute is not a string", + py_obj_module_str_ref.data()); + + return FileSpec(py_module_file_str.GetString()); + } + + llvm::Expected<std::map<llvm::StringLiteral, AbstractMethodCheckerPayload>> CheckAbstractMethodImplementation( const python::PythonDictionary &class_dict) const { using namespace python; - std::map<llvm::StringLiteral, AbstrackMethodCheckerPayload> checker; + std::map<llvm::StringLiteral, AbstractMethodCheckerPayload> checker; #define SET_CASE_AND_CONTINUE(method_name, case) \ { \ checker[method_name] = {case, {}}; \ @@ -74,7 +130,8 @@ public: if (!class_dict.HasKey(method_name)) SET_CASE_AND_CONTINUE(method_name, AbstractMethodCheckerCases::eNotImplemented) - auto callable_or_err = class_dict.GetItem(method_name); + llvm::Expected<PythonObject> callable_or_err = + class_dict.GetItem(method_name); if (!callable_or_err) { llvm::consumeError(callable_or_err.takeError()); SET_CASE_AND_CONTINUE(method_name, @@ -102,7 +159,7 @@ public: } else { checker[method_name] = { AbstractMethodCheckerCases::eInvalidArgumentCount, - AbstrackMethodCheckerPayload::InvalidArgumentCountPayload( + AbstractMethodCheckerPayload::InvalidArgumentCountPayload( requirement.min_arg_count, arg_info.max_positional_args)}; } } @@ -188,8 +245,13 @@ public: // This addresses the cases where the embedded interpreter session // dictionary is passed to the extension initializer which is not used // most of the time. + // Note, though none of our API's suggest defining the interfaces with + // varargs, we have some extant clients that were doing that. To keep + // from breaking them, we just say putting a varargs in these signatures + // turns off argument checking. size_t num_args = sizeof...(Args); - if (num_args != arg_info->max_positional_args) { + if (arg_info->max_positional_args != PythonCallable::ArgInfo::UNBOUNDED && + num_args != arg_info->max_positional_args) { if (num_args != arg_info->max_positional_args - 1) return create_error("Passed arguments ({0}) doesn't match the number " "of expected arguments ({1}).", @@ -286,7 +348,7 @@ public: case AbstractMethodCheckerCases::eInvalidArgumentCount: { auto &payload_variant = method_checker.second.payload; if (!std::holds_alternative< - AbstrackMethodCheckerPayload::InvalidArgumentCountPayload>( + AbstractMethodCheckerPayload::InvalidArgumentCountPayload>( payload_variant)) { abstract_method_errors = llvm::joinErrors( std::move(abstract_method_errors), @@ -295,7 +357,7 @@ public: obj_class_name.GetString(), method_checker.first))); } else { auto payload = std::get< - AbstrackMethodCheckerPayload::InvalidArgumentCountPayload>( + AbstractMethodCheckerPayload::InvalidArgumentCountPayload>( payload_variant); abstract_method_errors = llvm::joinErrors( std::move(abstract_method_errors), @@ -325,6 +387,112 @@ public: return m_object_instance_sp; } + /// Call a static method on a Python class without creating an instance. + /// + /// This method resolves a Python class by name and calls a static method + /// on it, returning the result. This is useful for calling class-level + /// methods that don't require an instance. + /// + /// \param class_name The fully-qualified name of the Python class. + /// \param method_name The name of the static method to call. + /// \param error Output parameter to receive error information if the call + /// fails. + /// \param args Arguments to pass to the static method. + /// + /// \return The return value of the static method call, or an error value. + template <typename T = StructuredData::ObjectSP, typename... Args> + T CallStaticMethod(llvm::StringRef class_name, llvm::StringRef method_name, + Status &error, Args &&...args) { + using namespace python; + using Locker = ScriptInterpreterPythonImpl::Locker; + + std::string caller_signature = + llvm::Twine(LLVM_PRETTY_FUNCTION + llvm::Twine(" (") + + llvm::Twine(class_name) + llvm::Twine(".") + + llvm::Twine(method_name) + llvm::Twine(")")) + .str(); + + if (class_name.empty()) + return ErrorWithMessage<T>(caller_signature, "missing script class name", + error); + + Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, + Locker::FreeLock); + + // Get the interpreter dictionary. + auto dict = + PythonModule::MainModule().ResolveName<python::PythonDictionary>( + m_interpreter.GetDictionaryName()); + if (!dict.IsAllocated()) + return ErrorWithMessage<T>( + caller_signature, + llvm::formatv("could not find interpreter dictionary: {0}", + m_interpreter.GetDictionaryName()) + .str(), + error); + + // Resolve the class. + auto class_obj = + PythonObject::ResolveNameWithDictionary<python::PythonCallable>( + class_name, dict); + if (!class_obj.IsAllocated()) + return ErrorWithMessage<T>( + caller_signature, + llvm::formatv("could not find script class: {0}", class_name).str(), + error); + + // Get the static method from the class. + if (!class_obj.HasAttribute(method_name)) + return ErrorWithMessage<T>( + caller_signature, + llvm::formatv("class {0} does not have method {1}", class_name, + method_name) + .str(), + error); + + PythonCallable method = + class_obj.GetAttributeValue(method_name).AsType<PythonCallable>(); + if (!method.IsAllocated()) + return ErrorWithMessage<T>(caller_signature, + llvm::formatv("method {0}.{1} is not callable", + class_name, method_name) + .str(), + error); + + // Transform the arguments. + std::tuple<Args...> original_args = std::forward_as_tuple(args...); + auto transformed_args = TransformArgs(original_args); + + // Call the static method. + llvm::Expected<PythonObject> expected_return_object = + llvm::make_error<llvm::StringError>("Not initialized.", + llvm::inconvertibleErrorCode()); + std::apply( + [&method, &expected_return_object](auto &&...args) { + llvm::consumeError(expected_return_object.takeError()); + expected_return_object = method(args...); + }, + transformed_args); + + if (llvm::Error e = expected_return_object.takeError()) { + error = Status::FromError(std::move(e)); + return ErrorWithMessage<T>( + caller_signature, "python static method could not be called", error); + } + + PythonObject py_return = std::move(expected_return_object.get()); + + // Re-assign reference and pointer arguments if needed. + if (sizeof...(Args) > 0) + if (!ReassignPtrsOrRefsArgs(original_args, transformed_args)) + return ErrorWithMessage<T>( + caller_signature, + "couldn't re-assign reference and pointer arguments", error); + + // Extract value from Python object (handles unallocated case). + return ExtractValueFromPythonObject<T>(py_return, error); + } + protected: template <typename T = StructuredData::ObjectSP> T ExtractValueFromPythonObject(python::PythonObject &p, Status &error) { @@ -341,7 +509,7 @@ protected: llvm::Twine(method_name) + llvm::Twine(")")) .str(); if (!m_object_instance_sp) - return ErrorWithMessage<T>(caller_signature, "Python object ill-formed", + return ErrorWithMessage<T>(caller_signature, "python object ill-formed", error); Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN, @@ -353,7 +521,7 @@ protected: if (!implementor.IsAllocated()) return llvm::is_contained(GetAbstractMethods(), method_name) ? ErrorWithMessage<T>(caller_signature, - "Python implementor not allocated.", + "python implementor not allocated", error) : T{}; @@ -374,20 +542,20 @@ protected: if (llvm::Error e = expected_return_object.takeError()) { error = Status::FromError(std::move(e)); return ErrorWithMessage<T>(caller_signature, - "Python method could not be called.", error); + "python method could not be called", error); } PythonObject py_return = std::move(expected_return_object.get()); // Now that we called the python method with the transformed arguments, - // we need to interate again over both the original and transformed + // we need to iterate again over both the original and transformed // parameter pack, and transform back the parameter that were passed in // the original parameter pack as references or pointers. if (sizeof...(Args) > 0) if (!ReassignPtrsOrRefsArgs(original_args, transformed_args)) return ErrorWithMessage<T>( caller_signature, - "Couldn't re-assign reference and pointer arguments.", error); + "couldn't re-assign reference and pointer arguments", error); if (!py_return.IsAllocated()) return {}; @@ -444,6 +612,14 @@ protected: return python::SWIGBridge::ToSWIGWrapper(arg); } + python::PythonObject Transform(lldb::ThreadSP arg) { + return python::SWIGBridge::ToSWIGWrapper(arg); + } + + python::PythonObject Transform(lldb::StackFrameListSP arg) { + return python::SWIGBridge::ToSWIGWrapper(arg); + } + python::PythonObject Transform(lldb::ThreadPlanSP arg) { return python::SWIGBridge::ToSWIGWrapper(arg); } @@ -586,6 +762,11 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>( python::PythonObject &p, Status &error); template <> +lldb::ThreadSP +ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ThreadSP>( + python::PythonObject &p, Status &error); + +template <> lldb::StackFrameSP ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StackFrameSP>( python::PythonObject &p, Status &error); @@ -628,6 +809,11 @@ lldb::DescriptionLevel ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DescriptionLevel>( python::PythonObject &p, Status &error); +template <> +lldb::StackFrameListSP +ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StackFrameListSP>( + python::PythonObject &p, Status &error); + } // namespace lldb_private #endif // LLDB_ENABLE_PYTHON diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp index a2a287a..d2f795c 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -810,6 +810,17 @@ bool PythonCallable::Check(PyObject *py_obj) { if (!py_obj) return false; + PythonObject python_obj(PyRefType::Borrowed, py_obj); + + // Handle staticmethod/classmethod descriptors by extracting the + // `__func__` attribute. + if (python_obj.HasAttribute("__func__")) { + PythonObject function_obj = python_obj.GetAttributeValue("__func__"); + if (!function_obj.IsAllocated()) + return false; + return PyCallable_Check(function_obj.release()); + } + return PyCallable_Check(py_obj); } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h index 27f5d2e..32948ff 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h @@ -93,6 +93,7 @@ public: static PythonObject ToSWIGWrapper(const StructuredDataImpl &data_impl); static PythonObject ToSWIGWrapper(lldb::ThreadSP thread_sp); static PythonObject ToSWIGWrapper(lldb::StackFrameSP frame_sp); + static PythonObject ToSWIGWrapper(lldb::StackFrameListSP frames_sp); static PythonObject ToSWIGWrapper(lldb::DebuggerSP debugger_sp); static PythonObject ToSWIGWrapper(lldb::WatchpointSP watchpoint_sp); static PythonObject ToSWIGWrapper(lldb::BreakpointLocationSP bp_loc_sp); @@ -264,11 +265,13 @@ void *LLDBSWIGPython_CastPyObjectToSBLaunchInfo(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBError(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBEvent(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBStream(PyObject *data); +void *LLDBSWIGPython_CastPyObjectToSBThread(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBFrame(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBSymbolContext(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBValue(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBExecutionContext(PyObject *data); +void *LLDBSWIGPython_CastPyObjectToSBFrameList(PyObject *data); } // namespace python } // namespace lldb_private diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index d257a08..35a772c 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -272,6 +272,7 @@ void ScriptInterpreterPython::SharedLibraryDirectoryHelper( // does. if (this_file.GetFileNameExtension() == ".pyd") { this_file.RemoveLastPathComponent(); // _lldb.pyd or _lldb_d.pyd + this_file.RemoveLastPathComponent(); // native this_file.RemoveLastPathComponent(); // lldb llvm::StringRef libdir = LLDB_PYTHON_RELATIVE_LIBDIR; for (auto it = llvm::sys::path::begin(libdir), @@ -1526,6 +1527,11 @@ ScriptInterpreterPythonImpl::CreateScriptedFrameInterface() { return std::make_shared<ScriptedFramePythonInterface>(*this); } +ScriptedFrameProviderInterfaceSP +ScriptInterpreterPythonImpl::CreateScriptedFrameProviderInterface() { + return std::make_shared<ScriptedFrameProviderPythonInterface>(*this); +} + ScriptedThreadPlanInterfaceSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlanInterface() { return std::make_shared<ScriptedThreadPlanPythonInterface>(*this); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index 00ae59c..ad2ddd2 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -101,6 +101,9 @@ public: lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override; + lldb::ScriptedFrameProviderInterfaceSP + CreateScriptedFrameProviderInterface() override; + lldb::ScriptedThreadPlanInterfaceSP CreateScriptedThreadPlanInterface() override; diff --git a/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp b/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp index ce2ba69..14932e9 100644 --- a/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp +++ b/lldb/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp @@ -437,7 +437,7 @@ void SymbolFileBreakpad::FindFunctions( sc.comp_unit = cu_sp.get(); sc.function = func_sp.get(); sc.module_sp = func_sp->CalculateSymbolContextModule(); - sc_list.Append(sc); + sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/true); } } } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 63b2dc4..d65aa40 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -623,6 +623,7 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, switch (tag) { case DW_TAG_typedef: + case DW_TAG_template_alias: case DW_TAG_base_type: case DW_TAG_pointer_type: case DW_TAG_reference_type: @@ -748,7 +749,7 @@ DWARFASTParserClang::ParseTypeModifier(const SymbolContext &sc, TypeSP type_sp; CompilerType clang_type; - if (tag == DW_TAG_typedef) { + if (tag == DW_TAG_typedef || tag == DW_TAG_template_alias) { // DeclContext will be populated when the clang type is materialized in // Type::ResolveCompilerType. PrepareContextToReceiveMembers( @@ -836,6 +837,7 @@ DWARFASTParserClang::ParseTypeModifier(const SymbolContext &sc, encoding_data_type = Type::eEncodingIsRValueReferenceUID; break; case DW_TAG_typedef: + case DW_TAG_template_alias: encoding_data_type = Type::eEncodingIsTypedefUID; break; case DW_TAG_const_type: @@ -1705,8 +1707,11 @@ void DWARFASTParserClang::GetUniqueTypeNameAndDeclaration( // For C++, we rely solely upon the one definition rule that says // only one thing can exist at a given decl context. We ignore the // file and line that things are declared on. - if (!die.IsValid() || !Language::LanguageIsCPlusPlus(language) || - unique_typename.IsEmpty()) + // FIXME: Rust pretends to be C++ for now, so use C++ name qualification rules + if (!Language::LanguageIsCPlusPlus(language) && + language != lldb::eLanguageTypeRust) + return; + if (!die.IsValid() || unique_typename.IsEmpty()) return; decl_declaration.Clear(); std::string qualified_name; @@ -3704,12 +3709,10 @@ bool DWARFASTParserClang::CopyUniqueClassMethodTypes( } } - DWARFASTParserClang *src_dwarf_ast_parser = - static_cast<DWARFASTParserClang *>( - SymbolFileDWARF::GetDWARFParser(*src_class_die.GetCU())); - DWARFASTParserClang *dst_dwarf_ast_parser = - static_cast<DWARFASTParserClang *>( - SymbolFileDWARF::GetDWARFParser(*dst_class_die.GetCU())); + auto *src_dwarf_ast_parser = llvm::cast<DWARFASTParserClang>( + SymbolFileDWARF::GetDWARFParser(*src_class_die.GetCU())); + auto *dst_dwarf_ast_parser = llvm::cast<DWARFASTParserClang>( + SymbolFileDWARF::GetDWARFParser(*dst_class_die.GetCU())); auto link = [&](DWARFDIE src, DWARFDIE dst) { auto &die_to_type = dst_class_die.GetDWARF()->GetDIEToType(); clang::DeclContext *dst_decl_ctx = diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h index f5f7071..6eb2b6b 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -47,6 +47,11 @@ public: ~DWARFASTParserClang() override; + // LLVM RTTI support + static bool classof(const DWARFASTParser *Parser) { + return Parser->GetKind() == Kind::DWARFASTParserClang; + } + // DWARFASTParser interface. lldb::TypeSP ParseTypeFromDWARF(const lldb_private::SymbolContext &sc, @@ -264,10 +269,6 @@ protected: lldb::ModuleSP GetModuleForType(const lldb_private::plugin::dwarf::DWARFDIE &die); - static bool classof(const DWARFASTParser *Parser) { - return Parser->GetKind() == Kind::DWARFASTParserClang; - } - private: struct FieldInfo { /// Size in bits that this field occupies. Can but diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp index 64a8005..c4efc06 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp @@ -173,6 +173,14 @@ void DWARFIndex::GetNamespacesWithParents( }); } +void DWARFIndex::GetFunctions( + const std::vector<Module::LookupInfo> &lookup_infos, SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + llvm::function_ref<IterationAction(DWARFDIE die)> callback) { + for (auto &lookup_info : lookup_infos) + GetFunctions(lookup_info, dwarf, parent_decl_ctx, callback); +} + IterationAction DWARFIndex::ProcessNamespaceDieMatchParents( const CompilerDeclContext &parent_decl_ctx, DWARFDIE die, llvm::function_ref<IterationAction(DWARFDIE die)> callback) { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h index be73255..eaf1da1 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h @@ -86,6 +86,11 @@ public: const CompilerDeclContext &parent_decl_ctx, llvm::function_ref<IterationAction(DWARFDIE die)> callback) = 0; virtual void + GetFunctions(const std::vector<Module::LookupInfo> &lookup_infos, + SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + llvm::function_ref<IterationAction(DWARFDIE die)> callback); + virtual void GetFunctions(const RegularExpression ®ex, llvm::function_ref<IterationAction(DWARFDIE die)> callback) = 0; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp index 94fc2e83..b78e6ce8 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -348,6 +348,10 @@ void DWARFUnit::ExtractDIEsRWLocked() { void DWARFUnit::SetDwoStrOffsetsBase() { lldb::offset_t baseOffset = 0; + // Size of offset for .debug_str_offsets is same as DWARF offset byte size + // of the DWARFUnit as a default. We might override this if below if needed. + m_str_offset_size = m_header.getDwarfOffsetByteSize(); + if (const llvm::DWARFUnitIndex::Entry *entry = m_header.getIndexEntry()) { if (const auto *contribution = entry->getContribution(llvm::DW_SECT_STR_OFFSETS)) @@ -357,14 +361,17 @@ void DWARFUnit::SetDwoStrOffsetsBase() { } if (GetVersion() >= 5) { - const DWARFDataExtractor &strOffsets = - GetSymbolFileDWARF().GetDWARFContext().getOrLoadStrOffsetsData(); - uint64_t length = strOffsets.GetU32(&baseOffset); - if (length == 0xffffffff) - length = strOffsets.GetU64(&baseOffset); - + const llvm::DWARFDataExtractor &strOffsets = GetSymbolFileDWARF() + .GetDWARFContext() + .getOrLoadStrOffsetsData() + .GetAsLLVMDWARF(); + + uint64_t length; + llvm::dwarf::DwarfFormat format; + std::tie(length, format) = strOffsets.getInitialLength(&baseOffset); + m_str_offset_size = format == llvm::dwarf::DwarfFormat::DWARF64 ? 8 : 4; // Check version. - if (strOffsets.GetU16(&baseOffset) < 5) + if (strOffsets.getU16(&baseOffset) < 5) return; // Skip padding. @@ -409,7 +416,16 @@ void DWARFUnit::AddUnitDIE(const DWARFDebugInfoEntry &cu_die) { SetRangesBase(form_value.Unsigned()); break; case DW_AT_str_offsets_base: + // When we have a DW_AT_str_offsets_base attribute, it points us to the + // first string offset for this DWARFUnit which is after the string + // offsets table header. In this case we use the DWARF32/DWARF64 of the + // DWARFUnit to determine the string offset byte size. DWO files do not + // use this attribute and they point to the start of the string offsets + // table header which can be used to determine the DWARF32/DWARF64 status + // of the string table. See SetDwoStrOffsetsBase() for now it figures out + // the m_str_offset_size value that should be used. SetStrOffsetsBase(form_value.Unsigned()); + m_str_offset_size = m_header.getDwarfOffsetByteSize(); break; case DW_AT_low_pc: SetBaseAddress(form_value.Address()); @@ -1079,10 +1095,9 @@ uint32_t DWARFUnit::GetHeaderByteSize() const { return m_header.getSize(); } std::optional<uint64_t> DWARFUnit::GetStringOffsetSectionItem(uint32_t index) const { - lldb::offset_t offset = - GetStrOffsetsBase() + index * m_header.getDwarfOffsetByteSize(); + lldb::offset_t offset = GetStrOffsetsBase() + index * m_str_offset_size; return m_dwarf.GetDWARFContext().getOrLoadStrOffsetsData().GetMaxU64( - &offset, m_header.getDwarfOffsetByteSize()); + &offset, m_str_offset_size); } llvm::Expected<llvm::DWARFAddressRangesVector> diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h index 91a6938..b5199a8 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h @@ -364,6 +364,7 @@ protected: dw_offset_t m_line_table_offset = DW_INVALID_OFFSET; dw_offset_t m_str_offsets_base = 0; // Value of DW_AT_str_offsets_base. + dw_offset_t m_str_offset_size = 4; // Size in bytes of a string offset. std::optional<llvm::DWARFDebugRnglistTable> m_rnglist_table; bool m_rnglist_table_done = false; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 881268b..b755f3a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -794,12 +794,12 @@ lldb::CompUnitSP SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit &dwarf_cu) { } else { ModuleSP module_sp(m_objfile_sp->GetModule()); if (module_sp) { - auto initialize_cu = [&](lldb::SupportFileSP support_file_sp, + auto initialize_cu = [&](SupportFileNSP support_file_nsp, LanguageType cu_language, SupportFileList &&support_files = {}) { BuildCuTranslationTable(); cu_sp = std::make_shared<CompileUnit>( - module_sp, &dwarf_cu, support_file_sp, + module_sp, &dwarf_cu, support_file_nsp, *GetDWARFUnitIndex(dwarf_cu.GetID()), cu_language, eLazyBoolCalculate, std::move(support_files)); @@ -1560,8 +1560,8 @@ bool SymbolFileDWARF::HasForwardDeclForCompilerType( auto clang_type_system = compiler_type.GetTypeSystem<TypeSystemClang>(); if (!clang_type_system) return false; - DWARFASTParserClang *ast_parser = - static_cast<DWARFASTParserClang *>(clang_type_system->GetDWARFParser()); + auto *ast_parser = + llvm::cast<DWARFASTParserClang>(clang_type_system->GetDWARFParser()); return ast_parser->GetClangASTImporter().CanImport(compiler_type); } @@ -1569,8 +1569,8 @@ bool SymbolFileDWARF::CompleteType(CompilerType &compiler_type) { std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); auto clang_type_system = compiler_type.GetTypeSystem<TypeSystemClang>(); if (clang_type_system) { - DWARFASTParserClang *ast_parser = - static_cast<DWARFASTParserClang *>(clang_type_system->GetDWARFParser()); + auto *ast_parser = + llvm::cast<DWARFASTParserClang>(clang_type_system->GetDWARFParser()); if (ast_parser && ast_parser->GetClangASTImporter().CanImport(compiler_type)) return ast_parser->GetClangASTImporter().CompleteType(compiler_type); @@ -1614,8 +1614,7 @@ bool SymbolFileDWARF::CompleteType(CompilerType &compiler_type) { if (decl_die != def_die) { GetDIEToType()[def_die.GetDIE()] = type; - DWARFASTParserClang *ast_parser = - static_cast<DWARFASTParserClang *>(dwarf_ast); + auto *ast_parser = llvm::cast<DWARFASTParserClang>(dwarf_ast); ast_parser->MapDeclDIEToDefDIE(decl_die, def_die); } @@ -2018,7 +2017,7 @@ void SymbolFileDWARF::UpdateExternalModuleListIfNeeded() { } Status error = ModuleList::GetSharedModule(dwo_module_spec, module_sp, - nullptr, nullptr, nullptr); + nullptr, nullptr); if (!module_sp) { // ReportWarning also rate-limits based on the warning string, // but in a -gmodules build, each object file has a similar DAG @@ -2341,7 +2340,7 @@ void SymbolFileDWARF::FindGlobalVariables( bool name_is_mangled = Mangled::GetManglingScheme(name.GetStringRef()) != Mangled::eManglingSchemeNone; - if (!CPlusPlusLanguage::ExtractContextAndIdentifier(name.GetCString(), + if (!CPlusPlusLanguage::ExtractContextAndIdentifier(name.GetStringRef(), context, basename)) basename = name.GetStringRef(); @@ -2482,7 +2481,7 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die, sc.block = function_block.FindBlockByID(inlined_die.GetOffset()); } - sc_list.Append(sc); + sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/true); return true; } @@ -2550,11 +2549,11 @@ SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label, const DWARFDIE &declaration) { auto do_lookup = [this](llvm::StringRef lookup_name) -> DWARFDIE { DWARFDIE found; - Module::LookupInfo info(ConstString(lookup_name), - lldb::eFunctionNameTypeFull, - lldb::eLanguageTypeUnknown); + auto lookup_infos = Module::LookupInfo::MakeLookupInfos( + ConstString(lookup_name), lldb::eFunctionNameTypeFull, + lldb::eLanguageTypeUnknown); - m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) { + m_index->GetFunctions(lookup_infos, *this, {}, [&](DWARFDIE entry) { if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) return IterationAction::Continue; diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp index aaec160..3bf113a 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -86,6 +86,40 @@ static lldb::LanguageType TranslateLanguage(PDB_Lang lang) { } } +static std::optional<std::string> +findMatchingPDBFilePath(llvm::StringRef original_pdb_path, + llvm::StringRef exe_path) { + const FileSystem &fs = FileSystem::Instance(); + + if (fs.Exists(original_pdb_path)) + return std::string(original_pdb_path); + + const auto exe_dir = FileSpec(exe_path).CopyByRemovingLastPathComponent(); + // While the exe_path uses the native style, the exe might be compiled on a + // different OS, so try to guess the style used. + const FileSpec original_pdb_spec(original_pdb_path, + FileSpec::GuessPathStyle(original_pdb_path) + .value_or(FileSpec::Style::native)); + const llvm::StringRef pdb_filename = original_pdb_spec.GetFilename(); + + // If the file doesn't exist, perhaps the path specified at build time + // doesn't match the PDB's current location, so check the location of the + // executable. + const FileSpec local_pdb = exe_dir.CopyByAppendingPathComponent(pdb_filename); + if (fs.Exists(local_pdb)) + return local_pdb.GetPath(); + + // Otherwise, search for one in target.debug-file-search-paths + FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths(); + for (const FileSpec &search_dir : search_paths) { + FileSpec pdb_path = search_dir.CopyByAppendingPathComponent(pdb_filename); + if (fs.Exists(pdb_path)) + return pdb_path.GetPath(); + } + + return std::nullopt; +} + static std::unique_ptr<PDBFile> loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) { // Try to find a matching PDB for an EXE. @@ -113,17 +147,14 @@ loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) { return nullptr; } - // If the file doesn't exist, perhaps the path specified at build time - // doesn't match the PDB's current location, so check the location of the - // executable. - if (!FileSystem::Instance().Exists(pdb_file)) { - const auto exe_dir = FileSpec(exe_path).CopyByRemovingLastPathComponent(); - const auto pdb_name = FileSpec(pdb_file).GetFilename().GetCString(); - pdb_file = exe_dir.CopyByAppendingPathComponent(pdb_name).GetPathAsConstString().GetStringRef(); - } + std::optional<std::string> resolved_pdb_path = + findMatchingPDBFilePath(pdb_file, exe_path); + if (!resolved_pdb_path) + return nullptr; // If the file is not a PDB or if it doesn't have a matching GUID, fail. - auto pdb = ObjectFilePDB::loadPDBFile(std::string(pdb_file), allocator); + auto pdb = + ObjectFilePDB::loadPDBFile(*std::move(resolved_pdb_path), allocator); if (!pdb) return nullptr; @@ -137,6 +168,9 @@ loadMatchingPDBFile(std::string exe_path, llvm::BumpPtrAllocator &allocator) { if (expected_info->getGuid() != guid) return nullptr; + + LLDB_LOG(GetLog(LLDBLog::Symbols), "Loading {0} for {1}", pdb->getFilePath(), + exe_path); return pdb; } @@ -1126,7 +1160,8 @@ lldb::LanguageType SymbolFileNativePDB::ParseLanguage(CompileUnit &comp_unit) { } void SymbolFileNativePDB::AddSymbols(Symtab &symtab) { - auto *section_list = m_objfile_sp->GetSectionList(); + auto *section_list = + m_objfile_sp->GetModule()->GetObjectFile()->GetSectionList(); if (!section_list) return; diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp index 1c575e9..46cf9b8 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp @@ -442,6 +442,10 @@ void UdtRecordCompleter::Record::ConstructRecord() { // The end offset to a vector of field/struct that ends at the offset. std::map<uint64_t, std::vector<Member *>> end_offset_map; + auto is_last_end_offset = [&](auto it) { + return it != end_offset_map.end() && ++it == end_offset_map.end(); + }; + for (auto &pair : fields_map) { uint64_t offset = pair.first; auto &fields = pair.second; @@ -462,8 +466,23 @@ void UdtRecordCompleter::Record::ConstructRecord() { } if (iter->second.empty()) continue; - parent = iter->second.back(); - iter->second.pop_back(); + + // If the new fields come after the already added ones + // without overlap, go back to the root. + if (iter->first <= offset && is_last_end_offset(iter)) { + if (record.kind == Member::Struct) { + parent = &record; + } else { + assert(record.kind == Member::Union && + "Current record must be a union"); + assert(!record.fields.empty()); + // For unions, append the field to the last struct + parent = record.fields.back().get(); + } + } else { + parent = iter->second.back(); + iter->second.pop_back(); + } } // If it's a field, then the field is inside a union, so we can safely // increase its size by converting it to a struct to hold multiple fields. diff --git a/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp b/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp index 0ccb1804..97c995fc 100644 --- a/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp +++ b/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp @@ -287,8 +287,10 @@ uint32_t SymbolFilePDB::CalculateAbilities() { } void SymbolFilePDB::InitializeObject() { - lldb::addr_t obj_load_address = - m_objfile_sp->GetBaseAddress().GetFileAddress(); + lldb::addr_t obj_load_address = m_objfile_sp->GetModule() + ->GetObjectFile() + ->GetBaseAddress() + .GetFileAddress(); lldbassert(obj_load_address && obj_load_address != LLDB_INVALID_ADDRESS); m_session_up->setLoadAddress(obj_load_address); if (!m_global_scope_up) @@ -1479,7 +1481,8 @@ void SymbolFilePDB::AddSymbols(lldb_private::Symtab &symtab) { if (!results) return; - auto section_list = m_objfile_sp->GetSectionList(); + auto section_list = + m_objfile_sp->GetModule()->GetObjectFile()->GetSectionList(); if (!section_list) return; diff --git a/lldb/source/Plugins/SyntheticFrameProvider/CMakeLists.txt b/lldb/source/Plugins/SyntheticFrameProvider/CMakeLists.txt new file mode 100644 index 0000000..85b405e --- /dev/null +++ b/lldb/source/Plugins/SyntheticFrameProvider/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(ScriptedFrameProvider) diff --git a/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/CMakeLists.txt b/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/CMakeLists.txt new file mode 100644 index 0000000..fe67d39 --- /dev/null +++ b/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/CMakeLists.txt @@ -0,0 +1,12 @@ +add_lldb_library(lldbPluginScriptedFrameProvider PLUGIN + ScriptedFrameProvider.cpp + + LINK_COMPONENTS + Support + + LINK_LIBS + lldbCore + lldbInterpreter + lldbTarget + lldbUtility + ) diff --git a/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.cpp b/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.cpp new file mode 100644 index 0000000..739963e --- /dev/null +++ b/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.cpp @@ -0,0 +1,221 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ScriptedFrameProvider.h" +#include "Plugins/Process/scripted/ScriptedFrame.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Target/BorrowedStackFrame.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/ScriptedMetadata.h" +#include "lldb/Utility/Status.h" +#include "llvm/Support/Error.h" +#include <cstdint> + +using namespace lldb; +using namespace lldb_private; + +void ScriptedFrameProvider::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + "Provides synthetic frames via scripting", + nullptr, ScriptedFrameProvider::CreateInstance); +} + +void ScriptedFrameProvider::Terminate() { + PluginManager::UnregisterPlugin(ScriptedFrameProvider::CreateInstance); +} + +llvm::Expected<lldb::SyntheticFrameProviderSP> +ScriptedFrameProvider::CreateInstance( + lldb::StackFrameListSP input_frames, + const ScriptedFrameProviderDescriptor &descriptor) { + if (!input_frames) + return llvm::createStringError( + "failed to create scripted frame provider: invalid input frames"); + + Thread &thread = input_frames->GetThread(); + ProcessSP process_sp = thread.GetProcess(); + if (!process_sp) + return nullptr; + + if (!descriptor.IsValid()) + return llvm::createStringError( + "failed to create scripted frame provider: invalid scripted metadata"); + + if (!descriptor.AppliesToThread(thread)) + return nullptr; + + ScriptInterpreter *script_interp = + process_sp->GetTarget().GetDebugger().GetScriptInterpreter(); + if (!script_interp) + return llvm::createStringError("cannot create scripted frame provider: No " + "script interpreter installed"); + + ScriptedFrameProviderInterfaceSP interface_sp = + script_interp->CreateScriptedFrameProviderInterface(); + if (!interface_sp) + return llvm::createStringError( + "cannot create scripted frame provider: script interpreter couldn't " + "create Scripted Frame Provider Interface"); + + const ScriptedMetadataSP scripted_metadata = descriptor.scripted_metadata_sp; + + // If we shouldn't attach a frame provider to this thread, just exit early. + if (!interface_sp->AppliesToThread(scripted_metadata->GetClassName(), + thread.shared_from_this())) + return nullptr; + + auto obj_or_err = interface_sp->CreatePluginObject( + scripted_metadata->GetClassName(), input_frames, + scripted_metadata->GetArgsSP()); + if (!obj_or_err) + return obj_or_err.takeError(); + + StructuredData::ObjectSP object_sp = *obj_or_err; + if (!object_sp || !object_sp->IsValid()) + return llvm::createStringError( + "cannot create scripted frame provider: failed to create valid scripted" + "frame provider object"); + + return std::make_shared<ScriptedFrameProvider>(input_frames, interface_sp, + descriptor); +} + +ScriptedFrameProvider::ScriptedFrameProvider( + StackFrameListSP input_frames, + lldb::ScriptedFrameProviderInterfaceSP interface_sp, + const ScriptedFrameProviderDescriptor &descriptor) + : SyntheticFrameProvider(input_frames), m_interface_sp(interface_sp), + m_descriptor(descriptor) {} + +ScriptedFrameProvider::~ScriptedFrameProvider() = default; + +std::string ScriptedFrameProvider::GetDescription() const { + if (!m_interface_sp) + return {}; + + return m_interface_sp->GetDescription(m_descriptor.GetName()); +} + +llvm::Expected<StackFrameSP> +ScriptedFrameProvider::GetFrameAtIndex(uint32_t idx) { + if (!m_interface_sp) + return llvm::createStringError( + "cannot get stack frame: scripted frame provider not initialized"); + + auto create_frame_from_dict = + [this](StructuredData::Dictionary *dict, + uint32_t index) -> llvm::Expected<StackFrameSP> { + lldb::addr_t pc; + if (!dict->GetValueForKeyAsInteger("pc", pc)) + return llvm::createStringError( + "missing 'pc' key from scripted frame dictionary"); + + Address symbol_addr; + symbol_addr.SetLoadAddress(pc, &GetThread().GetProcess()->GetTarget()); + + const lldb::addr_t cfa = LLDB_INVALID_ADDRESS; + const bool cfa_is_valid = false; + const bool artificial = false; + const bool behaves_like_zeroth_frame = false; + SymbolContext sc; + symbol_addr.CalculateSymbolContext(&sc); + + ThreadSP thread_sp = GetThread().shared_from_this(); + return std::make_shared<StackFrame>(thread_sp, index, index, cfa, + cfa_is_valid, pc, + StackFrame::Kind::Synthetic, artificial, + behaves_like_zeroth_frame, &sc); + }; + + auto create_frame_from_script_object = + [this]( + StructuredData::ObjectSP object_sp) -> llvm::Expected<StackFrameSP> { + Status error; + if (!object_sp || !object_sp->GetAsGeneric()) + return llvm::createStringError("invalid script object"); + + ThreadSP thread_sp = GetThread().shared_from_this(); + auto frame_or_error = ScriptedFrame::Create(thread_sp, nullptr, nullptr, + object_sp->GetAsGeneric()); + + if (!frame_or_error) { + ScriptedInterface::ErrorWithMessage<bool>( + LLVM_PRETTY_FUNCTION, toString(frame_or_error.takeError()), error); + return error.ToError(); + } + + return *frame_or_error; + }; + + StructuredData::ObjectSP obj_sp = m_interface_sp->GetFrameAtIndex(idx); + + // None/null means no more frames or error. + if (!obj_sp || !obj_sp->IsValid()) + return llvm::createStringError("invalid script object returned for frame " + + llvm::Twine(idx)); + + StackFrameSP synth_frame_sp = nullptr; + if (StructuredData::UnsignedInteger *int_obj = + obj_sp->GetAsUnsignedInteger()) { + uint32_t real_frame_index = int_obj->GetValue(); + if (real_frame_index < m_input_frames->GetNumFrames()) { + StackFrameSP real_frame_sp = + m_input_frames->GetFrameAtIndex(real_frame_index); + synth_frame_sp = + (real_frame_index == idx) + ? real_frame_sp + : std::make_shared<BorrowedStackFrame>(real_frame_sp, idx); + } + } else if (StructuredData::Dictionary *dict = obj_sp->GetAsDictionary()) { + // Check if it's a dictionary describing a frame. + auto frame_from_dict_or_err = create_frame_from_dict(dict, idx); + if (!frame_from_dict_or_err) { + return llvm::createStringError(llvm::Twine( + "couldn't create frame from dictionary at index " + llvm::Twine(idx) + + ": " + toString(frame_from_dict_or_err.takeError()))); + } + synth_frame_sp = *frame_from_dict_or_err; + } else if (obj_sp->GetAsGeneric()) { + // It's a ScriptedFrame object. + auto frame_from_script_obj_or_err = create_frame_from_script_object(obj_sp); + if (!frame_from_script_obj_or_err) { + return llvm::createStringError( + llvm::Twine("couldn't create frame from script object at index " + + llvm::Twine(idx) + ": " + + toString(frame_from_script_obj_or_err.takeError()))); + } + synth_frame_sp = *frame_from_script_obj_or_err; + } else { + return llvm::createStringError( + llvm::Twine("invalid return type from get_frame_at_index at index " + + llvm::Twine(idx))); + } + + if (!synth_frame_sp) + return llvm::createStringError( + llvm::Twine("failed to create frame at index " + llvm::Twine(idx))); + + synth_frame_sp->SetFrameIndex(idx); + + return synth_frame_sp; +} + +namespace lldb_private { +void lldb_initialize_ScriptedFrameProvider() { + ScriptedFrameProvider::Initialize(); +} + +void lldb_terminate_ScriptedFrameProvider() { + ScriptedFrameProvider::Terminate(); +} +} // namespace lldb_private diff --git a/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.h b/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.h new file mode 100644 index 0000000..3434bf2 --- /dev/null +++ b/lldb/source/Plugins/SyntheticFrameProvider/ScriptedFrameProvider/ScriptedFrameProvider.h @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_SYNTHETICFRAMEPROVIDER_SCRIPTEDFRAMEPROVIDER_SCRIPTEDFRAMEPROVIDER_H +#define LLDB_PLUGINS_SYNTHETICFRAMEPROVIDER_SCRIPTEDFRAMEPROVIDER_SCRIPTEDFRAMEPROVIDER_H + +#include "lldb/Target/SyntheticFrameProvider.h" +#include "lldb/Utility/ScriptedMetadata.h" +#include "lldb/Utility/Status.h" +#include "lldb/lldb-forward.h" +#include "llvm/Support/Error.h" + +namespace lldb_private { + +class ScriptedFrameProvider : public SyntheticFrameProvider { +public: + static llvm::StringRef GetPluginNameStatic() { + return "ScriptedFrameProvider"; + } + + static llvm::Expected<lldb::SyntheticFrameProviderSP> + CreateInstance(lldb::StackFrameListSP input_frames, + const ScriptedFrameProviderDescriptor &descriptor); + + static void Initialize(); + + static void Terminate(); + + ScriptedFrameProvider(lldb::StackFrameListSP input_frames, + lldb::ScriptedFrameProviderInterfaceSP interface_sp, + const ScriptedFrameProviderDescriptor &descriptor); + ~ScriptedFrameProvider() override; + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + std::string GetDescription() const override; + + /// Get a single stack frame at the specified index. + llvm::Expected<lldb::StackFrameSP> GetFrameAtIndex(uint32_t idx) override; + +private: + lldb::ScriptedFrameProviderInterfaceSP m_interface_sp; + const ScriptedFrameProviderDescriptor &m_descriptor; +}; + +} // namespace lldb_private + +#endif // LLDB_PLUGINS_SYNTHETICFRAMEPROVIDER_SCRIPTEDFRAMEPROVIDER_SCRIPTEDFRAMEPROVIDER_H diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 51cb883..625d0e5 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -2149,7 +2149,8 @@ PrintingPolicy TypeSystemClang::GetTypePrintingPolicy() { printing_policy.SuppressTagKeyword = true; // Inline namespaces are important for some type formatters (e.g., libc++ // and libstdc++ are differentiated by their inline namespaces). - printing_policy.SuppressInlineNamespace = false; + printing_policy.SuppressInlineNamespace = + llvm::to_underlying(PrintingPolicy::SuppressInlineNamespaceMode::None); printing_policy.SuppressUnwrittenScope = false; // Default arguments are also always important for type formatters. Otherwise // we would need to always specify two type names for the setups where we do @@ -3870,7 +3871,8 @@ TypeSystemClang::GetDisplayTypeName(lldb::opaque_compiler_type_t type) { printing_policy.SuppressTagKeyword = true; printing_policy.SuppressScope = false; printing_policy.SuppressUnwrittenScope = true; - printing_policy.SuppressInlineNamespace = true; + printing_policy.SuppressInlineNamespace = + llvm::to_underlying(PrintingPolicy::SuppressInlineNamespaceMode::All); return ConstString(qual_type.getAsString(printing_policy)); } @@ -7346,6 +7348,102 @@ CompilerType TypeSystemClang::GetTypeForFormatters(void *type) { return CompilerType(); } +bool TypeSystemClang::IsPromotableIntegerType( + lldb::opaque_compiler_type_t type) { + // Unscoped enums are always considered as promotable, even if their + // underlying type does not need to be promoted (e.g. "int"). + bool is_signed = false; + bool isUnscopedEnumerationType = + IsEnumerationType(type, is_signed) && !IsScopedEnumerationType(type); + if (isUnscopedEnumerationType) + return true; + + switch (GetBasicTypeEnumeration(type)) { + case lldb::eBasicTypeBool: + case lldb::eBasicTypeChar: + case lldb::eBasicTypeSignedChar: + case lldb::eBasicTypeUnsignedChar: + case lldb::eBasicTypeShort: + case lldb::eBasicTypeUnsignedShort: + case lldb::eBasicTypeWChar: + case lldb::eBasicTypeSignedWChar: + case lldb::eBasicTypeUnsignedWChar: + case lldb::eBasicTypeChar16: + case lldb::eBasicTypeChar32: + return true; + + default: + return false; + } + + llvm_unreachable("All cases handled above."); +} + +llvm::Expected<CompilerType> +TypeSystemClang::DoIntegralPromotion(CompilerType from, + ExecutionContextScope *exe_scope) { + if (!from.IsInteger() && !from.IsUnscopedEnumerationType()) + return from; + + if (!from.IsPromotableIntegerType()) + return from; + + if (from.IsUnscopedEnumerationType()) { + EnumDecl *enum_decl = GetAsEnumDecl(from); + CompilerType promotion_type = GetType(enum_decl->getPromotionType()); + return DoIntegralPromotion(promotion_type, exe_scope); + } + + lldb::BasicType builtin_type = + from.GetCanonicalType().GetBasicTypeEnumeration(); + uint64_t from_size = 0; + if (builtin_type == lldb::eBasicTypeWChar || + builtin_type == lldb::eBasicTypeSignedWChar || + builtin_type == lldb::eBasicTypeUnsignedWChar || + builtin_type == lldb::eBasicTypeChar16 || + builtin_type == lldb::eBasicTypeChar32) { + // Find the type that can hold the entire range of values for our type. + bool is_signed = from.IsSigned(); + llvm::Expected<uint64_t> from_size = from.GetByteSize(exe_scope); + if (!from_size) + return from_size.takeError(); + CompilerType promote_types[] = { + GetBasicTypeFromAST(lldb::eBasicTypeInt), + GetBasicTypeFromAST(lldb::eBasicTypeUnsignedInt), + GetBasicTypeFromAST(lldb::eBasicTypeLong), + GetBasicTypeFromAST(lldb::eBasicTypeUnsignedLong), + GetBasicTypeFromAST(lldb::eBasicTypeLongLong), + GetBasicTypeFromAST(lldb::eBasicTypeUnsignedLongLong), + }; + for (CompilerType &type : promote_types) { + llvm::Expected<uint64_t> byte_size = type.GetByteSize(exe_scope); + if (!byte_size) + return byte_size.takeError(); + if (*from_size < *byte_size || + (*from_size == *byte_size && is_signed == type.IsSigned())) { + return type; + } + } + llvm_unreachable("char type should fit into long long"); + } + + // Here we can promote only to "int" or "unsigned int". + CompilerType int_type = GetBasicTypeFromAST(lldb::eBasicTypeInt); + llvm::Expected<uint64_t> int_byte_size = int_type.GetByteSize(exe_scope); + if (!int_byte_size) + return int_byte_size.takeError(); + + // Signed integer types can be safely promoted to "int". + if (from.IsSigned()) { + return int_type; + } + // Unsigned integer types are promoted to "unsigned int" if "int" cannot hold + // their entire value range. + return (from_size == *int_byte_size) + ? GetBasicTypeFromAST(lldb::eBasicTypeUnsignedInt) + : int_type; +} + clang::EnumDecl *TypeSystemClang::GetAsEnumDecl(const CompilerType &type) { const clang::EnumType *enutype = llvm::dyn_cast<clang::EnumType>(ClangUtil::GetCanonicalQualType(type)); diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 375891b..67d206e 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -938,6 +938,14 @@ public: CompilerType GetTypeForFormatters(void *type) override; + // DIL + + bool IsPromotableIntegerType(lldb::opaque_compiler_type_t type) override; + + llvm::Expected<CompilerType> + DoIntegralPromotion(CompilerType from, + ExecutionContextScope *exe_scope) override; + #define LLDB_INVALID_DECL_LEVEL UINT32_MAX // LLDB_INVALID_DECL_LEVEL is returned by CountDeclLevels if child_decl_ctx // could not be found in decl_ctx. diff --git a/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp b/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp index 397c693..19ae1cf 100644 --- a/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp +++ b/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp @@ -25,6 +25,8 @@ #include "lldb/Utility/Log.h" #include "lldb/Utility/Status.h" #include "lldb/Utility/StreamString.h" +#include "llvm/ADT/SmallSet.h" +#include <deque> using namespace lldb; using namespace lldb_private; @@ -50,6 +52,33 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( range, function_text.data(), function_text.size(), unwind_plan); } +static void DumpUnwindRowsToLog(Log *log, AddressRange range, + const UnwindPlan &unwind_plan) { + if (!log || !log->GetVerbose()) + return; + StreamString strm; + lldb::addr_t base_addr = range.GetBaseAddress().GetFileAddress(); + strm.Printf("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):", + base_addr, base_addr + range.GetByteSize()); + unwind_plan.Dump(strm, nullptr, base_addr); + log->PutString(strm.GetString()); +} + +static void DumpInstToLog(Log *log, Instruction &inst, + const InstructionList &inst_list) { + if (!log || !log->GetVerbose()) + return; + const bool show_address = true; + const bool show_bytes = true; + const bool show_control_flow_kind = false; + StreamString strm; + lldb_private::FormatEntity::Entry format; + FormatEntity::Parse("${frame.pc}: ", format); + inst.Dump(&strm, inst_list.GetMaxOpcocdeByteSize(), show_address, show_bytes, + show_control_flow_kind, nullptr, nullptr, nullptr, &format, 0); + log->PutString(strm.GetString()); +} + bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( AddressRange &range, uint8_t *opcode_data, size_t opcode_size, UnwindPlan &unwind_plan) { @@ -82,11 +111,6 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( m_range_ptr = ⦥ m_unwind_plan_ptr = &unwind_plan; - const uint32_t addr_byte_size = m_arch.GetAddressByteSize(); - const bool show_address = true; - const bool show_bytes = true; - const bool show_control_flow_kind = false; - m_state.cfa_reg_info = *m_inst_emulator_up->GetRegisterInfo( unwind_plan.GetRegisterKind(), unwind_plan.GetInitialCFARegister()); m_state.fp_is_cfa = false; @@ -94,134 +118,142 @@ bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( m_pushed_regs.clear(); - // Initialize the CFA with a known value. In the 32 bit case it will be - // 0x80000000, and in the 64 bit case 0x8000000000000000. We use the address - // byte size to be safe for any future address sizes - m_initial_sp = (1ull << ((addr_byte_size * 8) - 1)); RegisterValue cfa_reg_value; - cfa_reg_value.SetUInt(m_initial_sp, m_state.cfa_reg_info.byte_size); + cfa_reg_value.SetUInt(m_initial_cfa, m_state.cfa_reg_info.byte_size); SetRegisterValue(m_state.cfa_reg_info, cfa_reg_value); - const InstructionList &inst_list = disasm_sp->GetInstructionList(); - const size_t num_instructions = inst_list.GetSize(); - - if (num_instructions > 0) { - Instruction *inst = inst_list.GetInstructionAtIndex(0).get(); - const lldb::addr_t base_addr = inst->GetAddress().GetFileAddress(); - - // Map for storing the unwind state at a given offset. When we see a forward - // branch we add a new entry to this map with the actual unwind plan row and - // register context for the target address of the branch as the current data - // have to be valid for the target address of the branch too if we are in - // the same function. - std::map<lldb::addr_t, UnwindState> saved_unwind_states; - - // Make a copy of the current instruction Row and save it in m_state so - // we can add updates as we process the instructions. - m_state.row = *unwind_plan.GetLastRow(); - - // Add the initial state to the save list with offset 0. - auto condition_block_start_state = - saved_unwind_states.emplace(0, m_state).first; - - // The architecture dependent condition code of the last processed - // instruction. - EmulateInstruction::InstructionCondition last_condition = - EmulateInstruction::UnconditionalCondition; - - for (size_t idx = 0; idx < num_instructions; ++idx) { - m_curr_row_modified = false; - m_forward_branch_offset = 0; - - inst = inst_list.GetInstructionAtIndex(idx).get(); - if (!inst) - continue; - - lldb::addr_t current_offset = - inst->GetAddress().GetFileAddress() - base_addr; - auto it = saved_unwind_states.upper_bound(current_offset); - assert(it != saved_unwind_states.begin() && - "Unwind row for the function entry missing"); - --it; // Move it to the row corresponding to the current offset - - // If the offset of m_curr_row don't match with the offset we see in - // saved_unwind_states then we have to update current unwind state to - // the saved values. It is happening after we processed an epilogue and a - // return to caller instruction. - if (it->second.row.GetOffset() != m_state.row.GetOffset()) - m_state = it->second; - - m_inst_emulator_up->SetInstruction(inst->GetOpcode(), inst->GetAddress(), - nullptr); - - if (last_condition != m_inst_emulator_up->GetInstructionCondition()) { - // If the last instruction was conditional with a different condition - // than the current condition then restore the state. - if (last_condition != EmulateInstruction::UnconditionalCondition) { - m_state = condition_block_start_state->second; - m_state.row.SetOffset(current_offset); - // The last instruction might already created a row for this offset - // and we want to overwrite it. - saved_unwind_states.insert_or_assign(current_offset, m_state); - } + InstructionList inst_list = disasm_sp->GetInstructionList(); - // We are starting a new conditional block at the actual offset - condition_block_start_state = it; - } + if (inst_list.GetSize() == 0) { + DumpUnwindRowsToLog(log, range, unwind_plan); + return unwind_plan.GetRowCount() > 0; + } - if (log && log->GetVerbose()) { - StreamString strm; - lldb_private::FormatEntity::Entry format; - FormatEntity::Parse("${frame.pc}: ", format); - inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize(), show_address, - show_bytes, show_control_flow_kind, nullptr, nullptr, - nullptr, &format, 0); - log->PutString(strm.GetString()); - } + Instruction &first_inst = *inst_list.GetInstructionAtIndex(0); + const lldb::addr_t base_addr = first_inst.GetAddress().GetFileAddress(); + + // Map for storing the unwind state at a given offset. When we see a forward + // branch we add a new entry to this map with the actual unwind plan row and + // register context for the target address of the branch as the current data + // have to be valid for the target address of the branch too if we are in + // the same function. + std::map<lldb::addr_t, UnwindState> saved_unwind_states; - last_condition = m_inst_emulator_up->GetInstructionCondition(); + // Make a copy of the current instruction Row and save it in m_state so + // we can add updates as we process the instructions. + m_state.row = *unwind_plan.GetLastRow(); - m_inst_emulator_up->EvaluateInstruction( - eEmulateInstructionOptionIgnoreConditions); + // Add the initial state to the save list with offset 0. + auto condition_block_start_state = + saved_unwind_states.emplace(0, m_state).first; - // If the current instruction is a branch forward then save the current - // CFI information for the offset where we are branching. - if (m_forward_branch_offset != 0 && - range.ContainsFileAddress(inst->GetAddress().GetFileAddress() + - m_forward_branch_offset)) { - if (auto [it, inserted] = saved_unwind_states.emplace( - current_offset + m_forward_branch_offset, m_state); - inserted) - it->second.row.SetOffset(current_offset + m_forward_branch_offset); + // The architecture dependent condition code of the last processed + // instruction. + EmulateInstruction::InstructionCondition last_condition = + EmulateInstruction::UnconditionalCondition; + + std::deque<std::size_t> to_visit = {0}; + llvm::SmallSet<std::size_t, 0> enqueued = {0}; + + // Instructions reachable through jumps are inserted on the front. + // The next instruction is inserted on the back. + // Pop from the back to ensure non-branching instructions are visited + // sequentially. + while (!to_visit.empty()) { + const std::size_t current_index = to_visit.back(); + Instruction &inst = *inst_list.GetInstructionAtIndex(current_index); + to_visit.pop_back(); + DumpInstToLog(log, inst, inst_list); + + m_curr_row_modified = false; + m_branch_offset = 0; + + lldb::addr_t current_offset = + inst.GetAddress().GetFileAddress() - base_addr; + auto it = saved_unwind_states.upper_bound(current_offset); + assert(it != saved_unwind_states.begin() && + "Unwind row for the function entry missing"); + --it; // Move it to the row corresponding to the current offset + + // When state is forwarded through a branch, the offset of m_state.row is + // different from the offset available in saved_unwind_states. Use the + // forwarded state in this case, as the previous instruction may have been + // an unconditional jump. + // FIXME: this assignment can always be done unconditionally. + if (it->second.row.GetOffset() != m_state.row.GetOffset()) + m_state = it->second; + + m_inst_emulator_up->SetInstruction(inst.GetOpcode(), inst.GetAddress(), + nullptr); + const EmulateInstruction::InstructionCondition new_condition = + m_inst_emulator_up->GetInstructionCondition(); + + if (last_condition != new_condition) { + // If the last instruction was conditional with a different condition + // than the current condition then restore the state. + if (last_condition != EmulateInstruction::UnconditionalCondition) { + m_state = condition_block_start_state->second; + m_state.row.SetOffset(current_offset); + // The last instruction might already created a row for this offset + // and we want to overwrite it. + saved_unwind_states.insert_or_assign(current_offset, m_state); } - // Were there any changes to the CFI while evaluating this instruction? - if (m_curr_row_modified) { - // Save the modified row if we don't already have a CFI row in the - // current address - if (saved_unwind_states.count(current_offset + - inst->GetOpcode().GetByteSize()) == 0) { - m_state.row.SetOffset(current_offset + - inst->GetOpcode().GetByteSize()); - saved_unwind_states.emplace( - current_offset + inst->GetOpcode().GetByteSize(), m_state); + // We are starting a new conditional block at the actual offset + condition_block_start_state = it; + } + + last_condition = new_condition; + + m_inst_emulator_up->EvaluateInstruction( + eEmulateInstructionOptionIgnoreConditions); + + // If the current instruction is a branch forward then save the current + // CFI information for the offset where we are branching. + Address branch_address = inst.GetAddress(); + branch_address.Slide(m_branch_offset); + if (m_branch_offset != 0 && + range.ContainsFileAddress(branch_address.GetFileAddress())) { + if (auto [it, inserted] = saved_unwind_states.emplace( + current_offset + m_branch_offset, m_state); + inserted) { + it->second.row.SetOffset(current_offset + m_branch_offset); + if (std::size_t dest_instr_index = + inst_list.GetIndexOfInstructionAtAddress(branch_address); + dest_instr_index < inst_list.GetSize()) { + to_visit.push_front(dest_instr_index); + enqueued.insert(dest_instr_index); } } } - for (auto &[_, state] : saved_unwind_states) { - unwind_plan.InsertRow(std::move(state.row), - /*replace_existing=*/true); + + // If inst is a barrier, do not propagate state to the next instruction. + if (inst.IsBarrier()) + continue; + + // Were there any changes to the CFI while evaluating this instruction? + if (m_curr_row_modified) { + // Save the modified row if we don't already have a CFI row in the + // current address + const lldb::addr_t next_inst_offset = + current_offset + inst.GetOpcode().GetByteSize(); + if (saved_unwind_states.count(next_inst_offset) == 0) { + m_state.row.SetOffset(next_inst_offset); + saved_unwind_states.emplace(next_inst_offset, m_state); + } } - } - if (log && log->GetVerbose()) { - StreamString strm; - lldb::addr_t base_addr = range.GetBaseAddress().GetFileAddress(); - strm.Printf("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):", - base_addr, base_addr + range.GetByteSize()); - unwind_plan.Dump(strm, nullptr, base_addr); - log->PutString(strm.GetString()); + const size_t next_idx = current_index + 1; + const bool never_enqueued = enqueued.insert(next_idx).second; + if (never_enqueued && next_idx < inst_list.GetSize()) + to_visit.push_back(next_idx); } + + for (auto &[_, state] : saved_unwind_states) + unwind_plan.InsertRow(std::move(state.row), + /*replace_existing=*/true); + + DumpUnwindRowsToLog(log, range, unwind_plan); return unwind_plan.GetRowCount() > 0; } @@ -382,7 +414,7 @@ size_t UnwindAssemblyInstEmulation::WriteMemory( if (reg_num != LLDB_INVALID_REGNUM && generic_regnum != LLDB_REGNUM_GENERIC_SP) { if (m_pushed_regs.try_emplace(reg_num, addr).second) { - const int32_t offset = addr - m_initial_sp; + const int32_t offset = addr - m_initial_cfa; m_state.row.SetRegisterLocationToAtCFAPlusOffset(reg_num, offset, /*can_replace=*/true); m_curr_row_modified = true; @@ -502,21 +534,20 @@ bool UnwindAssemblyInstEmulation::WriteRegister( case EmulateInstruction::eContextAbsoluteBranchRegister: case EmulateInstruction::eContextRelativeBranchImmediate: { if (context.GetInfoType() == EmulateInstruction::eInfoTypeISAAndImmediate && - context.info.ISAAndImmediate.unsigned_data32 > 0) { - m_forward_branch_offset = - context.info.ISAAndImmediateSigned.signed_data32; + context.info.ISAAndImmediate.unsigned_data32 != 0) { + m_branch_offset = context.info.ISAAndImmediate.unsigned_data32; } else if (context.GetInfoType() == EmulateInstruction::eInfoTypeISAAndImmediateSigned && - context.info.ISAAndImmediateSigned.signed_data32 > 0) { - m_forward_branch_offset = context.info.ISAAndImmediate.unsigned_data32; + context.info.ISAAndImmediateSigned.signed_data32 != 0) { + m_branch_offset = context.info.ISAAndImmediateSigned.signed_data32; } else if (context.GetInfoType() == EmulateInstruction::eInfoTypeImmediate && - context.info.unsigned_immediate > 0) { - m_forward_branch_offset = context.info.unsigned_immediate; + context.info.unsigned_immediate != 0) { + m_branch_offset = context.info.unsigned_immediate; } else if (context.GetInfoType() == EmulateInstruction::eInfoTypeImmediateSigned && - context.info.signed_immediate > 0) { - m_forward_branch_offset = context.info.signed_immediate; + context.info.signed_immediate != 0) { + m_branch_offset = context.info.signed_immediate; } } break; @@ -549,7 +580,7 @@ bool UnwindAssemblyInstEmulation::WriteRegister( sp_reg_info.kinds[m_unwind_plan_ptr->GetRegisterKind()]; assert(cfa_reg_num != LLDB_INVALID_REGNUM); m_state.row.GetCFAValue().SetIsRegisterPlusOffset( - cfa_reg_num, m_initial_sp - sp_reg_val.GetAsUInt64()); + cfa_reg_num, m_initial_cfa - sp_reg_val.GetAsUInt64()); } } } @@ -580,7 +611,7 @@ bool UnwindAssemblyInstEmulation::WriteRegister( reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; assert(cfa_reg_num != LLDB_INVALID_REGNUM); m_state.row.GetCFAValue().SetIsRegisterPlusOffset( - cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64()); + cfa_reg_num, m_initial_cfa - reg_value.GetAsUInt64()); m_curr_row_modified = true; } break; @@ -593,7 +624,7 @@ bool UnwindAssemblyInstEmulation::WriteRegister( reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; assert(cfa_reg_num != LLDB_INVALID_REGNUM); m_state.row.GetCFAValue().SetIsRegisterPlusOffset( - cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64()); + cfa_reg_num, m_initial_cfa - reg_value.GetAsUInt64()); m_curr_row_modified = true; } break; @@ -604,7 +635,7 @@ bool UnwindAssemblyInstEmulation::WriteRegister( if (!m_state.fp_is_cfa) { m_state.row.GetCFAValue().SetIsRegisterPlusOffset( m_state.row.GetCFAValue().GetRegisterNumber(), - m_initial_sp - reg_value.GetAsUInt64()); + m_initial_cfa - reg_value.GetAsUInt64()); m_curr_row_modified = true; } break; diff --git a/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h b/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h index 96a0881..43daf1c 100644 --- a/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h +++ b/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h @@ -63,13 +63,17 @@ private: UnwindAssemblyInstEmulation(const lldb_private::ArchSpec &arch, lldb_private::EmulateInstruction *inst_emulator) : UnwindAssembly(arch), m_inst_emulator_up(inst_emulator), - m_range_ptr(nullptr), m_unwind_plan_ptr(nullptr), m_initial_sp(0), - m_curr_row_modified(false), m_forward_branch_offset(0) { + m_range_ptr(nullptr), m_unwind_plan_ptr(nullptr), + m_curr_row_modified(false) { if (m_inst_emulator_up) { m_inst_emulator_up->SetBaton(this); m_inst_emulator_up->SetCallbacks(ReadMemory, WriteMemory, ReadRegister, WriteRegister); } + // Initialize the CFA with a known value. In the 32 bit case it will be + // 0x80000000, and in the 64 bit case 0x8000000000000000. We use the address + // byte size to be safe for any future address sizes + m_initial_cfa = (1ull << ((m_arch.GetAddressByteSize() * 8) - 1)); } static size_t @@ -134,8 +138,8 @@ private: lldb_private::AddressRange *m_range_ptr; lldb_private::UnwindPlan *m_unwind_plan_ptr; UnwindState m_state; + uint64_t m_initial_cfa; typedef std::map<uint64_t, uint64_t> PushedRegisterToAddrMap; - uint64_t m_initial_sp; PushedRegisterToAddrMap m_pushed_regs; // While processing the instruction stream, we need to communicate some state @@ -148,7 +152,7 @@ private: bool m_curr_row_modified; // The instruction is branching forward with the given offset. 0 value means // no branching. - uint32_t m_forward_branch_offset; + int64_t m_branch_offset = 0; }; #endif // LLDB_SOURCE_PLUGINS_UNWINDASSEMBLY_INSTEMULATION_UNWINDASSEMBLYINSTEMULATION_H diff --git a/lldb/source/Symbol/CompileUnit.cpp b/lldb/source/Symbol/CompileUnit.cpp index 166f111..703ef13 100644 --- a/lldb/source/Symbol/CompileUnit.cpp +++ b/lldb/source/Symbol/CompileUnit.cpp @@ -27,14 +27,14 @@ CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data, language, is_optimized) {} CompileUnit::CompileUnit(const lldb::ModuleSP &module_sp, void *user_data, - lldb::SupportFileSP support_file_sp, + SupportFileNSP support_file_nsp, const lldb::user_id_t cu_sym_id, lldb::LanguageType language, lldb_private::LazyBool is_optimized, SupportFileList &&support_files) : ModuleChild(module_sp), UserID(cu_sym_id), m_user_data(user_data), m_language(language), m_flags(0), - m_primary_support_file_sp(support_file_sp), + m_primary_support_file_nsp(support_file_nsp), m_support_files(std::move(support_files)), m_is_optimized(is_optimized) { if (language != eLanguageTypeUnknown) m_flags.Set(flagsParsedLanguage); diff --git a/lldb/source/Symbol/CompilerType.cpp b/lldb/source/Symbol/CompilerType.cpp index c999ab2..1a39ea9 100644 --- a/lldb/source/Symbol/CompilerType.cpp +++ b/lldb/source/Symbol/CompilerType.cpp @@ -370,30 +370,10 @@ bool CompilerType::IsScalarOrUnscopedEnumerationType() const { } bool CompilerType::IsPromotableIntegerType() const { - // Unscoped enums are always considered as promotable, even if their - // underlying type does not need to be promoted (e.g. "int"). - if (IsUnscopedEnumerationType()) - return true; - - switch (GetBasicTypeEnumeration()) { - case lldb::eBasicTypeBool: - case lldb::eBasicTypeChar: - case lldb::eBasicTypeSignedChar: - case lldb::eBasicTypeUnsignedChar: - case lldb::eBasicTypeShort: - case lldb::eBasicTypeUnsignedShort: - case lldb::eBasicTypeWChar: - case lldb::eBasicTypeSignedWChar: - case lldb::eBasicTypeUnsignedWChar: - case lldb::eBasicTypeChar16: - case lldb::eBasicTypeChar32: - return true; - - default: - return false; - } - - llvm_unreachable("All cases handled above."); + if (IsValid()) + if (auto type_system_sp = GetTypeSystem()) + return type_system_sp->IsPromotableIntegerType(m_type); + return false; } bool CompilerType::IsPointerToVoid() const { diff --git a/lldb/source/Symbol/Function.cpp b/lldb/source/Symbol/Function.cpp index 2be1e389..11b823c 100644 --- a/lldb/source/Symbol/Function.cpp +++ b/lldb/source/Symbol/Function.cpp @@ -272,7 +272,7 @@ Function::Function(CompileUnit *comp_unit, lldb::user_id_t func_uid, Function::~Function() = default; -void Function::GetStartLineSourceInfo(SupportFileSP &source_file_sp, +void Function::GetStartLineSourceInfo(SupportFileNSP &source_file_sp, uint32_t &line_no) { line_no = 0; source_file_sp = std::make_shared<SupportFile>(); @@ -300,9 +300,9 @@ void Function::GetStartLineSourceInfo(SupportFileSP &source_file_sp, } } -llvm::Expected<std::pair<SupportFileSP, Function::SourceRange>> +llvm::Expected<std::pair<SupportFileNSP, Function::SourceRange>> Function::GetSourceInfo() { - SupportFileSP source_file_sp; + SupportFileNSP source_file_sp = std::make_shared<SupportFile>(); uint32_t start_line; GetStartLineSourceInfo(source_file_sp, start_line); LineTable *line_table = m_comp_unit->GetLineTable(); diff --git a/lldb/source/Symbol/LineEntry.cpp b/lldb/source/Symbol/LineEntry.cpp index c941a69..dcfbac8 100644 --- a/lldb/source/Symbol/LineEntry.cpp +++ b/lldb/source/Symbol/LineEntry.cpp @@ -14,7 +14,7 @@ using namespace lldb_private; LineEntry::LineEntry() - : range(), file_sp(std::make_shared<SupportFile>()), + : range(), synthetic(false), file_sp(std::make_shared<SupportFile>()), original_file_sp(std::make_shared<SupportFile>()), is_start_of_statement(0), is_start_of_basic_block(0), is_prologue_end(0), is_epilogue_begin(0), is_terminal_entry(0) {} @@ -33,7 +33,8 @@ void LineEntry::Clear() { } bool LineEntry::IsValid() const { - return range.GetBaseAddress().IsValid() && line != LLDB_INVALID_LINE_NUMBER; + return (range.GetBaseAddress().IsValid() || synthetic) && + line != LLDB_INVALID_LINE_NUMBER; } bool LineEntry::DumpStopContext(Stream *s, bool show_fullpaths) const { diff --git a/lldb/source/Symbol/LineTable.cpp b/lldb/source/Symbol/LineTable.cpp index ca3accd..ae2abf5 100644 --- a/lldb/source/Symbol/LineTable.cpp +++ b/lldb/source/Symbol/LineTable.cpp @@ -327,7 +327,7 @@ void LineTable::Dump(Stream *s, Target *target, Address::DumpStyle style, Address::DumpStyle fallback_style, bool show_line_ranges) { const size_t count = m_entries.size(); LineEntry line_entry; - SupportFileSP prev_file; + SupportFileNSP prev_file = std::make_shared<SupportFile>(); for (size_t idx = 0; idx < count; ++idx) { ConvertEntryAtIndexToLineEntry(idx, line_entry); line_entry.Dump(s, target, !prev_file->Equal(*line_entry.original_file_sp), diff --git a/lldb/source/Symbol/ObjectFile.cpp b/lldb/source/Symbol/ObjectFile.cpp index 6f5348c..ab28c17 100644 --- a/lldb/source/Symbol/ObjectFile.cpp +++ b/lldb/source/Symbol/ObjectFile.cpp @@ -254,13 +254,14 @@ ObjectFile::ObjectFile(const lldb::ModuleSP &module_sp, : ModuleChild(module_sp), m_file(), // This file could be different from the original module's file m_type(eTypeInvalid), m_strata(eStrataInvalid), - m_file_offset(file_offset), m_length(length), m_data(), m_process_wp(), + m_file_offset(file_offset), m_length(length), + m_data_nsp(std::make_shared<DataExtractor>()), m_process_wp(), m_memory_addr(LLDB_INVALID_ADDRESS), m_sections_up(), m_symtab_up(), m_symtab_once_up(new llvm::once_flag()) { if (file_spec_ptr) m_file = *file_spec_ptr; if (data_sp) - m_data.SetData(data_sp, data_offset, length); + m_data_nsp->SetData(data_sp, data_offset, length); Log *log = GetLog(LLDBLog::Object); LLDB_LOGF(log, "%p ObjectFile::ObjectFile() module = %p (%s), file = %s, " @@ -275,11 +276,12 @@ ObjectFile::ObjectFile(const lldb::ModuleSP &module_sp, const ProcessSP &process_sp, lldb::addr_t header_addr, DataBufferSP header_data_sp) : ModuleChild(module_sp), m_file(), m_type(eTypeInvalid), - m_strata(eStrataInvalid), m_file_offset(0), m_length(0), m_data(), - m_process_wp(process_sp), m_memory_addr(header_addr), m_sections_up(), - m_symtab_up(), m_symtab_once_up(new llvm::once_flag()) { + m_strata(eStrataInvalid), m_file_offset(0), m_length(0), + m_data_nsp(std::make_shared<DataExtractor>()), m_process_wp(process_sp), + m_memory_addr(header_addr), m_sections_up(), m_symtab_up(), + m_symtab_once_up(new llvm::once_flag()) { if (header_data_sp) - m_data.SetData(header_data_sp, 0, header_data_sp->GetByteSize()); + m_data_nsp->SetData(header_data_sp, 0, header_data_sp->GetByteSize()); Log *log = GetLog(LLDBLog::Object); LLDB_LOGF(log, "%p ObjectFile::ObjectFile() module = %p (%s), process = %p, " @@ -474,16 +476,16 @@ WritableDataBufferSP ObjectFile::ReadMemory(const ProcessSP &process_sp, size_t ObjectFile::GetData(lldb::offset_t offset, size_t length, DataExtractor &data) const { - // The entire file has already been mmap'ed into m_data, so just copy from + // The entire file has already been mmap'ed into m_data_nsp, so just copy from // there as the back mmap buffer will be shared with shared pointers. - return data.SetData(m_data, offset, length); + return data.SetData(*m_data_nsp.get(), offset, length); } size_t ObjectFile::CopyData(lldb::offset_t offset, size_t length, void *dst) const { - // The entire file has already been mmap'ed into m_data, so just copy from + // The entire file has already been mmap'ed into m_data_nsp, so just copy from // there Note that the data remains in target byte order. - return m_data.CopyData(offset, length, dst); + return m_data_nsp->CopyData(offset, length, dst); } size_t ObjectFile::ReadSectionData(Section *section, diff --git a/lldb/source/Symbol/SymbolContext.cpp b/lldb/source/Symbol/SymbolContext.cpp index 3bbd1ef..ead924a 100644 --- a/lldb/source/Symbol/SymbolContext.cpp +++ b/lldb/source/Symbol/SymbolContext.cpp @@ -324,12 +324,32 @@ uint32_t SymbolContext::GetResolvedMask() const { bool lldb_private::operator==(const SymbolContext &lhs, const SymbolContext &rhs) { - return lhs.function == rhs.function && lhs.symbol == rhs.symbol && + return SymbolContext::CompareWithoutSymbol(lhs, rhs) && + lhs.symbol == rhs.symbol; +} + +bool SymbolContext::CompareConsideringPossiblyNullSymbol( + const SymbolContext &lhs, const SymbolContext &rhs) { + if (!CompareWithoutSymbol(lhs, rhs)) + return false; + + // If one (or both) of the symbol context's symbol is empty, consider them + // equal. + if (!lhs.symbol || !rhs.symbol) + return true; + + // If both symbols are present, make sure they're the same. + return lhs.symbol == rhs.symbol; +} + +bool SymbolContext::CompareWithoutSymbol(const SymbolContext &lhs, + const SymbolContext &rhs) { + return lhs.function == rhs.function && lhs.module_sp.get() == rhs.module_sp.get() && lhs.comp_unit == rhs.comp_unit && lhs.target_sp.get() == rhs.target_sp.get() && LineEntry::Compare(lhs.line_entry, rhs.line_entry) == 0 && - lhs.variable == rhs.variable; + lhs.variable == rhs.variable && lhs.block == rhs.block; } bool lldb_private::operator!=(const SymbolContext &lhs, @@ -1200,7 +1220,10 @@ bool SymbolContextList::AppendIfUnique(const SymbolContext &sc, bool merge_symbol_into_function) { collection::iterator pos, end = m_symbol_contexts.end(); for (pos = m_symbol_contexts.begin(); pos != end; ++pos) { - if (*pos == sc) + // Because symbol contexts might first be built without the symbol, + // which is then appended later on, compare the symbol contexts taking into + // accout that one (or either) of them might not have a symbol yet. + if (SymbolContext::CompareConsideringPossiblyNullSymbol(*pos, sc)) return false; } if (merge_symbol_into_function && sc.symbol != nullptr && diff --git a/lldb/source/Symbol/SymbolFile.cpp b/lldb/source/Symbol/SymbolFile.cpp index 870d778d..bfc6300 100644 --- a/lldb/source/Symbol/SymbolFile.cpp +++ b/lldb/source/Symbol/SymbolFile.cpp @@ -126,6 +126,14 @@ void SymbolFile::FindFunctions(const Module::LookupInfo &lookup_info, bool include_inlines, SymbolContextList &sc_list) {} +void SymbolFile::FindFunctions(llvm::ArrayRef<Module::LookupInfo> lookup_infos, + const CompilerDeclContext &parent_decl_ctx, + bool include_inlines, + SymbolContextList &sc_list) { + for (const auto &lookup_info : lookup_infos) + FindFunctions(lookup_info, parent_decl_ctx, include_inlines, sc_list); +} + void SymbolFile::FindFunctions(const RegularExpression ®ex, bool include_inlines, SymbolContextList &sc_list) {} diff --git a/lldb/source/Symbol/Symtab.cpp b/lldb/source/Symbol/Symtab.cpp index 6080703..9964ae4 100644 --- a/lldb/source/Symbol/Symtab.cpp +++ b/lldb/source/Symbol/Symtab.cpp @@ -722,15 +722,11 @@ Symtab::AppendSymbolIndexesWithNameAndType(ConstString symbol_name, std::vector<uint32_t> &indexes) { std::lock_guard<std::recursive_mutex> guard(m_mutex); - if (AppendSymbolIndexesWithName(symbol_name, indexes) > 0) { - std::vector<uint32_t>::iterator pos = indexes.begin(); - while (pos != indexes.end()) { - if (symbol_type == eSymbolTypeAny || - m_symbols[*pos].GetType() == symbol_type) - ++pos; - else - pos = indexes.erase(pos); - } + if (AppendSymbolIndexesWithName(symbol_name, indexes) > 0 && + symbol_type != eSymbolTypeAny) { + llvm::erase_if(indexes, [this, symbol_type](uint32_t index) { + return m_symbols[index].GetType() != symbol_type; + }); } return indexes.size(); } @@ -742,15 +738,11 @@ uint32_t Symtab::AppendSymbolIndexesWithNameAndType( std::lock_guard<std::recursive_mutex> guard(m_mutex); if (AppendSymbolIndexesWithName(symbol_name, symbol_debug_type, - symbol_visibility, indexes) > 0) { - std::vector<uint32_t>::iterator pos = indexes.begin(); - while (pos != indexes.end()) { - if (symbol_type == eSymbolTypeAny || - m_symbols[*pos].GetType() == symbol_type) - ++pos; - else - pos = indexes.erase(pos); - } + symbol_visibility, indexes) > 0 && + symbol_type != eSymbolTypeAny) { + llvm::erase_if(indexes, [this, symbol_type](uint32_t index) { + return m_symbols[index].GetType() != symbol_type; + }); } return indexes.size(); } diff --git a/lldb/source/Symbol/TypeSystem.cpp b/lldb/source/Symbol/TypeSystem.cpp index f7d634f..8712142 100644 --- a/lldb/source/Symbol/TypeSystem.cpp +++ b/lldb/source/Symbol/TypeSystem.cpp @@ -123,6 +123,17 @@ CompilerType TypeSystem::GetTypeForFormatters(void *type) { return CompilerType(weak_from_this(), type); } +bool TypeSystem::IsPromotableIntegerType(lldb::opaque_compiler_type_t type) { + return false; +} + +llvm::Expected<CompilerType> +TypeSystem::DoIntegralPromotion(CompilerType from, + ExecutionContextScope *exe_scope) { + return llvm::createStringError( + "Integral promotion is not implemented for this TypeSystem"); +} + bool TypeSystem::IsTemplateType(lldb::opaque_compiler_type_t type) { return false; } diff --git a/lldb/source/Target/BorrowedStackFrame.cpp b/lldb/source/Target/BorrowedStackFrame.cpp new file mode 100644 index 0000000..5afadf2 --- /dev/null +++ b/lldb/source/Target/BorrowedStackFrame.cpp @@ -0,0 +1,187 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/BorrowedStackFrame.h" + +using namespace lldb; +using namespace lldb_private; + +char BorrowedStackFrame::ID; + +BorrowedStackFrame::BorrowedStackFrame( + StackFrameSP borrowed_frame_sp, uint32_t new_frame_index, + std::optional<uint32_t> new_concrete_frame_index) + : StackFrame( + borrowed_frame_sp->GetThread(), new_frame_index, + borrowed_frame_sp->GetConcreteFrameIndex(), + borrowed_frame_sp->GetRegisterContextSP(), + borrowed_frame_sp->GetStackID().GetPC(), + borrowed_frame_sp->GetStackID().GetCallFrameAddressWithoutMetadata(), + borrowed_frame_sp->m_behaves_like_zeroth_frame, + &borrowed_frame_sp->GetSymbolContext(eSymbolContextEverything)), + m_borrowed_frame_sp(borrowed_frame_sp), + m_new_frame_index(new_frame_index) { + if (new_concrete_frame_index) + m_new_concrete_frame_index = *new_concrete_frame_index; + else + m_new_concrete_frame_index = + IsInlined() ? LLDB_INVALID_FRAME_ID : new_frame_index; +} + +uint32_t BorrowedStackFrame::GetFrameIndex() const { return m_new_frame_index; } + +void BorrowedStackFrame::SetFrameIndex(uint32_t index) { + m_new_frame_index = index; +} + +uint32_t BorrowedStackFrame::GetConcreteFrameIndex() { + // FIXME: We need to find where the concrete frame into which this frame was + // inlined landed in the new stack frame list as that is the correct concrete + // frame index in this + // stack frame. + return m_new_concrete_frame_index; +} + +StackID &BorrowedStackFrame::GetStackID() { + return m_borrowed_frame_sp->GetStackID(); +} + +const Address &BorrowedStackFrame::GetFrameCodeAddress() { + return m_borrowed_frame_sp->GetFrameCodeAddress(); +} + +Address BorrowedStackFrame::GetFrameCodeAddressForSymbolication() { + return m_borrowed_frame_sp->GetFrameCodeAddressForSymbolication(); +} + +bool BorrowedStackFrame::ChangePC(addr_t pc) { + return m_borrowed_frame_sp->ChangePC(pc); +} + +const SymbolContext & +BorrowedStackFrame::GetSymbolContext(SymbolContextItem resolve_scope) { + return m_borrowed_frame_sp->GetSymbolContext(resolve_scope); +} + +llvm::Error BorrowedStackFrame::GetFrameBaseValue(Scalar &value) { + return m_borrowed_frame_sp->GetFrameBaseValue(value); +} + +DWARFExpressionList * +BorrowedStackFrame::GetFrameBaseExpression(Status *error_ptr) { + return m_borrowed_frame_sp->GetFrameBaseExpression(error_ptr); +} + +Block *BorrowedStackFrame::GetFrameBlock() { + return m_borrowed_frame_sp->GetFrameBlock(); +} + +RegisterContextSP BorrowedStackFrame::GetRegisterContext() { + return m_borrowed_frame_sp->GetRegisterContext(); +} + +VariableList *BorrowedStackFrame::GetVariableList(bool get_file_globals, + Status *error_ptr) { + return m_borrowed_frame_sp->GetVariableList(get_file_globals, error_ptr); +} + +VariableListSP +BorrowedStackFrame::GetInScopeVariableList(bool get_file_globals, + bool must_have_valid_location) { + return m_borrowed_frame_sp->GetInScopeVariableList(get_file_globals, + must_have_valid_location); +} + +ValueObjectSP BorrowedStackFrame::GetValueForVariableExpressionPath( + llvm::StringRef var_expr, DynamicValueType use_dynamic, uint32_t options, + VariableSP &var_sp, Status &error) { + return m_borrowed_frame_sp->GetValueForVariableExpressionPath( + var_expr, use_dynamic, options, var_sp, error); +} + +bool BorrowedStackFrame::HasDebugInformation() { + return m_borrowed_frame_sp->HasDebugInformation(); +} + +const char *BorrowedStackFrame::Disassemble() { + return m_borrowed_frame_sp->Disassemble(); +} + +ValueObjectSP BorrowedStackFrame::GetValueObjectForFrameVariable( + const VariableSP &variable_sp, DynamicValueType use_dynamic) { + return m_borrowed_frame_sp->GetValueObjectForFrameVariable(variable_sp, + use_dynamic); +} + +bool BorrowedStackFrame::IsInlined() { + return m_borrowed_frame_sp->IsInlined(); +} + +bool BorrowedStackFrame::IsSynthetic() const { + return m_borrowed_frame_sp->IsSynthetic(); +} + +bool BorrowedStackFrame::IsHistorical() const { + return m_borrowed_frame_sp->IsHistorical(); +} + +bool BorrowedStackFrame::IsArtificial() const { + return m_borrowed_frame_sp->IsArtificial(); +} + +bool BorrowedStackFrame::IsHidden() { return m_borrowed_frame_sp->IsHidden(); } + +const char *BorrowedStackFrame::GetFunctionName() { + return m_borrowed_frame_sp->GetFunctionName(); +} + +const char *BorrowedStackFrame::GetDisplayFunctionName() { + return m_borrowed_frame_sp->GetDisplayFunctionName(); +} + +ValueObjectSP BorrowedStackFrame::FindVariable(ConstString name) { + return m_borrowed_frame_sp->FindVariable(name); +} + +SourceLanguage BorrowedStackFrame::GetLanguage() { + return m_borrowed_frame_sp->GetLanguage(); +} + +SourceLanguage BorrowedStackFrame::GuessLanguage() { + return m_borrowed_frame_sp->GuessLanguage(); +} + +ValueObjectSP BorrowedStackFrame::GuessValueForAddress(addr_t addr) { + return m_borrowed_frame_sp->GuessValueForAddress(addr); +} + +ValueObjectSP +BorrowedStackFrame::GuessValueForRegisterAndOffset(ConstString reg, + int64_t offset) { + return m_borrowed_frame_sp->GuessValueForRegisterAndOffset(reg, offset); +} + +StructuredData::ObjectSP BorrowedStackFrame::GetLanguageSpecificData() { + return m_borrowed_frame_sp->GetLanguageSpecificData(); +} + +RecognizedStackFrameSP BorrowedStackFrame::GetRecognizedFrame() { + return m_borrowed_frame_sp->GetRecognizedFrame(); +} + +StackFrameSP BorrowedStackFrame::GetBorrowedFrame() const { + return m_borrowed_frame_sp; +} + +bool BorrowedStackFrame::isA(const void *ClassID) const { + return ClassID == &ID || StackFrame::isA(ClassID); +} + +bool BorrowedStackFrame::classof(const StackFrame *obj) { + return obj->isA(&ID); +} diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt index b7788e8..df2ee03 100644 --- a/lldb/source/Target/CMakeLists.txt +++ b/lldb/source/Target/CMakeLists.txt @@ -38,8 +38,10 @@ add_lldb_library(lldbTarget RegisterNumber.cpp RemoteAwarePlatform.cpp ScriptedThreadPlan.cpp + SyntheticFrameProvider.cpp SectionLoadHistory.cpp SectionLoadList.cpp + BorrowedStackFrame.cpp StackFrame.cpp StackFrameList.cpp StackFrameRecognizer.cpp @@ -80,7 +82,6 @@ add_lldb_library(lldbTarget UnixSignals.cpp UnwindAssembly.cpp UnwindLLDB.cpp - VerboseTrapFrameRecognizer.cpp ADDITIONAL_HEADER_DIRS ${LLDB_INCLUDE_DIR}/lldb/Target diff --git a/lldb/source/Target/ExecutionContext.cpp b/lldb/source/Target/ExecutionContext.cpp index a795913..b16ff26 100644 --- a/lldb/source/Target/ExecutionContext.cpp +++ b/lldb/source/Target/ExecutionContext.cpp @@ -466,10 +466,13 @@ operator=(const ExecutionContext &exe_ctx) { else m_tid = LLDB_INVALID_THREAD_ID; lldb::StackFrameSP frame_sp(exe_ctx.GetFrameSP()); - if (frame_sp) + if (frame_sp) { m_stack_id = frame_sp->GetStackID(); - else + m_frame_list_wp = frame_sp->GetContainingStackFrameList(); + } else { m_stack_id.Clear(); + m_frame_list_wp.reset(); + } return *this; } @@ -511,6 +514,7 @@ void ExecutionContextRef::SetThreadSP(const lldb::ThreadSP &thread_sp) { void ExecutionContextRef::SetFrameSP(const lldb::StackFrameSP &frame_sp) { if (frame_sp) { m_stack_id = frame_sp->GetStackID(); + m_frame_list_wp = frame_sp->GetContainingStackFrameList(); SetThreadSP(frame_sp->GetThread()); } else { ClearFrame(); @@ -638,6 +642,15 @@ lldb::ThreadSP ExecutionContextRef::GetThreadSP() const { lldb::StackFrameSP ExecutionContextRef::GetFrameSP() const { if (m_stack_id.IsValid()) { + // Try the remembered frame list first to avoid circular dependencies + // during frame provider initialization. + if (auto frame_list_sp = m_frame_list_wp.lock()) { + if (auto frame_sp = frame_list_sp->GetFrameWithStackID(m_stack_id)) + return frame_sp; + } + + // Fallback: ask the thread, which might re-trigger the frame provider + // initialization. lldb::ThreadSP thread_sp(GetThreadSP()); if (thread_sp) return thread_sp->GetFrameWithStackID(m_stack_id); diff --git a/lldb/source/Target/InstrumentationRuntime.cpp b/lldb/source/Target/InstrumentationRuntime.cpp index 7e58e8b..d9800a8 100644 --- a/lldb/source/Target/InstrumentationRuntime.cpp +++ b/lldb/source/Target/InstrumentationRuntime.cpp @@ -55,7 +55,8 @@ void InstrumentationRuntime::ModulesDidLoad( return IterationAction::Continue; const RegularExpression &runtime_regex = GetPatternForRuntimeLibrary(); - if (runtime_regex.Execute(file_spec.GetFilename().GetCString()) || + if (MatchAllModules() || + runtime_regex.Execute(file_spec.GetFilename().GetCString()) || module_sp->IsExecutable()) { if (CheckIfRuntimeIsValid(module_sp)) { SetRuntimeModuleSP(module_sp); diff --git a/lldb/source/Target/Language.cpp b/lldb/source/Target/Language.cpp index 8268d4a..c8b09c3 100644 --- a/lldb/source/Target/Language.cpp +++ b/lldb/source/Target/Language.cpp @@ -159,6 +159,48 @@ void Language::ForEach( } } +llvm::Expected<LanguageType> +Language::GetExceptionLanguageForLanguage(llvm::StringRef lang_name) { + LanguageType language = Language::GetLanguageTypeFromString(lang_name); + LanguageType exception_language = eLanguageTypeUnknown; + + llvm::StringRef error_context; + switch (language) { + case eLanguageTypeC89: + case eLanguageTypeC: + case eLanguageTypeC99: + case eLanguageTypeC11: + exception_language = eLanguageTypeC; + break; + case eLanguageTypeC_plus_plus: + case eLanguageTypeC_plus_plus_03: + case eLanguageTypeC_plus_plus_11: + case eLanguageTypeC_plus_plus_14: + exception_language = eLanguageTypeC_plus_plus; + break; + case eLanguageTypeObjC_plus_plus: + error_context = + "Set exception breakpoints separately for c++ and objective-c"; + break; + case eLanguageTypeUnknown: + error_context = "Unknown language type for exception breakpoint"; + break; + default: + if (Language *languagePlugin = Language::FindPlugin(language)) { + if (languagePlugin->SupportsExceptionBreakpointsOnThrow() || + languagePlugin->SupportsExceptionBreakpointsOnCatch()) { + exception_language = language; + break; + } + } + error_context = "Unsupported language type for exception breakpoint"; + } + if (!error_context.empty()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + error_context); + return exception_language; +} + bool Language::IsTopLevelFunction(Function &function) { return false; } lldb::TypeCategoryImplSP Language::GetFormatters() { return nullptr; } diff --git a/lldb/source/Target/ModuleCache.cpp b/lldb/source/Target/ModuleCache.cpp index f737836..9978946 100644 --- a/lldb/source/Target/ModuleCache.cpp +++ b/lldb/source/Target/ModuleCache.cpp @@ -255,7 +255,7 @@ Status ModuleCache::Get(const FileSpec &root_dir_spec, const char *hostname, cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec(); error = ModuleList::GetSharedModule(cached_module_spec, cached_module_sp, - nullptr, nullptr, did_create_ptr, false); + nullptr, did_create_ptr, false); if (error.Fail()) return error; diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp index 8681ada..5b0930c 100644 --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -163,11 +163,12 @@ Platform::LocateExecutableScriptingResources(Target *target, Module &module, Status Platform::GetSharedModule( const ModuleSpec &module_spec, Process *process, ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr, llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules, bool *did_create_ptr) { if (IsHost()) - return ModuleList::GetSharedModule(module_spec, module_sp, - module_search_paths_ptr, old_modules, + // Note: module_search_paths_ptr functionality is now handled internally + // by getting target from module_spec and calling + // target->GetExecutableSearchPaths() + return ModuleList::GetSharedModule(module_spec, module_sp, old_modules, did_create_ptr, false); // Module resolver lambda. @@ -180,16 +181,14 @@ Status Platform::GetSharedModule( resolved_spec = spec; resolved_spec.GetFileSpec().PrependPathComponent(m_sdk_sysroot); // Try to get shared module with resolved spec. - error = ModuleList::GetSharedModule(resolved_spec, module_sp, - module_search_paths_ptr, old_modules, + error = ModuleList::GetSharedModule(resolved_spec, module_sp, old_modules, did_create_ptr, false); } // If we don't have sysroot or it didn't work then // try original module spec. if (!error.Success()) { resolved_spec = spec; - error = ModuleList::GetSharedModule(resolved_spec, module_sp, - module_search_paths_ptr, old_modules, + error = ModuleList::GetSharedModule(resolved_spec, module_sp, old_modules, did_create_ptr, false); } if (error.Success() && module_sp) @@ -731,10 +730,8 @@ bool Platform::SetOSVersion(llvm::VersionTuple version) { return false; } -Status -Platform::ResolveExecutable(const ModuleSpec &module_spec, - lldb::ModuleSP &exe_module_sp, - const FileSpecList *module_search_paths_ptr) { +Status Platform::ResolveExecutable(const ModuleSpec &module_spec, + lldb::ModuleSP &exe_module_sp) { // We may connect to a process and use the provided executable (Don't use // local $PATH). @@ -750,9 +747,8 @@ Platform::ResolveExecutable(const ModuleSpec &module_spec, if (resolved_module_spec.GetArchitecture().IsValid() || resolved_module_spec.GetUUID().IsValid()) { - Status error = - ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, - module_search_paths_ptr, nullptr, nullptr); + Status error = ModuleList::GetSharedModule(resolved_module_spec, + exe_module_sp, nullptr, nullptr); if (exe_module_sp && exe_module_sp->GetObjectFile()) return error; @@ -767,9 +763,9 @@ Platform::ResolveExecutable(const ModuleSpec &module_spec, Status error; for (const ArchSpec &arch : GetSupportedArchitectures(process_host_arch)) { resolved_module_spec.GetArchitecture() = arch; - error = - ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, - module_search_paths_ptr, nullptr, nullptr); + + error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, + nullptr, nullptr); if (error.Success()) { if (exe_module_sp && exe_module_sp->GetObjectFile()) break; @@ -1446,16 +1442,13 @@ const std::vector<ConstString> &Platform::GetTrapHandlerSymbolNames() { return m_trap_handlers; } -Status -Platform::GetCachedExecutable(ModuleSpec &module_spec, - lldb::ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr) { +Status Platform::GetCachedExecutable(ModuleSpec &module_spec, + lldb::ModuleSP &module_sp) { FileSpec platform_spec = module_spec.GetFileSpec(); Status error = GetRemoteSharedModule( module_spec, nullptr, module_sp, [&](const ModuleSpec &spec) { - return Platform::ResolveExecutable(spec, module_sp, - module_search_paths_ptr); + return Platform::ResolveExecutable(spec, module_sp); }, nullptr); if (error.Success()) { @@ -1497,7 +1490,7 @@ Status Platform::GetRemoteSharedModule(const ModuleSpec &module_spec, for (const ArchSpec &arch : GetSupportedArchitectures(process_host_arch)) { arch_module_spec.GetArchitecture() = arch; error = ModuleList::GetSharedModule(arch_module_spec, module_sp, nullptr, - nullptr, nullptr); + nullptr); // Did we find an executable using one of the if (error.Success() && module_sp) break; @@ -1673,11 +1666,12 @@ void Platform::CallLocateModuleCallbackIfSet(const ModuleSpec &module_spec, cached_module_spec.GetUUID().Clear(); // Clear UUID since it may contain md5 // content hash instead of real UUID. cached_module_spec.GetFileSpec() = module_file_spec; + cached_module_spec.GetSymbolFileSpec() = symbol_file_spec; cached_module_spec.GetPlatformFileSpec() = module_spec.GetFileSpec(); cached_module_spec.SetObjectOffset(0); error = ModuleList::GetSharedModule(cached_module_spec, module_sp, nullptr, - nullptr, did_create_ptr, false); + did_create_ptr, false, false); if (error.Success() && module_sp) { // Succeeded to load the module file. LLDB_LOGF(log, "%s: locate module callback succeeded: module=%s symbol=%s", diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index fb9e7eb..9c8e8fa7 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -65,7 +65,6 @@ #include "lldb/Target/ThreadPlanCallFunction.h" #include "lldb/Target/ThreadPlanStack.h" #include "lldb/Target/UnixSignals.h" -#include "lldb/Target/VerboseTrapFrameRecognizer.h" #include "lldb/Utility/AddressableBits.h" #include "lldb/Utility/Event.h" #include "lldb/Utility/LLDBLog.h" @@ -513,7 +512,6 @@ Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp, // We should have a plugin do the registration instead, for example, a // common C LanguageRuntime plugin. RegisterAssertFrameRecognizer(this); - RegisterVerboseTrapFrameRecognizer(*this); } Process::~Process() { @@ -2454,8 +2452,10 @@ size_t Process::ReadScalarIntegerFromMemory(addr_t addr, uint32_t byte_size, scalar = data.GetMaxU32(&offset, byte_size); else scalar = data.GetMaxU64(&offset, byte_size); - if (is_signed) + if (is_signed) { + scalar.MakeSigned(); scalar.SignExtend(byte_size * 8); + } return bytes_read; } } else { @@ -3258,6 +3258,7 @@ Status Process::ConnectRemote(llvm::StringRef remote_url) { if (state == eStateStopped || state == eStateCrashed) { // If we attached and actually have a process on the other end, then // this ended up being the equivalent of an attach. + SetShouldDetach(true); CompleteAttach(); // This delays passing the stopped event to listeners till @@ -6546,7 +6547,7 @@ Status Process::WriteMemoryTags(lldb::addr_t addr, size_t len, // Create a CoreFileMemoryRange from a MemoryRegionInfo static CoreFileMemoryRange -CreateCoreFileMemoryRange(const MemoryRegionInfo ®ion) { +CreateCoreFileMemoryRange(const lldb_private::MemoryRegionInfo ®ion) { const addr_t addr = region.GetRange().GetRangeBase(); llvm::AddressRange range(addr, addr + region.GetRange().GetByteSize()); return {range, region.GetLLDBPermissions()}; @@ -6555,7 +6556,7 @@ CreateCoreFileMemoryRange(const MemoryRegionInfo ®ion) { // Add dirty pages to the core file ranges and return true if dirty pages // were added. Return false if the dirty page information is not valid or in // the region. -static bool AddDirtyPages(const MemoryRegionInfo ®ion, +static bool AddDirtyPages(const lldb_private::MemoryRegionInfo ®ion, CoreFileMemoryRanges &ranges) { const auto &dirty_page_list = region.GetDirtyPageList(); if (!dirty_page_list) @@ -6594,8 +6595,8 @@ static bool AddDirtyPages(const MemoryRegionInfo ®ion, // given region. If the region has dirty page information, only dirty pages // will be added to \a ranges, else the entire range will be added to \a // ranges. -static void AddRegion(const MemoryRegionInfo ®ion, bool try_dirty_pages, - CoreFileMemoryRanges &ranges) { +static void AddRegion(const lldb_private::MemoryRegionInfo ®ion, + bool try_dirty_pages, CoreFileMemoryRanges &ranges) { // Don't add empty ranges. if (region.GetRange().GetByteSize() == 0) return; @@ -6618,7 +6619,7 @@ static void SaveDynamicLoaderSections(Process &process, if (!dyld) return; - std::vector<MemoryRegionInfo> dynamic_loader_mem_regions; + std::vector<lldb_private::MemoryRegionInfo> dynamic_loader_mem_regions; std::function<bool(const lldb_private::Thread &)> save_thread_predicate = [&](const lldb_private::Thread &t) -> bool { return options.ShouldThreadBeSaved(t.GetID()); @@ -6743,10 +6744,11 @@ static void GetCoreFileSaveRangesStackOnly(Process &process, // TODO: We should refactor CoreFileMemoryRanges to use the lldb range type, and // then add an intersect method on it, or MemoryRegionInfo. -static MemoryRegionInfo Intersect(const MemoryRegionInfo &lhs, - const MemoryRegionInfo::RangeType &rhs) { +static lldb_private::MemoryRegionInfo +Intersect(const lldb_private::MemoryRegionInfo &lhs, + const lldb_private::MemoryRegionInfo::RangeType &rhs) { - MemoryRegionInfo region_info; + lldb_private::MemoryRegionInfo region_info; region_info.SetLLDBPermissions(lhs.GetLLDBPermissions()); region_info.GetRange() = lhs.GetRange().Intersect(rhs); diff --git a/lldb/source/Target/RemoteAwarePlatform.cpp b/lldb/source/Target/RemoteAwarePlatform.cpp index cac738e..89b946b 100644 --- a/lldb/source/Target/RemoteAwarePlatform.cpp +++ b/lldb/source/Target/RemoteAwarePlatform.cpp @@ -29,9 +29,8 @@ bool RemoteAwarePlatform::GetModuleSpec(const FileSpec &module_file_spec, return false; } -Status RemoteAwarePlatform::ResolveExecutable( - const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp, - const FileSpecList *module_search_paths_ptr) { +Status RemoteAwarePlatform::ResolveExecutable(const ModuleSpec &module_spec, + lldb::ModuleSP &exe_module_sp) { ModuleSpec resolved_module_spec(module_spec); // The host platform can resolve the path more aggressively. @@ -47,12 +46,10 @@ Status RemoteAwarePlatform::ResolveExecutable( if (!FileSystem::Instance().Exists(resolved_file_spec)) FileSystem::Instance().ResolveExecutableLocation(resolved_file_spec); } else if (m_remote_platform_sp) { - return GetCachedExecutable(resolved_module_spec, exe_module_sp, - module_search_paths_ptr); + return GetCachedExecutable(resolved_module_spec, exe_module_sp); } - return Platform::ResolveExecutable(resolved_module_spec, exe_module_sp, - module_search_paths_ptr); + return Platform::ResolveExecutable(resolved_module_spec, exe_module_sp); } Status RemoteAwarePlatform::RunShellCommand( diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index 2ed58c53..3bbb851 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -45,6 +45,9 @@ using namespace lldb; using namespace lldb_private; +// LLVM RTTI support. +char StackFrame::ID; + // The first bits in the flags are reserved for the SymbolContext::Scope bits // so we know if we have tried to look up information in our internal symbol // context (m_sc) already. @@ -328,6 +331,13 @@ StackFrame::GetSymbolContext(SymbolContextItem resolve_scope) { // following the function call instruction... Address lookup_addr(GetFrameCodeAddressForSymbolication()); + // For PC-less frames (e.g., scripted frames), skip PC-based symbol + // resolution and preserve any already-populated SymbolContext fields. + if (!lookup_addr.IsValid()) { + m_flags.Set(resolve_scope | resolved); + return m_sc; + } + if (m_sc.module_sp) { // We have something in our stack frame symbol context, lets check if we // haven't already tried to lookup one of those things. If we haven't @@ -1344,18 +1354,18 @@ const char *StackFrame::GetDisplayFunctionName() { SourceLanguage StackFrame::GetLanguage() { CompileUnit *cu = GetSymbolContext(eSymbolContextCompUnit).comp_unit; if (cu) - return cu->GetLanguage(); + return SourceLanguage{cu->GetLanguage()}; return {}; } SourceLanguage StackFrame::GuessLanguage() { SourceLanguage lang_type = GetLanguage(); - if (lang_type == eLanguageTypeUnknown) { + if (!lang_type) { SymbolContext sc = GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol); if (sc.function) - lang_type = LanguageType(sc.function->GetMangled().GuessLanguage()); + lang_type = SourceLanguage(sc.function->GetMangled().GuessLanguage()); else if (sc.symbol) lang_type = SourceLanguage(sc.symbol->GetMangled().GuessLanguage()); } @@ -2054,10 +2064,10 @@ bool StackFrame::GetStatus(Stream &strm, bool show_frame_info, bool show_source, disasm_display = debugger.GetStopDisassemblyDisplay(); GetSymbolContext(eSymbolContextCompUnit | eSymbolContextLineEntry); - if (m_sc.comp_unit && m_sc.line_entry.IsValid()) { + if (m_sc.comp_unit || m_sc.line_entry.IsValid()) { have_debuginfo = true; if (source_lines_before > 0 || source_lines_after > 0) { - SupportFileSP source_file_sp = m_sc.line_entry.file_sp; + SupportFileNSP source_file_sp = m_sc.line_entry.file_sp; uint32_t start_line = m_sc.line_entry.line; if (!start_line && m_sc.function) { m_sc.function->GetStartLineSourceInfo(source_file_sp, start_line); diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp index ccf874f..896a760 100644 --- a/lldb/source/Target/StackFrameList.cpp +++ b/lldb/source/Target/StackFrameList.cpp @@ -20,6 +20,7 @@ #include "lldb/Target/StackFrame.h" #include "lldb/Target/StackFrameRecognizer.h" #include "lldb/Target/StopInfo.h" +#include "lldb/Target/SyntheticFrameProvider.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Target/Unwind.h" @@ -55,6 +56,49 @@ StackFrameList::~StackFrameList() { Clear(); } +SyntheticStackFrameList::SyntheticStackFrameList( + Thread &thread, lldb::StackFrameListSP input_frames, + const lldb::StackFrameListSP &prev_frames_sp, bool show_inline_frames) + : StackFrameList(thread, prev_frames_sp, show_inline_frames), + m_input_frames(std::move(input_frames)) {} + +bool SyntheticStackFrameList::FetchFramesUpTo( + uint32_t end_idx, InterruptionControl allow_interrupt) { + + size_t num_synthetic_frames = 0; + // Check if the thread has a synthetic frame provider. + if (auto provider_sp = m_thread.GetFrameProvider()) { + // Use the synthetic frame provider to generate frames lazily. + // Keep fetching until we reach end_idx or the provider returns an error. + for (uint32_t idx = m_frames.size(); idx <= end_idx; idx++) { + if (allow_interrupt && + m_thread.GetProcess()->GetTarget().GetDebugger().InterruptRequested()) + return true; + auto frame_or_err = provider_sp->GetFrameAtIndex(idx); + if (!frame_or_err) { + // Provider returned error - we've reached the end. + LLDB_LOG_ERROR(GetLog(LLDBLog::Thread), frame_or_err.takeError(), + "Frame provider reached end at index {0}: {1}", idx); + SetAllFramesFetched(); + break; + } + StackFrameSP frame_sp = *frame_or_err; + if (frame_sp->IsSynthetic()) + frame_sp->GetStackID().SetCFA(num_synthetic_frames++, + GetThread().GetProcess().get()); + // Set the frame list weak pointer so ExecutionContextRef can resolve + // the frame without calling Thread::GetStackFrameList(). + frame_sp->m_frame_list_wp = shared_from_this(); + m_frames.push_back(frame_sp); + } + + return false; // Not interrupted. + } + + // If no provider, fall back to the base implementation. + return StackFrameList::FetchFramesUpTo(end_idx, allow_interrupt); +} + void StackFrameList::CalculateCurrentInlinedDepth() { uint32_t cur_inlined_depth = GetCurrentInlinedDepth(); if (cur_inlined_depth == UINT32_MAX) { @@ -330,6 +374,7 @@ void StackFrameList::SynthesizeTailCallFrames(StackFrame &next_frame) { m_thread.shared_from_this(), frame_idx, concrete_frame_idx, cfa, cfa_is_valid, pc, StackFrame::Kind::Regular, artificial, behaves_like_zeroth_frame, &sc); + synth_frame->m_frame_list_wp = shared_from_this(); m_frames.push_back(synth_frame); LLDB_LOG(log, "Pushed frame {0} at {1:x}", callee->GetDisplayName(), pc); } @@ -445,6 +490,7 @@ bool StackFrameList::FetchFramesUpTo(uint32_t end_idx, unwind_frame_sp = std::make_shared<StackFrame>( m_thread.shared_from_this(), m_frames.size(), idx, reg_ctx_sp, cfa, pc, behaves_like_zeroth_frame, nullptr); + unwind_frame_sp->m_frame_list_wp = shared_from_this(); m_frames.push_back(unwind_frame_sp); } } else { @@ -479,6 +525,7 @@ bool StackFrameList::FetchFramesUpTo(uint32_t end_idx, // although its concrete index will stay the same. SynthesizeTailCallFrames(*unwind_frame_sp.get()); + unwind_frame_sp->m_frame_list_wp = shared_from_this(); m_frames.push_back(unwind_frame_sp); } @@ -503,6 +550,7 @@ bool StackFrameList::FetchFramesUpTo(uint32_t end_idx, unwind_frame_sp->GetRegisterContextSP(), cfa, next_frame_address, behaves_like_zeroth_frame, &next_frame_sc)); + frame_sp->m_frame_list_wp = shared_from_this(); m_frames.push_back(frame_sp); unwind_sc = next_frame_sc; curr_frame_address = next_frame_address; @@ -559,6 +607,7 @@ bool StackFrameList::FetchFramesUpTo(uint32_t end_idx, prev_frame->UpdatePreviousFrameFromCurrentFrame(*curr_frame); // Now copy the fixed up previous frame into the current frames so the // pointer doesn't change. + prev_frame_sp->m_frame_list_wp = shared_from_this(); m_frames[curr_frame_idx] = prev_frame_sp; #if defined(DEBUG_STACK_FRAMES) diff --git a/lldb/source/Target/SyntheticFrameProvider.cpp b/lldb/source/Target/SyntheticFrameProvider.cpp new file mode 100644 index 0000000..97ff42d --- /dev/null +++ b/lldb/source/Target/SyntheticFrameProvider.cpp @@ -0,0 +1,121 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Target/SyntheticFrameProvider.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +SyntheticFrameProvider::SyntheticFrameProvider(StackFrameListSP input_frames) + : m_input_frames(std::move(input_frames)) {} + +SyntheticFrameProvider::~SyntheticFrameProvider() = default; + +void ScriptedFrameProviderDescriptor::Dump(Stream *s) const { + if (!s) + return; + + s->Format(" ID: {0:x}\n", GetID()); + s->Printf(" Name: %s\n", GetName().str().c_str()); + + std::string description = GetDescription(); + if (!description.empty()) + s->Printf(" Description: %s\n", description.c_str()); + + // Show thread filter information. + if (thread_specs.empty()) { + s->PutCString(" Thread Filter: (applies to all threads)\n"); + } else { + s->Printf(" Thread Filter: %zu specification(s)\n", thread_specs.size()); + for (size_t i = 0; i < thread_specs.size(); ++i) { + const ThreadSpec &spec = thread_specs[i]; + s->Printf(" [%zu] ", i); + spec.GetDescription(s, lldb::eDescriptionLevelVerbose); + s->PutChar('\n'); + } + } +} + +uint32_t ScriptedFrameProviderDescriptor::GetID() const { + if (!scripted_metadata_sp) + return 0; + + return scripted_metadata_sp->GetID(); +} + +std::string ScriptedFrameProviderDescriptor::GetDescription() const { + // If we have an interface, call get_description() to fetch it. + if (interface_sp && scripted_metadata_sp) + return interface_sp->GetDescription(scripted_metadata_sp->GetClassName()); + return {}; +} + +llvm::Expected<SyntheticFrameProviderSP> SyntheticFrameProvider::CreateInstance( + StackFrameListSP input_frames, + const ScriptedFrameProviderDescriptor &descriptor) { + if (!input_frames) + return llvm::createStringError( + "cannot create synthetic frame provider: invalid input frames"); + + // Iterate through all registered ScriptedFrameProvider plugins. + ScriptedFrameProviderCreateInstance create_callback = nullptr; + for (uint32_t idx = 0; + (create_callback = + PluginManager::GetScriptedFrameProviderCreateCallbackAtIndex( + idx)) != nullptr; + ++idx) { + auto provider_or_err = create_callback(input_frames, descriptor); + if (!provider_or_err) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Target), provider_or_err.takeError(), + "Failed to create synthetic frame provider: {0}"); + continue; + } + + if (auto frame_provider_up = std::move(*provider_or_err)) + return std::move(frame_provider_up); + } + + return llvm::createStringError( + "cannot create synthetic frame provider: no suitable plugin found"); +} + +llvm::Expected<SyntheticFrameProviderSP> SyntheticFrameProvider::CreateInstance( + StackFrameListSP input_frames, llvm::StringRef plugin_name, + const std::vector<ThreadSpec> &thread_specs) { + if (!input_frames) + return llvm::createStringError( + "cannot create synthetic frame provider: invalid input frames"); + + // Look up the specific C++ plugin by name. + SyntheticFrameProviderCreateInstance create_callback = + PluginManager::GetSyntheticFrameProviderCreateCallbackForPluginName( + plugin_name); + + if (!create_callback) + return llvm::createStringError( + "cannot create synthetic frame provider: C++ plugin '%s' not found", + plugin_name.str().c_str()); + + auto provider_or_err = create_callback(input_frames, thread_specs); + if (!provider_or_err) + return provider_or_err.takeError(); + + if (auto frame_provider_sp = std::move(*provider_or_err)) + return std::move(frame_provider_sp); + + return llvm::createStringError( + "cannot create synthetic frame provider: C++ plugin '%s' returned null", + plugin_name.str().c_str()); +} diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index 1e43094..2305f10 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -156,8 +156,6 @@ static Status installExecutable(const Installer &installer) { return Status(); } -constexpr std::chrono::milliseconds EvaluateExpressionOptions::default_timeout; - Target::Arch::Arch(const ArchSpec &spec) : m_spec(spec), m_plugin_up(PluginManager::CreateArchitectureInstance(spec)) {} @@ -187,6 +185,8 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch, m_internal_stop_hooks(), m_latest_stop_hook_id(0), m_valid(true), m_suppress_stop_hooks(false), m_is_dummy_target(is_dummy_target), m_target_unique_id(g_target_unique_id++), + m_target_session_name( + llvm::formatv("Session {0}", m_target_unique_id).str()), m_frame_recognizer_manager_up( std::make_unique<StackFrameRecognizerManager>()) { SetEventName(eBroadcastBitBreakpointChanged, "breakpoint-changed"); @@ -194,6 +194,7 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch, SetEventName(eBroadcastBitModulesUnloaded, "modules-unloaded"); SetEventName(eBroadcastBitWatchpointChanged, "watchpoint-changed"); SetEventName(eBroadcastBitSymbolsLoaded, "symbols-loaded"); + SetEventName(eBroadcastBitNewTargetCreated, "new-target-created"); CheckInWithManager(); @@ -1779,9 +1780,9 @@ bool Target::SetArchitecture(const ArchSpec &arch_spec, bool set_platform, arch_spec.GetArchitectureName(), arch_spec.GetTriple().getTriple().c_str()); ModuleSpec module_spec(executable_sp->GetFileSpec(), other); - FileSpecList search_paths = GetExecutableSearchPaths(); + module_spec.SetTarget(shared_from_this()); Status error = ModuleList::GetSharedModule(module_spec, executable_sp, - &search_paths, nullptr, nullptr); + nullptr, nullptr); if (!error.Fail() && executable_sp) { SetExecutableModule(executable_sp, eLoadDependentsYes); @@ -1855,6 +1856,9 @@ void Target::NotifyModulesRemoved(lldb_private::ModuleList &module_list) { } void Target::ModulesDidLoad(ModuleList &module_list) { + if (GetPreloadSymbols()) + module_list.PreloadSymbols(GetParallelModuleLoad()); + const size_t num_images = module_list.GetSize(); if (m_valid && num_images) { for (size_t idx = 0; idx < num_images; ++idx) { @@ -2279,8 +2283,10 @@ size_t Target::ReadScalarIntegerFromMemory(const Address &addr, uint32_t byte_si else scalar = data.GetMaxU64(&offset, byte_size); - if (is_signed) + if (is_signed) { + scalar.MakeSigned(); scalar.SignExtend(byte_size * 8); + } return bytes_read; } } else { @@ -2295,7 +2301,7 @@ int64_t Target::ReadSignedIntegerFromMemory(const Address &addr, int64_t fail_value, Status &error, bool force_live_memory) { Scalar scalar; - if (ReadScalarIntegerFromMemory(addr, integer_byte_size, false, scalar, error, + if (ReadScalarIntegerFromMemory(addr, integer_byte_size, true, scalar, error, force_live_memory)) return scalar.SLongLong(fail_value); return fail_value; @@ -2350,6 +2356,7 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &orig_module_spec, // Apply any remappings specified in target.object-map: ModuleSpec module_spec(orig_module_spec); + module_spec.SetTarget(shared_from_this()); PathMappingList &obj_mapping = GetObjectPathMap(); if (std::optional<FileSpec> remapped_obj_file = obj_mapping.RemapPath(orig_module_spec.GetFileSpec().GetPath(), @@ -2408,9 +2415,9 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &orig_module_spec, transformed_spec.GetFileSpec().SetDirectory(transformed_dir); transformed_spec.GetFileSpec().SetFilename( module_spec.GetFileSpec().GetFilename()); + transformed_spec.SetTarget(shared_from_this()); error = ModuleList::GetSharedModule(transformed_spec, module_sp, - &search_paths, &old_modules, - &did_create_module); + &old_modules, &did_create_module); } } } @@ -2426,9 +2433,8 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &orig_module_spec, // cache. if (module_spec.GetUUID().IsValid()) { // We have a UUID, it is OK to check the global module list... - error = - ModuleList::GetSharedModule(module_spec, module_sp, &search_paths, - &old_modules, &did_create_module); + error = ModuleList::GetSharedModule(module_spec, module_sp, + &old_modules, &did_create_module); } if (!module_sp) { @@ -2436,8 +2442,8 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &orig_module_spec, // module in the shared module cache. if (m_platform_sp) { error = m_platform_sp->GetSharedModule( - module_spec, m_process_sp.get(), module_sp, &search_paths, - &old_modules, &did_create_module); + module_spec, m_process_sp.get(), module_sp, &old_modules, + &did_create_module); } else { error = Status::FromErrorString("no platform is currently set"); } @@ -2509,10 +2515,6 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &orig_module_spec, if (symbol_file_spec) module_sp->SetSymbolFileFileSpec(symbol_file_spec); - // Preload symbols outside of any lock, so hopefully we can do this for - // each library in parallel. - if (GetPreloadSymbols()) - module_sp->PreloadSymbols(); llvm::SmallVector<ModuleSP, 1> replaced_modules; for (ModuleSP &old_module_sp : old_modules) { if (m_images.GetIndexForModule(old_module_sp.get()) != @@ -3207,6 +3209,11 @@ bool Target::RunStopHooks(bool at_initial_stop) { bool should_stop = false; bool requested_continue = false; + // A stop hook might get deleted while running stop hooks. + // We have to decide what that means. We will follow the rule that deleting + // a stop hook while processing these stop hooks will delete it for FUTURE + // stops but not this stop. Fortunately, copying the m_stop_hooks to the + // active_hooks list before iterating over the hooks has this effect. for (auto cur_hook_sp : active_hooks) { bool any_thread_matched = false; for (auto exc_ctx : exc_ctx_with_reasons) { @@ -3713,6 +3720,61 @@ Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) { return error; } +llvm::Expected<uint32_t> Target::AddScriptedFrameProviderDescriptor( + const ScriptedFrameProviderDescriptor &descriptor) { + if (!descriptor.IsValid()) + return llvm::createStringError("invalid frame provider descriptor"); + + llvm::StringRef name = descriptor.GetName(); + if (name.empty()) + return llvm::createStringError( + "frame provider descriptor has no class name"); + + std::lock_guard<std::recursive_mutex> guard( + m_frame_provider_descriptors_mutex); + + uint32_t descriptor_id = descriptor.GetID(); + m_frame_provider_descriptors[descriptor_id] = descriptor; + + // Clear frame providers on existing threads so they reload with new config. + if (ProcessSP process_sp = GetProcessSP()) + for (ThreadSP thread_sp : process_sp->Threads()) + thread_sp->ClearScriptedFrameProvider(); + + return descriptor_id; +} + +bool Target::RemoveScriptedFrameProviderDescriptor(uint32_t id) { + std::lock_guard<std::recursive_mutex> guard( + m_frame_provider_descriptors_mutex); + bool removed = m_frame_provider_descriptors.erase(id); + + if (removed) + if (ProcessSP process_sp = GetProcessSP()) + for (ThreadSP thread_sp : process_sp->Threads()) + thread_sp->ClearScriptedFrameProvider(); + + return removed; +} + +void Target::ClearScriptedFrameProviderDescriptors() { + std::lock_guard<std::recursive_mutex> guard( + m_frame_provider_descriptors_mutex); + + m_frame_provider_descriptors.clear(); + + if (ProcessSP process_sp = GetProcessSP()) + for (ThreadSP thread_sp : process_sp->Threads()) + thread_sp->ClearScriptedFrameProvider(); +} + +const llvm::DenseMap<uint32_t, ScriptedFrameProviderDescriptor> & +Target::GetScriptedFrameProviderDescriptors() const { + std::lock_guard<std::recursive_mutex> guard( + m_frame_provider_descriptors_mutex); + return m_frame_provider_descriptors; +} + void Target::FinalizeFileActions(ProcessLaunchInfo &info) { Log *log = GetLog(LLDBLog::Process); @@ -3962,9 +4024,7 @@ void Target::StopHook::GetDescription(Stream &s, return; } - unsigned indent_level = s.GetIndentLevel(); - - s.SetIndentLevel(indent_level + 2); + auto indent_scope = s.MakeIndentScope(); s.Printf("Hook: %" PRIu64 "\n", GetID()); if (m_active) @@ -3978,19 +4038,17 @@ void Target::StopHook::GetDescription(Stream &s, if (m_specifier_sp) { s.Indent(); s.PutCString("Specifier:\n"); - s.SetIndentLevel(indent_level + 4); + auto indent_scope = s.MakeIndentScope(); m_specifier_sp->GetDescription(&s, level); - s.SetIndentLevel(indent_level + 2); } if (m_thread_spec_up) { StreamString tmp; s.Indent("Thread:\n"); m_thread_spec_up->GetDescription(&tmp, level); - s.SetIndentLevel(indent_level + 4); + auto indent_scope = s.MakeIndentScope(); s.Indent(tmp.GetString()); s.PutCString("\n"); - s.SetIndentLevel(indent_level + 2); } GetSubclassDescription(s, level); } @@ -4003,14 +4061,13 @@ void Target::StopHookCommandLine::GetSubclassDescription( s.PutCString(m_commands.GetStringAtIndex(0)); return; } - s.Indent("Commands: \n"); - s.SetIndentLevel(s.GetIndentLevel() + 4); + s.Indent("Commands:\n"); + auto indent_scope = s.MakeIndentScope(4); uint32_t num_commands = m_commands.GetSize(); for (uint32_t i = 0; i < num_commands; i++) { s.Indent(m_commands.GetStringAtIndex(i)); s.PutCString("\n"); } - s.SetIndentLevel(s.GetIndentLevel() - 4); } // Target::StopHookCommandLine @@ -4145,7 +4202,7 @@ void Target::StopHookScripted::GetSubclassDescription( return; s.Indent("Args:\n"); - s.SetIndentLevel(s.GetIndentLevel() + 4); + auto indent_scope = s.MakeIndentScope(4); auto print_one_element = [&s](llvm::StringRef key, StructuredData::Object *object) { @@ -4155,8 +4212,6 @@ void Target::StopHookScripted::GetSubclassDescription( }; as_dict->ForEach(print_one_element); - - s.SetIndentLevel(s.GetIndentLevel() - 4); } static constexpr OptionEnumValueElement g_dynamic_value_types[] = { @@ -4952,7 +5007,7 @@ void TargetProperties::SetStandardErrorPath(llvm::StringRef path) { SourceLanguage TargetProperties::GetLanguage() const { const uint32_t idx = ePropertyLanguage; - return {GetPropertyAtIndexAs<LanguageType>(idx, {})}; + return SourceLanguage{GetPropertyAtIndexAs<LanguageType>(idx, {})}; } llvm::StringRef TargetProperties::GetExpressionPrefixContents() { @@ -5090,17 +5145,17 @@ void TargetProperties::SetProcessLaunchInfo( const FileAction *input_file_action = launch_info.GetFileActionForFD(STDIN_FILENO); if (input_file_action) { - SetStandardInputPath(input_file_action->GetPath()); + SetStandardInputPath(input_file_action->GetFileSpec().GetPath()); } const FileAction *output_file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); if (output_file_action) { - SetStandardOutputPath(output_file_action->GetPath()); + SetStandardOutputPath(output_file_action->GetFileSpec().GetPath()); } const FileAction *error_file_action = launch_info.GetFileActionForFD(STDERR_FILENO); if (error_file_action) { - SetStandardErrorPath(error_file_action->GetPath()); + SetStandardErrorPath(error_file_action->GetFileSpec().GetPath()); } SetDetachOnError(launch_info.GetFlags().Test(lldb::eLaunchFlagDetachOnError)); SetDisableASLR(launch_info.GetFlags().Test(lldb::eLaunchFlagDisableASLR)); @@ -5203,6 +5258,11 @@ Target::TargetEventData::TargetEventData(const lldb::TargetSP &target_sp, const ModuleList &module_list) : EventData(), m_target_sp(target_sp), m_module_list(module_list) {} +Target::TargetEventData::TargetEventData( + const lldb::TargetSP &target_sp, const lldb::TargetSP &created_target_sp) + : EventData(), m_target_sp(target_sp), + m_created_target_sp(created_target_sp), m_module_list() {} + Target::TargetEventData::~TargetEventData() = default; llvm::StringRef Target::TargetEventData::GetFlavorString() { @@ -5237,6 +5297,15 @@ TargetSP Target::TargetEventData::GetTargetFromEvent(const Event *event_ptr) { return target_sp; } +TargetSP +Target::TargetEventData::GetCreatedTargetFromEvent(const Event *event_ptr) { + TargetSP created_target_sp; + const TargetEventData *event_data = GetEventDataFromEvent(event_ptr); + if (event_data) + created_target_sp = event_data->m_created_target_sp; + return created_target_sp; +} + ModuleList Target::TargetEventData::GetModuleListFromEvent(const Event *event_ptr) { ModuleList module_list; diff --git a/lldb/source/Target/TargetList.cpp b/lldb/source/Target/TargetList.cpp index 188c250..ce04e9c 100644 --- a/lldb/source/Target/TargetList.cpp +++ b/lldb/source/Target/TargetList.cpp @@ -48,7 +48,7 @@ Status TargetList::CreateTarget(Debugger &debugger, LoadDependentFiles load_dependent_files, const OptionGroupPlatform *platform_options, TargetSP &target_sp) { - std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + auto result = TargetList::CreateTargetInternal( debugger, user_exe_path, triple_str, load_dependent_files, platform_options, target_sp); @@ -63,7 +63,7 @@ Status TargetList::CreateTarget(Debugger &debugger, const ArchSpec &specified_arch, LoadDependentFiles load_dependent_files, PlatformSP &platform_sp, TargetSP &target_sp) { - std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); + auto result = TargetList::CreateTargetInternal( debugger, user_exe_path, specified_arch, load_dependent_files, platform_sp, target_sp); @@ -304,13 +304,9 @@ Status TargetList::CreateTargetInternal(Debugger &debugger, ModuleSP exe_module_sp; if (platform_sp) { - FileSpecList executable_search_paths( - Target::GetDefaultExecutableSearchPaths()); ModuleSpec module_spec(file, arch); - error = platform_sp->ResolveExecutable(module_spec, exe_module_sp, - executable_search_paths.GetSize() - ? &executable_search_paths - : nullptr); + module_spec.SetTarget(target_sp); + error = platform_sp->ResolveExecutable(module_spec, exe_module_sp); } if (error.Success() && exe_module_sp) { @@ -525,6 +521,7 @@ uint32_t TargetList::GetIndexOfTarget(lldb::TargetSP target_sp) const { } void TargetList::AddTargetInternal(TargetSP target_sp, bool do_select) { + std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); lldbassert(!llvm::is_contained(m_target_list, target_sp) && "target already exists it the list"); UnregisterInProcessTarget(target_sp); diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index 8c3e197..b40e753 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -13,9 +13,12 @@ #include "lldb/Core/Module.h" #include "lldb/Core/StructuredDataImpl.h" #include "lldb/Host/Host.h" +#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h" +#include "lldb/Interpreter/Interfaces/ScriptedFrameProviderInterface.h" #include "lldb/Interpreter/OptionValueFileSpecList.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/Property.h" +#include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Symbol/Function.h" #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" @@ -26,6 +29,7 @@ #include "lldb/Target/ScriptedThreadPlan.h" #include "lldb/Target/StackFrameRecognizer.h" #include "lldb/Target/StopInfo.h" +#include "lldb/Target/SyntheticFrameProvider.h" #include "lldb/Target/SystemRuntime.h" #include "lldb/Target/Target.h" #include "lldb/Target/ThreadPlan.h" @@ -45,6 +49,7 @@ #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/ScriptedMetadata.h" #include "lldb/Utility/State.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StreamString.h" @@ -257,6 +262,7 @@ void Thread::DestroyThread() { std::lock_guard<std::recursive_mutex> guard(m_frame_mutex); m_curr_frames_sp.reset(); m_prev_frames_sp.reset(); + m_frame_provider_sp.reset(); m_prev_framezero_pc.reset(); } @@ -1439,13 +1445,76 @@ void Thread::CalculateExecutionContext(ExecutionContext &exe_ctx) { StackFrameListSP Thread::GetStackFrameList() { std::lock_guard<std::recursive_mutex> guard(m_frame_mutex); - if (!m_curr_frames_sp) + if (m_curr_frames_sp) + return m_curr_frames_sp; + + // First, try to load a frame provider if we don't have one yet. + if (!m_frame_provider_sp) { + ProcessSP process_sp = GetProcess(); + if (process_sp) { + Target &target = process_sp->GetTarget(); + const auto &descriptors = target.GetScriptedFrameProviderDescriptors(); + + // Find first descriptor that applies to this thread. + for (const auto &entry : descriptors) { + const ScriptedFrameProviderDescriptor &descriptor = entry.second; + if (descriptor.IsValid() && descriptor.AppliesToThread(*this)) { + if (llvm::Error error = LoadScriptedFrameProvider(descriptor)) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Thread), std::move(error), + "Failed to load scripted frame provider: {0}"); + } + break; // Use first matching descriptor (success or failure). + } + } + } + } + + // Create the frame list based on whether we have a provider. + if (m_frame_provider_sp) { + // We have a provider - create synthetic frame list. + StackFrameListSP input_frames = m_frame_provider_sp->GetInputFrames(); + m_curr_frames_sp = std::make_shared<SyntheticStackFrameList>( + *this, input_frames, m_prev_frames_sp, true); + } else { + // No provider - use normal unwinder frames. m_curr_frames_sp = std::make_shared<StackFrameList>(*this, m_prev_frames_sp, true); + } return m_curr_frames_sp; } +llvm::Error Thread::LoadScriptedFrameProvider( + const ScriptedFrameProviderDescriptor &descriptor) { + std::lock_guard<std::recursive_mutex> guard(m_frame_mutex); + + // Note: We don't create input_frames here - it will be created lazily + // by SyntheticStackFrameList when frames are first fetched. + // Creating them too early can cause crashes during thread initialization. + + // Create a temporary StackFrameList just to get the thread reference for the + // provider. The provider won't actually use this - it will get real input + // frames from SyntheticStackFrameList later. + StackFrameListSP temp_frames = + std::make_shared<StackFrameList>(*this, m_prev_frames_sp, true); + + auto provider_or_err = + SyntheticFrameProvider::CreateInstance(temp_frames, descriptor); + if (!provider_or_err) + return provider_or_err.takeError(); + + ClearScriptedFrameProvider(); + m_frame_provider_sp = *provider_or_err; + return llvm::Error::success(); +} + +void Thread::ClearScriptedFrameProvider() { + std::lock_guard<std::recursive_mutex> guard(m_frame_mutex); + m_frame_provider_sp.reset(); + m_curr_frames_sp.reset(); + m_prev_frames_sp.reset(); +} + std::optional<addr_t> Thread::GetPreviousFrameZeroPC() { return m_prev_framezero_pc; } @@ -1466,6 +1535,7 @@ void Thread::ClearStackFrames() { m_prev_frames_sp.swap(m_curr_frames_sp); m_curr_frames_sp.reset(); + m_frame_provider_sp.reset(); m_extended_info.reset(); m_extended_info_fetched = false; } diff --git a/lldb/source/Target/ThreadPlanStepOut.cpp b/lldb/source/Target/ThreadPlanStepOut.cpp index d49a01b..0307b38 100644 --- a/lldb/source/Target/ThreadPlanStepOut.cpp +++ b/lldb/source/Target/ThreadPlanStepOut.cpp @@ -356,13 +356,10 @@ bool ThreadPlanStepOut::DoPlanExplainsStop(Event *event_ptr) { } } - // If there was only one owner, then we're done. But if we also hit - // some user breakpoint on our way out, we should mark ourselves as - // done, but also not claim to explain the stop, since it is more - // important to report the user breakpoint than the step out - // completion. - - if (site_sp->GetNumberOfConstituents() == 1) + // If the thread also hit a user breakpoint on its way out, the plan is + // done but should not claim to explain the stop. It is more important + // to report the user breakpoint than the step out completion. + if (!site_sp->ContainsUserBreakpointForThread(GetThread())) return true; } return false; diff --git a/lldb/source/Target/ThreadPlanStepRange.cpp b/lldb/source/Target/ThreadPlanStepRange.cpp index dca96cc..3a9deb6 100644 --- a/lldb/source/Target/ThreadPlanStepRange.cpp +++ b/lldb/source/Target/ThreadPlanStepRange.cpp @@ -431,10 +431,10 @@ bool ThreadPlanStepRange::SetNextBranchBreakpoint() { top_most_line_entry.original_file_sp = std::make_shared<SupportFile>(call_site_file_spec); top_most_line_entry.range = range; - top_most_line_entry.file_sp.reset(); + top_most_line_entry.file_sp = std::make_shared<SupportFile>(); top_most_line_entry.ApplyFileMappings( GetThread().CalculateTarget()); - if (!top_most_line_entry.file_sp) + if (!top_most_line_entry.file_sp->GetSpecOnly()) top_most_line_entry.file_sp = top_most_line_entry.original_file_sp; } diff --git a/lldb/source/Target/ThreadSpec.cpp b/lldb/source/Target/ThreadSpec.cpp index ba4c3aa..624f64e 100644 --- a/lldb/source/Target/ThreadSpec.cpp +++ b/lldb/source/Target/ThreadSpec.cpp @@ -19,6 +19,10 @@ const char *ThreadSpec::g_option_names[static_cast<uint32_t>( ThreadSpec::ThreadSpec() : m_name(), m_queue_name() {} +ThreadSpec::ThreadSpec(Thread &thread) + : m_index(thread.GetIndexID()), m_tid(thread.GetID()), + m_name(thread.GetName()), m_queue_name(thread.GetQueueName()) {} + std::unique_ptr<ThreadSpec> ThreadSpec::CreateFromStructuredData( const StructuredData::Dictionary &spec_dict, Status &error) { uint32_t index = UINT32_MAX; diff --git a/lldb/source/Target/UnixSignals.cpp b/lldb/source/Target/UnixSignals.cpp index 6113c66..881431f 100644 --- a/lldb/source/Target/UnixSignals.cpp +++ b/lldb/source/Target/UnixSignals.cpp @@ -137,6 +137,13 @@ llvm::StringRef UnixSignals::GetSignalAsStringRef(int32_t signo) const { return pos->second.m_name; } +llvm::StringRef UnixSignals::GetSignalNumberDescription(int32_t signo) const { + const auto pos = m_signals.find(signo); + if (pos == m_signals.end()) + return {}; + return pos->second.m_description; +} + std::string UnixSignals::GetSignalDescription( int32_t signo, std::optional<int32_t> code, std::optional<lldb::addr_t> addr, std::optional<lldb::addr_t> lower, diff --git a/lldb/source/Utility/CMakeLists.txt b/lldb/source/Utility/CMakeLists.txt index 1dd4d63..80b53f8 100644 --- a/lldb/source/Utility/CMakeLists.txt +++ b/lldb/source/Utility/CMakeLists.txt @@ -38,7 +38,6 @@ add_lldb_library(lldbUtility NO_INTERNAL_DEPENDENCIES DataEncoder.cpp DataExtractor.cpp Diagnostics.cpp - DiagnosticsRendering.cpp Environment.cpp ErrorMessages.cpp Event.cpp @@ -78,6 +77,7 @@ add_lldb_library(lldbUtility NO_INTERNAL_DEPENDENCIES UserIDResolver.cpp VASprintf.cpp VMRange.cpp + VirtualDataExtractor.cpp XcodeSDK.cpp ZipFile.cpp diff --git a/lldb/source/Utility/DataExtractor.cpp b/lldb/source/Utility/DataExtractor.cpp index e9be0cb..a9aea16 100644 --- a/lldb/source/Utility/DataExtractor.cpp +++ b/lldb/source/Utility/DataExtractor.cpp @@ -662,10 +662,6 @@ size_t DataExtractor::ExtractBytes(offset_t offset, offset_t length, const uint8_t *src = PeekData(offset, length); if (src) { if (dst_byte_order != GetByteOrder()) { - // Validate that only a word- or register-sized dst is byte swapped - assert(length == 1 || length == 2 || length == 4 || length == 8 || - length == 10 || length == 16 || length == 32); - for (uint32_t i = 0; i < length; ++i) (static_cast<uint8_t *>(dst))[i] = src[length - i - 1]; } else diff --git a/lldb/source/Utility/FileSpecList.cpp b/lldb/source/Utility/FileSpecList.cpp index 5852367..8aa0820 100644 --- a/lldb/source/Utility/FileSpecList.cpp +++ b/lldb/source/Utility/FileSpecList.cpp @@ -45,10 +45,9 @@ bool FileSpecList::AppendIfUnique(const FileSpec &file_spec) { // FIXME: Replace this with a DenseSet at the call site. It is inefficient. bool SupportFileList::AppendIfUnique(const FileSpec &file_spec) { collection::iterator end = m_files.end(); - if (find_if(m_files.begin(), end, - [&](const std::shared_ptr<SupportFile> &support_file) { - return support_file->GetSpecOnly() == file_spec; - }) == end) { + if (find_if(m_files.begin(), end, [&](const SupportFileNSP &support_file) { + return support_file->GetSpecOnly() == file_spec; + }) == end) { Append(file_spec); return true; } @@ -214,11 +213,10 @@ const FileSpec &SupportFileList::GetFileSpecAtIndex(size_t idx) const { return g_empty_file_spec; } -std::shared_ptr<SupportFile> -SupportFileList::GetSupportFileAtIndex(size_t idx) const { +SupportFileNSP SupportFileList::GetSupportFileAtIndex(size_t idx) const { if (idx < m_files.size()) return m_files[idx]; - return {}; + return std::make_shared<SupportFile>(); } // Return the size in bytes that this object takes in memory. This returns the diff --git a/lldb/source/Utility/LLDBLog.cpp b/lldb/source/Utility/LLDBLog.cpp index 613dae4..a08764d 100644 --- a/lldb/source/Utility/LLDBLog.cpp +++ b/lldb/source/Utility/LLDBLog.cpp @@ -67,6 +67,9 @@ static constexpr Log::Category g_categories[] = { {{"disassembler"}, {"log disassembler related activities"}, LLDBLog::Disassembler}, + {{"instrumentation-runtime"}, + {"log instrumentation runtime plugin related activities"}, + LLDBLog::InstrumentationRuntime}, }; static Log::Channel g_log_channel(g_categories, diff --git a/lldb/source/Utility/RegisterValue.cpp b/lldb/source/Utility/RegisterValue.cpp index 12c349a..4d762dc 100644 --- a/lldb/source/Utility/RegisterValue.cpp +++ b/lldb/source/Utility/RegisterValue.cpp @@ -127,7 +127,7 @@ bool RegisterValue::GetScalarValue(Scalar &scalar) const { case eTypeUInt16: case eTypeUInt32: case eTypeUInt64: - case eTypeUInt128: + case eTypeUIntN: case eTypeFloat: case eTypeDouble: case eTypeLongDouble: @@ -180,8 +180,6 @@ Status RegisterValue::SetValueFromData(const RegisterInfo ®_info, if (src_len > reg_info.byte_size) src_len = reg_info.byte_size; - type128 int128; - m_type = eTypeInvalid; switch (reg_info.encoding) { case eEncodingInvalid: @@ -196,17 +194,13 @@ Status RegisterValue::SetValueFromData(const RegisterInfo ®_info, SetUInt32(src.GetMaxU32(&src_offset, src_len)); else if (reg_info.byte_size <= 8) SetUInt64(src.GetMaxU64(&src_offset, src_len)); - else if (reg_info.byte_size <= 16) { - uint64_t data1 = src.GetU64(&src_offset); - uint64_t data2 = src.GetU64(&src_offset); - if (src.GetByteOrder() == eByteOrderLittle) { - int128.x[0] = data1; - int128.x[1] = data2; - } else { - int128.x[0] = data2; - int128.x[1] = data1; - } - SetUInt128(llvm::APInt(128, 2, int128.x)); + else { + std::vector<uint8_t> native_endian_src(src_len, 0); + src.ExtractBytes(src_offset, src_len, endian::InlHostByteOrder(), + native_endian_src.data()); + llvm::APInt uint = llvm::APInt::getZero(src_len * 8); + llvm::LoadIntFromMemory(uint, native_endian_src.data(), src_len); + SetUIntN(uint); } break; case eEncodingIEEE754: @@ -442,7 +436,7 @@ bool RegisterValue::SignExtend(uint32_t sign_bitpos) { case eTypeUInt16: case eTypeUInt32: case eTypeUInt64: - case eTypeUInt128: + case eTypeUIntN: return m_scalar.SignExtend(sign_bitpos); case eTypeFloat: case eTypeDouble: @@ -465,7 +459,7 @@ bool RegisterValue::CopyValue(const RegisterValue &rhs) { case eTypeUInt16: case eTypeUInt32: case eTypeUInt64: - case eTypeUInt128: + case eTypeUIntN: case eTypeFloat: case eTypeDouble: case eTypeLongDouble: @@ -581,7 +575,7 @@ llvm::APInt RegisterValue::GetAsUInt128(const llvm::APInt &fail_value, case eTypeUInt16: case eTypeUInt32: case eTypeUInt64: - case eTypeUInt128: + case eTypeUIntN: case eTypeFloat: case eTypeDouble: case eTypeLongDouble: @@ -596,8 +590,10 @@ llvm::APInt RegisterValue::GetAsUInt128(const llvm::APInt &fail_value, case 8: case 16: return llvm::APInt( - BITWIDTH_INT128, NUM_OF_WORDS_INT128, - (reinterpret_cast<const type128 *>(buffer.bytes.data()))->x); + BITWIDTH_INT128, + llvm::ArrayRef( + (reinterpret_cast<const type128 *>(buffer.bytes.data()))->x, + NUM_OF_WORDS_INT128)); } } break; } @@ -614,7 +610,7 @@ float RegisterValue::GetAsFloat(float fail_value, bool *success_ptr) const { break; case eTypeUInt32: case eTypeUInt64: - case eTypeUInt128: + case eTypeUIntN: case eTypeFloat: case eTypeDouble: case eTypeLongDouble: @@ -634,7 +630,7 @@ double RegisterValue::GetAsDouble(double fail_value, bool *success_ptr) const { case eTypeUInt32: case eTypeUInt64: - case eTypeUInt128: + case eTypeUIntN: case eTypeFloat: case eTypeDouble: case eTypeLongDouble: @@ -655,7 +651,7 @@ long double RegisterValue::GetAsLongDouble(long double fail_value, case eTypeUInt32: case eTypeUInt64: - case eTypeUInt128: + case eTypeUIntN: case eTypeFloat: case eTypeDouble: case eTypeLongDouble: @@ -674,7 +670,7 @@ const void *RegisterValue::GetBytes() const { case eTypeUInt16: case eTypeUInt32: case eTypeUInt64: - case eTypeUInt128: + case eTypeUIntN: case eTypeFloat: case eTypeDouble: case eTypeLongDouble: @@ -696,7 +692,7 @@ uint32_t RegisterValue::GetByteSize() const { return 2; case eTypeUInt32: case eTypeUInt64: - case eTypeUInt128: + case eTypeUIntN: case eTypeFloat: case eTypeDouble: case eTypeLongDouble: @@ -719,7 +715,7 @@ bool RegisterValue::SetUInt(uint64_t uint, uint32_t byte_size) { } else if (byte_size <= 8) { SetUInt64(uint); } else if (byte_size <= 16) { - SetUInt128(llvm::APInt(128, uint)); + SetUIntN(llvm::APInt(128, uint)); } else return false; return true; @@ -747,7 +743,7 @@ bool RegisterValue::operator==(const RegisterValue &rhs) const { case eTypeUInt16: case eTypeUInt32: case eTypeUInt64: - case eTypeUInt128: + case eTypeUIntN: case eTypeFloat: case eTypeDouble: case eTypeLongDouble: @@ -772,7 +768,7 @@ bool RegisterValue::ClearBit(uint32_t bit) { case eTypeUInt16: case eTypeUInt32: case eTypeUInt64: - case eTypeUInt128: + case eTypeUIntN: if (bit < (GetByteSize() * 8)) { return m_scalar.ClearBit(bit); } @@ -812,7 +808,7 @@ bool RegisterValue::SetBit(uint32_t bit) { case eTypeUInt16: case eTypeUInt32: case eTypeUInt64: - case eTypeUInt128: + case eTypeUIntN: if (bit < (GetByteSize() * 8)) { return m_scalar.SetBit(bit); } diff --git a/lldb/source/Utility/Stream.cpp b/lldb/source/Utility/Stream.cpp index 89dce9f..e9632c3 100644 --- a/lldb/source/Utility/Stream.cpp +++ b/lldb/source/Utility/Stream.cpp @@ -202,6 +202,14 @@ void Stream::IndentLess(unsigned amount) { m_indent_level = 0; } +// Create an indentation scope that restores the original indent level when the +// object goes out of scope (RAII). +Stream::IndentScope Stream::MakeIndentScope(unsigned indent_amount) { + IndentScope indent_scope(*this); + IndentMore(indent_amount); + return indent_scope; +} + // Get the address size in bytes uint32_t Stream::GetAddressByteSize() const { return m_addr_size; } diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp index 010149a..40b5d03 100644 --- a/lldb/source/Utility/StringExtractorGDBRemote.cpp +++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -12,9 +12,6 @@ #include <cstring> #include <optional> -constexpr lldb::pid_t StringExtractorGDBRemote::AllProcesses; -constexpr lldb::tid_t StringExtractorGDBRemote::AllThreads; - StringExtractorGDBRemote::ResponseType StringExtractorGDBRemote::GetResponseType() const { if (m_packet.empty()) diff --git a/lldb/source/Utility/VirtualDataExtractor.cpp b/lldb/source/Utility/VirtualDataExtractor.cpp new file mode 100644 index 0000000..a23e43b --- /dev/null +++ b/lldb/source/Utility/VirtualDataExtractor.cpp @@ -0,0 +1,139 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/VirtualDataExtractor.h" +#include <cassert> + +using namespace lldb; +using namespace lldb_private; + +VirtualDataExtractor::VirtualDataExtractor(const void *data, + offset_t data_length, + ByteOrder byte_order, + uint32_t addr_size, + LookupTable lookup_table) + : DataExtractor(data, data_length, byte_order, addr_size), + m_lookup_table(std::move(lookup_table)) { + m_lookup_table.Sort(); +} + +VirtualDataExtractor::VirtualDataExtractor(const DataBufferSP &data_sp, + ByteOrder byte_order, + uint32_t addr_size, + LookupTable lookup_table) + : DataExtractor(data_sp, byte_order, addr_size), + m_lookup_table(std::move(lookup_table)) { + m_lookup_table.Sort(); +} + +const VirtualDataExtractor::LookupTable::Entry * +VirtualDataExtractor::FindEntry(offset_t virtual_addr) const { + // Use RangeDataVector's binary search instead of linear search. + return m_lookup_table.FindEntryThatContains(virtual_addr); +} + +bool VirtualDataExtractor::ValidateVirtualRead(offset_t virtual_addr, + offset_t length) const { + const LookupTable::Entry *entry = FindEntry(virtual_addr); + if (!entry) + return false; + + // Assert that the read does not cross entry boundaries. + // RangeData.Contains() checks if a range is fully contained. + assert(entry->Contains(LookupTable::Range(virtual_addr, length)) && + "Read crosses lookup table entry boundary"); + + // Also validate that the physical offset is within the data buffer. + // RangeData.data contains the physical offset. + offset_t physical_offset = entry->data + (virtual_addr - entry->base); + return ValidOffsetForDataOfSize(physical_offset, length); +} + +const void *VirtualDataExtractor::GetData(offset_t *offset_ptr, + offset_t length) const { + // Override to treat offset as virtual address. + if (!offset_ptr) + return nullptr; + + offset_t virtual_addr = *offset_ptr; + + if (!ValidateVirtualRead(virtual_addr, length)) + return nullptr; + + const LookupTable::Entry *entry = FindEntry(virtual_addr); + assert(entry && "ValidateVirtualRead should have found an entry"); + + offset_t physical_offset = entry->data + (virtual_addr - entry->base); + // Use base class PeekData directly to avoid recursion. + const void *result = DataExtractor::PeekData(physical_offset, length); + + if (result) { + // Advance the virtual offset pointer. + *offset_ptr += length; + } + + return result; +} + +const uint8_t *VirtualDataExtractor::PeekData(offset_t offset, + offset_t length) const { + // Override to treat offset as virtual address. + if (!ValidateVirtualRead(offset, length)) + return nullptr; + + const LookupTable::Entry *entry = FindEntry(offset); + assert(entry && "ValidateVirtualRead should have found an entry"); + + offset_t physical_offset = entry->data + (offset - entry->base); + // Use the base class PeekData with the physical offset. + return DataExtractor::PeekData(physical_offset, length); +} + +uint8_t VirtualDataExtractor::GetU8_unchecked(offset_t *offset_ptr) const { + offset_t virtual_addr = *offset_ptr; + const LookupTable::Entry *entry = FindEntry(virtual_addr); + assert(entry && "Unchecked methods require valid virtual address"); + + offset_t physical_offset = entry->data + (virtual_addr - entry->base); + uint8_t result = DataExtractor::GetU8_unchecked(&physical_offset); + *offset_ptr += 1; + return result; +} + +uint16_t VirtualDataExtractor::GetU16_unchecked(offset_t *offset_ptr) const { + offset_t virtual_addr = *offset_ptr; + const LookupTable::Entry *entry = FindEntry(virtual_addr); + assert(entry && "Unchecked methods require valid virtual address"); + + offset_t physical_offset = entry->data + (virtual_addr - entry->base); + uint16_t result = DataExtractor::GetU16_unchecked(&physical_offset); + *offset_ptr += 2; + return result; +} + +uint32_t VirtualDataExtractor::GetU32_unchecked(offset_t *offset_ptr) const { + offset_t virtual_addr = *offset_ptr; + const LookupTable::Entry *entry = FindEntry(virtual_addr); + assert(entry && "Unchecked methods require valid virtual address"); + + offset_t physical_offset = entry->data + (virtual_addr - entry->base); + uint32_t result = DataExtractor::GetU32_unchecked(&physical_offset); + *offset_ptr += 4; + return result; +} + +uint64_t VirtualDataExtractor::GetU64_unchecked(offset_t *offset_ptr) const { + offset_t virtual_addr = *offset_ptr; + const LookupTable::Entry *entry = FindEntry(virtual_addr); + assert(entry && "Unchecked methods require valid virtual address"); + + offset_t physical_offset = entry->data + (virtual_addr - entry->base); + uint64_t result = DataExtractor::GetU64_unchecked(&physical_offset); + *offset_ptr += 8; + return result; +} diff --git a/lldb/source/ValueObject/CMakeLists.txt b/lldb/source/ValueObject/CMakeLists.txt index 2a61407..f0fe7f3 100644 --- a/lldb/source/ValueObject/CMakeLists.txt +++ b/lldb/source/ValueObject/CMakeLists.txt @@ -1,4 +1,4 @@ -add_lldb_library(lldbValueObject +add_lldb_library(lldbValueObject NO_PLUGIN_DEPENDENCIES DILAST.cpp DILEval.cpp DILLexer.cpp @@ -34,6 +34,4 @@ add_lldb_library(lldbValueObject lldbSymbol lldbTarget lldbUtility - lldbPluginCPlusPlusLanguage - lldbPluginObjCLanguage ) diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index 7ed34db..0b9e1f4 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -51,4 +51,8 @@ BooleanLiteralNode::Accept(Visitor *v) const { return v->Visit(this); } +llvm::Expected<lldb::ValueObjectSP> CastNode::Accept(Visitor *v) const { + return v->Visit(this); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index a9dbfad..dc0d93d 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -21,6 +21,101 @@ namespace lldb_private::dil { +static llvm::Expected<lldb::TypeSystemSP> +GetTypeSystemFromCU(std::shared_ptr<ExecutionContextScope> ctx) { + auto stack_frame = ctx->CalculateStackFrame(); + if (!stack_frame) + return llvm::createStringError("no stack frame in this context"); + SymbolContext symbol_context = + stack_frame->GetSymbolContext(lldb::eSymbolContextCompUnit); + lldb::LanguageType language = symbol_context.comp_unit->GetLanguage(); + + symbol_context = stack_frame->GetSymbolContext(lldb::eSymbolContextModule); + return symbol_context.module_sp->GetTypeSystemForLanguage(language); +} + +static CompilerType GetBasicType(lldb::TypeSystemSP type_system, + lldb::BasicType basic_type) { + if (type_system) + return type_system.get()->GetBasicTypeFromAST(basic_type); + + return CompilerType(); +} + +static lldb::ValueObjectSP +ArrayToPointerConversion(ValueObject &valobj, ExecutionContextScope &ctx) { + uint64_t addr = valobj.GetLoadAddress(); + ExecutionContext exe_ctx; + ctx.CalculateExecutionContext(exe_ctx); + return ValueObject::CreateValueObjectFromAddress( + "result", addr, exe_ctx, + valobj.GetCompilerType().GetArrayElementType(&ctx).GetPointerType(), + /* do_deref */ false); +} + +llvm::Expected<lldb::ValueObjectSP> +Interpreter::UnaryConversion(lldb::ValueObjectSP valobj, uint32_t location) { + if (!valobj) + return llvm::make_error<DILDiagnosticError>(m_expr, "invalid value object", + location); + llvm::Expected<lldb::TypeSystemSP> type_system = + GetTypeSystemFromCU(m_exe_ctx_scope); + if (!type_system) + return type_system.takeError(); + + CompilerType in_type = valobj->GetCompilerType(); + if (valobj->IsBitfield()) { + // Promote bitfields. If `int` can represent the bitfield value, it is + // converted to `int`. Otherwise, if `unsigned int` can represent it, it + // is converted to `unsigned int`. Otherwise, it is treated as its + // underlying type. + uint32_t bitfield_size = valobj->GetBitfieldBitSize(); + // Some bitfields have undefined size (e.g. result of ternary operation). + // The AST's `bitfield_size` of those is 0, and no promotion takes place. + if (bitfield_size > 0 && in_type.IsInteger()) { + CompilerType int_type = GetBasicType(*type_system, lldb::eBasicTypeInt); + CompilerType uint_type = + GetBasicType(*type_system, lldb::eBasicTypeUnsignedInt); + llvm::Expected<uint64_t> int_bit_size = + int_type.GetBitSize(m_exe_ctx_scope.get()); + if (!int_bit_size) + return int_bit_size.takeError(); + llvm::Expected<uint64_t> uint_bit_size = + uint_type.GetBitSize(m_exe_ctx_scope.get()); + if (!uint_bit_size) + return int_bit_size.takeError(); + if (bitfield_size < *int_bit_size || + (in_type.IsSigned() && bitfield_size == *int_bit_size)) + return valobj->CastToBasicType(int_type); + if (bitfield_size <= *uint_bit_size) + return valobj->CastToBasicType(uint_type); + // Re-create as a const value with the same underlying type + Scalar scalar; + bool resolved = valobj->ResolveValue(scalar); + if (!resolved) + return llvm::createStringError("invalid scalar value"); + return ValueObject::CreateValueObjectFromScalar(m_target, scalar, in_type, + "result"); + } + } + + if (in_type.IsArrayType()) + valobj = ArrayToPointerConversion(*valobj, *m_exe_ctx_scope); + + if (valobj->GetCompilerType().IsInteger() || + valobj->GetCompilerType().IsUnscopedEnumerationType()) { + llvm::Expected<CompilerType> promoted_type = + type_system.get()->DoIntegralPromotion(valobj->GetCompilerType(), + m_exe_ctx_scope.get()); + if (!promoted_type) + return promoted_type.takeError(); + if (!promoted_type->CompareTypes(valobj->GetCompilerType())) + return valobj->CastToBasicType(*promoted_type); + } + + return valobj; +} + static lldb::VariableSP DILFindVariable(ConstString name, VariableList &variable_list) { lldb::VariableSP exact_match; @@ -147,6 +242,10 @@ Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr, llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *node) { // Evaluate an AST. auto value_or_error = node->Accept(this); + // Convert SP with a nullptr to an error. + if (value_or_error && !*value_or_error) + return llvm::make_error<DILDiagnosticError>(m_expr, "invalid value object", + node->GetLocation()); // Return the computed value-or-error. The caller is responsible for // checking if an error occured during the evaluation. return value_or_error; @@ -175,21 +274,21 @@ Interpreter::Visit(const IdentifierNode *node) { llvm::Expected<lldb::ValueObjectSP> Interpreter::Visit(const UnaryOpNode *node) { Status error; - auto rhs_or_err = Evaluate(node->GetOperand()); - if (!rhs_or_err) - return rhs_or_err; + auto op_or_err = Evaluate(node->GetOperand()); + if (!op_or_err) + return op_or_err; - lldb::ValueObjectSP rhs = *rhs_or_err; + lldb::ValueObjectSP operand = *op_or_err; switch (node->GetKind()) { case UnaryOpKind::Deref: { - lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_use_dynamic); - if (dynamic_rhs) - rhs = dynamic_rhs; + lldb::ValueObjectSP dynamic_op = operand->GetDynamicValue(m_use_dynamic); + if (dynamic_op) + operand = dynamic_op; - lldb::ValueObjectSP child_sp = rhs->Dereference(error); + lldb::ValueObjectSP child_sp = operand->Dereference(error); if (!child_sp && m_use_synthetic) { - if (lldb::ValueObjectSP synth_obj_sp = rhs->GetSyntheticValue()) { + if (lldb::ValueObjectSP synth_obj_sp = operand->GetSyntheticValue()) { error.Clear(); child_sp = synth_obj_sp->Dereference(error); } @@ -202,18 +301,69 @@ Interpreter::Visit(const UnaryOpNode *node) { } case UnaryOpKind::AddrOf: { Status error; - lldb::ValueObjectSP value = rhs->AddressOf(error); + lldb::ValueObjectSP value = operand->AddressOf(error); if (error.Fail()) return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(), node->GetLocation()); return value; } + case UnaryOpKind::Minus: { + if (operand->GetCompilerType().IsReferenceType()) { + operand = operand->Dereference(error); + if (error.Fail()) + return error.ToError(); + } + llvm::Expected<lldb::ValueObjectSP> conv_op = + UnaryConversion(operand, node->GetOperand()->GetLocation()); + if (!conv_op) + return conv_op; + operand = *conv_op; + CompilerType operand_type = operand->GetCompilerType(); + if (!operand_type.IsScalarType()) { + std::string errMsg = + llvm::formatv("invalid argument type '{0}' to unary expression", + operand_type.GetTypeName()); + return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, + node->GetLocation()); + } + Scalar scalar; + bool resolved = operand->ResolveValue(scalar); + if (!resolved) + break; + + bool negated = scalar.UnaryNegate(); + if (negated) + return ValueObject::CreateValueObjectFromScalar( + m_target, scalar, operand->GetCompilerType(), "result"); + break; } - - // Unsupported/invalid operation. - return llvm::make_error<DILDiagnosticError>( - m_expr, "invalid ast: unexpected binary operator", node->GetLocation()); + case UnaryOpKind::Plus: { + if (operand->GetCompilerType().IsReferenceType()) { + operand = operand->Dereference(error); + if (error.Fail()) + return error.ToError(); + } + llvm::Expected<lldb::ValueObjectSP> conv_op = + UnaryConversion(operand, node->GetOperand()->GetLocation()); + if (!conv_op) + return conv_op; + operand = *conv_op; + CompilerType operand_type = operand->GetCompilerType(); + if (!operand_type.IsScalarType() && + // Unary plus is allowed for pointers. + !operand_type.IsPointerType()) { + std::string errMsg = + llvm::formatv("invalid argument type '{0}' to unary expression", + operand_type.GetTypeName()); + return llvm::make_error<DILDiagnosticError>(m_expr, errMsg, + node->GetLocation()); + } + return operand; + } + } + return llvm::make_error<DILDiagnosticError>(m_expr, "invalid unary operation", + node->GetLocation()); } llvm::Expected<lldb::ValueObjectSP> @@ -499,24 +649,6 @@ Interpreter::Visit(const BitFieldExtractionNode *node) { return child_valobj_sp; } -static llvm::Expected<lldb::TypeSystemSP> -GetTypeSystemFromCU(std::shared_ptr<StackFrame> ctx) { - SymbolContext symbol_context = - ctx->GetSymbolContext(lldb::eSymbolContextCompUnit); - lldb::LanguageType language = symbol_context.comp_unit->GetLanguage(); - - symbol_context = ctx->GetSymbolContext(lldb::eSymbolContextModule); - return symbol_context.module_sp->GetTypeSystemForLanguage(language); -} - -static CompilerType GetBasicType(lldb::TypeSystemSP type_system, - lldb::BasicType basic_type) { - if (type_system) - return type_system.get()->GetBasicTypeFromAST(basic_type); - - return CompilerType(); -} - llvm::Expected<CompilerType> Interpreter::PickIntegerType(lldb::TypeSystemSP type_system, std::shared_ptr<ExecutionContextScope> ctx, @@ -608,4 +740,16 @@ Interpreter::Visit(const BooleanLiteralNode *node) { return ValueObject::CreateValueObjectFromBool(m_target, value, "result"); } +llvm::Expected<lldb::ValueObjectSP> Interpreter::Visit(const CastNode *node) { + auto operand_or_err = Evaluate(node->GetOperand()); + if (!operand_or_err) + return operand_or_err; + + lldb::ValueObjectSP operand = *operand_or_err; + // Don't actually do the cast for now -- that code will be added later. + // For now just return an error message. + return llvm::make_error<DILDiagnosticError>( + m_expr, "Type casting is not supported here.", node->GetLocation()); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index 566bcaf..e94ce31 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -12,8 +12,10 @@ //===----------------------------------------------------------------------===// #include "lldb/ValueObject/DILParser.h" +#include "lldb/Host/common/DiagnosticsRendering.h" +#include "lldb/Symbol/CompileUnit.h" #include "lldb/Target/ExecutionContextScope.h" -#include "lldb/Utility/DiagnosticsRendering.h" +#include "lldb/Target/LanguageRuntime.h" #include "lldb/ValueObject/DILAST.h" #include "lldb/ValueObject/DILEval.h" #include "llvm/ADT/StringRef.h" @@ -80,26 +82,77 @@ ASTNodeUP DILParser::Run() { // Parse an expression. // // expression: -// unary_expression +// cast_expression // -ASTNodeUP DILParser::ParseExpression() { return ParseUnaryExpression(); } +ASTNodeUP DILParser::ParseExpression() { return ParseCastExpression(); } + +// Parse a cast_expression. +// +// cast_expression: +// unary_expression +// "(" type_id ")" cast_expression + +ASTNodeUP DILParser::ParseCastExpression() { + if (!CurToken().Is(Token::l_paren)) + return ParseUnaryExpression(); + + // This could be a type cast, try parsing the contents as a type declaration. + Token token = CurToken(); + uint32_t loc = token.GetLocation(); + + // Enable lexer backtracking, so that we can rollback in case it's not + // actually a type declaration. + + // Start tentative parsing (save token location/idx, for possible rollback). + uint32_t save_token_idx = m_dil_lexer.GetCurrentTokenIdx(); + + // Consume the token only after enabling the backtracking. + m_dil_lexer.Advance(); + + // Try parsing the type declaration. If the returned value is not valid, + // then we should rollback and try parsing the expression. + auto type_id = ParseTypeId(); + if (type_id) { + // Successfully parsed the type declaration. Commit the backtracked + // tokens and parse the cast_expression. + + if (!type_id.value().IsValid()) + return std::make_unique<ErrorNode>(); + + Expect(Token::r_paren); + m_dil_lexer.Advance(); + auto rhs = ParseCastExpression(); + + return std::make_unique<CastNode>(loc, type_id.value(), std::move(rhs), + CastKind::eNone); + } + + // Failed to parse the contents of the parentheses as a type declaration. + // Rollback the lexer and try parsing it as unary_expression. + TentativeParsingRollback(save_token_idx); + + return ParseUnaryExpression(); +} // Parse an unary_expression. // // unary_expression: // postfix_expression -// unary_operator expression +// unary_operator cast_expression // // unary_operator: // "&" // "*" +// "+" +// "-" // ASTNodeUP DILParser::ParseUnaryExpression() { - if (CurToken().IsOneOf({Token::amp, Token::star})) { + if (CurToken().IsOneOf( + {Token::amp, Token::star, Token::minus, Token::plus})) { Token token = CurToken(); uint32_t loc = token.GetLocation(); m_dil_lexer.Advance(); - auto rhs = ParseExpression(); + auto rhs = ParseCastExpression(); switch (token.GetKind()) { case Token::star: return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::Deref, @@ -107,7 +160,12 @@ ASTNodeUP DILParser::ParseUnaryExpression() { case Token::amp: return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::AddrOf, std::move(rhs)); - + case Token::minus: + return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::Minus, + std::move(rhs)); + case Token::plus: + return std::make_unique<UnaryOpNode>(loc, UnaryOpKind::Plus, + std::move(rhs)); default: llvm_unreachable("invalid token kind"); } @@ -274,6 +332,81 @@ std::string DILParser::ParseNestedNameSpecifier() { } } +// Parse a type_id. +// +// type_id: +// type_specifier_seq [abstract_declarator] +// +// type_specifier_seq: +// type_specifier [type_specifier] +// +// type_specifier: +// ["::"] [nested_name_specifier] type_name // not handled for now! +// builtin_typename +// +std::optional<CompilerType> DILParser::ParseTypeId() { + CompilerType type; + // For now only allow builtin types -- will expand add to this later. + auto maybe_builtin_type = ParseBuiltinType(); + if (maybe_builtin_type) { + type = *maybe_builtin_type; + } else + return {}; + + // + // abstract_declarator: + // ptr_operator [abstract_declarator] + // + std::vector<Token> ptr_operators; + while (CurToken().IsOneOf({Token::star, Token::amp})) { + Token tok = CurToken(); + ptr_operators.push_back(std::move(tok)); + m_dil_lexer.Advance(); + } + type = ResolveTypeDeclarators(type, ptr_operators); + + return type; +} + +// Parse a built-in type +// +// builtin_typename: +// identifer_seq +// +// identifier_seq +// identifer [identifier_seq] +// +// A built-in type can be a single identifier or a space-separated +// list of identifiers (e.g. "short" or "long long"). +std::optional<CompilerType> DILParser::ParseBuiltinType() { + std::string type_name = ""; + uint32_t save_token_idx = m_dil_lexer.GetCurrentTokenIdx(); + bool first_word = true; + while (CurToken().GetKind() == Token::identifier) { + if (CurToken().GetSpelling() == "const" || + CurToken().GetSpelling() == "volatile") + continue; + if (!first_word) + type_name.push_back(' '); + else + first_word = false; + type_name.append(CurToken().GetSpelling()); + m_dil_lexer.Advance(); + } + + if (type_name.size() > 0) { + lldb::TargetSP target_sp = m_ctx_scope->CalculateTarget(); + ConstString const_type_name(type_name.c_str()); + for (auto type_system_sp : target_sp->GetScratchTypeSystems()) + if (auto compiler_type = + type_system_sp->GetBuiltinTypeByName(const_type_name)) + return compiler_type; + } + + TentativeParsingRollback(save_token_idx); + return {}; +} + // Parse an id_expression. // // id_expression: @@ -339,6 +472,40 @@ std::string DILParser::ParseUnqualifiedId() { return identifier; } +CompilerType +DILParser::ResolveTypeDeclarators(CompilerType type, + const std::vector<Token> &ptr_operators) { + // Resolve pointers/references. + for (Token tk : ptr_operators) { + uint32_t loc = tk.GetLocation(); + if (tk.GetKind() == Token::star) { + // Pointers to reference types are forbidden. + if (type.IsReferenceType()) { + BailOut(llvm::formatv("'type name' declared as a pointer to a " + "reference of type {0}", + type.TypeDescription()), + loc, CurToken().GetSpelling().length()); + return {}; + } + // Get pointer type for the base type: e.g. int* -> int**. + type = type.GetPointerType(); + + } else if (tk.GetKind() == Token::amp) { + // References to references are forbidden. + // FIXME: In future we may want to allow rvalue references (i.e. &&). + if (type.IsReferenceType()) { + BailOut("type name declared as a reference to a reference", loc, + CurToken().GetSpelling().length()); + return {}; + } + // Get reference type for the base type: e.g. int -> int&. + type = type.GetLValueReferenceType(); + } + } + + return type; +} + // Parse an boolean_literal. // // boolean_literal: diff --git a/lldb/source/ValueObject/ValueObjectSynthetic.cpp b/lldb/source/ValueObject/ValueObjectSynthetic.cpp index f673c51..44e53bd 100644 --- a/lldb/source/ValueObject/ValueObjectSynthetic.cpp +++ b/lldb/source/ValueObject/ValueObjectSynthetic.cpp @@ -443,3 +443,18 @@ void ValueObjectSynthetic::SetLanguageFlags(uint64_t flags) { else this->ValueObject::SetLanguageFlags(flags); } + +void ValueObjectSynthetic::GetExpressionPath(Stream &stream, + GetExpressionPathFormat epformat) { + // A synthetic ValueObject may wrap an underlying Register or RegisterSet + // ValueObject, which requires a different approach to generating the + // expression path. In such cases, delegate to the non-synthetic value object. + if (const lldb::ValueType obj_value_type = GetValueType(); + IsSynthetic() && (obj_value_type == lldb::eValueTypeRegister || + obj_value_type == lldb::eValueTypeRegisterSet)) { + + if (const lldb::ValueObjectSP raw_value = GetNonSyntheticValue()) + return raw_value->GetExpressionPath(stream, epformat); + } + return ValueObject::GetExpressionPath(stream, epformat); +} diff --git a/lldb/source/Version/CMakeLists.txt b/lldb/source/Version/CMakeLists.txt index 8b0acb9..d179805 100644 --- a/lldb/source/Version/CMakeLists.txt +++ b/lldb/source/Version/CMakeLists.txt @@ -40,4 +40,7 @@ add_lldb_library(lldbVersion NO_PLUGIN_DEPENDENCIES ADDITIONAL_HEADERS ${version_inc} ${vcs_version_inc} + + CLANG_LIBS + clangBasic ) |
