diff options
| author | Koakuma <koachan@protonmail.com> | 2024-07-08 19:19:54 +0700 |
|---|---|---|
| committer | Koakuma <koachan@protonmail.com> | 2024-07-08 19:19:54 +0700 |
| commit | 5c4fdc2fd5898ebd9e89999a4f4b8aa289ca637f (patch) | |
| tree | f3b92a07f3dfc6e70f36d1000605f36a3c15af46 /lldb/source | |
| parent | dbda8e2f2cd8764e0badd983915d62a2c3377f4d (diff) | |
| parent | e9b8cd0c806db00f0981fb36717077c941426302 (diff) | |
[𝘀𝗽𝗿] changes introduced through rebaseusers/koachan/spr/main.sparcias-enable-parseforallfeatures-in-matchoperandparserimpl
Created using spr 1.3.5
[skip ci]
Diffstat (limited to 'lldb/source')
113 files changed, 3849 insertions, 2126 deletions
diff --git a/lldb/source/API/SBAddressRange.cpp b/lldb/source/API/SBAddressRange.cpp index 9b1affdade43..5834ebe5e63c 100644 --- a/lldb/source/API/SBAddressRange.cpp +++ b/lldb/source/API/SBAddressRange.cpp @@ -50,9 +50,7 @@ const SBAddressRange &SBAddressRange::operator=(const SBAddressRange &rhs) { bool SBAddressRange::operator==(const SBAddressRange &rhs) { LLDB_INSTRUMENT_VA(this, rhs); - if (!IsValid() || !rhs.IsValid()) - return false; - return m_opaque_up->operator==(*(rhs.m_opaque_up)); + return ref().operator==(rhs.ref()); } bool SBAddressRange::operator!=(const SBAddressRange &rhs) { @@ -64,40 +62,35 @@ bool SBAddressRange::operator!=(const SBAddressRange &rhs) { void SBAddressRange::Clear() { LLDB_INSTRUMENT_VA(this); - m_opaque_up.reset(); + ref().Clear(); } bool SBAddressRange::IsValid() const { LLDB_INSTRUMENT_VA(this); - return m_opaque_up && m_opaque_up->IsValid(); + return ref().IsValid(); } lldb::SBAddress SBAddressRange::GetBaseAddress() const { LLDB_INSTRUMENT_VA(this); - if (!IsValid()) - return lldb::SBAddress(); - return lldb::SBAddress(m_opaque_up->GetBaseAddress()); + return lldb::SBAddress(ref().GetBaseAddress()); } lldb::addr_t SBAddressRange::GetByteSize() const { LLDB_INSTRUMENT_VA(this); - if (!IsValid()) - return 0; - return m_opaque_up->GetByteSize(); + return ref().GetByteSize(); } bool SBAddressRange::GetDescription(SBStream &description, const SBTarget target) { LLDB_INSTRUMENT_VA(this, description, target); - Stream &stream = description.ref(); - if (!IsValid()) { - stream << "<invalid>"; - return true; - } - m_opaque_up->GetDescription(&stream, target.GetSP().get()); - return true; + return ref().GetDescription(&description.ref(), target.GetSP().get()); +} + +lldb_private::AddressRange &SBAddressRange::ref() const { + assert(m_opaque_up && "opaque pointer must always be valid"); + return *m_opaque_up; } diff --git a/lldb/source/API/SBAddressRangeList.cpp b/lldb/source/API/SBAddressRangeList.cpp index 20660b3ff208..957155d5125e 100644 --- a/lldb/source/API/SBAddressRangeList.cpp +++ b/lldb/source/API/SBAddressRangeList.cpp @@ -37,40 +37,40 @@ SBAddressRangeList::operator=(const SBAddressRangeList &rhs) { LLDB_INSTRUMENT_VA(this, rhs); if (this != &rhs) - *m_opaque_up = *rhs.m_opaque_up; + ref() = rhs.ref(); return *this; } uint32_t SBAddressRangeList::GetSize() const { LLDB_INSTRUMENT_VA(this); - return m_opaque_up->GetSize(); + return ref().GetSize(); } SBAddressRange SBAddressRangeList::GetAddressRangeAtIndex(uint64_t idx) { LLDB_INSTRUMENT_VA(this, idx); SBAddressRange sb_addr_range; - (*sb_addr_range.m_opaque_up) = m_opaque_up->GetAddressRangeAtIndex(idx); + (*sb_addr_range.m_opaque_up) = ref().GetAddressRangeAtIndex(idx); return sb_addr_range; } void SBAddressRangeList::Clear() { LLDB_INSTRUMENT_VA(this); - m_opaque_up->Clear(); + ref().Clear(); } void SBAddressRangeList::Append(const SBAddressRange &sb_addr_range) { LLDB_INSTRUMENT_VA(this, sb_addr_range); - m_opaque_up->Append(*sb_addr_range.m_opaque_up); + ref().Append(*sb_addr_range.m_opaque_up); } void SBAddressRangeList::Append(const SBAddressRangeList &sb_addr_range_list) { LLDB_INSTRUMENT_VA(this, sb_addr_range_list); - m_opaque_up->Append(*sb_addr_range_list.m_opaque_up); + ref().Append(*sb_addr_range_list.m_opaque_up); } bool SBAddressRangeList::GetDescription(SBStream &description, @@ -92,3 +92,8 @@ bool SBAddressRangeList::GetDescription(SBStream &description, stream << "]"; return true; } + +lldb_private::AddressRangeListImpl &SBAddressRangeList::ref() const { + assert(m_opaque_up && "opaque pointer must always be valid"); + return *m_opaque_up; +} diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp index 7ef0d6efd4aa..29da7d33dd80 100644 --- a/lldb/source/API/SBDebugger.cpp +++ b/lldb/source/API/SBDebugger.cpp @@ -1742,3 +1742,7 @@ bool SBDebugger::InterruptRequested() { return m_opaque_sp->InterruptRequested(); return false; } + +bool SBDebugger::SupportsLanguage(lldb::LanguageType language) { + return TypeSystem::SupportsLanguageStatic(language); +} diff --git a/lldb/source/API/SBProcess.cpp b/lldb/source/API/SBProcess.cpp index c37c111c5a58..efb3c8def279 100644 --- a/lldb/source/API/SBProcess.cpp +++ b/lldb/source/API/SBProcess.cpp @@ -14,6 +14,7 @@ #include "lldb/lldb-defines.h" #include "lldb/lldb-types.h" +#include "lldb/Core/AddressRangeListImpl.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" @@ -26,6 +27,7 @@ #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/Args.h" +#include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/ProcessInfo.h" #include "lldb/Utility/State.h" #include "lldb/Utility/Stream.h" @@ -320,8 +322,8 @@ void SBProcess::ReportEventState(const SBEvent &event, FileSP out) const { if (process_sp) { StreamFile stream(out); const StateType event_state = SBProcess::GetStateFromEvent(event); - stream.Printf("Process %" PRIu64 " %s\n", - process_sp->GetID(), SBDebugger::StateAsCString(event_state)); + stream.Printf("Process %" PRIu64 " %s\n", process_sp->GetID(), + SBDebugger::StateAsCString(event_state)); } } @@ -378,7 +380,6 @@ bool SBProcess::SetSelectedThreadByIndexID(uint32_t index_id) { ret_val = process_sp->GetThreadList().SetSelectedThreadByIndexID(index_id); } - return ret_val; } @@ -546,7 +547,6 @@ ByteOrder SBProcess::GetByteOrder() const { if (process_sp) byteOrder = process_sp->GetTarget().GetArchitecture().GetByteOrder(); - return byteOrder; } @@ -558,7 +558,6 @@ uint32_t SBProcess::GetAddressByteSize() const { if (process_sp) size = process_sp->GetTarget().GetArchitecture().GetAddressByteSize(); - return size; } @@ -810,6 +809,55 @@ const char *SBProcess::GetBroadcasterClass() { return ConstString(Process::GetStaticBroadcasterClass()).AsCString(); } +lldb::SBAddressRangeList SBProcess::FindRangesInMemory( + const void *buf, uint64_t size, const SBAddressRangeList &ranges, + uint32_t alignment, uint32_t max_matches, SBError &error) { + LLDB_INSTRUMENT_VA(this, buf, size, ranges, alignment, max_matches, error); + + lldb::SBAddressRangeList matches; + + ProcessSP process_sp(GetSP()); + if (!process_sp) { + error.SetErrorString("SBProcess is invalid"); + return matches; + } + Process::StopLocker stop_locker; + if (!stop_locker.TryLock(&process_sp->GetRunLock())) { + error.SetErrorString("process is running"); + return matches; + } + std::lock_guard<std::recursive_mutex> guard( + process_sp->GetTarget().GetAPIMutex()); + matches.m_opaque_up->ref() = process_sp->FindRangesInMemory( + reinterpret_cast<const uint8_t *>(buf), size, ranges.ref().ref(), + alignment, max_matches, error.ref()); + return matches; +} + +lldb::addr_t SBProcess::FindInMemory(const void *buf, uint64_t size, + const SBAddressRange &range, + uint32_t alignment, SBError &error) { + LLDB_INSTRUMENT_VA(this, buf, size, range, alignment, error); + + ProcessSP process_sp(GetSP()); + + if (!process_sp) { + error.SetErrorString("SBProcess is invalid"); + return LLDB_INVALID_ADDRESS; + } + + Process::StopLocker stop_locker; + if (!stop_locker.TryLock(&process_sp->GetRunLock())) { + error.SetErrorString("process is running"); + return LLDB_INVALID_ADDRESS; + } + + std::lock_guard<std::recursive_mutex> guard( + process_sp->GetTarget().GetAPIMutex()); + return process_sp->FindInMemory(reinterpret_cast<const uint8_t *>(buf), size, + range.ref(), alignment, error.ref()); +} + size_t SBProcess::ReadMemory(addr_t addr, void *dst, size_t dst_len, SBError &sb_error) { LLDB_INSTRUMENT_VA(this, addr, dst, dst_len, sb_error); diff --git a/lldb/source/API/SBStatisticsOptions.cpp b/lldb/source/API/SBStatisticsOptions.cpp index 7e826c4c93eb..71d4048573b5 100644 --- a/lldb/source/API/SBStatisticsOptions.cpp +++ b/lldb/source/API/SBStatisticsOptions.cpp @@ -18,7 +18,6 @@ using namespace lldb_private; SBStatisticsOptions::SBStatisticsOptions() : m_opaque_up(new StatisticsOptions()) { LLDB_INSTRUMENT_VA(this); - m_opaque_up->summary_only = false; } SBStatisticsOptions::SBStatisticsOptions(const SBStatisticsOptions &rhs) { @@ -39,17 +38,43 @@ SBStatisticsOptions::operator=(const SBStatisticsOptions &rhs) { } void SBStatisticsOptions::SetSummaryOnly(bool b) { - m_opaque_up->summary_only = b; + m_opaque_up->SetSummaryOnly(b); } -bool SBStatisticsOptions::GetSummaryOnly() { return m_opaque_up->summary_only; } +bool SBStatisticsOptions::GetSummaryOnly() { + return m_opaque_up->GetSummaryOnly(); +} + +void SBStatisticsOptions::SetIncludeTargets(bool b) { + m_opaque_up->SetIncludeTargets(b); +} + +bool SBStatisticsOptions::GetIncludeTargets() const { + return m_opaque_up->GetIncludeTargets(); +} + +void SBStatisticsOptions::SetIncludeModules(bool b) { + m_opaque_up->SetIncludeModules(b); +} + +bool SBStatisticsOptions::GetIncludeModules() const { + return m_opaque_up->GetIncludeModules(); +} + +void SBStatisticsOptions::SetIncludeTranscript(bool b) { + m_opaque_up->SetIncludeTranscript(b); +} + +bool SBStatisticsOptions::GetIncludeTranscript() const { + return m_opaque_up->GetIncludeTranscript(); +} void SBStatisticsOptions::SetReportAllAvailableDebugInfo(bool b) { - m_opaque_up->load_all_debug_info = b; + m_opaque_up->SetLoadAllDebugInfo(b); } bool SBStatisticsOptions::GetReportAllAvailableDebugInfo() { - return m_opaque_up->load_all_debug_info; + return m_opaque_up->GetLoadAllDebugInfo(); } const lldb_private::StatisticsOptions &SBStatisticsOptions::ref() const { diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index adb9e645610b..2c336296f0b8 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -1789,6 +1789,11 @@ lldb::SBSymbolContextList SBTarget::FindGlobalFunctions(const char *name, target_sp->GetImages().FindFunctions(RegularExpression(name_ref), function_options, *sb_sc_list); break; + case eMatchTypeRegexInsensitive: + target_sp->GetImages().FindFunctions( + RegularExpression(name_ref, llvm::Regex::RegexFlags::IgnoreCase), + function_options, *sb_sc_list); + break; case eMatchTypeStartsWith: regexstr = llvm::Regex::escape(name) + ".*"; target_sp->GetImages().FindFunctions(RegularExpression(regexstr), @@ -1936,6 +1941,11 @@ SBValueList SBTarget::FindGlobalVariables(const char *name, target_sp->GetImages().FindGlobalVariables(RegularExpression(name_ref), max_matches, variable_list); break; + case eMatchTypeRegexInsensitive: + target_sp->GetImages().FindGlobalVariables( + RegularExpression(name_ref, llvm::Regex::IgnoreCase), max_matches, + variable_list); + break; case eMatchTypeStartsWith: regexstr = "^" + llvm::Regex::escape(name) + ".*"; target_sp->GetImages().FindGlobalVariables(RegularExpression(regexstr), diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp index c53ec5a74648..10a691c40341 100644 --- a/lldb/source/API/SBValue.cpp +++ b/lldb/source/API/SBValue.cpp @@ -379,7 +379,10 @@ const char *SBValue::GetObjectDescription() { if (!value_sp) return nullptr; - return ConstString(value_sp->GetObjectDescription()).GetCString(); + llvm::Expected<std::string> str = value_sp->GetObjectDescription(); + if (!str) + return ConstString("error: " + toString(str.takeError())).AsCString(); + return ConstString(*str).AsCString(); } SBType SBValue::GetType() { @@ -1233,7 +1236,10 @@ bool SBValue::GetDescription(SBStream &description) { DumpValueObjectOptions options; options.SetUseDynamicType(m_opaque_sp->GetUseDynamic()); options.SetUseSyntheticValue(m_opaque_sp->GetUseSynthetic()); - value_sp->Dump(strm, options); + if (llvm::Error error = value_sp->Dump(strm, options)) { + strm << "error: " << toString(std::move(error)); + return false; + } } else { strm.PutCString("No value"); } @@ -1281,26 +1287,8 @@ lldb::addr_t SBValue::GetLoadAddress() { lldb::addr_t value = LLDB_INVALID_ADDRESS; ValueLocker locker; lldb::ValueObjectSP value_sp(GetSP(locker)); - if (value_sp) { - TargetSP target_sp(value_sp->GetTargetSP()); - if (target_sp) { - const bool scalar_is_load_address = true; - AddressType addr_type; - value = value_sp->GetAddressOf(scalar_is_load_address, &addr_type); - if (addr_type == eAddressTypeFile) { - ModuleSP module_sp(value_sp->GetModule()); - if (!module_sp) - value = LLDB_INVALID_ADDRESS; - else { - Address addr; - module_sp->ResolveFileAddress(value, addr); - value = addr.GetLoadAddress(target_sp.get()); - } - } else if (addr_type == eAddressTypeHost || - addr_type == eAddressTypeInvalid) - value = LLDB_INVALID_ADDRESS; - } - } + if (value_sp) + return value_sp->GetLoadAddress(); return value; } diff --git a/lldb/source/Breakpoint/BreakpointOptions.cpp b/lldb/source/Breakpoint/BreakpointOptions.cpp index 6c6037dd9edd..1db840169811 100644 --- a/lldb/source/Breakpoint/BreakpointOptions.cpp +++ b/lldb/source/Breakpoint/BreakpointOptions.cpp @@ -102,19 +102,11 @@ const char *BreakpointOptions::g_option_names[( "ConditionText", "IgnoreCount", "EnabledState", "OneShotState", "AutoContinue"}; -bool BreakpointOptions::NullCallback(void *baton, - StoppointCallbackContext *context, - lldb::user_id_t break_id, - lldb::user_id_t break_loc_id) { - return true; -} - // BreakpointOptions constructor BreakpointOptions::BreakpointOptions(bool all_flags_set) - : m_callback(BreakpointOptions::NullCallback), - m_baton_is_command_baton(false), m_callback_is_synchronous(false), - m_enabled(true), m_one_shot(false), m_ignore_count(0), - m_condition_text_hash(0), m_inject_condition(false), + : m_callback(nullptr), m_baton_is_command_baton(false), + m_callback_is_synchronous(false), m_enabled(true), m_one_shot(false), + m_ignore_count(0), m_condition_text_hash(0), m_inject_condition(false), m_auto_continue(false), m_set_flags(0) { if (all_flags_set) m_set_flags.Set(~((Flags::ValueType)0)); @@ -420,7 +412,7 @@ void BreakpointOptions::SetCallback( } void BreakpointOptions::ClearCallback() { - m_callback = BreakpointOptions::NullCallback; + m_callback = nullptr; m_callback_is_synchronous = false; m_callback_baton_sp.reset(); m_baton_is_command_baton = false; @@ -449,7 +441,7 @@ bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context, } bool BreakpointOptions::HasCallback() const { - return m_callback != BreakpointOptions::NullCallback; + return static_cast<bool>(m_callback); } bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) { diff --git a/lldb/source/Breakpoint/Watchpoint.cpp b/lldb/source/Breakpoint/Watchpoint.cpp index edb1a0e93460..715e83c76697 100644 --- a/lldb/source/Breakpoint/Watchpoint.cpp +++ b/lldb/source/Breakpoint/Watchpoint.cpp @@ -299,7 +299,9 @@ bool Watchpoint::DumpSnapshots(Stream *s, const char *prefix) const { .SetHideRootType(true) .SetHideRootName(true) .SetHideName(true); - m_old_value_sp->Dump(strm, options); + if (llvm::Error error = m_old_value_sp->Dump(strm, options)) + strm << "error: " << toString(std::move(error)); + if (strm.GetData()) values_ss.Printf("old value: %s", strm.GetData()); } @@ -322,7 +324,9 @@ bool Watchpoint::DumpSnapshots(Stream *s, const char *prefix) const { .SetHideRootType(true) .SetHideRootName(true) .SetHideName(true); - m_new_value_sp->Dump(strm, options); + if (llvm::Error error = m_new_value_sp->Dump(strm, options)) + strm << "error: " << toString(std::move(error)); + if (strm.GetData()) values_ss.Printf("new value: %s", strm.GetData()); } diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp index 57a372a762e1..b7cd955e0020 100644 --- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp +++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp @@ -133,13 +133,22 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, auto dump_val_object = [&](ValueObject &valobj) { if (is_po) { StreamString temp_result_stream; - valobj.Dump(temp_result_stream, dump_options); + if (llvm::Error error = valobj.Dump(temp_result_stream, dump_options)) { + result.AppendError(toString(std::move(error))); + return; + } llvm::StringRef output = temp_result_stream.GetString(); maybe_add_hint(output); result.GetOutputStream() << output; } else { - valobj.Dump(result.GetOutputStream(), dump_options); + llvm::Error error = + valobj.Dump(result.GetOutputStream(), dump_options); + if (error) { + result.AppendError(toString(std::move(error))); + return; + } } + result.SetStatus(eReturnStatusSuccessFinishResult); }; // First, try `expr` as the name of a frame variable. @@ -160,7 +169,6 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, } dump_val_object(*valobj_sp); - result.SetStatus(eReturnStatusSuccessFinishResult); return; } } @@ -171,7 +179,6 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, if (auto var_sp = state->GetVariable(expr)) if (auto valobj_sp = var_sp->GetValueObject()) { dump_val_object(*valobj_sp); - result.SetStatus(eReturnStatusSuccessFinishResult); return; } @@ -192,34 +199,36 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, error_stream << " " << fixed_expression << "\n"; } - if (expr_result == eExpressionCompleted) { - if (verbosity != eDWIMPrintVerbosityNone) { - StringRef flags; - if (args.HasArgs()) - flags = args.GetArgStringWithDelimiter(); - result.AppendMessageWithFormatv("note: ran `expression {0}{1}`", flags, - expr); - } - - if (valobj_sp->GetError().GetError() != UserExpression::kNoResult) - dump_val_object(*valobj_sp); - - if (suppress_result) - if (auto result_var_sp = - target.GetPersistentVariable(valobj_sp->GetName())) { - auto language = valobj_sp->GetPreferredDisplayLanguage(); - if (auto *persistent_state = - target.GetPersistentExpressionStateForLanguage(language)) - persistent_state->RemovePersistentVariable(result_var_sp); - } - - result.SetStatus(eReturnStatusSuccessFinishResult); - } else { + // If the expression failed, return an error. + if (expr_result != eExpressionCompleted) { if (valobj_sp) result.SetError(valobj_sp->GetError()); else result.AppendErrorWithFormatv( "unknown error evaluating expression `{0}`", expr); + return; + } + + if (verbosity != eDWIMPrintVerbosityNone) { + StringRef flags; + if (args.HasArgs()) + flags = args.GetArgStringWithDelimiter(); + result.AppendMessageWithFormatv("note: ran `expression {0}{1}`", flags, + expr); } + + if (valobj_sp->GetError().GetError() != UserExpression::kNoResult) + dump_val_object(*valobj_sp); + else + result.SetStatus(eReturnStatusSuccessFinishResult); + + if (suppress_result) + if (auto result_var_sp = + target.GetPersistentVariable(valobj_sp->GetName())) { + auto language = valobj_sp->GetPreferredDisplayLanguage(); + if (auto *persistent_state = + target.GetPersistentExpressionStateForLanguage(language)) + persistent_state->RemovePersistentVariable(result_var_sp); + } } } diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp index 2319ddd3c80a..eb76753d98ef 100644 --- a/lldb/source/Commands/CommandObjectExpression.cpp +++ b/lldb/source/Commands/CommandObjectExpression.cpp @@ -461,7 +461,11 @@ bool CommandObjectExpression::EvaluateExpression(llvm::StringRef expr, options.SetVariableFormatDisplayLanguage( result_valobj_sp->GetPreferredDisplayLanguage()); - result_valobj_sp->Dump(output_stream, options); + if (llvm::Error error = + result_valobj_sp->Dump(output_stream, options)) { + result.AppendError(toString(std::move(error))); + return false; + } if (suppress_result) if (auto result_var_sp = diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp index b1d060b3c6cf..3f4178c1a959 100644 --- a/lldb/source/Commands/CommandObjectFrame.cpp +++ b/lldb/source/Commands/CommandObjectFrame.cpp @@ -170,7 +170,8 @@ protected: assert(valobj_sp.get() && "Must have a valid ValueObject to print"); ValueObjectPrinter printer(*valobj_sp, &result.GetOutputStream(), options); - printer.PrintValueObject(); + if (llvm::Error error = printer.PrintValueObject()) + result.AppendError(toString(std::move(error))); } CommandOptions m_options; @@ -555,7 +556,9 @@ protected: show_module)) s.PutCString(": "); } - valobj_sp->Dump(result.GetOutputStream(), options); + auto &strm = result.GetOutputStream(); + if (llvm::Error error = valobj_sp->Dump(strm, options)) + result.AppendError(toString(std::move(error))); } } } else { @@ -597,7 +600,8 @@ protected: Stream &output_stream = result.GetOutputStream(); options.SetRootValueObjectName( valobj_sp->GetParent() ? entry.c_str() : nullptr); - valobj_sp->Dump(output_stream, options); + if (llvm::Error error = valobj_sp->Dump(output_stream, options)) + result.AppendError(toString(std::move(error))); } else { if (auto error_cstr = error.AsCString(nullptr)) result.AppendError(error_cstr); @@ -648,7 +652,9 @@ protected: valobj_sp->GetPreferredDisplayLanguage()); options.SetRootValueObjectName( var_sp ? var_sp->GetName().AsCString() : nullptr); - valobj_sp->Dump(result.GetOutputStream(), options); + if (llvm::Error error = + valobj_sp->Dump(result.GetOutputStream(), options)) + result.AppendError(toString(std::move(error))); } } } @@ -669,7 +675,9 @@ protected: options.SetVariableFormatDisplayLanguage( rec_value_sp->GetPreferredDisplayLanguage()); options.SetRootValueObjectName(rec_value_sp->GetName().AsCString()); - rec_value_sp->Dump(result.GetOutputStream(), options); + if (llvm::Error error = + rec_value_sp->Dump(result.GetOutputStream(), options)) + result.AppendError(toString(std::move(error))); } } } diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp index 1c13484dede6..137b1ad98107 100644 --- a/lldb/source/Commands/CommandObjectMemory.cpp +++ b/lldb/source/Commands/CommandObjectMemory.cpp @@ -815,7 +815,10 @@ protected: DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions( eLanguageRuntimeDescriptionDisplayVerbosityFull, format)); - valobj_sp->Dump(*output_stream_p, options); + if (llvm::Error error = valobj_sp->Dump(*output_stream_p, options)) { + result.AppendError(toString(std::move(error))); + return; + } } else { result.AppendErrorWithFormat( "failed to create a value object for: (%s) %s\n", diff --git a/lldb/source/Commands/CommandObjectStats.cpp b/lldb/source/Commands/CommandObjectStats.cpp index 1935b0fdfadf..53855e7d0316 100644 --- a/lldb/source/Commands/CommandObjectStats.cpp +++ b/lldb/source/Commands/CommandObjectStats.cpp @@ -11,6 +11,7 @@ #include "lldb/Host/OptionParser.h" #include "lldb/Interpreter/CommandOptionArgumentTable.h" #include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Target/Target.h" using namespace lldb; @@ -76,13 +77,31 @@ class CommandObjectStatsDump : public CommandObjectParsed { m_all_targets = true; break; case 's': - m_stats_options.summary_only = true; + m_stats_options.SetSummaryOnly(true); break; case 'f': - m_stats_options.load_all_debug_info = true; + m_stats_options.SetLoadAllDebugInfo(true); + break; + case 'r': + if (llvm::Expected<bool> bool_or_error = + OptionArgParser::ToBoolean("--targets", option_arg)) + m_stats_options.SetIncludeTargets(*bool_or_error); + else + error = bool_or_error.takeError(); + break; + case 'm': + if (llvm::Expected<bool> bool_or_error = + OptionArgParser::ToBoolean("--modules", option_arg)) + m_stats_options.SetIncludeModules(*bool_or_error); + else + error = bool_or_error.takeError(); break; case 't': - m_stats_options.include_transcript = true; + if (llvm::Expected<bool> bool_or_error = + OptionArgParser::ToBoolean("--transcript", option_arg)) + m_stats_options.SetIncludeTranscript(*bool_or_error); + else + error = bool_or_error.takeError(); break; default: llvm_unreachable("Unimplemented option"); diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index ae6c6d5479a1..80181a9b3cb7 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -784,7 +784,8 @@ public: options.SetRootValueObjectName(root_name); - valobj_sp->Dump(s, options); + if (llvm::Error error = valobj_sp->Dump(s, options)) + s << "error: " << toString(std::move(error)); } static size_t GetVariableCallback(void *baton, const char *name, diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index db96ee2cec38..5e64dd2f8f08 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -383,7 +383,7 @@ public: eCommandProcessMustBePaused), m_step_type(step_type), m_step_scope(step_scope), m_class_options("scripted step") { - AddSimpleArgumentList(eArgTypeThreadID, eArgRepeatOptional); + AddSimpleArgumentList(eArgTypeThreadIndex, eArgRepeatOptional); if (step_type == eStepTypeScripted) { m_all_options.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2, @@ -1386,7 +1386,10 @@ public: Stream &strm = result.GetOutputStream(); ValueObjectSP exception_object_sp = thread_sp->GetCurrentException(); if (exception_object_sp) { - exception_object_sp->Dump(strm); + if (llvm::Error error = exception_object_sp->Dump(strm)) { + result.AppendError(toString(std::move(error))); + return false; + } } ThreadSP exception_thread_sp = thread_sp->GetCurrentExceptionBacktrace(); @@ -1438,9 +1441,12 @@ public: return false; } ValueObjectSP exception_object_sp = thread_sp->GetSiginfoValue(); - if (exception_object_sp) - exception_object_sp->Dump(strm); - else + if (exception_object_sp) { + if (llvm::Error error = exception_object_sp->Dump(strm)) { + result.AppendError(toString(std::move(error))); + return false; + } + } else strm.Printf("(no siginfo)\n"); strm.PutChar('\n'); diff --git a/lldb/source/Commands/CommandObjectThreadUtil.h b/lldb/source/Commands/CommandObjectThreadUtil.h index 74d1136bab7f..3fc28efe8cf7 100644 --- a/lldb/source/Commands/CommandObjectThreadUtil.h +++ b/lldb/source/Commands/CommandObjectThreadUtil.h @@ -10,6 +10,7 @@ #define LLDB_SOURCE_COMMANDS_COMMANDOBJECTTHREADUTIL_H #include "lldb/Interpreter/CommandObjectMultiword.h" +#include <stack> namespace lldb_private { diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index cee5a81d3796..ba256e5ab917 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -1425,8 +1425,28 @@ let Command = "statistics dump" in { Desc<"Dump the total possible debug info statistics. " "Force loading all the debug information if not yet loaded, and collect " "statistics with those.">; + def statistics_dump_targets: Option<"targets", "r">, Group<1>, + Arg<"Boolean">, + Desc<"Dump statistics for the targets, including breakpoints, expression " + "evaluations, frame variables, etc. " + "Defaults to true, unless the '--summary' mode is enabled, in which case " + "this is turned off unless specified. " + "If both the '--targets' and the '--modules' options are 'true', a list " + "of module identifiers will be added to the 'targets' section.">; + def statistics_dump_modules: Option<"modules", "m">, Group<1>, + Arg<"Boolean">, + Desc<"Dump statistics for the modules, including time and size of various " + "aspects of the module and debug information, type system, path, etc. " + "Defaults to true, unless the '--summary' mode is enabled, in which case " + "this is turned off unless specified. " + "If both the '--targets' and the '--modules' options are 'true', a list " + "of module identifiers will be added to the 'targets' section.">; def statistics_dump_transcript: Option<"transcript", "t">, Group<1>, + Arg<"Boolean">, Desc<"If the setting interpreter.save-transcript is enabled and this " - "option is specified, include a JSON array with all commands the user and/" - "or scripts executed during a debug session.">; + "option is 'true', include a JSON array with all commands the user and/or " + "scripts executed during a debug session. " + "Defaults to true, unless the '--summary' mode is enabled, in which case " + "this is turned off unless specified.">; + } diff --git a/lldb/source/Core/DumpRegisterInfo.cpp b/lldb/source/Core/DumpRegisterInfo.cpp index 833479541690..eccc6784cd49 100644 --- a/lldb/source/Core/DumpRegisterInfo.cpp +++ b/lldb/source/Core/DumpRegisterInfo.cpp @@ -111,6 +111,11 @@ void lldb_private::DoDumpRegisterInfo( }; DumpList(strm, " In sets: ", in_sets, emit_set); - if (flags_type) + if (flags_type) { strm.Printf("\n\n%s", flags_type->AsTable(terminal_width).c_str()); + + std::string enumerators = flags_type->DumpEnums(terminal_width); + if (enumerators.size()) + strm << "\n\n" << enumerators; + } } diff --git a/lldb/source/Core/DumpRegisterValue.cpp b/lldb/source/Core/DumpRegisterValue.cpp index 463aa5926772..90b31fd0e865 100644 --- a/lldb/source/Core/DumpRegisterValue.cpp +++ b/lldb/source/Core/DumpRegisterValue.cpp @@ -54,7 +54,8 @@ static void dump_type_value(lldb_private::CompilerType &fields_type, T value, }; dump_options.SetChildPrintingDecider(decider).SetHideRootType(true); - vobj_sp->Dump(strm, dump_options); + if (llvm::Error error = vobj_sp->Dump(strm, dump_options)) + strm << "error: " << toString(std::move(error)); } void lldb_private::DumpRegisterValue(const RegisterValue ®_val, Stream &s, diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 1c3a4cb1062f..fe95858f35c9 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -1401,7 +1401,10 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, ValueObjectSP return_valobj_sp = StopInfo::GetReturnValueObject(stop_info_sp); if (return_valobj_sp) { - return_valobj_sp->Dump(s); + if (llvm::Error error = return_valobj_sp->Dump(s)) { + s << "error: " << toString(std::move(error)); + return false; + } return true; } } @@ -1418,7 +1421,11 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, ExpressionVariableSP expression_var_sp = StopInfo::GetExpressionVariable(stop_info_sp); if (expression_var_sp && expression_var_sp->GetValueObject()) { - expression_var_sp->GetValueObject()->Dump(s); + if (llvm::Error error = + expression_var_sp->GetValueObject()->Dump(s)) { + s << "error: " << toString(std::move(error)); + return false; + } return true; } } diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index f32cb82faa00..8f72efc2299b 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -989,41 +989,46 @@ ValueObject::ReadPointedString(lldb::WritableDataBufferSP &buffer_sp, return {total_bytes_read, was_capped}; } -const char *ValueObject::GetObjectDescription() { +llvm::Expected<std::string> ValueObject::GetObjectDescription() { if (!UpdateValueIfNeeded(true)) - return nullptr; + return llvm::createStringError("could not update value"); // Return cached value. if (!m_object_desc_str.empty()) - return m_object_desc_str.c_str(); + return m_object_desc_str; ExecutionContext exe_ctx(GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); if (!process) - return nullptr; + return llvm::createStringError("no process"); // Returns the object description produced by one language runtime. - auto get_object_description = [&](LanguageType language) -> const char * { + auto get_object_description = + [&](LanguageType language) -> llvm::Expected<std::string> { if (LanguageRuntime *runtime = process->GetLanguageRuntime(language)) { StreamString s; - if (runtime->GetObjectDescription(s, *this)) { - m_object_desc_str.append(std::string(s.GetString())); - return m_object_desc_str.c_str(); - } + if (llvm::Error error = runtime->GetObjectDescription(s, *this)) + return error; + m_object_desc_str = s.GetString(); + return m_object_desc_str; } - return nullptr; + return llvm::createStringError("no native language runtime"); }; // Try the native language runtime first. LanguageType native_language = GetObjectRuntimeLanguage(); - if (const char *desc = get_object_description(native_language)) + llvm::Expected<std::string> desc = get_object_description(native_language); + if (desc) return desc; // Try the Objective-C language runtime. This fallback is necessary // for Objective-C++ and mixed Objective-C / C++ programs. - if (Language::LanguageIsCFamily(native_language)) + if (Language::LanguageIsCFamily(native_language)) { + // We're going to try again, so let's drop the first error. + llvm::consumeError(desc.takeError()); return get_object_description(eLanguageTypeObjC); - return nullptr; + } + return desc; } bool ValueObject::GetValueAsCString(const lldb_private::TypeFormatImpl &format, @@ -1116,6 +1121,153 @@ int64_t ValueObject::GetValueAsSigned(int64_t fail_value, bool *success) { return fail_value; } +llvm::Expected<llvm::APSInt> ValueObject::GetValueAsAPSInt() { + // Make sure the type can be converted to an APSInt. + if (!GetCompilerType().IsInteger() && + !GetCompilerType().IsScopedEnumerationType() && + !GetCompilerType().IsEnumerationType() && + !GetCompilerType().IsPointerType() && + !GetCompilerType().IsNullPtrType() && + !GetCompilerType().IsReferenceType() && !GetCompilerType().IsBoolean()) + return llvm::make_error<llvm::StringError>( + "type cannot be converted to APSInt", llvm::inconvertibleErrorCode()); + + if (CanProvideValue()) { + Scalar scalar; + if (ResolveValue(scalar)) + return scalar.GetAPSInt(); + } + + return llvm::make_error<llvm::StringError>( + "error occurred; unable to convert to APSInt", + llvm::inconvertibleErrorCode()); +} + +llvm::Expected<llvm::APFloat> ValueObject::GetValueAsAPFloat() { + if (!GetCompilerType().IsFloat()) + return llvm::make_error<llvm::StringError>( + "type cannot be converted to APFloat", llvm::inconvertibleErrorCode()); + + if (CanProvideValue()) { + Scalar scalar; + if (ResolveValue(scalar)) + return scalar.GetAPFloat(); + } + + return llvm::make_error<llvm::StringError>( + "error occurred; unable to convert to APFloat", + llvm::inconvertibleErrorCode()); +} + +llvm::Expected<bool> ValueObject::GetValueAsBool() { + CompilerType val_type = GetCompilerType(); + if (val_type.IsInteger() || val_type.IsUnscopedEnumerationType() || + val_type.IsPointerType()) { + auto value_or_err = GetValueAsAPSInt(); + if (value_or_err) + return value_or_err->getBoolValue(); + } + if (val_type.IsFloat()) { + auto value_or_err = GetValueAsAPFloat(); + if (value_or_err) + return value_or_err->isNonZero(); + } + if (val_type.IsArrayType()) + return GetAddressOf() != 0; + + return llvm::make_error<llvm::StringError>("type cannot be converted to bool", + llvm::inconvertibleErrorCode()); +} + +void ValueObject::SetValueFromInteger(const llvm::APInt &value, Status &error) { + // Verify the current object is an integer object + CompilerType val_type = GetCompilerType(); + if (!val_type.IsInteger() && !val_type.IsUnscopedEnumerationType() && + !val_type.IsFloat() && !val_type.IsPointerType() && + !val_type.IsScalarType()) { + error.SetErrorString("current value object is not an integer objet"); + return; + } + + // Verify the current object is not actually associated with any program + // variable. + if (GetVariable()) { + error.SetErrorString("current value object is not a temporary object"); + return; + } + + // Verify the proposed new value is the right size. + lldb::TargetSP target = GetTargetSP(); + uint64_t byte_size = 0; + if (auto temp = GetCompilerType().GetByteSize(target.get())) + byte_size = temp.value(); + if (value.getBitWidth() != byte_size * CHAR_BIT) { + error.SetErrorString( + "illegal argument: new value should be of the same size"); + return; + } + + lldb::DataExtractorSP data_sp; + data_sp->SetData(value.getRawData(), byte_size, + target->GetArchitecture().GetByteOrder()); + data_sp->SetAddressByteSize( + static_cast<uint8_t>(target->GetArchitecture().GetAddressByteSize())); + SetData(*data_sp, error); +} + +void ValueObject::SetValueFromInteger(lldb::ValueObjectSP new_val_sp, + Status &error) { + // Verify the current object is an integer object + CompilerType val_type = GetCompilerType(); + if (!val_type.IsInteger() && !val_type.IsUnscopedEnumerationType() && + !val_type.IsFloat() && !val_type.IsPointerType() && + !val_type.IsScalarType()) { + error.SetErrorString("current value object is not an integer objet"); + return; + } + + // Verify the current object is not actually associated with any program + // variable. + if (GetVariable()) { + error.SetErrorString("current value object is not a temporary object"); + return; + } + + // Verify the proposed new value is the right type. + CompilerType new_val_type = new_val_sp->GetCompilerType(); + if (!new_val_type.IsInteger() && !new_val_type.IsFloat() && + !new_val_type.IsPointerType()) { + error.SetErrorString( + "illegal argument: new value should be of the same size"); + return; + } + + if (new_val_type.IsInteger()) { + auto value_or_err = new_val_sp->GetValueAsAPSInt(); + if (value_or_err) + SetValueFromInteger(*value_or_err, error); + else + error.SetErrorString("error getting APSInt from new_val_sp"); + } else if (new_val_type.IsFloat()) { + auto value_or_err = new_val_sp->GetValueAsAPFloat(); + if (value_or_err) + SetValueFromInteger(value_or_err->bitcastToAPInt(), error); + else + error.SetErrorString("error getting APFloat from new_val_sp"); + } else if (new_val_type.IsPointerType()) { + bool success = true; + uint64_t int_val = new_val_sp->GetValueAsUnsigned(0, &success); + if (success) { + lldb::TargetSP target = GetTargetSP(); + uint64_t num_bits = 0; + if (auto temp = new_val_sp->GetCompilerType().GetBitSize(target.get())) + num_bits = temp.value(); + SetValueFromInteger(llvm::APInt(num_bits, int_val), error); + } else + error.SetErrorString("error converting new_val_sp to integer"); + } +} + // if any more "special cases" are added to // ValueObject::DumpPrintableRepresentation() please keep this call up to date // by returning true for your new special cases. We will eventually move to @@ -1325,9 +1477,16 @@ bool ValueObject::DumpPrintableRepresentation( str = GetSummaryAsCString(); break; - case eValueObjectRepresentationStyleLanguageSpecific: - str = GetObjectDescription(); - break; + case eValueObjectRepresentationStyleLanguageSpecific: { + llvm::Expected<std::string> desc = GetObjectDescription(); + if (!desc) { + strm << "error: " << toString(desc.takeError()); + str = strm.GetString(); + } else { + strm << *desc; + str = strm.GetString(); + } + } break; case eValueObjectRepresentationStyleLocation: str = GetLocationAsCString(); @@ -2566,11 +2725,14 @@ ValueObjectSP ValueObject::GetValueForExpressionPath_Impl( } } -void ValueObject::Dump(Stream &s) { Dump(s, DumpValueObjectOptions(*this)); } +llvm::Error ValueObject::Dump(Stream &s) { + return Dump(s, DumpValueObjectOptions(*this)); +} -void ValueObject::Dump(Stream &s, const DumpValueObjectOptions &options) { +llvm::Error ValueObject::Dump(Stream &s, + const DumpValueObjectOptions &options) { ValueObjectPrinter printer(*this, &s, options); - printer.PrintValueObject(); + return printer.PrintValueObject(); } ValueObjectSP ValueObject::CreateConstantValue(ConstString name) { @@ -2843,6 +3005,374 @@ ValueObjectSP ValueObject::CastPointerType(const char *name, TypeSP &type_sp) { return valobj_sp; } +lldb::addr_t ValueObject::GetLoadAddress() { + lldb::addr_t addr_value = LLDB_INVALID_ADDRESS; + if (auto target_sp = GetTargetSP()) { + const bool scalar_is_load_address = true; + AddressType addr_type; + addr_value = GetAddressOf(scalar_is_load_address, &addr_type); + if (addr_type == eAddressTypeFile) { + lldb::ModuleSP module_sp(GetModule()); + if (!module_sp) + addr_value = LLDB_INVALID_ADDRESS; + else { + Address tmp_addr; + module_sp->ResolveFileAddress(addr_value, tmp_addr); + addr_value = tmp_addr.GetLoadAddress(target_sp.get()); + } + } else if (addr_type == eAddressTypeHost || + addr_type == eAddressTypeInvalid) + addr_value = LLDB_INVALID_ADDRESS; + } + return addr_value; +} + +llvm::Expected<lldb::ValueObjectSP> ValueObject::CastDerivedToBaseType( + CompilerType type, const llvm::ArrayRef<uint32_t> &base_type_indices) { + // Make sure the starting type and the target type are both valid for this + // type of cast; otherwise return the shared pointer to the original + // (unchanged) ValueObject. + if (!type.IsPointerType() && !type.IsReferenceType()) + return llvm::make_error<llvm::StringError>( + "Invalid target type: should be a pointer or a reference", + llvm::inconvertibleErrorCode()); + + CompilerType start_type = GetCompilerType(); + if (start_type.IsReferenceType()) + start_type = start_type.GetNonReferenceType(); + + auto target_record_type = + type.IsPointerType() ? type.GetPointeeType() : type.GetNonReferenceType(); + auto start_record_type = + start_type.IsPointerType() ? start_type.GetPointeeType() : start_type; + + if (!target_record_type.IsRecordType() || !start_record_type.IsRecordType()) + return llvm::make_error<llvm::StringError>( + "Underlying start & target types should be record types", + llvm::inconvertibleErrorCode()); + + if (target_record_type.CompareTypes(start_record_type)) + return llvm::make_error<llvm::StringError>( + "Underlying start & target types should be different", + llvm::inconvertibleErrorCode()); + + if (base_type_indices.empty()) + return llvm::make_error<llvm::StringError>( + "Children sequence must be non-empty", llvm::inconvertibleErrorCode()); + + // Both the starting & target types are valid for the cast, and the list of + // base class indices is non-empty, so we can proceed with the cast. + + lldb::TargetSP target = GetTargetSP(); + // The `value` can be a pointer, but GetChildAtIndex works for pointers too. + lldb::ValueObjectSP inner_value = GetSP(); + + for (const uint32_t i : base_type_indices) + // Create synthetic value if needed. + inner_value = + inner_value->GetChildAtIndex(i, /*can_create_synthetic*/ true); + + // At this point type of `inner_value` should be the dereferenced target + // type. + CompilerType inner_value_type = inner_value->GetCompilerType(); + if (type.IsPointerType()) { + if (!inner_value_type.CompareTypes(type.GetPointeeType())) + return llvm::make_error<llvm::StringError>( + "casted value doesn't match the desired type", + llvm::inconvertibleErrorCode()); + + uintptr_t addr = inner_value->GetLoadAddress(); + llvm::StringRef name = ""; + ExecutionContext exe_ctx(target.get(), false); + return ValueObject::CreateValueObjectFromAddress(name, addr, exe_ctx, type, + /* do deref */ false); + } + + // At this point the target type should be a reference. + if (!inner_value_type.CompareTypes(type.GetNonReferenceType())) + return llvm::make_error<llvm::StringError>( + "casted value doesn't match the desired type", + llvm::inconvertibleErrorCode()); + + return lldb::ValueObjectSP(inner_value->Cast(type.GetNonReferenceType())); +} + +llvm::Expected<lldb::ValueObjectSP> +ValueObject::CastBaseToDerivedType(CompilerType type, uint64_t offset) { + // Make sure the starting type and the target type are both valid for this + // type of cast; otherwise return the shared pointer to the original + // (unchanged) ValueObject. + if (!type.IsPointerType() && !type.IsReferenceType()) + return llvm::make_error<llvm::StringError>( + "Invalid target type: should be a pointer or a reference", + llvm::inconvertibleErrorCode()); + + CompilerType start_type = GetCompilerType(); + if (start_type.IsReferenceType()) + start_type = start_type.GetNonReferenceType(); + + auto target_record_type = + type.IsPointerType() ? type.GetPointeeType() : type.GetNonReferenceType(); + auto start_record_type = + start_type.IsPointerType() ? start_type.GetPointeeType() : start_type; + + if (!target_record_type.IsRecordType() || !start_record_type.IsRecordType()) + return llvm::make_error<llvm::StringError>( + "Underlying start & target types should be record types", + llvm::inconvertibleErrorCode()); + + if (target_record_type.CompareTypes(start_record_type)) + return llvm::make_error<llvm::StringError>( + "Underlying start & target types should be different", + llvm::inconvertibleErrorCode()); + + CompilerType virtual_base; + if (target_record_type.IsVirtualBase(start_record_type, &virtual_base)) { + if (!virtual_base.IsValid()) + return llvm::make_error<llvm::StringError>( + "virtual base should be valid", llvm::inconvertibleErrorCode()); + return llvm::make_error<llvm::StringError>( + llvm::Twine("cannot cast " + start_type.TypeDescription() + " to " + + type.TypeDescription() + " via virtual base " + + virtual_base.TypeDescription()), + llvm::inconvertibleErrorCode()); + } + + // Both the starting & target types are valid for the cast, so we can + // proceed with the cast. + + lldb::TargetSP target = GetTargetSP(); + auto pointer_type = + type.IsPointerType() ? type : type.GetNonReferenceType().GetPointerType(); + + uintptr_t addr = + type.IsPointerType() ? GetValueAsUnsigned(0) : GetLoadAddress(); + + llvm::StringRef name = ""; + ExecutionContext exe_ctx(target.get(), false); + lldb::ValueObjectSP value = ValueObject::CreateValueObjectFromAddress( + name, addr - offset, exe_ctx, pointer_type, /* do_deref */ false); + + if (type.IsPointerType()) + return value; + + // At this point the target type is a reference. Since `value` is a pointer, + // it has to be dereferenced. + Status error; + return value->Dereference(error); +} + +lldb::ValueObjectSP ValueObject::CastToBasicType(CompilerType type) { + bool is_scalar = GetCompilerType().IsScalarType(); + bool is_enum = GetCompilerType().IsEnumerationType(); + bool is_pointer = + GetCompilerType().IsPointerType() || GetCompilerType().IsNullPtrType(); + bool is_float = GetCompilerType().IsFloat(); + bool is_integer = GetCompilerType().IsInteger(); + + if (!type.IsScalarType()) { + m_error.SetErrorString("target type must be a scalar"); + return GetSP(); + } + + if (!is_scalar && !is_enum && !is_pointer) { + m_error.SetErrorString("argument must be a scalar, enum, or pointer"); + return GetSP(); + } + + lldb::TargetSP target = GetTargetSP(); + uint64_t type_byte_size = 0; + uint64_t val_byte_size = 0; + if (auto temp = type.GetByteSize(target.get())) + type_byte_size = temp.value(); + if (auto temp = GetCompilerType().GetByteSize(target.get())) + val_byte_size = temp.value(); + + if (is_pointer) { + if (!type.IsInteger() && !type.IsBoolean()) { + m_error.SetErrorString("target type must be an integer or boolean"); + return GetSP(); + } + if (!type.IsBoolean() && type_byte_size < val_byte_size) { + m_error.SetErrorString( + "target type cannot be smaller than the pointer type"); + return GetSP(); + } + } + + if (type.IsBoolean()) { + if (!is_scalar || is_integer) + return ValueObject::CreateValueObjectFromBool( + target, GetValueAsUnsigned(0) != 0, "result"); + else if (is_scalar && is_float) { + auto float_value_or_err = GetValueAsAPFloat(); + if (float_value_or_err) + return ValueObject::CreateValueObjectFromBool( + target, !float_value_or_err->isZero(), "result"); + else { + m_error.SetErrorStringWithFormat( + "cannot get value as APFloat: %s", + llvm::toString(float_value_or_err.takeError()).c_str()); + return GetSP(); + } + } + } + + if (type.IsInteger()) { + if (!is_scalar || is_integer) { + auto int_value_or_err = GetValueAsAPSInt(); + if (int_value_or_err) { + // Get the value as APSInt and extend or truncate it to the requested + // size. + llvm::APSInt ext = + int_value_or_err->extOrTrunc(type_byte_size * CHAR_BIT); + return ValueObject::CreateValueObjectFromAPInt(target, ext, type, + "result"); + } else { + m_error.SetErrorStringWithFormat( + "cannot get value as APSInt: %s", + llvm::toString(int_value_or_err.takeError()).c_str()); + ; + return GetSP(); + } + } else if (is_scalar && is_float) { + llvm::APSInt integer(type_byte_size * CHAR_BIT, !type.IsSigned()); + bool is_exact; + auto float_value_or_err = GetValueAsAPFloat(); + if (float_value_or_err) { + llvm::APFloatBase::opStatus status = + float_value_or_err->convertToInteger( + integer, llvm::APFloat::rmTowardZero, &is_exact); + + // Casting floating point values that are out of bounds of the target + // type is undefined behaviour. + if (status & llvm::APFloatBase::opInvalidOp) { + m_error.SetErrorStringWithFormat( + "invalid type cast detected: %s", + llvm::toString(float_value_or_err.takeError()).c_str()); + return GetSP(); + } + return ValueObject::CreateValueObjectFromAPInt(target, integer, type, + "result"); + } + } + } + + if (type.IsFloat()) { + if (!is_scalar) { + auto int_value_or_err = GetValueAsAPSInt(); + if (int_value_or_err) { + llvm::APSInt ext = + int_value_or_err->extOrTrunc(type_byte_size * CHAR_BIT); + Scalar scalar_int(ext); + llvm::APFloat f = scalar_int.CreateAPFloatFromAPSInt( + type.GetCanonicalType().GetBasicTypeEnumeration()); + return ValueObject::CreateValueObjectFromAPFloat(target, f, type, + "result"); + } else { + m_error.SetErrorStringWithFormat( + "cannot get value as APSInt: %s", + llvm::toString(int_value_or_err.takeError()).c_str()); + return GetSP(); + } + } else { + if (is_integer) { + auto int_value_or_err = GetValueAsAPSInt(); + if (int_value_or_err) { + Scalar scalar_int(*int_value_or_err); + llvm::APFloat f = scalar_int.CreateAPFloatFromAPSInt( + type.GetCanonicalType().GetBasicTypeEnumeration()); + return ValueObject::CreateValueObjectFromAPFloat(target, f, type, + "result"); + } else { + m_error.SetErrorStringWithFormat( + "cannot get value as APSInt: %s", + llvm::toString(int_value_or_err.takeError()).c_str()); + return GetSP(); + } + } + if (is_float) { + auto float_value_or_err = GetValueAsAPFloat(); + if (float_value_or_err) { + Scalar scalar_float(*float_value_or_err); + llvm::APFloat f = scalar_float.CreateAPFloatFromAPFloat( + type.GetCanonicalType().GetBasicTypeEnumeration()); + return ValueObject::CreateValueObjectFromAPFloat(target, f, type, + "result"); + } else { + m_error.SetErrorStringWithFormat( + "cannot get value as APFloat: %s", + llvm::toString(float_value_or_err.takeError()).c_str()); + return GetSP(); + } + } + } + } + + m_error.SetErrorString("Unable to perform requested cast"); + return GetSP(); +} + +lldb::ValueObjectSP ValueObject::CastToEnumType(CompilerType type) { + bool is_enum = GetCompilerType().IsEnumerationType(); + bool is_integer = GetCompilerType().IsInteger(); + bool is_float = GetCompilerType().IsFloat(); + + if (!is_enum && !is_integer && !is_float) { + m_error.SetErrorString("argument must be an integer, a float, or an enum"); + return GetSP(); + } + + if (!type.IsEnumerationType()) { + m_error.SetErrorString("target type must be an enum"); + return GetSP(); + } + + lldb::TargetSP target = GetTargetSP(); + uint64_t byte_size = 0; + if (auto temp = type.GetByteSize(target.get())) + byte_size = temp.value(); + + if (is_float) { + llvm::APSInt integer(byte_size * CHAR_BIT, !type.IsSigned()); + bool is_exact; + auto value_or_err = GetValueAsAPFloat(); + if (value_or_err) { + llvm::APFloatBase::opStatus status = value_or_err->convertToInteger( + integer, llvm::APFloat::rmTowardZero, &is_exact); + + // Casting floating point values that are out of bounds of the target + // type is undefined behaviour. + if (status & llvm::APFloatBase::opInvalidOp) { + m_error.SetErrorStringWithFormat( + "invalid type cast detected: %s", + llvm::toString(value_or_err.takeError()).c_str()); + return GetSP(); + } + return ValueObject::CreateValueObjectFromAPInt(target, integer, type, + "result"); + } else { + m_error.SetErrorString("cannot get value as APFloat"); + return GetSP(); + } + } else { + // Get the value as APSInt and extend or truncate it to the requested size. + auto value_or_err = GetValueAsAPSInt(); + if (value_or_err) { + llvm::APSInt ext = value_or_err->extOrTrunc(byte_size * CHAR_BIT); + return ValueObject::CreateValueObjectFromAPInt(target, ext, type, + "result"); + } else { + m_error.SetErrorStringWithFormat( + "cannot get value as APSInt: %s", + llvm::toString(value_or_err.takeError()).c_str()); + return GetSP(); + } + } + m_error.SetErrorString("Cannot perform requested cast"); + return GetSP(); +} + ValueObject::EvaluationPoint::EvaluationPoint() : m_mod_id(), m_exe_ctx_ref() {} ValueObject::EvaluationPoint::EvaluationPoint(ExecutionContextScope *exe_scope, @@ -3028,9 +3558,11 @@ lldb::ValueObjectSP ValueObject::CreateValueObjectFromExpression( lldb::ValueObjectSP ValueObject::CreateValueObjectFromAddress( llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx, - CompilerType type) { + CompilerType type, bool do_deref) { if (type) { CompilerType pointer_type(type.GetPointerType()); + if (!do_deref) + pointer_type = type; if (pointer_type) { lldb::DataBufferSP buffer( new lldb_private::DataBufferHeap(&address, sizeof(lldb::addr_t))); @@ -3039,10 +3571,12 @@ lldb::ValueObjectSP ValueObject::CreateValueObjectFromAddress( ConstString(name), buffer, exe_ctx.GetByteOrder(), exe_ctx.GetAddressByteSize())); if (ptr_result_valobj_sp) { - ptr_result_valobj_sp->GetValue().SetValueType( - Value::ValueType::LoadAddress); + if (do_deref) + ptr_result_valobj_sp->GetValue().SetValueType( + Value::ValueType::LoadAddress); Status err; - ptr_result_valobj_sp = ptr_result_valobj_sp->Dereference(err); + if (do_deref) + ptr_result_valobj_sp = ptr_result_valobj_sp->Dereference(err); if (ptr_result_valobj_sp && !name.empty()) ptr_result_valobj_sp->SetName(ConstString(name)); } @@ -3065,6 +3599,66 @@ lldb::ValueObjectSP ValueObject::CreateValueObjectFromData( return new_value_sp; } +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromAPInt(lldb::TargetSP target, + const llvm::APInt &v, CompilerType type, + llvm::StringRef name) { + ExecutionContext exe_ctx(target.get(), false); + uint64_t byte_size = 0; + if (auto temp = type.GetByteSize(target.get())) + byte_size = temp.value(); + lldb::DataExtractorSP data_sp = std::make_shared<DataExtractor>( + reinterpret_cast<const void *>(v.getRawData()), byte_size, + exe_ctx.GetByteOrder(), exe_ctx.GetAddressByteSize()); + return ValueObject::CreateValueObjectFromData(name, *data_sp, exe_ctx, type); +} + +lldb::ValueObjectSP ValueObject::CreateValueObjectFromAPFloat( + lldb::TargetSP target, const llvm::APFloat &v, CompilerType type, + llvm::StringRef name) { + return CreateValueObjectFromAPInt(target, v.bitcastToAPInt(), type, name); +} + +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromBool(lldb::TargetSP target, bool value, + llvm::StringRef name) { + CompilerType target_type; + if (target) { + for (auto type_system_sp : target->GetScratchTypeSystems()) + if (auto compiler_type = + type_system_sp->GetBasicTypeFromAST(lldb::eBasicTypeBool)) { + target_type = compiler_type; + break; + } + } + ExecutionContext exe_ctx(target.get(), false); + uint64_t byte_size = 0; + if (auto temp = target_type.GetByteSize(target.get())) + byte_size = temp.value(); + lldb::DataExtractorSP data_sp = std::make_shared<DataExtractor>( + reinterpret_cast<const void *>(&value), byte_size, exe_ctx.GetByteOrder(), + exe_ctx.GetAddressByteSize()); + return ValueObject::CreateValueObjectFromData(name, *data_sp, exe_ctx, + target_type); +} + +lldb::ValueObjectSP ValueObject::CreateValueObjectFromNullptr( + lldb::TargetSP target, CompilerType type, llvm::StringRef name) { + if (!type.IsNullPtrType()) { + lldb::ValueObjectSP ret_val; + return ret_val; + } + uintptr_t zero = 0; + ExecutionContext exe_ctx(target.get(), false); + uint64_t byte_size = 0; + if (auto temp = type.GetByteSize(target.get())) + byte_size = temp.value(); + lldb::DataExtractorSP data_sp = std::make_shared<DataExtractor>( + reinterpret_cast<const void *>(zero), byte_size, exe_ctx.GetByteOrder(), + exe_ctx.GetAddressByteSize()); + return ValueObject::CreateValueObjectFromData(name, *data_sp, exe_ctx, type); +} + ModuleSP ValueObject::GetModule() { ValueObject *root(GetRoot()); if (root != this) diff --git a/lldb/source/DataFormatters/ValueObjectPrinter.cpp b/lldb/source/DataFormatters/ValueObjectPrinter.cpp index c2933d857458..ce24a7866e53 100644 --- a/lldb/source/DataFormatters/ValueObjectPrinter.cpp +++ b/lldb/source/DataFormatters/ValueObjectPrinter.cpp @@ -69,15 +69,13 @@ void ValueObjectPrinter::Init( SetupMostSpecializedValue(); } -bool ValueObjectPrinter::PrintValueObject() { +llvm::Error ValueObjectPrinter::PrintValueObject() { // If the incoming ValueObject is in an error state, the best we're going to // get out of it is its type. But if we don't even have that, just print // the error and exit early. if (m_orig_valobj.GetError().Fail() && - !m_orig_valobj.GetCompilerType().IsValid()) { - m_stream->Printf("Error: '%s'", m_orig_valobj.GetError().AsCString()); - return true; - } + !m_orig_valobj.GetCompilerType().IsValid()) + return m_orig_valobj.GetError().ToError(); if (ShouldPrintValueObject()) { PrintLocationIfNeeded(); @@ -93,11 +91,10 @@ bool ValueObjectPrinter::PrintValueObject() { PrintValueAndSummaryIfNeeded(value_printed, summary_printed); if (m_val_summary_ok) - PrintChildrenIfNeeded(value_printed, summary_printed); - else - m_stream->EOL(); + return PrintChildrenIfNeeded(value_printed, summary_printed); + m_stream->EOL(); - return true; + return llvm::Error::success(); } ValueObject &ValueObjectPrinter::GetMostSpecializedValue() { @@ -147,13 +144,21 @@ void ValueObjectPrinter::SetupMostSpecializedValue() { "SetupMostSpecialized value must compute a valid ValueObject"); } -const char *ValueObjectPrinter::GetDescriptionForDisplay() { +llvm::Expected<std::string> ValueObjectPrinter::GetDescriptionForDisplay() { ValueObject &valobj = GetMostSpecializedValue(); - const char *str = valobj.GetObjectDescription(); + llvm::Expected<std::string> maybe_str = valobj.GetObjectDescription(); + if (maybe_str) + return maybe_str; + + const char *str = nullptr; if (!str) str = valobj.GetSummaryAsCString(); if (!str) str = valobj.GetValueAsCString(); + + if (!str) + return maybe_str; + llvm::consumeError(maybe_str.takeError()); return str; } @@ -463,34 +468,38 @@ bool ValueObjectPrinter::PrintValueAndSummaryIfNeeded(bool &value_printed, return !error_printed; } -bool ValueObjectPrinter::PrintObjectDescriptionIfNeeded(bool value_printed, - bool summary_printed) { +llvm::Error +ValueObjectPrinter::PrintObjectDescriptionIfNeeded(bool value_printed, + bool summary_printed) { if (ShouldPrintValueObject()) { // let's avoid the overly verbose no description error for a nil thing if (m_options.m_use_objc && !IsNil() && !IsUninitialized() && (!m_options.m_pointer_as_array)) { if (!m_options.m_hide_value || ShouldShowName()) - m_stream->Printf(" "); - const char *object_desc = nullptr; - if (value_printed || summary_printed) - object_desc = GetMostSpecializedValue().GetObjectDescription(); - else - object_desc = GetDescriptionForDisplay(); - if (object_desc && *object_desc) { + *m_stream << ' '; + llvm::Expected<std::string> object_desc = + (value_printed || summary_printed) + ? GetMostSpecializedValue().GetObjectDescription() + : GetDescriptionForDisplay(); + if (!object_desc) { + // If no value or summary was printed, surface the error. + if (!value_printed && !summary_printed) + return object_desc.takeError(); + // Otherwise gently nudge the user that they should have used + // `p` instead of `po`. Unfortunately we cannot be more direct + // about this, because we don't actually know what the user did. + *m_stream << "warning: no object description available\n"; + llvm::consumeError(object_desc.takeError()); + } else { + *m_stream << *object_desc; // If the description already ends with a \n don't add another one. - size_t object_end = strlen(object_desc) - 1; - if (object_desc[object_end] == '\n') - m_stream->Printf("%s", object_desc); - else - m_stream->Printf("%s\n", object_desc); - return true; - } else if (!value_printed && !summary_printed) - return true; - else - return false; + if (object_desc->empty() || object_desc->back() != '\n') + *m_stream << '\n'; + } + return llvm::Error::success(); } } - return true; + return llvm::Error::success(); } bool DumpValueObjectOptions::PointerDepth::CanAllowExpansion() const { @@ -619,7 +628,13 @@ void ValueObjectPrinter::PrintChild( ValueObjectPrinter child_printer(*(child_sp.get()), m_stream, child_options, ptr_depth, m_curr_depth + 1, m_printed_instance_pointers); - child_printer.PrintValueObject(); + llvm::Error error = child_printer.PrintValueObject(); + if (error) { + if (m_stream) + *m_stream << "error: " << toString(std::move(error)); + else + llvm::consumeError(std::move(error)); + } } } @@ -808,9 +823,12 @@ bool ValueObjectPrinter::PrintChildrenOneLiner(bool hide_names) { return true; } -void ValueObjectPrinter::PrintChildrenIfNeeded(bool value_printed, - bool summary_printed) { - PrintObjectDescriptionIfNeeded(value_printed, summary_printed); +llvm::Error ValueObjectPrinter::PrintChildrenIfNeeded(bool value_printed, + bool summary_printed) { + auto error = PrintObjectDescriptionIfNeeded(value_printed, summary_printed); + if (error) + return error; + ValueObject &valobj = GetMostSpecializedValue(); DumpValueObjectOptions::PointerDepth curr_ptr_depth = m_ptr_depth; @@ -826,7 +844,7 @@ void ValueObjectPrinter::PrintChildrenIfNeeded(bool value_printed, if (m_printed_instance_pointers->count(instance_ptr_value)) { // We already printed this instance-is-pointer thing, so don't expand it. m_stream->PutCString(" {...}\n"); - return; + return llvm::Error::success(); } else { // Remember this guy for future reference. m_printed_instance_pointers->emplace(instance_ptr_value); @@ -854,6 +872,7 @@ void ValueObjectPrinter::PrintChildrenIfNeeded(bool value_printed, .SetReachedMaximumDepth(); } else m_stream->EOL(); + return llvm::Error::success(); } bool ValueObjectPrinter::HasReachedMaximumDepth() { diff --git a/lldb/source/Expression/CMakeLists.txt b/lldb/source/Expression/CMakeLists.txt index 9ba5fefc09b6..be1e132f7aaa 100644 --- a/lldb/source/Expression/CMakeLists.txt +++ b/lldb/source/Expression/CMakeLists.txt @@ -3,6 +3,7 @@ add_lldb_library(lldbExpression NO_PLUGIN_DEPENDENCIES DWARFExpression.cpp DWARFExpressionList.cpp Expression.cpp + ExpressionParser.cpp ExpressionTypeSystemHelper.cpp ExpressionVariable.cpp FunctionCaller.cpp diff --git a/lldb/source/Expression/ExpressionParser.cpp b/lldb/source/Expression/ExpressionParser.cpp new file mode 100644 index 000000000000..e9f7121c2499 --- /dev/null +++ b/lldb/source/Expression/ExpressionParser.cpp @@ -0,0 +1,72 @@ +//===-- ExpressionParser.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/Expression/ExpressionParser.h" +#include "lldb/Expression/DiagnosticManager.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ThreadPlanCallFunction.h" + +using namespace lldb; +using namespace lldb_private; + +Status ExpressionParser::PrepareForExecution( + addr_t &func_addr, addr_t &func_end, + std::shared_ptr<IRExecutionUnit> &execution_unit_sp, + ExecutionContext &exe_ctx, bool &can_interpret, + ExecutionPolicy execution_policy) { + Status status = + DoPrepareForExecution(func_addr, func_end, execution_unit_sp, exe_ctx, + can_interpret, execution_policy); + if (status.Success() && exe_ctx.GetProcessPtr() && exe_ctx.HasThreadScope()) + status = RunStaticInitializers(execution_unit_sp, exe_ctx); + + return status; +} + +Status +ExpressionParser::RunStaticInitializers(IRExecutionUnitSP &execution_unit_sp, + ExecutionContext &exe_ctx) { + Status err; + + if (!execution_unit_sp.get()) { + err.SetErrorString( + "can't run static initializers for a NULL execution unit"); + return err; + } + + if (!exe_ctx.HasThreadScope()) { + err.SetErrorString("can't run static initializers without a thread"); + return err; + } + + std::vector<addr_t> static_initializers; + + execution_unit_sp->GetStaticInitializers(static_initializers); + + for (addr_t static_initializer : static_initializers) { + EvaluateExpressionOptions options; + + ThreadPlanSP call_static_initializer(new ThreadPlanCallFunction( + exe_ctx.GetThreadRef(), Address(static_initializer), CompilerType(), + llvm::ArrayRef<addr_t>(), options)); + + DiagnosticManager execution_errors; + ExpressionResults results = + exe_ctx.GetThreadRef().GetProcess()->RunThreadPlan( + exe_ctx, call_static_initializer, options, execution_errors); + + if (results != eExpressionCompleted) { + err.SetErrorStringWithFormat("couldn't run static initializer: %s", + execution_errors.GetString().c_str()); + return err; + } + } + + return err; +} diff --git a/lldb/source/Host/linux/Host.cpp b/lldb/source/Host/linux/Host.cpp index c6490f2fc9e2..5545f9ef4d70 100644 --- a/lldb/source/Host/linux/Host.cpp +++ b/lldb/source/Host/linux/Host.cpp @@ -37,6 +37,7 @@ using namespace lldb; using namespace lldb_private; namespace { + enum class ProcessState { Unknown, Dead, @@ -70,6 +71,12 @@ struct StatFields { long unsigned stime; long cutime; long cstime; + // In proc_pid_stat(5) this field is specified as priority + // but documented as realtime priority. To keep with the adopted + // nomenclature in ProcessInstanceInfo, we adopt the documented + // naming here. + long realtime_priority; + long priority; // .... other things. We don't need them below }; } @@ -91,14 +98,16 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, if (Rest.empty()) return false; StatFields stat_fields; - if (sscanf(Rest.data(), - "%d %s %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld", - &stat_fields.pid, stat_fields.comm, &stat_fields.state, - &stat_fields.ppid, &stat_fields.pgrp, &stat_fields.session, - &stat_fields.tty_nr, &stat_fields.tpgid, &stat_fields.flags, - &stat_fields.minflt, &stat_fields.cminflt, &stat_fields.majflt, - &stat_fields.cmajflt, &stat_fields.utime, &stat_fields.stime, - &stat_fields.cutime, &stat_fields.cstime) < 0) { + if (sscanf( + Rest.data(), + "%d %s %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld", + &stat_fields.pid, stat_fields.comm, &stat_fields.state, + &stat_fields.ppid, &stat_fields.pgrp, &stat_fields.session, + &stat_fields.tty_nr, &stat_fields.tpgid, &stat_fields.flags, + &stat_fields.minflt, &stat_fields.cminflt, &stat_fields.majflt, + &stat_fields.cmajflt, &stat_fields.utime, &stat_fields.stime, + &stat_fields.cutime, &stat_fields.cstime, + &stat_fields.realtime_priority, &stat_fields.priority) < 0) { return false; } @@ -115,6 +124,11 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, return ts; }; + // Priority (nice) values run from 19 to -20 inclusive (in linux). In the + // prpsinfo struct pr_nice is a char. + auto priority_value = static_cast<int8_t>( + (stat_fields.priority < 0 ? 0x80 : 0x00) | (stat_fields.priority & 0x7f)); + ProcessInfo.SetParentProcessID(stat_fields.ppid); ProcessInfo.SetProcessGroupID(stat_fields.pgrp); ProcessInfo.SetProcessSessionID(stat_fields.session); @@ -122,6 +136,7 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, ProcessInfo.SetSystemTime(convert(stat_fields.stime)); ProcessInfo.SetCumulativeUserTime(convert(stat_fields.cutime)); ProcessInfo.SetCumulativeSystemTime(convert(stat_fields.cstime)); + ProcessInfo.SetPriorityValue(priority_value); switch (stat_fields.state) { case 'R': State = ProcessState::Running; @@ -156,6 +171,7 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, State = ProcessState::Unknown; break; } + ProcessInfo.SetIsZombie(State == ProcessState::Zombie); if (State == ProcessState::Unknown) { LLDB_LOG(log, "Unknown process state {0}", stat_fields.state); diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index da995de1407c..40c915b2c94f 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -48,6 +48,7 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/PluginManager.h" #include "lldb/Host/StreamFile.h" +#include "lldb/Utility/ErrorMessages.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/State.h" @@ -1817,51 +1818,8 @@ CommandInterpreter::PreprocessToken(std::string &expr_str) { error = expr_result_valobj_sp->GetError(); if (error.Success()) { - switch (expr_result) { - case eExpressionSetupError: - error.SetErrorStringWithFormat( - "expression setup error for the expression '%s'", expr_str.c_str()); - break; - case eExpressionParseError: - error.SetErrorStringWithFormat( - "expression parse error for the expression '%s'", expr_str.c_str()); - break; - case eExpressionResultUnavailable: - error.SetErrorStringWithFormat( - "expression error fetching result for the expression '%s'", - expr_str.c_str()); - break; - case eExpressionCompleted: - break; - case eExpressionDiscarded: - error.SetErrorStringWithFormat( - "expression discarded for the expression '%s'", expr_str.c_str()); - break; - case eExpressionInterrupted: - error.SetErrorStringWithFormat( - "expression interrupted for the expression '%s'", expr_str.c_str()); - break; - case eExpressionHitBreakpoint: - error.SetErrorStringWithFormat( - "expression hit breakpoint for the expression '%s'", - expr_str.c_str()); - break; - case eExpressionTimedOut: - error.SetErrorStringWithFormat( - "expression timed out for the expression '%s'", expr_str.c_str()); - break; - case eExpressionStoppedForDebug: - error.SetErrorStringWithFormat("expression stop at entry point " - "for debugging for the " - "expression '%s'", - expr_str.c_str()); - break; - case eExpressionThreadVanished: - error.SetErrorStringWithFormat( - "expression thread vanished for the expression '%s'", - expr_str.c_str()); - break; - } + std::string result = lldb_private::toString(expr_result); + error.SetErrorString(result + "for the expression '" + expr_str + "'"); } return error; } diff --git a/lldb/source/Interpreter/OptionArgParser.cpp b/lldb/source/Interpreter/OptionArgParser.cpp index 9a8275128ede..105d4846da14 100644 --- a/lldb/source/Interpreter/OptionArgParser.cpp +++ b/lldb/source/Interpreter/OptionArgParser.cpp @@ -35,6 +35,20 @@ bool OptionArgParser::ToBoolean(llvm::StringRef ref, bool fail_value, return fail_value; } +llvm::Expected<bool> OptionArgParser::ToBoolean(llvm::StringRef option_name, + llvm::StringRef option_arg) { + bool parse_success; + const bool option_value = + ToBoolean(option_arg, false /* doesn't matter */, &parse_success); + if (parse_success) + return option_value; + else + return llvm::createStringError( + "Invalid boolean value for option '%s': '%s'", + option_name.str().c_str(), + option_arg.empty() ? "<null>" : option_arg.str().c_str()); +} + char OptionArgParser::ToChar(llvm::StringRef s, char fail_value, bool *success_ptr) { if (success_ptr) diff --git a/lldb/source/Interpreter/Options.cpp b/lldb/source/Interpreter/Options.cpp index 4e7d074ace1b..c5e75e0b9dce 100644 --- a/lldb/source/Interpreter/Options.cpp +++ b/lldb/source/Interpreter/Options.cpp @@ -1197,21 +1197,12 @@ OptionElementVector Options::ParseForCompletion(const Args &args, } break; case OptionParser::eOptionalArgument: - if (OptionParser::GetOptionArgument() != nullptr) { - option_element_vector.push_back(OptionArgElement( - opt_defs_index, - FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2], - args), - FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], - args))); - } else { - option_element_vector.push_back(OptionArgElement( - opt_defs_index, - FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2], - args), - FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], - args))); - } + option_element_vector.push_back(OptionArgElement( + opt_defs_index, + FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2], + args), + FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1], + args))); break; default: // The options table is messed up. Here we'll just continue diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp index 8dd499ce819a..75b2a39a8d11 100644 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -101,6 +101,19 @@ ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const { return Status(); } +Event * +ScriptInterpreter::GetOpaqueTypeFromSBEvent(const lldb::SBEvent &event) const { + return event.m_opaque_ptr; +} + +Stream *ScriptInterpreter::GetOpaqueTypeFromSBStream( + const lldb::SBStream &stream) const { + if (stream.m_opaque_up) + return const_cast<lldb::SBStream &>(stream).m_opaque_up.get(); + + return nullptr; +} + std::optional<MemoryRegionInfo> ScriptInterpreter::GetOpaqueTypeFromSBMemoryRegionInfo( const lldb::SBMemoryRegionInfo &mem_region) const { diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp index 82a7a2cc3f1e..1fdd272dcbec 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp @@ -637,7 +637,7 @@ void ClangASTSource::FindExternalVisibleDecls( FindDeclInModules(context, name); } - if (!context.m_found_type) { + if (!context.m_found_type && m_ast_context->getLangOpts().ObjC) { FindDeclInObjCRuntime(context, name); } } diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp index 1dd98567f8d6..303e88feea20 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -1296,7 +1296,7 @@ static bool FindFunctionInModule(ConstString &mangled_name, return false; } -lldb_private::Status ClangExpressionParser::PrepareForExecution( +lldb_private::Status ClangExpressionParser::DoPrepareForExecution( lldb::addr_t &func_addr, lldb::addr_t &func_end, lldb::IRExecutionUnitSP &execution_unit_sp, ExecutionContext &exe_ctx, bool &can_interpret, ExecutionPolicy execution_policy) { @@ -1472,47 +1472,3 @@ lldb_private::Status ClangExpressionParser::PrepareForExecution( return err; } - -lldb_private::Status ClangExpressionParser::RunStaticInitializers( - lldb::IRExecutionUnitSP &execution_unit_sp, ExecutionContext &exe_ctx) { - lldb_private::Status err; - - lldbassert(execution_unit_sp.get()); - lldbassert(exe_ctx.HasThreadScope()); - - if (!execution_unit_sp.get()) { - err.SetErrorString( - "can't run static initializers for a NULL execution unit"); - return err; - } - - if (!exe_ctx.HasThreadScope()) { - err.SetErrorString("can't run static initializers without a thread"); - return err; - } - - std::vector<lldb::addr_t> static_initializers; - - execution_unit_sp->GetStaticInitializers(static_initializers); - - for (lldb::addr_t static_initializer : static_initializers) { - EvaluateExpressionOptions options; - - lldb::ThreadPlanSP call_static_initializer(new ThreadPlanCallFunction( - exe_ctx.GetThreadRef(), Address(static_initializer), CompilerType(), - llvm::ArrayRef<lldb::addr_t>(), options)); - - DiagnosticManager execution_errors; - lldb::ExpressionResults results = - exe_ctx.GetThreadRef().GetProcess()->RunThreadPlan( - exe_ctx, call_static_initializer, options, execution_errors); - - if (results != lldb::eExpressionCompleted) { - err.SetErrorStringWithFormat("couldn't run static initializer: %s", - execution_errors.GetString().c_str()); - return err; - } - } - - return err; -} diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h index 185a5a381f23..0852e928a9d4 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h @@ -113,24 +113,11 @@ public: /// \return /// An error code indicating the success or failure of the operation. /// Test with Success(). - Status - PrepareForExecution(lldb::addr_t &func_addr, lldb::addr_t &func_end, - lldb::IRExecutionUnitSP &execution_unit_sp, - ExecutionContext &exe_ctx, bool &can_interpret, - lldb_private::ExecutionPolicy execution_policy) override; - - /// Run all static initializers for an execution unit. - /// - /// \param[in] execution_unit_sp - /// The execution unit. - /// - /// \param[in] exe_ctx - /// The execution context to use when running them. Thread can't be null. - /// - /// \return - /// The error code indicating the - Status RunStaticInitializers(lldb::IRExecutionUnitSP &execution_unit_sp, - ExecutionContext &exe_ctx); + Status DoPrepareForExecution( + lldb::addr_t &func_addr, lldb::addr_t &func_end, + lldb::IRExecutionUnitSP &execution_unit_sp, ExecutionContext &exe_ctx, + bool &can_interpret, + lldb_private::ExecutionPolicy execution_policy) override; /// Returns a string representing current ABI. /// diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp index c7e98d12590d..35038a56440d 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp @@ -698,21 +698,6 @@ bool ClangUserExpression::Parse(DiagnosticManager &diagnostic_manager, if (!parse_success) return false; - if (exe_ctx.GetProcessPtr() && execution_policy == eExecutionPolicyTopLevel) { - Status static_init_error = - m_parser->RunStaticInitializers(m_execution_unit_sp, exe_ctx); - - if (!static_init_error.Success()) { - const char *error_cstr = static_init_error.AsCString(); - if (error_cstr && error_cstr[0]) - diagnostic_manager.Printf(lldb::eSeverityError, "%s\n", error_cstr); - else - diagnostic_manager.PutString(lldb::eSeverityError, - "couldn't run static initializers\n"); - return false; - } - } - if (m_execution_unit_sp) { bool register_execution_unit = false; diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp index b0e6fb7d6f5a..0f9f93b727ce 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -808,6 +808,9 @@ ExtractLibcxxStringInfo(ValueObject &valobj) { size = (layout == StringLayout::DSC) ? size_mode_value : ((size_mode_value >> 1) % 256); + if (!location_sp) + return {}; + // When the small-string optimization takes place, the data must fit in the // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's // likely that the string isn't initialized and we're reading garbage. @@ -815,7 +818,7 @@ ExtractLibcxxStringInfo(ValueObject &valobj) { const std::optional<uint64_t> max_bytes = location_sp->GetCompilerType().GetByteSize( exe_ctx.GetBestExecutionContextScope()); - if (!max_bytes || size > *max_bytes || !location_sp) + if (!max_bytes || size > *max_bytes) return {}; return std::make_pair(size, location_sp); diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp index 300ecc8e8ed5..c7202a47d015 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp @@ -47,16 +47,17 @@ bool CPPLanguageRuntime::IsAllowedRuntimeValue(ConstString name) { return name == g_this || name == g_promise || name == g_coro_frame; } -bool CPPLanguageRuntime::GetObjectDescription(Stream &str, - ValueObject &object) { +llvm::Error CPPLanguageRuntime::GetObjectDescription(Stream &str, + ValueObject &object) { // C++ has no generic way to do this. - return false; + return llvm::createStringError("C++ does not support object descriptions"); } -bool CPPLanguageRuntime::GetObjectDescription( - Stream &str, Value &value, ExecutionContextScope *exe_scope) { +llvm::Error +CPPLanguageRuntime::GetObjectDescription(Stream &str, Value &value, + ExecutionContextScope *exe_scope) { // C++ has no generic way to do this. - return false; + return llvm::createStringError("C++ does not support object descriptions"); } bool contains_lambda_identifier(llvm::StringRef &str_ref) { diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h index 1be58b7bf9ea..57cfe2824580 100644 --- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h @@ -59,10 +59,10 @@ public: process.GetLanguageRuntime(lldb::eLanguageTypeC_plus_plus)); } - bool GetObjectDescription(Stream &str, ValueObject &object) override; + llvm::Error GetObjectDescription(Stream &str, ValueObject &object) override; - bool GetObjectDescription(Stream &str, Value &value, - ExecutionContextScope *exe_scope) override; + llvm::Error GetObjectDescription(Stream &str, Value &value, + ExecutionContextScope *exe_scope) override; /// Obtain a ThreadPlan to get us into C++ constructs such as std::function. /// diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp index 9434376f7d9e..5ff267720629 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp @@ -31,6 +31,7 @@ #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/ConstString.h" +#include "lldb/Utility/ErrorMessages.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/Scalar.h" @@ -67,20 +68,21 @@ void AppleObjCRuntime::Terminate() { AppleObjCRuntimeV1::Terminate(); } -bool AppleObjCRuntime::GetObjectDescription(Stream &str, ValueObject &valobj) { +llvm::Error AppleObjCRuntime::GetObjectDescription(Stream &str, + ValueObject &valobj) { CompilerType compiler_type(valobj.GetCompilerType()); bool is_signed; // ObjC objects can only be pointers (or numbers that actually represents // pointers but haven't been typecast, because reasons..) if (!compiler_type.IsIntegerType(is_signed) && !compiler_type.IsPointerType()) - return false; + return llvm::createStringError("not a pointer type"); // Make the argument list: we pass one arg, the address of our pointer, to // the print function. Value val; if (!valobj.ResolveValue(val.GetScalar())) - return false; + return llvm::createStringError("pointer value could not be resolved"); // Value Objects may not have a process in their ExecutionContextRef. But we // need to have one in the ref we pass down to eventually call description. @@ -91,20 +93,22 @@ bool AppleObjCRuntime::GetObjectDescription(Stream &str, ValueObject &valobj) { } else { exe_ctx.SetContext(valobj.GetTargetSP(), true); if (!exe_ctx.HasProcessScope()) - return false; + return llvm::createStringError("no process"); } return GetObjectDescription(str, val, exe_ctx.GetBestExecutionContextScope()); } -bool AppleObjCRuntime::GetObjectDescription(Stream &strm, Value &value, - ExecutionContextScope *exe_scope) { + +llvm::Error +AppleObjCRuntime::GetObjectDescription(Stream &strm, Value &value, + ExecutionContextScope *exe_scope) { if (!m_read_objc_library) - return false; + return llvm::createStringError("Objective-C runtime not loaded"); ExecutionContext exe_ctx; exe_scope->CalculateExecutionContext(exe_ctx); Process *process = exe_ctx.GetProcessPtr(); if (!process) - return false; + return llvm::createStringError("no process"); // We need other parts of the exe_ctx, but the processes have to match. assert(m_process == process); @@ -112,25 +116,25 @@ bool AppleObjCRuntime::GetObjectDescription(Stream &strm, Value &value, // Get the function address for the print function. const Address *function_address = GetPrintForDebuggerAddr(); if (!function_address) - return false; + return llvm::createStringError("no print function"); Target *target = exe_ctx.GetTargetPtr(); CompilerType compiler_type = value.GetCompilerType(); if (compiler_type) { - if (!TypeSystemClang::IsObjCObjectPointerType(compiler_type)) { - strm.Printf("Value doesn't point to an ObjC object.\n"); - return false; - } + if (!TypeSystemClang::IsObjCObjectPointerType(compiler_type)) + return llvm::createStringError( + "Value doesn't point to an ObjC object.\n"); } else { // If it is not a pointer, see if we can make it into a pointer. TypeSystemClangSP scratch_ts_sp = ScratchTypeSystemClang::GetForTarget(*target); if (!scratch_ts_sp) - return false; + return llvm::createStringError("no scratch type system"); CompilerType opaque_type = scratch_ts_sp->GetBasicType(eBasicTypeObjCID); if (!opaque_type) - opaque_type = scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType(); + opaque_type = + scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType(); // value.SetContext(Value::eContextTypeClangType, opaque_type_ptr); value.SetCompilerType(opaque_type); } @@ -142,14 +146,14 @@ bool AppleObjCRuntime::GetObjectDescription(Stream &strm, Value &value, TypeSystemClangSP scratch_ts_sp = ScratchTypeSystemClang::GetForTarget(*target); if (!scratch_ts_sp) - return false; + return llvm::createStringError("no scratch type system"); CompilerType return_compiler_type = scratch_ts_sp->GetCStringType(true); Value ret; // ret.SetContext(Value::eContextTypeClangType, return_compiler_type); ret.SetCompilerType(return_compiler_type); - if (exe_ctx.GetFramePtr() == nullptr) { + if (!exe_ctx.GetFramePtr()) { Thread *thread = exe_ctx.GetThreadPtr(); if (thread == nullptr) { exe_ctx.SetThreadSP(process->GetThreadList().GetSelectedThread()); @@ -173,10 +177,11 @@ bool AppleObjCRuntime::GetObjectDescription(Stream &strm, Value &value, arg_value_list, "objc-object-description", error)); if (error.Fail()) { m_print_object_caller_up.reset(); - strm.Printf("Could not get function runner to call print for debugger " - "function: %s.", - error.AsCString()); - return false; + return llvm::createStringError( + llvm::Twine( + "could not get function runner to call print for debugger " + "function: ") + + error.AsCString()); } m_print_object_caller_up->InsertFunction(exe_ctx, wrapper_struct_addr, diagnostics); @@ -195,10 +200,9 @@ bool AppleObjCRuntime::GetObjectDescription(Stream &strm, Value &value, ExpressionResults results = m_print_object_caller_up->ExecuteFunction( exe_ctx, &wrapper_struct_addr, options, diagnostics, ret); - if (results != eExpressionCompleted) { - strm.Printf("Error evaluating Print Object function: %d.\n", results); - return false; - } + if (results != eExpressionCompleted) + return llvm::createStringError( + "could not evaluate print object function: " + toString(results)); addr_t result_ptr = ret.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); @@ -213,7 +217,9 @@ bool AppleObjCRuntime::GetObjectDescription(Stream &strm, Value &value, strm.Write(buf, curr_len); cstr_len += curr_len; } - return cstr_len > 0; + if (cstr_len > 0) + return llvm::Error::success(); + return llvm::createStringError("empty object description"); } lldb::ModuleSP AppleObjCRuntime::GetObjCModule() { diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h index cfeec3d679be..da58d44db19a 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h @@ -44,10 +44,10 @@ public: } // These are generic runtime functions: - bool GetObjectDescription(Stream &str, Value &value, - ExecutionContextScope *exe_scope) override; + llvm::Error GetObjectDescription(Stream &str, Value &value, + ExecutionContextScope *exe_scope) override; - bool GetObjectDescription(Stream &str, ValueObject &object) override; + llvm::Error GetObjectDescription(Stream &str, ValueObject &object) override; bool CouldHaveDynamicValue(ValueObject &in_value) override; diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp index 39b3e816f4be..58b838752be5 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp @@ -104,15 +104,17 @@ GNUstepObjCRuntime::GNUstepObjCRuntime(Process *process) ReadObjCLibraryIfNeeded(process->GetTarget().GetImages()); } -bool GNUstepObjCRuntime::GetObjectDescription(Stream &str, - ValueObject &valobj) { - // TODO: ObjC has a generic way to do this - return false; +llvm::Error GNUstepObjCRuntime::GetObjectDescription(Stream &str, + ValueObject &valobj) { + return llvm::createStringError( + "LLDB's GNUStep runtime does not support object description"); } -bool GNUstepObjCRuntime::GetObjectDescription( - Stream &strm, Value &value, ExecutionContextScope *exe_scope) { - // TODO: ObjC has a generic way to do this - return false; + +llvm::Error +GNUstepObjCRuntime::GetObjectDescription(Stream &strm, Value &value, + ExecutionContextScope *exe_scope) { + return llvm::createStringError( + "LLDB's GNUStep runtime does not support object description"); } bool GNUstepObjCRuntime::CouldHaveDynamicValue(ValueObject &in_value) { diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.h b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.h index b22088807f97..de24466ebb00 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.h @@ -57,10 +57,10 @@ public: // // LanguageRuntime implementation // - bool GetObjectDescription(Stream &str, Value &value, - ExecutionContextScope *exe_scope) override; + llvm::Error GetObjectDescription(Stream &str, Value &value, + ExecutionContextScope *exe_scope) override; - bool GetObjectDescription(Stream &str, ValueObject &object) override; + llvm::Error GetObjectDescription(Stream &str, ValueObject &object) override; bool CouldHaveDynamicValue(ValueObject &in_value) override; diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp index ba52444f0c2f..a812ffba7a64 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp @@ -350,18 +350,17 @@ ObjCLanguageRuntime::EncodingToTypeSP ObjCLanguageRuntime::GetEncodingToType() { return nullptr; } -bool ObjCLanguageRuntime::GetTypeBitSize(const CompilerType &compiler_type, - uint64_t &size) { +std::optional<uint64_t> +ObjCLanguageRuntime::GetTypeBitSize(const CompilerType &compiler_type) { void *opaque_ptr = compiler_type.GetOpaqueQualType(); - size = m_type_size_cache.Lookup(opaque_ptr); - // an ObjC object will at least have an ISA, so 0 is definitely not OK - if (size > 0) - return true; + uint64_t cached_size = m_type_size_cache.Lookup(opaque_ptr); + if (cached_size > 0) + return cached_size; ClassDescriptorSP class_descriptor_sp = GetClassDescriptorFromClassName(compiler_type.GetTypeName()); if (!class_descriptor_sp) - return false; + return {}; int32_t max_offset = INT32_MIN; uint64_t sizeof_max = 0; @@ -377,11 +376,13 @@ bool ObjCLanguageRuntime::GetTypeBitSize(const CompilerType &compiler_type, } } - size = 8 * (max_offset + sizeof_max); - if (found) + uint64_t size = 8 * (max_offset + sizeof_max); + if (found && size > 0) { m_type_size_cache.Insert(opaque_ptr, size); + return size; + } - return found; + return {}; } lldb::BreakpointPreconditionSP diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h index 0a8b6e8d56ed..ffe9725fa682 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h @@ -107,7 +107,7 @@ public: int64_t *value_bits = nullptr, uint64_t *payload = nullptr) = 0; /// @} - + virtual uint64_t GetInstanceSize() = 0; // use to implement version-specific additional constraints on pointers @@ -321,8 +321,8 @@ public: m_negative_complete_class_cache.clear(); } - bool GetTypeBitSize(const CompilerType &compiler_type, - uint64_t &size) override; + std::optional<uint64_t> + GetTypeBitSize(const CompilerType &compiler_type) override; /// Check whether the name is "self" or "_cmd" and should show up in /// "frame variable". diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index 4dd23bb1e4db..2979bf69bf76 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -5140,12 +5140,20 @@ uint32_t ObjectFileMachO::GetDependentModules(FileSpecList &files) { case LC_LOADFVMLIB: case LC_LOAD_UPWARD_DYLIB: { uint32_t name_offset = cmd_offset + m_data.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); 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); + // 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, + // but will most likely not be load at launch. if (flags & 0x08 /* DYLIB_USE_DELAYED_INIT */) is_delayed_init = true; } diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp index 7231433619ff..de212c6b20da 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp @@ -20,25 +20,100 @@ #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/ThreadList.h" +#include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" +#include "lldb/Utility/RangeMap.h" #include "lldb/Utility/RegisterValue.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Minidump.h" #include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" +#include "llvm/TargetParser/Triple.h" #include "Plugins/Process/minidump/MinidumpTypes.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" +#include "lldb/lldb-types.h" +#include <algorithm> #include <cinttypes> +#include <climits> +#include <cstddef> +#include <cstdint> +#include <functional> +#include <iostream> +#include <set> +#include <utility> +#include <vector> using namespace lldb; using namespace lldb_private; using namespace llvm::minidump; -void MinidumpFileBuilder::AddDirectory(StreamType type, size_t stream_size) { +Status MinidumpFileBuilder::AddHeaderAndCalculateDirectories() { + // First set the offset on the file, and on the bytes saved + m_saved_data_size = HEADER_SIZE; + // We know we will have at least Misc, SystemInfo, Modules, and ThreadList + // (corresponding memory list for stacks) And an additional memory list for + // non-stacks. + lldb_private::Target &target = m_process_sp->GetTarget(); + m_expected_directories = 6; + // Check if OS is linux and reserve directory space for all linux specific + // breakpad extension directories. + if (target.GetArchitecture().GetTriple().getOS() == + llvm::Triple::OSType::Linux) + m_expected_directories += 9; + + // Go through all of the threads and check for exceptions. + lldb_private::ThreadList thread_list = m_process_sp->GetThreadList(); + const uint32_t num_threads = thread_list.GetSize(); + for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) { + ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx)); + StopInfoSP stop_info_sp = thread_sp->GetStopInfo(); + if (stop_info_sp) { + const StopReason &stop_reason = stop_info_sp->GetStopReason(); + if (stop_reason == StopReason::eStopReasonException || + stop_reason == StopReason::eStopReasonSignal) + m_expected_directories++; + } + } + + m_saved_data_size += + m_expected_directories * sizeof(llvm::minidump::Directory); + Status error; + offset_t new_offset = m_core_file->SeekFromStart(m_saved_data_size); + if (new_offset != m_saved_data_size) + error.SetErrorStringWithFormat("Failed to fill in header and directory " + "sections. Written / Expected (%" PRIx64 + " / %" PRIx64 ")", + new_offset, m_saved_data_size); + + return error; +} + +Status MinidumpFileBuilder::AddDirectory(StreamType type, + uint64_t stream_size) { + // We explicitly cast type, an 32b enum, to uint32_t to avoid warnings. + Status error; + if (GetCurrentDataEndOffset() > UINT32_MAX) { + error.SetErrorStringWithFormat("Unable to add directory for stream type " + "%x, offset is greater then 32 bit limit.", + (uint32_t)type); + return error; + } + + if (m_directories.size() + 1 > m_expected_directories) { + error.SetErrorStringWithFormat( + "Unable to add directory for stream type %x, exceeded expected number " + "of directories %zu.", + (uint32_t)type, m_expected_directories); + return error; + } + LocationDescriptor loc; loc.DataSize = static_cast<llvm::support::ulittle32_t>(stream_size); // Stream will begin at the current end of data section @@ -49,11 +124,17 @@ void MinidumpFileBuilder::AddDirectory(StreamType type, size_t stream_size) { dir.Location = loc; m_directories.push_back(dir); + return error; } -Status MinidumpFileBuilder::AddSystemInfo(const llvm::Triple &target_triple) { +Status MinidumpFileBuilder::AddSystemInfo() { Status error; - AddDirectory(StreamType::SystemInfo, sizeof(llvm::minidump::SystemInfo)); + const llvm::Triple &target_triple = + m_process_sp->GetTarget().GetArchitecture().GetTriple(); + error = + AddDirectory(StreamType::SystemInfo, sizeof(llvm::minidump::SystemInfo)); + if (error.Fail()) + return error; llvm::minidump::ProcessorArchitecture arch; switch (target_triple.getArch()) { @@ -165,7 +246,6 @@ llvm::Expected<uint64_t> getModuleFileSize(Target &target, } return SizeOfImage; } - SectionSP sect_sp = mod->GetObjectFile()->GetBaseAddress().GetSection(); if (!sect_sp) { @@ -203,10 +283,11 @@ llvm::Expected<uint64_t> getModuleFileSize(Target &target, // single module. Additional data of variable length, such as module's names, // are stored just after the ModuleList stream. The llvm::minidump::Module // structures point to this helper data by global offset. -Status MinidumpFileBuilder::AddModuleList(Target &target) { +Status MinidumpFileBuilder::AddModuleList() { constexpr size_t minidump_module_size = sizeof(llvm::minidump::Module); Status error; + lldb_private::Target &target = m_process_sp->GetTarget(); const ModuleList &modules = target.GetImages(); llvm::support::ulittle32_t modules_count = static_cast<llvm::support::ulittle32_t>(modules.GetSize()); @@ -223,7 +304,9 @@ Status MinidumpFileBuilder::AddModuleList(Target &target) { sizeof(llvm::support::ulittle32_t) + modules_count * minidump_module_size; // Adding directory describing this stream. - AddDirectory(StreamType::ModuleList, module_stream_size); + error = AddDirectory(StreamType::ModuleList, module_stream_size); + if (error.Fail()) + return error; m_data.AppendData(&modules_count, sizeof(llvm::support::ulittle32_t)); @@ -480,42 +563,32 @@ public: } }; -// Function returns start and size of the memory region that contains -// memory location pointed to by the current stack pointer. -llvm::Expected<std::pair<addr_t, addr_t>> -findStackHelper(const lldb::ProcessSP &process_sp, uint64_t rsp) { - MemoryRegionInfo range_info; - Status error = process_sp->GetMemoryRegionInfo(rsp, range_info); - // Skip failed memory region requests or any regions with no permissions. - if (error.Fail() || range_info.GetLLDBPermissions() == 0) - return llvm::createStringError( - std::errc::not_supported, - "unable to load stack segment of the process"); - - // This is a duplicate of the logic in - // Process::SaveOffRegionsWithStackPointers but ultimately, we need to only - // save up from the start of the stack down to the stack pointer - const addr_t range_end = range_info.GetRange().GetRangeEnd(); - const addr_t red_zone = process_sp->GetABI()->GetRedZoneSize(); - const addr_t stack_head = rsp - red_zone; - if (stack_head > range_info.GetRange().GetRangeEnd()) { - range_info.GetRange().SetRangeBase(stack_head); - range_info.GetRange().SetByteSize(range_end - stack_head); - } - - const addr_t addr = range_info.GetRange().GetRangeBase(); - const addr_t size = range_info.GetRange().GetByteSize(); - - if (size == 0) - return llvm::createStringError(std::errc::not_supported, - "stack segment of the process is empty"); - - return std::make_pair(addr, size); +Status MinidumpFileBuilder::FixThreadStacks() { + Status error; + // If we have anything in the heap flush it. + FlushBufferToDisk(); + m_core_file->SeekFromStart(m_thread_list_start); + for (auto &pair : m_thread_by_range_end) { + // The thread objects will get a new memory descriptor added + // When we are emitting the memory list and then we write it here + const llvm::minidump::Thread &thread = pair.second; + size_t bytes_to_write = sizeof(llvm::minidump::Thread); + size_t bytes_written = bytes_to_write; + error = m_core_file->Write(&thread, bytes_written); + if (error.Fail() || bytes_to_write != bytes_written) { + error.SetErrorStringWithFormat( + "Wrote incorrect number of bytes to minidump file. (written %zd/%zd)", + bytes_written, bytes_to_write); + return error; + } + } + + return error; } -Status MinidumpFileBuilder::AddThreadList(const lldb::ProcessSP &process_sp) { +Status MinidumpFileBuilder::AddThreadList() { constexpr size_t minidump_thread_size = sizeof(llvm::minidump::Thread); - lldb_private::ThreadList thread_list = process_sp->GetThreadList(); + lldb_private::ThreadList thread_list = m_process_sp->GetThreadList(); // size of the entire thread stream consists of: // number of threads and threads array @@ -523,28 +596,31 @@ Status MinidumpFileBuilder::AddThreadList(const lldb::ProcessSP &process_sp) { thread_list.GetSize() * minidump_thread_size; // save for the ability to set up RVA size_t size_before = GetCurrentDataEndOffset(); - - AddDirectory(StreamType::ThreadList, thread_stream_size); + Status error; + error = AddDirectory(StreamType::ThreadList, thread_stream_size); + if (error.Fail()) + return error; llvm::support::ulittle32_t thread_count = static_cast<llvm::support::ulittle32_t>(thread_list.GetSize()); m_data.AppendData(&thread_count, sizeof(llvm::support::ulittle32_t)); + // Take the offset after the thread count. + m_thread_list_start = GetCurrentDataEndOffset(); DataBufferHeap helper_data; const uint32_t num_threads = thread_list.GetSize(); - + Log *log = GetLog(LLDBLog::Object); for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) { ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx)); RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext()); - Status error; if (!reg_ctx_sp) { error.SetErrorString("Unable to get the register context."); return error; } RegisterContext *reg_ctx = reg_ctx_sp.get(); - Target &target = process_sp->GetTarget(); + Target &target = m_process_sp->GetTarget(); const ArchSpec &arch = target.GetArchitecture(); ArchThreadContexts thread_context(arch.GetMachine()); if (!thread_context.prepareRegisterContext(reg_ctx)) { @@ -553,38 +629,18 @@ Status MinidumpFileBuilder::AddThreadList(const lldb::ProcessSP &process_sp) { arch.GetTriple().getArchName().str().c_str()); return error; } - uint64_t sp = reg_ctx->GetSP(); - auto expected_address_range = findStackHelper(process_sp, sp); - if (!expected_address_range) { - consumeError(expected_address_range.takeError()); - error.SetErrorString("Unable to get the stack address."); - return error; - } - - std::pair<uint64_t, uint64_t> range = std::move(*expected_address_range); - uint64_t addr = range.first; - uint64_t size = range.second; - - auto data_up = std::make_unique<DataBufferHeap>(size, 0); - const size_t stack_bytes_read = - process_sp->ReadMemory(addr, data_up->GetBytes(), size, error); - - if (error.Fail()) - return error; - - LocationDescriptor stack_memory; - stack_memory.DataSize = - static_cast<llvm::support::ulittle32_t>(stack_bytes_read); - stack_memory.RVA = static_cast<llvm::support::ulittle32_t>( - size_before + thread_stream_size + helper_data.GetByteSize()); + uint64_t sp = reg_ctx->GetSP(); + MemoryRegionInfo sp_region; + m_process_sp->GetMemoryRegionInfo(sp, sp_region); + // Emit a blank descriptor MemoryDescriptor stack; - stack.StartOfMemoryRange = static_cast<llvm::support::ulittle64_t>(addr); - stack.Memory = stack_memory; - - helper_data.AppendData(data_up->GetBytes(), stack_bytes_read); - + LocationDescriptor empty_label; + empty_label.DataSize = 0; + empty_label.RVA = 0; + stack.Memory = empty_label; + stack.StartOfMemoryRange = 0; LocationDescriptor thread_context_memory_locator; thread_context_memory_locator.DataSize = static_cast<llvm::support::ulittle32_t>(thread_context.size()); @@ -593,6 +649,8 @@ Status MinidumpFileBuilder::AddThreadList(const lldb::ProcessSP &process_sp) { // Cache thie thread context memory so we can reuse for exceptions. m_tid_to_reg_ctx[thread_sp->GetID()] = thread_context_memory_locator; + LLDB_LOGF(log, "AddThreadList for thread %d: thread_context %zu bytes", + thread_idx, thread_context.size()); helper_data.AppendData(thread_context.data(), thread_context.size()); llvm::minidump::Thread t; @@ -604,16 +662,20 @@ Status MinidumpFileBuilder::AddThreadList(const lldb::ProcessSP &process_sp) { t.EnvironmentBlock = static_cast<llvm::support::ulittle64_t>(0); t.Stack = stack, t.Context = thread_context_memory_locator; + // We save off the stack object so we can circle back and clean it up. + m_thread_by_range_end[sp_region.GetRange().GetRangeEnd()] = t; m_data.AppendData(&t, sizeof(llvm::minidump::Thread)); } + LLDB_LOGF(log, "AddThreadList(): total helper_data %" PRIx64 " bytes", + helper_data.GetByteSize()); m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize()); return Status(); } -void MinidumpFileBuilder::AddExceptions(const lldb::ProcessSP &process_sp) { - lldb_private::ThreadList thread_list = process_sp->GetThreadList(); - +Status MinidumpFileBuilder::AddExceptions() { + lldb_private::ThreadList thread_list = m_process_sp->GetThreadList(); + Status error; const uint32_t num_threads = thread_list.GetSize(); for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) { ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx)); @@ -632,7 +694,10 @@ void MinidumpFileBuilder::AddExceptions(const lldb::ProcessSP &process_sp) { if (add_exception) { constexpr size_t minidump_exception_size = sizeof(llvm::minidump::ExceptionStream); - AddDirectory(StreamType::Exception, minidump_exception_size); + error = AddDirectory(StreamType::Exception, minidump_exception_size); + if (error.Fail()) + return error; + StopInfoSP stop_info_sp = thread_sp->GetStopInfo(); RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext()); Exception exp_record = {}; @@ -660,69 +725,16 @@ void MinidumpFileBuilder::AddExceptions(const lldb::ProcessSP &process_sp) { m_data.AppendData(&exp_stream, minidump_exception_size); } } -} - -lldb_private::Status -MinidumpFileBuilder::AddMemoryList(const lldb::ProcessSP &process_sp, - lldb::SaveCoreStyle core_style) { - Status error; - Process::CoreFileMemoryRanges core_ranges; - error = process_sp->CalculateCoreFileSaveRanges(core_style, core_ranges); - if (error.Fail()) { - error.SetErrorString("Process doesn't support getting memory region info."); - return error; - } - - DataBufferHeap helper_data; - std::vector<MemoryDescriptor> mem_descriptors; - for (const auto &core_range : core_ranges) { - // Skip empty memory regions. - if (core_range.range.empty()) - continue; - const addr_t addr = core_range.range.start(); - const addr_t size = core_range.range.size(); - auto data_up = std::make_unique<DataBufferHeap>(size, 0); - const size_t bytes_read = - process_sp->ReadMemory(addr, data_up->GetBytes(), size, error); - if (error.Fail()) { - Log *log = GetLog(LLDBLog::Object); - LLDB_LOGF(log, "Failed to read memory region. Bytes read: %zu, error: %s", - bytes_read, error.AsCString()); - error.Clear(); - } - if (bytes_read == 0) - continue; - // We have a good memory region with valid bytes to store. - LocationDescriptor memory_dump; - memory_dump.DataSize = static_cast<llvm::support::ulittle32_t>(bytes_read); - memory_dump.RVA = - static_cast<llvm::support::ulittle32_t>(GetCurrentDataEndOffset()); - MemoryDescriptor memory_desc; - memory_desc.StartOfMemoryRange = - static_cast<llvm::support::ulittle64_t>(addr); - memory_desc.Memory = memory_dump; - mem_descriptors.push_back(memory_desc); - m_data.AppendData(data_up->GetBytes(), bytes_read); - } - - AddDirectory(StreamType::MemoryList, - sizeof(llvm::support::ulittle32_t) + - mem_descriptors.size() * - sizeof(llvm::minidump::MemoryDescriptor)); - llvm::support::ulittle32_t memory_ranges_num(mem_descriptors.size()); - - m_data.AppendData(&memory_ranges_num, sizeof(llvm::support::ulittle32_t)); - for (auto memory_descriptor : mem_descriptors) { - m_data.AppendData(&memory_descriptor, - sizeof(llvm::minidump::MemoryDescriptor)); - } return error; } -void MinidumpFileBuilder::AddMiscInfo(const lldb::ProcessSP &process_sp) { - AddDirectory(StreamType::MiscInfo, - sizeof(lldb_private::minidump::MinidumpMiscInfo)); +lldb_private::Status MinidumpFileBuilder::AddMiscInfo() { + Status error; + error = AddDirectory(StreamType::MiscInfo, + sizeof(lldb_private::minidump::MinidumpMiscInfo)); + if (error.Fail()) + return error; lldb_private::minidump::MinidumpMiscInfo misc_info; misc_info.size = static_cast<llvm::support::ulittle32_t>( @@ -732,7 +744,7 @@ void MinidumpFileBuilder::AddMiscInfo(const lldb::ProcessSP &process_sp) { misc_info.flags1 = static_cast<llvm::support::ulittle32_t>(0); lldb_private::ProcessInstanceInfo process_info; - process_sp->GetProcessInfo(process_info); + m_process_sp->GetProcessInfo(process_info); if (process_info.ProcessIDIsValid()) { // Set flags1 to reflect that PID is filled in misc_info.flags1 = @@ -744,6 +756,7 @@ void MinidumpFileBuilder::AddMiscInfo(const lldb::ProcessSP &process_sp) { m_data.AppendData(&misc_info, sizeof(lldb_private::minidump::MinidumpMiscInfo)); + return error; } std::unique_ptr<llvm::MemoryBuffer> @@ -754,15 +767,20 @@ getFileStreamHelper(const std::string &path) { return std::move(maybe_stream.get()); } -void MinidumpFileBuilder::AddLinuxFileStreams( - const lldb::ProcessSP &process_sp) { +Status MinidumpFileBuilder::AddLinuxFileStreams() { + Status error; + // No-op if we are not on linux. + if (m_process_sp->GetTarget().GetArchitecture().GetTriple().getOS() != + llvm::Triple::Linux) + return error; + std::vector<std::pair<StreamType, std::string>> files_with_stream_types = { {StreamType::LinuxCPUInfo, "/proc/cpuinfo"}, {StreamType::LinuxLSBRelease, "/etc/lsb-release"}, }; lldb_private::ProcessInstanceInfo process_info; - process_sp->GetProcessInfo(process_info); + m_process_sp->GetProcessInfo(process_info); if (process_info.ProcessIDIsValid()) { lldb::pid_t pid = process_info.GetProcessID(); std::string pid_str = std::to_string(pid); @@ -791,16 +809,94 @@ void MinidumpFileBuilder::AddLinuxFileStreams( size_t size = memory_buffer->getBufferSize(); if (size == 0) continue; - AddDirectory(stream, size); + error = AddDirectory(stream, size); + if (error.Fail()) + return error; m_data.AppendData(memory_buffer->getBufferStart(), size); } } + + return error; } -Status MinidumpFileBuilder::Dump(lldb::FileUP &core_file) const { - constexpr size_t header_size = sizeof(llvm::minidump::Header); - constexpr size_t directory_size = sizeof(llvm::minidump::Directory); +Status MinidumpFileBuilder::AddMemoryList(SaveCoreStyle core_style) { + Status error; + + // We first save the thread stacks to ensure they fit in the first UINT32_MAX + // bytes of the core file. Thread structures in minidump files can only use + // 32 bit memory descriptiors, so we emit them first to ensure the memory is + // in accessible with a 32 bit offset. + Process::CoreFileMemoryRanges ranges_32; + Process::CoreFileMemoryRanges ranges_64; + error = m_process_sp->CalculateCoreFileSaveRanges( + SaveCoreStyle::eSaveCoreStackOnly, ranges_32); + if (error.Fail()) + return error; + + // Calculate totalsize including the current offset. + uint64_t total_size = GetCurrentDataEndOffset(); + total_size += ranges_32.size() * sizeof(llvm::minidump::MemoryDescriptor); + std::unordered_set<addr_t> stack_start_addresses; + for (const auto &core_range : ranges_32) { + stack_start_addresses.insert(core_range.range.start()); + total_size += core_range.range.size(); + } + + if (total_size >= UINT32_MAX) { + error.SetErrorStringWithFormat("Unable to write minidump. Stack memory " + "exceeds 32b limit. (Num Stacks %zu)", + ranges_32.size()); + return error; + } + + Process::CoreFileMemoryRanges all_core_memory_ranges; + if (core_style != SaveCoreStyle::eSaveCoreStackOnly) { + error = m_process_sp->CalculateCoreFileSaveRanges(core_style, + all_core_memory_ranges); + if (error.Fail()) + return error; + } + + // After saving the stacks, we start packing as much as we can into 32b. + // We apply a generous padding here so that the Directory, MemoryList and + // Memory64List sections all begin in 32b addressable space. + // Then anything overflow extends into 64b addressable space. + // All core memeroy ranges will either container nothing on stacks only + // or all the memory ranges including stacks + if (!all_core_memory_ranges.empty()) + total_size += + 256 + (all_core_memory_ranges.size() - stack_start_addresses.size()) * + sizeof(llvm::minidump::MemoryDescriptor_64); + + for (const auto &core_range : all_core_memory_ranges) { + const addr_t range_size = core_range.range.size(); + if (stack_start_addresses.count(core_range.range.start()) > 0) + // Don't double save stacks. + continue; + + if (total_size + range_size < UINT32_MAX) { + ranges_32.push_back(core_range); + total_size += range_size; + } else { + ranges_64.push_back(core_range); + } + } + error = AddMemoryList_32(ranges_32); + if (error.Fail()) + return error; + + // Add the remaining memory as a 64b range. + if (!ranges_64.empty()) { + error = AddMemoryList_64(ranges_64); + if (error.Fail()) + return error; + } + + return FixThreadStacks(); +} + +Status MinidumpFileBuilder::DumpHeader() const { // write header llvm::minidump::Header header; header.Signature = static_cast<llvm::support::ulittle32_t>( @@ -808,9 +904,10 @@ Status MinidumpFileBuilder::Dump(lldb::FileUP &core_file) const { header.Version = static_cast<llvm::support::ulittle32_t>( llvm::minidump::Header::MagicVersion); header.NumberOfStreams = - static_cast<llvm::support::ulittle32_t>(GetDirectoriesNum()); + static_cast<llvm::support::ulittle32_t>(m_directories.size()); + // We write the directories right after the header. header.StreamDirectoryRVA = - static_cast<llvm::support::ulittle32_t>(GetCurrentDataEndOffset()); + static_cast<llvm::support::ulittle32_t>(HEADER_SIZE); header.Checksum = static_cast<llvm::support::ulittle32_t>( 0u), // not used in most of the writers header.TimeDateStamp = @@ -821,36 +918,35 @@ Status MinidumpFileBuilder::Dump(lldb::FileUP &core_file) const { Status error; size_t bytes_written; - bytes_written = header_size; - error = core_file->Write(&header, bytes_written); - if (error.Fail() || bytes_written != header_size) { - if (bytes_written != header_size) + m_core_file->SeekFromStart(0); + bytes_written = HEADER_SIZE; + error = m_core_file->Write(&header, bytes_written); + if (error.Fail() || bytes_written != HEADER_SIZE) { + if (bytes_written != HEADER_SIZE) error.SetErrorStringWithFormat( - "unable to write the header (written %zd/%zd)", bytes_written, - header_size); + "Unable to write the minidump header (written %zd/%zd)", + bytes_written, HEADER_SIZE); return error; } + return error; +} - // write data - bytes_written = m_data.GetByteSize(); - error = core_file->Write(m_data.GetBytes(), bytes_written); - if (error.Fail() || bytes_written != m_data.GetByteSize()) { - if (bytes_written != m_data.GetByteSize()) - error.SetErrorStringWithFormat( - "unable to write the data (written %zd/%" PRIu64 ")", bytes_written, - m_data.GetByteSize()); - return error; - } +offset_t MinidumpFileBuilder::GetCurrentDataEndOffset() const { + return m_data.GetByteSize() + m_saved_data_size; +} - // write directories +Status MinidumpFileBuilder::DumpDirectories() const { + Status error; + size_t bytes_written; + m_core_file->SeekFromStart(HEADER_SIZE); for (const Directory &dir : m_directories) { - bytes_written = directory_size; - error = core_file->Write(&dir, bytes_written); - if (error.Fail() || bytes_written != directory_size) { - if (bytes_written != directory_size) + bytes_written = DIRECTORY_SIZE; + error = m_core_file->Write(&dir, bytes_written); + if (error.Fail() || bytes_written != DIRECTORY_SIZE) { + if (bytes_written != DIRECTORY_SIZE) error.SetErrorStringWithFormat( "unable to write the directory (written %zd/%zd)", bytes_written, - directory_size); + DIRECTORY_SIZE); return error; } } @@ -858,10 +954,254 @@ Status MinidumpFileBuilder::Dump(lldb::FileUP &core_file) const { return error; } -size_t MinidumpFileBuilder::GetDirectoriesNum() const { - return m_directories.size(); +static uint64_t +GetLargestRangeSize(const Process::CoreFileMemoryRanges &ranges) { + uint64_t max_size = 0; + for (const auto &core_range : ranges) + max_size = std::max(max_size, core_range.range.size()); + return max_size; +} + +Status +MinidumpFileBuilder::AddMemoryList_32(Process::CoreFileMemoryRanges &ranges) { + std::vector<MemoryDescriptor> descriptors; + Status error; + if (ranges.size() == 0) + return error; + + Log *log = GetLog(LLDBLog::Object); + size_t region_index = 0; + auto data_up = + std::make_unique<DataBufferHeap>(GetLargestRangeSize(ranges), 0); + for (const auto &core_range : ranges) { + // Take the offset before we write. + const offset_t offset_for_data = GetCurrentDataEndOffset(); + const addr_t addr = core_range.range.start(); + const addr_t size = core_range.range.size(); + const addr_t end = core_range.range.end(); + + LLDB_LOGF(log, + "AddMemoryList %zu/%zu reading memory for region " + "(%" PRIx64 " bytes) [%" PRIx64 ", %" PRIx64 ")", + region_index, ranges.size(), size, addr, addr + size); + ++region_index; + + const size_t bytes_read = + m_process_sp->ReadMemory(addr, data_up->GetBytes(), size, error); + if (error.Fail() || bytes_read == 0) { + LLDB_LOGF(log, "Failed to read memory region. Bytes read: %zu, error: %s", + bytes_read, error.AsCString()); + // Just skip sections with errors or zero bytes in 32b mode + continue; + } else if (bytes_read != size) { + LLDB_LOGF( + log, "Memory region at: %" PRIx64 " failed to read %" PRIx64 " bytes", + addr, size); + } + + MemoryDescriptor descriptor; + descriptor.StartOfMemoryRange = + static_cast<llvm::support::ulittle64_t>(addr); + descriptor.Memory.DataSize = + static_cast<llvm::support::ulittle32_t>(bytes_read); + descriptor.Memory.RVA = + static_cast<llvm::support::ulittle32_t>(offset_for_data); + descriptors.push_back(descriptor); + if (m_thread_by_range_end.count(end) > 0) + m_thread_by_range_end[end].Stack = descriptor; + + // Add the data to the buffer, flush as needed. + error = AddData(data_up->GetBytes(), bytes_read); + if (error.Fail()) + return error; + } + + // Add a directory that references this list + // With a size of the number of ranges as a 32 bit num + // And then the size of all the ranges + error = AddDirectory(StreamType::MemoryList, + sizeof(llvm::support::ulittle32_t) + + descriptors.size() * + sizeof(llvm::minidump::MemoryDescriptor)); + if (error.Fail()) + return error; + + llvm::support::ulittle32_t memory_ranges_num = + static_cast<llvm::support::ulittle32_t>(descriptors.size()); + m_data.AppendData(&memory_ranges_num, sizeof(llvm::support::ulittle32_t)); + // For 32b we can get away with writing off the descriptors after the data. + // This means no cleanup loop needed. + m_data.AppendData(descriptors.data(), + descriptors.size() * sizeof(MemoryDescriptor)); + + return error; } -size_t MinidumpFileBuilder::GetCurrentDataEndOffset() const { - return sizeof(llvm::minidump::Header) + m_data.GetByteSize(); +Status +MinidumpFileBuilder::AddMemoryList_64(Process::CoreFileMemoryRanges &ranges) { + Status error; + if (ranges.empty()) + return error; + + error = AddDirectory(StreamType::Memory64List, + (sizeof(llvm::support::ulittle64_t) * 2) + + ranges.size() * + sizeof(llvm::minidump::MemoryDescriptor_64)); + if (error.Fail()) + return error; + + llvm::support::ulittle64_t memory_ranges_num = + static_cast<llvm::support::ulittle64_t>(ranges.size()); + m_data.AppendData(&memory_ranges_num, sizeof(llvm::support::ulittle64_t)); + // Capture the starting offset for all the descriptors so we can clean them up + // if needed. + offset_t starting_offset = + GetCurrentDataEndOffset() + sizeof(llvm::support::ulittle64_t); + // The base_rva needs to start after the directories, which is right after + // this 8 byte variable. + offset_t base_rva = + starting_offset + + (ranges.size() * sizeof(llvm::minidump::MemoryDescriptor_64)); + llvm::support::ulittle64_t memory_ranges_base_rva = + static_cast<llvm::support::ulittle64_t>(base_rva); + m_data.AppendData(&memory_ranges_base_rva, + sizeof(llvm::support::ulittle64_t)); + + bool cleanup_required = false; + std::vector<MemoryDescriptor_64> descriptors; + // Enumerate the ranges and create the memory descriptors so we can append + // them first + for (const auto core_range : ranges) { + // Add the space required to store the memory descriptor + MemoryDescriptor_64 memory_desc; + memory_desc.StartOfMemoryRange = + static_cast<llvm::support::ulittle64_t>(core_range.range.start()); + memory_desc.DataSize = + static_cast<llvm::support::ulittle64_t>(core_range.range.size()); + descriptors.push_back(memory_desc); + // Now write this memory descriptor to the buffer. + m_data.AppendData(&memory_desc, sizeof(MemoryDescriptor_64)); + } + + Log *log = GetLog(LLDBLog::Object); + size_t region_index = 0; + auto data_up = + std::make_unique<DataBufferHeap>(GetLargestRangeSize(ranges), 0); + for (const auto &core_range : ranges) { + const addr_t addr = core_range.range.start(); + const addr_t size = core_range.range.size(); + + LLDB_LOGF(log, + "AddMemoryList_64 %zu/%zu reading memory for region " + "(%" PRIx64 "bytes) " + "[%" PRIx64 ", %" PRIx64 ")", + region_index, ranges.size(), size, addr, addr + size); + ++region_index; + + const size_t bytes_read = + m_process_sp->ReadMemory(addr, data_up->GetBytes(), size, error); + if (error.Fail()) { + LLDB_LOGF(log, "Failed to read memory region. Bytes read: %zu, error: %s", + bytes_read, error.AsCString()); + error.Clear(); + cleanup_required = true; + descriptors[region_index].DataSize = 0; + } + if (bytes_read != size) { + LLDB_LOGF( + log, "Memory region at: %" PRIx64 " failed to read %" PRIx64 " bytes", + addr, size); + cleanup_required = true; + descriptors[region_index].DataSize = bytes_read; + } + + // Add the data to the buffer, flush as needed. + error = AddData(data_up->GetBytes(), bytes_read); + if (error.Fail()) + return error; + } + + // Early return if there is no cleanup needed. + if (!cleanup_required) { + return error; + } else { + // Flush to disk we can make the fixes in place. + FlushBufferToDisk(); + // Fixup the descriptors that were not read correctly. + m_core_file->SeekFromStart(starting_offset); + size_t bytes_written = sizeof(MemoryDescriptor_64) * descriptors.size(); + error = m_core_file->Write(descriptors.data(), bytes_written); + if (error.Fail() || + bytes_written != sizeof(MemoryDescriptor_64) * descriptors.size()) { + error.SetErrorStringWithFormat( + "unable to write the memory descriptors (written %zd/%zd)", + bytes_written, sizeof(MemoryDescriptor_64) * descriptors.size()); + } + + return error; + } +} + +Status MinidumpFileBuilder::AddData(const void *data, uint64_t size) { + // This should also get chunked, because worst case we copy over a big + // object / memory range, say 5gb. In that case, we'd have to allocate 10gb + // 5 gb for the buffer we're copying from, and then 5gb for the buffer we're + // copying to. Which will be short lived and immedaitely go to disk, the goal + // here is to limit the number of bytes we need to host in memory at any given + // time. + m_data.AppendData(data, size); + if (m_data.GetByteSize() > MAX_WRITE_CHUNK_SIZE) + return FlushBufferToDisk(); + + return Status(); +} + +Status MinidumpFileBuilder::FlushBufferToDisk() { + Status error; + // Set the stream to it's end. + m_core_file->SeekFromStart(m_saved_data_size); + addr_t starting_size = m_data.GetByteSize(); + addr_t remaining_bytes = starting_size; + offset_t offset = 0; + + while (remaining_bytes > 0) { + size_t bytes_written = remaining_bytes; + // We don't care how many bytes we wrote unless we got an error + // so just decrement the remaining bytes. + error = m_core_file->Write(m_data.GetBytes() + offset, bytes_written); + if (error.Fail()) { + error.SetErrorStringWithFormat( + "Wrote incorrect number of bytes to minidump file. (written %" PRIx64 + "/%" PRIx64 ")", + starting_size - remaining_bytes, starting_size); + return error; + } + + offset += bytes_written; + remaining_bytes -= bytes_written; + } + + m_saved_data_size += starting_size; + m_data.Clear(); + return error; +} + +Status MinidumpFileBuilder::DumpFile() { + Status error; + // If anything is left unsaved, dump it. + error = FlushBufferToDisk(); + if (error.Fail()) + return error; + + // Overwrite the header which we filled in earlier. + error = DumpHeader(); + if (error.Fail()) + return error; + + // Overwrite the space saved for directories + error = DumpDirectories(); + if (error.Fail()) + return error; + + return error; } diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h index b2e984191983..20564e0661f2 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h @@ -17,12 +17,20 @@ #define LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H #include <cstddef> +#include <cstdint> #include <map> +#include <unordered_map> +#include <utility> +#include <variant> +#include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/Status.h" +#include "lldb/lldb-forward.h" +#include "lldb/lldb-types.h" +#include "llvm/BinaryFormat/Minidump.h" #include "llvm/Object/Minidump.h" // Write std::string to minidump in the UTF16 format(with null termination char) @@ -36,11 +44,39 @@ lldb_private::Status WriteString(const std::string &to_write, /// Minidump writer for Linux /// /// This class provides a Minidump writer that is able to -/// snapshot the current process state. For the whole time, it stores all -/// the data on heap. +/// snapshot the current process state. +/// +/// Minidumps are a Microsoft format for dumping process state. +/// This class constructs the minidump on disk starting with +/// Headers and Directories are written at the top of the file, +/// with the amount of bytes being precalculates before any writing takes place +/// Then the smaller data sections are written +/// SystemInfo, ModuleList, Misc Info. +/// Then Threads are emitted, threads are the first section that needs to be +/// 'fixed up' this happens when later we emit the memory stream, we identify if +/// that stream is the expected stack, and if so we update the stack with the +/// current RVA. Lastly the Memory lists are added. For Memory List, this will +/// contain everything that can fit within 4.2gb. MemoryList has it's +/// descriptors written at the end so it cannot be allowed to overflow. +/// +/// Memory64List is a special case where it has to be begin before 4.2gb but can +/// expand forever The difference in Memory64List is there are no RVA's and all +/// the addresses are figured out by starting at the base RVA, and adding the +/// antecedent memory sections. +/// +/// Because Memory64List can be arbitrarily large, this class has to write +/// chunks to disk this means we have to precalculate the descriptors and write +/// them first, and if we encounter any error, or are unable to read the same +/// number of bytes we have to go back and update them on disk. +/// +/// And as the last step, after all the directories have been added, we go back +/// to the top of the file to fill in the header and the redirectory sections +/// that we preallocated. class MinidumpFileBuilder { public: - MinidumpFileBuilder() = default; + MinidumpFileBuilder(lldb::FileUP &&core_file, + const lldb::ProcessSP &process_sp) + : m_process_sp(process_sp), m_core_file(std::move(core_file)){}; MinidumpFileBuilder(const MinidumpFileBuilder &) = delete; MinidumpFileBuilder &operator=(const MinidumpFileBuilder &) = delete; @@ -50,48 +86,84 @@ public: ~MinidumpFileBuilder() = default; + // This method only calculates the amount of bytes the header and directories + // will take up. It does not write the directories or headers. This function + // must be called with a followup to fill in the data. + lldb_private::Status AddHeaderAndCalculateDirectories(); // Add SystemInfo stream, used for storing the most basic information // about the system, platform etc... - lldb_private::Status AddSystemInfo(const llvm::Triple &target_triple); + lldb_private::Status AddSystemInfo(); // Add ModuleList stream, containing information about all loaded modules // at the time of saving minidump. - lldb_private::Status AddModuleList(lldb_private::Target &target); + lldb_private::Status AddModuleList(); // Add ThreadList stream, containing information about all threads running // at the moment of core saving. Contains information about thread // contexts. - lldb_private::Status AddThreadList(const lldb::ProcessSP &process_sp); + lldb_private::Status AddThreadList(); // Add Exception streams for any threads that stopped with exceptions. - void AddExceptions(const lldb::ProcessSP &process_sp); + lldb_private::Status AddExceptions(); // Add MemoryList stream, containing dumps of important memory segments - lldb_private::Status AddMemoryList(const lldb::ProcessSP &process_sp, - lldb::SaveCoreStyle core_style); + lldb_private::Status AddMemoryList(lldb::SaveCoreStyle core_style); // Add MiscInfo stream, mainly providing ProcessId - void AddMiscInfo(const lldb::ProcessSP &process_sp); + lldb_private::Status AddMiscInfo(); // Add informative files about a Linux process - void AddLinuxFileStreams(const lldb::ProcessSP &process_sp); - // Dump the prepared data into file. In case of the failure data are - // intact. - lldb_private::Status Dump(lldb::FileUP &core_file) const; - // Returns the current number of directories(streams) that have been so far - // created. This number of directories will be dumped when calling Dump() - size_t GetDirectoriesNum() const; + lldb_private::Status AddLinuxFileStreams(); + + // Run cleanup and write all remaining bytes to file + lldb_private::Status DumpFile(); private: + // Add data to the end of the buffer, if the buffer exceeds the flush level, + // trigger a flush. + lldb_private::Status AddData(const void *data, uint64_t size); + // Add MemoryList stream, containing dumps of important memory segments + lldb_private::Status + AddMemoryList_64(lldb_private::Process::CoreFileMemoryRanges &ranges); + lldb_private::Status + AddMemoryList_32(lldb_private::Process::CoreFileMemoryRanges &ranges); + // Update the thread list on disk with the newly emitted stack RVAs. + lldb_private::Status FixThreadStacks(); + lldb_private::Status FlushBufferToDisk(); + + lldb_private::Status DumpHeader() const; + lldb_private::Status DumpDirectories() const; // Add directory of StreamType pointing to the current end of the prepared // file with the specified size. - void AddDirectory(llvm::minidump::StreamType type, size_t stream_size); - size_t GetCurrentDataEndOffset() const; - - // Stores directories to later put them at the end of minidump file + lldb_private::Status AddDirectory(llvm::minidump::StreamType type, + uint64_t stream_size); + lldb::offset_t GetCurrentDataEndOffset() const; + // Stores directories to fill in later std::vector<llvm::minidump::Directory> m_directories; + // When we write off the threads for the first time, we need to clean them up + // and give them the correct RVA once we write the stack memory list. + // We save by the end because we only take from the stack pointer up + // So the saved off range base can differ from the memory region the stack + // pointer is in. + std::unordered_map<lldb::addr_t, llvm::minidump::Thread> + m_thread_by_range_end; // Main data buffer consisting of data without the minidump header and // directories lldb_private::DataBufferHeap m_data; + lldb::ProcessSP m_process_sp; + + size_t m_expected_directories = 0; + uint64_t m_saved_data_size = 0; + lldb::offset_t m_thread_list_start = 0; + // We set the max write amount to 128 mb, this is arbitrary + // but we want to try to keep the size of m_data small + // and we will only exceed a 128 mb buffer if we get a memory region + // that is larger than 128 mb. + static constexpr size_t MAX_WRITE_CHUNK_SIZE = (1024 * 1024 * 128); + + static constexpr size_t HEADER_SIZE = sizeof(llvm::minidump::Header); + static constexpr size_t DIRECTORY_SIZE = sizeof(llvm::minidump::Directory); // More that one place can mention the register thread context locations, // so when we emit the thread contents, remember where it is so we don't have // to duplicate it in the exception data. - std::map<lldb::tid_t, llvm::minidump::LocationDescriptor> m_tid_to_reg_ctx; + std::unordered_map<lldb::tid_t, llvm::minidump::LocationDescriptor> + m_tid_to_reg_ctx; + lldb::FileUP m_core_file; }; #endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H diff --git a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp index 1af5d99f0b16..7c875aa3223e 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp @@ -15,6 +15,7 @@ #include "lldb/Core/Section.h" #include "lldb/Target/Process.h" #include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" #include "llvm/Support/FileSystem.h" @@ -65,56 +66,70 @@ bool ObjectFileMinidump::SaveCore(const lldb::ProcessSP &process_sp, if (!process_sp) return false; - MinidumpFileBuilder builder; - - Target &target = process_sp->GetTarget(); + llvm::Expected<lldb::FileUP> maybe_core_file = FileSystem::Instance().Open( + outfile, File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate); + if (!maybe_core_file) { + error = maybe_core_file.takeError(); + return false; + } + MinidumpFileBuilder builder(std::move(maybe_core_file.get()), process_sp); Log *log = GetLog(LLDBLog::Object); - error = builder.AddSystemInfo(target.GetArchitecture().GetTriple()); + error = builder.AddHeaderAndCalculateDirectories(); if (error.Fail()) { - LLDB_LOG(log, "AddSystemInfo failed: %s", error.AsCString()); + LLDB_LOGF(log, "AddHeaderAndCalculateDirectories failed: %s", + error.AsCString()); + return false; + }; + error = builder.AddSystemInfo(); + if (error.Fail()) { + LLDB_LOGF(log, "AddSystemInfo failed: %s", error.AsCString()); return false; } - error = builder.AddModuleList(target); + error = builder.AddModuleList(); if (error.Fail()) { - LLDB_LOG(log, "AddModuleList failed: %s", error.AsCString()); + LLDB_LOGF(log, "AddModuleList failed: %s", error.AsCString()); return false; } - - builder.AddMiscInfo(process_sp); - - error = builder.AddThreadList(process_sp); + error = builder.AddMiscInfo(); if (error.Fail()) { - LLDB_LOG(log, "AddThreadList failed: %s", error.AsCString()); + LLDB_LOGF(log, "AddMiscInfo failed: %s", error.AsCString()); return false; } - // Add any exceptions but only if there are any in any threads. - builder.AddExceptions(process_sp); + error = builder.AddThreadList(); + if (error.Fail()) { + LLDB_LOGF(log, "AddThreadList failed: %s", error.AsCString()); + return false; + } - error = builder.AddMemoryList(process_sp, core_style); + error = builder.AddLinuxFileStreams(); if (error.Fail()) { - LLDB_LOG(log, "AddMemoryList failed: %s", error.AsCString()); + LLDB_LOGF(log, "AddLinuxFileStreams failed: %s", error.AsCString()); return false; } - if (target.GetArchitecture().GetTriple().getOS() == - llvm::Triple::OSType::Linux) { - builder.AddLinuxFileStreams(process_sp); + // Add any exceptions but only if there are any in any threads. + error = builder.AddExceptions(); + if (error.Fail()) { + LLDB_LOGF(log, "AddExceptions failed: %s", error.AsCString()); + return false; } - llvm::Expected<lldb::FileUP> maybe_core_file = FileSystem::Instance().Open( - outfile, File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate); - if (!maybe_core_file) { - error = maybe_core_file.takeError(); + // Note: add memory HAS to be the last thing we do. It can overflow into 64b + // land and many RVA's only support 32b + error = builder.AddMemoryList(core_style); + if (error.Fail()) { + LLDB_LOGF(log, "AddMemoryList failed: %s", error.AsCString()); return false; } - lldb::FileUP core_file = std::move(maybe_core_file.get()); - error = builder.Dump(core_file); - if (error.Fail()) + error = builder.DumpFile(); + if (error.Fail()) { + LLDB_LOGF(log, "DumpFile failed: %s", error.AsCString()); return false; + } return true; } diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp index 0c4b566b7d53..c27a34b7201a 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.cpp @@ -383,81 +383,6 @@ PlatformSP PlatformAppleSimulator::CreateInstance( return PlatformSP(); } -Status PlatformAppleSimulator::ResolveExecutable( - const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp, - const FileSpecList *module_search_paths_ptr) { - Status error; - // Nothing special to do here, just use the actual file and architecture - - ModuleSpec resolved_module_spec(module_spec); - - // If we have "ls" as the exe_file, resolve the executable loation based on - // the current path variables - // TODO: resolve bare executables in the Platform SDK - // if (!resolved_exe_file.Exists()) - // resolved_exe_file.ResolveExecutableLocation (); - - // Resolve any executable within a bundle on MacOSX - // TODO: verify that this handles shallow bundles, if not then implement one - // ourselves - Host::ResolveExecutableInBundle(resolved_module_spec.GetFileSpec()); - - if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec())) { - if (resolved_module_spec.GetArchitecture().IsValid()) { - error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, - NULL, NULL, NULL); - - if (exe_module_sp && exe_module_sp->GetObjectFile()) - return error; - exe_module_sp.reset(); - } - // No valid architecture was specified or the exact ARM slice wasn't found - // so ask the platform for the architectures that we should be using (in - // the correct order) and see if we can find a match that way - StreamString arch_names; - llvm::ListSeparator LS; - ArchSpec platform_arch; - for (const ArchSpec &arch : GetSupportedArchitectures({})) { - resolved_module_spec.GetArchitecture() = arch; - - // Only match x86 with x86 and x86_64 with x86_64... - if (!module_spec.GetArchitecture().IsValid() || - module_spec.GetArchitecture().GetCore() == - resolved_module_spec.GetArchitecture().GetCore()) { - error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, - NULL, NULL, NULL); - // Did we find an executable using one of the - if (error.Success()) { - if (exe_module_sp && exe_module_sp->GetObjectFile()) - break; - else - error.SetErrorToGenericError(); - } - - arch_names << LS << platform_arch.GetArchitectureName(); - } - } - - if (error.Fail() || !exe_module_sp) { - if (FileSystem::Instance().Readable(resolved_module_spec.GetFileSpec())) { - error.SetErrorStringWithFormatv( - "'{0}' doesn't contain any '{1}' platform architectures: {2}", - resolved_module_spec.GetFileSpec(), GetPluginName(), - arch_names.GetString()); - } else { - error.SetErrorStringWithFormat( - "'%s' is not readable", - resolved_module_spec.GetFileSpec().GetPath().c_str()); - } - } - } else { - error.SetErrorStringWithFormat("'%s' does not exist", - module_spec.GetFileSpec().GetPath().c_str()); - } - - return error; -} - Status PlatformAppleSimulator::GetSymbolFile(const FileSpec &platform_file, const UUID *uuid_ptr, FileSpec &local_file) { diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.h b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.h index e17e7b6992ee..7fcf2c502ca6 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformAppleSimulator.h @@ -87,10 +87,6 @@ public: std::vector<ArchSpec> GetSupportedArchitectures(const ArchSpec &process_host_arch) override; - Status - ResolveExecutable(const ModuleSpec &module_spec, lldb::ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr) override; - Status GetSharedModule(const ModuleSpec &module_spec, Process *process, lldb::ModuleSP &module_sp, const FileSpecList *module_search_paths_ptr, diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp index a244ee35976b..9323b66181c7 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.cpp @@ -63,71 +63,6 @@ void PlatformRemoteDarwinDevice::GetStatus(Stream &strm) { } } -Status PlatformRemoteDarwinDevice::ResolveExecutable( - const ModuleSpec &ms, lldb::ModuleSP &exe_module_sp, - const FileSpecList *module_search_paths_ptr) { - Status error; - // Nothing special to do here, just use the actual file and architecture - - ModuleSpec resolved_module_spec(ms); - - // Resolve any executable within a bundle on MacOSX - // TODO: verify that this handles shallow bundles, if not then implement one - // ourselves - Host::ResolveExecutableInBundle(resolved_module_spec.GetFileSpec()); - - if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec())) { - if (resolved_module_spec.GetArchitecture().IsValid() || - resolved_module_spec.GetUUID().IsValid()) { - error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, - nullptr, nullptr, nullptr); - - if (exe_module_sp && exe_module_sp->GetObjectFile()) - return error; - exe_module_sp.reset(); - } - // No valid architecture was specified or the exact ARM slice wasn't found - // so ask the platform for the architectures that we should be using (in - // the correct order) and see if we can find a match that way - StreamString arch_names; - llvm::ListSeparator LS; - ArchSpec process_host_arch; - for (const ArchSpec &arch : GetSupportedArchitectures(process_host_arch)) { - resolved_module_spec.GetArchitecture() = arch; - error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, - nullptr, nullptr, nullptr); - // Did we find an executable using one of the - if (error.Success()) { - if (exe_module_sp && exe_module_sp->GetObjectFile()) - break; - else - error.SetErrorToGenericError(); - } - - arch_names << LS << arch.GetArchitectureName(); - } - - if (error.Fail() || !exe_module_sp) { - if (FileSystem::Instance().Readable(resolved_module_spec.GetFileSpec())) { - error.SetErrorStringWithFormatv( - "'{0}' doesn't contain any '{1}' platform architectures: {2}", - resolved_module_spec.GetFileSpec(), GetPluginName(), - arch_names.GetData()); - } else { - error.SetErrorStringWithFormat( - "'%s' is not readable", - resolved_module_spec.GetFileSpec().GetPath().c_str()); - } - } - } else { - error.SetErrorStringWithFormat( - "'%s' does not exist", - resolved_module_spec.GetFileSpec().GetPath().c_str()); - } - - return error; -} - bool PlatformRemoteDarwinDevice::GetFileInSDK(const char *platform_file_path, uint32_t sdk_idx, lldb_private::FileSpec &local_file) { diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.h b/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.h index c60486604099..557f4876e91a 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformRemoteDarwinDevice.h @@ -40,10 +40,6 @@ public: ~PlatformRemoteDarwinDevice() override; // Platform functions - Status - ResolveExecutable(const ModuleSpec &module_spec, lldb::ModuleSP &module_sp, - const FileSpecList *module_search_paths_ptr) override; - void GetStatus(Stream &strm) override; virtual Status GetSymbolFile(const FileSpec &platform_file, diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp index 5ad2f7a8e945..4668c25eab08 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp @@ -506,7 +506,7 @@ uint32_t NativeRegisterContextLinux_arm::SetHardwareWatchpoint( return LLDB_INVALID_INDEX32; else if (watch_mask <= 0x02) size = 2; - else if (watch_mask <= 0x04) + else size = 4; addr = addr & (~0x03); diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp index 9b5f7aef1efe..5397e90022f3 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp @@ -388,7 +388,7 @@ NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info, return error; } - // ZA is part of the SME set but uses a seperate member buffer for + // ZA is part of the SME set but uses a separate member buffer for // storage. Therefore its effective byte offset is always 0 even if it // isn't 0 within the SME register set. src = (uint8_t *)GetZABuffer() + GetZAHeaderSize(); diff --git a/lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.cpp b/lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.cpp index 51553817921f..8ed75d700f22 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.cpp +++ b/lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.cpp @@ -20,6 +20,7 @@ #define HWCAP2_BTI (1ULL << 17) #define HWCAP2_MTE (1ULL << 18) #define HWCAP2_AFP (1ULL << 20) +#define HWCAP2_SME (1ULL << 23) #define HWCAP2_EBF16 (1ULL << 32) using namespace lldb_private; @@ -27,7 +28,10 @@ using namespace lldb_private; LinuxArm64RegisterFlags::Fields LinuxArm64RegisterFlags::DetectSVCRFields(uint64_t hwcap, uint64_t hwcap2) { (void)hwcap; - (void)hwcap2; + + if (!(hwcap2 & HWCAP2_SME)) + return {}; + // Represents the pseudo register that lldb-server builds, which itself // matches the architectural register SCVR. The fields match SVCR in the Arm // manual. @@ -40,7 +44,10 @@ LinuxArm64RegisterFlags::DetectSVCRFields(uint64_t hwcap, uint64_t hwcap2) { LinuxArm64RegisterFlags::Fields LinuxArm64RegisterFlags::DetectMTECtrlFields(uint64_t hwcap, uint64_t hwcap2) { (void)hwcap; - (void)hwcap2; + + if (!(hwcap2 & HWCAP2_MTE)) + return {}; + // Represents the contents of NT_ARM_TAGGED_ADDR_CTRL and the value passed // to prctl(PR_TAGGED_ADDR_CTRL...). Fields are derived from the defines // used to build the value. diff --git a/lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.h b/lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.h index 660bef08700f..49b1d90db64f 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.h +++ b/lldb/source/Plugins/Process/Utility/RegisterFlagsLinux_arm64.h @@ -38,8 +38,8 @@ public: /// For the registers listed in this class, detect which fields are /// present. Must be called before UpdateRegisterInfos. /// If called more than once, fields will be redetected each time from - /// scratch. If you do not have access to hwcap, just pass 0 for each one, you - /// will only get unconditional fields. + /// scratch. If the target would not have this register at all, the list of + /// fields will be left empty. void DetectFields(uint64_t hwcap, uint64_t hwcap2); /// Add the field information of any registers named in this class, @@ -63,7 +63,7 @@ private: struct RegisterEntry { RegisterEntry(llvm::StringRef name, unsigned size, DetectorFn detector) - : m_name(name), m_flags(std::string(name) + "_flags", size, {{"", 0}}), + : m_name(name), m_flags(std::string(name) + "_flags", size, {}), m_detector(detector) {} llvm::StringRef m_name; diff --git a/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp b/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp index 75504323b4fd..25cee369d7ee 100644 --- a/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp +++ b/lldb/source/Plugins/Process/Utility/StopInfoMachException.cpp @@ -92,9 +92,7 @@ bool StopInfoMachException::DeterminePtrauthFailure(ExecutionContext &exe_ctx) { Target &target = *exe_ctx.GetTargetPtr(); Process &process = *exe_ctx.GetProcessPtr(); - ABISP abi_sp = process.GetABI(); const ArchSpec &arch = target.GetArchitecture(); - assert(abi_sp && "Missing ABI info"); // Check for a ptrauth-enabled target. const bool ptrauth_enabled_target = @@ -110,6 +108,9 @@ bool StopInfoMachException::DeterminePtrauthFailure(ExecutionContext &exe_ctx) { strm.Printf("Note: Possible pointer authentication failure detected.\n"); }; + ABISP abi_sp = process.GetABI(); + assert(abi_sp && "Missing ABI info"); + // Check if we have a "brk 0xc47x" trap, where the value that failed to // authenticate is in x16. Address current_address = current_frame->GetFrameCodeAddress(); diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index eb0834b1159f..f383b3d40a4f 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -405,8 +405,7 @@ void ProcessWindows::RefreshStateAfterStop() { "{1:x} with watchpoint {2}", m_session_data->m_debugger->GetProcess().GetProcessId(), pc, id); - stop_info = StopInfo::CreateStopReasonWithWatchpointID( - *stop_thread, id, m_watchpoints[id].address); + stop_info = StopInfo::CreateStopReasonWithWatchpointID(*stop_thread, id); stop_thread->SetStopInfo(stop_info); return; @@ -857,7 +856,7 @@ Status ProcessWindows::EnableWatchpoint(WatchpointSP wp_sp, bool notify) { info.address = wp_sp->GetLoadAddress(); info.size = wp_sp->GetByteSize(); info.read = wp_sp->WatchpointRead(); - info.write = wp_sp->WatchpointWrite(); + info.write = wp_sp->WatchpointWrite() || wp_sp->WatchpointModify(); for (unsigned i = 0U; i < m_thread_list.GetSize(); i++) { Thread *thread = m_thread_list.GetThreadAtIndex(i).get(); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp index 8a47eed3d7cb..187370eb36ca 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -539,9 +539,8 @@ bool GDBRemoteCommunication::DecompressPacket() { else if (m_compression_type == CompressionType::ZlibDeflate) scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_ZLIB); else if (m_compression_type == CompressionType::LZMA) - scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZMA); - else if (m_compression_type == CompressionType::LZFSE) - scratchbuf_size = compression_decode_scratch_buffer_size (COMPRESSION_LZFSE); + scratchbuf_size = + compression_decode_scratch_buffer_size(COMPRESSION_LZMA); if (scratchbuf_size > 0) { m_decompression_scratch = (void*) malloc (scratchbuf_size); m_decompression_scratch_type = m_compression_type; diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index a5a731981299..3195587eb567 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -4179,21 +4179,134 @@ struct GdbServerTargetInfo { RegisterSetMap reg_set_map; }; -static std::vector<RegisterFlags::Field> ParseFlagsFields(XMLNode flags_node, - unsigned size) { +static FieldEnum::Enumerators ParseEnumEvalues(const XMLNode &enum_node) { + Log *log(GetLog(GDBRLog::Process)); + // We will use the last instance of each value. Also we preserve the order + // of declaration in the XML, as it may not be numerical. + // For example, hardware may intially release with two states that softwware + // can read from a register field: + // 0 = startup, 1 = running + // If in a future hardware release, the designers added a pre-startup state: + // 0 = startup, 1 = running, 2 = pre-startup + // Now it makes more sense to list them in this logical order as opposed to + // numerical order: + // 2 = pre-startup, 1 = startup, 0 = startup + // This only matters for "register info" but let's trust what the server + // chose regardless. + std::map<uint64_t, FieldEnum::Enumerator> enumerators; + + enum_node.ForEachChildElementWithName( + "evalue", [&enumerators, &log](const XMLNode &enumerator_node) { + std::optional<llvm::StringRef> name; + std::optional<uint64_t> value; + + enumerator_node.ForEachAttribute( + [&name, &value, &log](const llvm::StringRef &attr_name, + const llvm::StringRef &attr_value) { + if (attr_name == "name") { + if (attr_value.size()) + name = attr_value; + else + LLDB_LOG(log, "ProcessGDBRemote::ParseEnumEvalues " + "Ignoring empty name in evalue"); + } else if (attr_name == "value") { + uint64_t parsed_value = 0; + if (llvm::to_integer(attr_value, parsed_value)) + value = parsed_value; + else + LLDB_LOG(log, + "ProcessGDBRemote::ParseEnumEvalues " + "Invalid value \"{0}\" in " + "evalue", + attr_value.data()); + } else + LLDB_LOG(log, + "ProcessGDBRemote::ParseEnumEvalues Ignoring " + "unknown attribute " + "\"{0}\" in evalue", + attr_name.data()); + + // Keep walking attributes. + return true; + }); + + if (value && name) + enumerators.insert_or_assign( + *value, FieldEnum::Enumerator(*value, name->str())); + + // Find all evalue elements. + return true; + }); + + FieldEnum::Enumerators final_enumerators; + for (auto [_, enumerator] : enumerators) + final_enumerators.push_back(enumerator); + + return final_enumerators; +} + +static void +ParseEnums(XMLNode feature_node, + llvm::StringMap<std::unique_ptr<FieldEnum>> ®isters_enum_types) { + Log *log(GetLog(GDBRLog::Process)); + + // The top level element is "<enum...". + feature_node.ForEachChildElementWithName( + "enum", [log, ®isters_enum_types](const XMLNode &enum_node) { + std::string id; + + enum_node.ForEachAttribute([&id](const llvm::StringRef &attr_name, + const llvm::StringRef &attr_value) { + if (attr_name == "id") + id = attr_value; + + // There is also a "size" attribute that is supposed to be the size in + // bytes of the register this applies to. However: + // * LLDB doesn't need this information. + // * It is difficult to verify because you have to wait until the + // enum is applied to a field. + // + // So we will emit this attribute in XML for GDB's sake, but will not + // bother ingesting it. + + // Walk all attributes. + return true; + }); + + if (!id.empty()) { + FieldEnum::Enumerators enumerators = ParseEnumEvalues(enum_node); + if (!enumerators.empty()) { + LLDB_LOG(log, + "ProcessGDBRemote::ParseEnums Found enum type \"{0}\"", + id); + registers_enum_types.insert_or_assign( + id, std::make_unique<FieldEnum>(id, enumerators)); + } + } + + // Find all <enum> elements. + return true; + }); +} + +static std::vector<RegisterFlags::Field> ParseFlagsFields( + XMLNode flags_node, unsigned size, + const llvm::StringMap<std::unique_ptr<FieldEnum>> ®isters_enum_types) { Log *log(GetLog(GDBRLog::Process)); const unsigned max_start_bit = size * 8 - 1; // Process the fields of this set of flags. std::vector<RegisterFlags::Field> fields; - flags_node.ForEachChildElementWithName("field", [&fields, max_start_bit, - &log](const XMLNode - &field_node) { + flags_node.ForEachChildElementWithName("field", [&fields, max_start_bit, &log, + ®isters_enum_types]( + const XMLNode + &field_node) { std::optional<llvm::StringRef> name; std::optional<unsigned> start; std::optional<unsigned> end; + std::optional<llvm::StringRef> type; - field_node.ForEachAttribute([&name, &start, &end, max_start_bit, + field_node.ForEachAttribute([&name, &start, &end, &type, max_start_bit, &log](const llvm::StringRef &attr_name, const llvm::StringRef &attr_value) { // Note that XML in general requires that each of these attributes only @@ -4240,8 +4353,7 @@ static std::vector<RegisterFlags::Field> ParseFlagsFields(XMLNode flags_node, attr_value.data()); } } else if (attr_name == "type") { - // Type is a known attribute but we do not currently use it and it is - // not required. + type = attr_value; } else { LLDB_LOG( log, @@ -4254,14 +4366,55 @@ static std::vector<RegisterFlags::Field> ParseFlagsFields(XMLNode flags_node, }); if (name && start && end) { - if (*start > *end) { + if (*start > *end) LLDB_LOG( log, "ProcessGDBRemote::ParseFlagsFields Start {0} > end {1} in field " "\"{2}\", ignoring", *start, *end, name->data()); - } else { - fields.push_back(RegisterFlags::Field(name->str(), *start, *end)); + else { + if (RegisterFlags::Field::GetSizeInBits(*start, *end) > 64) + LLDB_LOG(log, + "ProcessGDBRemote::ParseFlagsFields Ignoring field \"{2}\" " + "that has " + "size > 64 bits, this is not supported", + name->data()); + else { + // A field's type may be set to the name of an enum type. + const FieldEnum *enum_type = nullptr; + if (type && !type->empty()) { + auto found = registers_enum_types.find(*type); + if (found != registers_enum_types.end()) { + enum_type = found->second.get(); + + // No enumerator can exceed the range of the field itself. + uint64_t max_value = + RegisterFlags::Field::GetMaxValue(*start, *end); + for (const auto &enumerator : enum_type->GetEnumerators()) { + if (enumerator.m_value > max_value) { + enum_type = nullptr; + LLDB_LOG( + log, + "ProcessGDBRemote::ParseFlagsFields In enum \"{0}\" " + "evalue \"{1}\" with value {2} exceeds the maximum value " + "of field \"{3}\" ({4}), ignoring enum", + type->data(), enumerator.m_name, enumerator.m_value, + name->data(), max_value); + break; + } + } + } else { + LLDB_LOG(log, + "ProcessGDBRemote::ParseFlagsFields Could not find type " + "\"{0}\" " + "for field \"{1}\", ignoring", + type->data(), name->data()); + } + } + + fields.push_back( + RegisterFlags::Field(name->str(), *start, *end, enum_type)); + } } } @@ -4272,12 +4425,14 @@ static std::vector<RegisterFlags::Field> ParseFlagsFields(XMLNode flags_node, void ParseFlags( XMLNode feature_node, - llvm::StringMap<std::unique_ptr<RegisterFlags>> ®isters_flags_types) { + llvm::StringMap<std::unique_ptr<RegisterFlags>> ®isters_flags_types, + const llvm::StringMap<std::unique_ptr<FieldEnum>> ®isters_enum_types) { Log *log(GetLog(GDBRLog::Process)); feature_node.ForEachChildElementWithName( "flags", - [&log, ®isters_flags_types](const XMLNode &flags_node) -> bool { + [&log, ®isters_flags_types, + ®isters_enum_types](const XMLNode &flags_node) -> bool { LLDB_LOG(log, "ProcessGDBRemote::ParseFlags Found flags node \"{0}\"", flags_node.GetAttributeValue("id").c_str()); @@ -4310,7 +4465,7 @@ void ParseFlags( if (id && size) { // Process the fields of this set of flags. std::vector<RegisterFlags::Field> fields = - ParseFlagsFields(flags_node, *size); + ParseFlagsFields(flags_node, *size, registers_enum_types); if (fields.size()) { // Sort so that the fields with the MSBs are first. std::sort(fields.rbegin(), fields.rend()); @@ -4375,15 +4530,21 @@ void ParseFlags( bool ParseRegisters( XMLNode feature_node, GdbServerTargetInfo &target_info, std::vector<DynamicRegisterInfo::Register> ®isters, - llvm::StringMap<std::unique_ptr<RegisterFlags>> ®isters_flags_types) { + llvm::StringMap<std::unique_ptr<RegisterFlags>> ®isters_flags_types, + llvm::StringMap<std::unique_ptr<FieldEnum>> ®isters_enum_types) { if (!feature_node) return false; Log *log(GetLog(GDBRLog::Process)); - ParseFlags(feature_node, registers_flags_types); + // Enums first because they are referenced by fields in the flags. + ParseEnums(feature_node, registers_enum_types); + for (const auto &enum_type : registers_enum_types) + enum_type.second->DumpToLog(log); + + ParseFlags(feature_node, registers_flags_types, registers_enum_types); for (const auto &flags : registers_flags_types) - flags.second->log(log); + flags.second->DumpToLog(log); feature_node.ForEachChildElementWithName( "reg", @@ -4643,7 +4804,7 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfoXMLAndProcess( if (arch_to_use.IsValid()) { for (auto &feature_node : feature_nodes) { ParseRegisters(feature_node, target_info, registers, - m_registers_flags_types); + m_registers_flags_types, m_registers_enum_types); } for (const auto &include : target_info.includes) { @@ -4708,13 +4869,14 @@ bool ProcessGDBRemote::GetGDBServerRegisterInfo(ArchSpec &arch_to_use) { if (!m_gdb_comm.GetQXferFeaturesReadSupported()) return false; - // This holds register flags information for the whole of target.xml. + // These hold register type information for the whole of target.xml. // target.xml may include further documents that // GetGDBServerRegisterInfoXMLAndProcess will recurse to fetch and process. // That's why we clear the cache here, and not in // GetGDBServerRegisterInfoXMLAndProcess. To prevent it being cleared on every // include read. m_registers_flags_types.clear(); + m_registers_enum_types.clear(); std::vector<DynamicRegisterInfo::Register> registers; if (GetGDBServerRegisterInfoXMLAndProcess(arch_to_use, "target.xml", registers)) diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 610a1ee0b34d..b44ffefcd0d3 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -484,6 +484,11 @@ private: // entries are added. Which would invalidate any pointers set in the register // info up to that point. llvm::StringMap<std::unique_ptr<RegisterFlags>> m_registers_flags_types; + + // Enum types are referenced by register fields. This does not store the data + // directly because the map may reallocate. Pointers to these are contained + // within instances of RegisterFlags. + llvm::StringMap<std::unique_ptr<FieldEnum>> m_registers_enum_types; }; } // namespace process_gdb_remote diff --git a/lldb/source/Plugins/REPL/Clang/ClangREPL.cpp b/lldb/source/Plugins/REPL/Clang/ClangREPL.cpp index 0aaddad53126..0fb5490defc6 100644 --- a/lldb/source/Plugins/REPL/Clang/ClangREPL.cpp +++ b/lldb/source/Plugins/REPL/Clang/ClangREPL.cpp @@ -95,7 +95,9 @@ bool ClangREPL::PrintOneVariable(Debugger &debugger, if (m_implicit_expr_result_regex.Execute(var->GetName().GetStringRef())) return true; } - valobj_sp->Dump(*output_sp); + if (llvm::Error error = valobj_sp->Dump(*output_sp)) + *output_sp << "error: " << toString(std::move(error)); + return true; } diff --git a/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp b/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp index 067768537c06..a088a8742718 100644 --- a/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp +++ b/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp @@ -43,8 +43,7 @@ CompilerType RegisterTypeBuilderClang::GetRegisterType( ScratchTypeSystemClang::GetForTarget(m_target); assert(type_system); - std::string register_type_name = "__lldb_register_fields_"; - register_type_name += name; + std::string register_type_name = "__lldb_register_fields_" + name; // See if we have made this type before and can reuse it. CompilerType fields_type = type_system->GetTypeForIdentifier<clang::CXXRecordDecl>( @@ -67,8 +66,43 @@ CompilerType RegisterTypeBuilderClang::GetRegisterType( // We assume that RegisterFlags has padded and sorted the fields // already. for (const RegisterFlags::Field &field : flags.GetFields()) { + CompilerType field_type = field_uint_type; + + if (const FieldEnum *enum_type = field.GetEnum()) { + const FieldEnum::Enumerators &enumerators = enum_type->GetEnumerators(); + if (!enumerators.empty()) { + std::string enum_type_name = + "__lldb_register_fields_enum_" + enum_type->GetID(); + + // Enums can be used by mutiple fields and multiple registers, so we + // may have built this one already. + CompilerType field_enum_type = + type_system->GetTypeForIdentifier<clang::EnumDecl>( + enum_type_name); + + if (field_enum_type) + field_type = field_enum_type; + else { + field_type = type_system->CreateEnumerationType( + enum_type_name, type_system->GetTranslationUnitDecl(), + OptionalClangModuleID(), Declaration(), field_uint_type, false); + + type_system->StartTagDeclarationDefinition(field_type); + + Declaration decl; + for (auto enumerator : enumerators) { + type_system->AddEnumerationValueToEnumerationType( + field_type, decl, enumerator.m_name.c_str(), + enumerator.m_value, byte_size * 8); + } + + type_system->CompleteTagDeclarationDefinition(field_type); + } + } + } + type_system->AddFieldToRecordType(fields_type, field.GetName(), - field_uint_type, lldb::eAccessPublic, + field_type, lldb::eAccessPublic, field.GetSizeInBits()); } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt index b22abc49c92a..c60e4bb503a3 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt @@ -24,6 +24,7 @@ add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces ScriptedPythonInterface.cpp ScriptedProcessPythonInterface.cpp ScriptedThreadPythonInterface.cpp + ScriptedThreadPlanPythonInterface.cpp ScriptedPlatformPythonInterface.cpp LINK_LIBS diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.cpp index 9ba4731032bd..6e93bec80056 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.cpp @@ -20,6 +20,8 @@ #include "../ScriptInterpreterPythonImpl.h" #include "ScriptedPlatformPythonInterface.h" +#include "lldb/Target/ExecutionContext.h" + using namespace lldb; using namespace lldb_private; using namespace lldb_private::python; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.cpp index e86b34d6b930..313c597ce48f 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.cpp @@ -49,7 +49,8 @@ StructuredData::DictionarySP ScriptedProcessPythonInterface::GetCapabilities() { StructuredData::DictionarySP dict = Dispatch<StructuredData::DictionarySP>("get_capabilities", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, + error)) return {}; return dict; @@ -90,7 +91,8 @@ StructuredData::DictionarySP ScriptedProcessPythonInterface::GetThreadsInfo() { StructuredData::DictionarySP dict = Dispatch<StructuredData::DictionarySP>("get_threads_info", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, + error)) return {}; return dict; @@ -106,7 +108,8 @@ bool ScriptedProcessPythonInterface::CreateBreakpoint(lldb::addr_t addr, if (py_error.Fail()) error = py_error; - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) return {}; return obj->GetBooleanValue(); @@ -131,7 +134,8 @@ lldb::offset_t ScriptedProcessPythonInterface::WriteMemoryAtAddress( StructuredData::ObjectSP obj = Dispatch("write_memory_at_address", py_error, addr, data_sp, error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) return LLDB_INVALID_OFFSET; // If there was an error on the python call, surface it to the user. @@ -146,7 +150,8 @@ StructuredData::ArraySP ScriptedProcessPythonInterface::GetLoadedImages() { StructuredData::ArraySP array = Dispatch<StructuredData::ArraySP>("get_loaded_images", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, array, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, array, + error)) return {}; return array; @@ -156,7 +161,8 @@ lldb::pid_t ScriptedProcessPythonInterface::GetProcessID() { Status error; StructuredData::ObjectSP obj = Dispatch("get_process_id", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) return LLDB_INVALID_PROCESS_ID; return obj->GetUnsignedIntegerValue(LLDB_INVALID_PROCESS_ID); @@ -166,7 +172,8 @@ bool ScriptedProcessPythonInterface::IsAlive() { Status error; StructuredData::ObjectSP obj = Dispatch("is_alive", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) return {}; return obj->GetBooleanValue(); @@ -177,7 +184,8 @@ ScriptedProcessPythonInterface::GetScriptedThreadPluginName() { Status error; StructuredData::ObjectSP obj = Dispatch("get_scripted_thread_plugin", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) return {}; return obj->GetStringValue().str(); @@ -193,7 +201,8 @@ StructuredData::DictionarySP ScriptedProcessPythonInterface::GetMetadata() { StructuredData::DictionarySP dict = Dispatch<StructuredData::DictionarySP>("get_process_metadata", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, + error)) return {}; return dict; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp index 6f22503b279c..7d072212676e 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp @@ -27,6 +27,15 @@ ScriptedPythonInterface::ScriptedPythonInterface( : ScriptedInterface(), m_interpreter(interpreter) {} template <> +void ScriptedPythonInterface::ReverseTransform( + lldb_private::Stream *&original_arg, python::PythonObject transformed_arg, + Status &error) { + Stream *s = ExtractValueFromPythonObject<Stream *>(transformed_arg, error); + *original_arg = *s; + original_arg->PutCString(static_cast<StreamString *>(s)->GetData()); +} + +template <> StructuredData::ArraySP ScriptedPythonInterface::ExtractValueFromPythonObject<StructuredData::ArraySP>( python::PythonObject &p, Status &error) { @@ -48,13 +57,34 @@ Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>( if (lldb::SBError *sb_error = reinterpret_cast<lldb::SBError *>( python::LLDBSWIGPython_CastPyObjectToSBError(p.get()))) return m_interpreter.GetStatusFromSBError(*sb_error); - else - error.SetErrorString("Couldn't cast lldb::SBError to lldb::Status."); + error.SetErrorString("Couldn't cast lldb::SBError to lldb::Status."); return {}; } template <> +Event *ScriptedPythonInterface::ExtractValueFromPythonObject<Event *>( + python::PythonObject &p, Status &error) { + if (lldb::SBEvent *sb_event = reinterpret_cast<lldb::SBEvent *>( + python::LLDBSWIGPython_CastPyObjectToSBEvent(p.get()))) + return m_interpreter.GetOpaqueTypeFromSBEvent(*sb_event); + error.SetErrorString("Couldn't cast lldb::SBEvent to lldb_private::Event."); + + return nullptr; +} + +template <> +Stream *ScriptedPythonInterface::ExtractValueFromPythonObject<Stream *>( + python::PythonObject &p, Status &error) { + if (lldb::SBStream *sb_stream = reinterpret_cast<lldb::SBStream *>( + python::LLDBSWIGPython_CastPyObjectToSBStream(p.get()))) + return m_interpreter.GetOpaqueTypeFromSBStream(*sb_stream); + error.SetErrorString("Couldn't cast lldb::SBStream to lldb_private::Stream."); + + return nullptr; +} + +template <> lldb::DataExtractorSP ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DataExtractorSP>( python::PythonObject &p, Status &error) { diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h index 163659234466..062bf1fcff4a 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h @@ -115,7 +115,7 @@ public: PythonObject::ResolveNameWithDictionary<python::PythonCallable>( class_name, dict); if (!init.IsAllocated()) - return create_error(llvm::formatv("Could not find script class: %s", + return create_error(llvm::formatv("Could not find script class: {0}", class_name.data())); std::tuple<Args...> original_args = std::forward_as_tuple(args...); @@ -248,8 +248,11 @@ protected: (PyObject *)m_object_instance_sp->GetValue()); if (!implementor.IsAllocated()) - return ErrorWithMessage<T>(caller_signature, - "Python implementor not allocated.", error); + return llvm::is_contained(GetAbstractMethods(), method_name) + ? ErrorWithMessage<T>(caller_signature, + "Python implementor not allocated.", + error) + : T{}; std::tuple<Args...> original_args = std::forward_as_tuple(args...); auto transformed_args = TransformArgs(original_args); @@ -322,6 +325,10 @@ protected: return python::SWIGBridge::ToSWIGWrapper(arg); } + python::PythonObject Transform(lldb::ThreadPlanSP arg) { + return python::SWIGBridge::ToSWIGWrapper(arg); + } + python::PythonObject Transform(lldb::ProcessAttachInfoSP arg) { return python::SWIGBridge::ToSWIGWrapper(arg); } @@ -330,6 +337,14 @@ protected: return python::SWIGBridge::ToSWIGWrapper(arg); } + python::PythonObject Transform(Event *arg) { + return python::SWIGBridge::ToSWIGWrapper(arg); + } + + python::PythonObject Transform(Stream *arg) { + return python::SWIGBridge::ToSWIGWrapper(arg); + } + python::PythonObject Transform(lldb::DataExtractorSP arg) { return python::SWIGBridge::ToSWIGWrapper(arg); } @@ -428,6 +443,14 @@ Status ScriptedPythonInterface::ExtractValueFromPythonObject<Status>( python::PythonObject &p, Status &error); template <> +Event *ScriptedPythonInterface::ExtractValueFromPythonObject<Event *>( + python::PythonObject &p, Status &error); + +template <> +Stream *ScriptedPythonInterface::ExtractValueFromPythonObject<Stream *>( + python::PythonObject &p, Status &error); + +template <> lldb::BreakpointSP ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>( python::PythonObject &p, Status &error); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPlanPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPlanPythonInterface.cpp new file mode 100644 index 000000000000..b7e475812f22 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPlanPythonInterface.cpp @@ -0,0 +1,105 @@ +//===-- ScriptedThreadPlanPythonInterface.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/Host/Config.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 "ScriptedThreadPlanPythonInterface.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::python; + +ScriptedThreadPlanPythonInterface::ScriptedThreadPlanPythonInterface( + ScriptInterpreterPythonImpl &interpreter) + : ScriptedThreadPlanInterface(), ScriptedPythonInterface(interpreter) {} + +llvm::Expected<StructuredData::GenericSP> +ScriptedThreadPlanPythonInterface::CreatePluginObject( + const llvm::StringRef class_name, lldb::ThreadPlanSP thread_plan_sp, + const StructuredDataImpl &args_sp) { + return ScriptedPythonInterface::CreatePluginObject(class_name, nullptr, + thread_plan_sp, args_sp); +} + +llvm::Expected<bool> +ScriptedThreadPlanPythonInterface::ExplainsStop(Event *event) { + Status error; + StructuredData::ObjectSP obj = Dispatch("explains_stop", error, event); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) { + if (!obj) + return false; + return error.ToError(); + } + + return obj->GetBooleanValue(); +} + +llvm::Expected<bool> +ScriptedThreadPlanPythonInterface::ShouldStop(Event *event) { + Status error; + StructuredData::ObjectSP obj = Dispatch("should_stop", error, event); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) { + if (!obj) + return false; + return error.ToError(); + } + + return obj->GetBooleanValue(); +} + +llvm::Expected<bool> ScriptedThreadPlanPythonInterface::IsStale() { + Status error; + StructuredData::ObjectSP obj = Dispatch("is_stale", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) { + if (!obj) + return false; + return error.ToError(); + } + + return obj->GetBooleanValue(); +} + +lldb::StateType ScriptedThreadPlanPythonInterface::GetRunState() { + Status error; + StructuredData::ObjectSP obj = Dispatch("should_step", error); + + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) + return lldb::eStateStepping; + + return static_cast<lldb::StateType>(obj->GetUnsignedIntegerValue( + static_cast<uint32_t>(lldb::eStateStepping))); +} + +llvm::Expected<bool> +ScriptedThreadPlanPythonInterface::GetStopDescription(lldb_private::Stream *s) { + Status error; + Dispatch("stop_description", error, s); + + if (error.Fail()) + return error.ToError(); + + return true; +} + +#endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPlanPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPlanPythonInterface.h new file mode 100644 index 000000000000..33f086786c47 --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPlanPythonInterface.h @@ -0,0 +1,48 @@ +//===-- ScriptedThreadPlanPythonInterface.h ---------------------*- C++ -*-===// +// +// 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_SCRIPTEDTHREADPLANPYTHONINTERFACE_H +#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDTHREADPLANPYTHONINTERFACE_H + +#include "lldb/Host/Config.h" + +#if LLDB_ENABLE_PYTHON + +#include "ScriptedPythonInterface.h" +#include "lldb/Interpreter/Interfaces/ScriptedThreadPlanInterface.h" +#include <optional> + +namespace lldb_private { +class ScriptedThreadPlanPythonInterface : public ScriptedThreadPlanInterface, + public ScriptedPythonInterface { +public: + ScriptedThreadPlanPythonInterface(ScriptInterpreterPythonImpl &interpreter); + + llvm::Expected<StructuredData::GenericSP> + CreatePluginObject(const llvm::StringRef class_name, + lldb::ThreadPlanSP thread_plan_sp, + const StructuredDataImpl &args_sp) override; + + llvm::SmallVector<llvm::StringLiteral> GetAbstractMethods() const override { + return {}; + } + + llvm::Expected<bool> ExplainsStop(Event *event) override; + + llvm::Expected<bool> ShouldStop(Event *event) override; + + llvm::Expected<bool> IsStale() override; + + lldb::StateType GetRunState() override; + + llvm::Expected<bool> GetStopDescription(lldb_private::Stream *s) override; +}; +} // namespace lldb_private + +#endif // LLDB_ENABLE_PYTHON +#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDTHREADPLANPYTHONINTERFACE_H diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp index 18e268527eb2..8af89d761764 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "lldb/Host/Config.h" +#include "lldb/Target/ExecutionContext.h" #include "lldb/Utility/Log.h" #include "lldb/lldb-enumerations.h" @@ -44,7 +45,8 @@ lldb::tid_t ScriptedThreadPythonInterface::GetThreadID() { Status error; StructuredData::ObjectSP obj = Dispatch("get_thread_id", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) return LLDB_INVALID_THREAD_ID; return obj->GetUnsignedIntegerValue(LLDB_INVALID_THREAD_ID); @@ -54,7 +56,8 @@ std::optional<std::string> ScriptedThreadPythonInterface::GetName() { Status error; StructuredData::ObjectSP obj = Dispatch("get_name", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) return {}; return obj->GetStringValue().str(); @@ -64,7 +67,8 @@ lldb::StateType ScriptedThreadPythonInterface::GetState() { Status error; StructuredData::ObjectSP obj = Dispatch("get_state", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) return eStateInvalid; return static_cast<StateType>(obj->GetUnsignedIntegerValue(eStateInvalid)); @@ -74,7 +78,8 @@ std::optional<std::string> ScriptedThreadPythonInterface::GetQueue() { Status error; StructuredData::ObjectSP obj = Dispatch("get_queue", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) return {}; return obj->GetStringValue().str(); @@ -85,7 +90,8 @@ StructuredData::DictionarySP ScriptedThreadPythonInterface::GetStopReason() { StructuredData::DictionarySP dict = Dispatch<StructuredData::DictionarySP>("get_stop_reason", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, + error)) return {}; return dict; @@ -96,7 +102,8 @@ StructuredData::ArraySP ScriptedThreadPythonInterface::GetStackFrames() { StructuredData::ArraySP arr = Dispatch<StructuredData::ArraySP>("get_stackframes", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, arr, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, arr, + error)) return {}; return arr; @@ -107,7 +114,8 @@ StructuredData::DictionarySP ScriptedThreadPythonInterface::GetRegisterInfo() { StructuredData::DictionarySP dict = Dispatch<StructuredData::DictionarySP>("get_register_info", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict, + error)) return {}; return dict; @@ -117,7 +125,8 @@ std::optional<std::string> ScriptedThreadPythonInterface::GetRegisterContext() { Status error; StructuredData::ObjectSP obj = Dispatch("get_register_context", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj, + error)) return {}; return obj->GetAsString()->GetValue().str(); @@ -128,7 +137,8 @@ StructuredData::ArraySP ScriptedThreadPythonInterface::GetExtendedInfo() { StructuredData::ArraySP arr = Dispatch<StructuredData::ArraySP>("get_extended_info", error); - if (!CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, arr, error)) + if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, arr, + error)) return {}; return arr; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h index c1a11b9134d6..95eb5a782097 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h @@ -97,12 +97,14 @@ public: static PythonObject ToSWIGWrapper(lldb::ExecutionContextRefSP ctx_sp); static PythonObject ToSWIGWrapper(const TypeSummaryOptions &summary_options); static PythonObject ToSWIGWrapper(const SymbolContext &sym_ctx); + static PythonObject ToSWIGWrapper(const Stream *stream); + static PythonObject ToSWIGWrapper(std::shared_ptr<lldb::SBStream> stream_sb); + static PythonObject ToSWIGWrapper(Event *event); static PythonObject ToSWIGWrapper(lldb::ProcessAttachInfoSP attach_info_sp); static PythonObject ToSWIGWrapper(lldb::ProcessLaunchInfoSP launch_info_sp); static PythonObject ToSWIGWrapper(lldb::DataExtractorSP data_extractor_sp); - static PythonObject ToSWIGWrapper(std::unique_ptr<lldb::SBStream> stream_sb); static PythonObject ToSWIGWrapper(std::unique_ptr<lldb::SBStructuredData> data_sb); static PythonObject @@ -112,7 +114,6 @@ public: static python::ScopedPythonObject<lldb::SBCommandReturnObject> ToSWIGWrapper(CommandReturnObject &cmd_retobj); - static python::ScopedPythonObject<lldb::SBEvent> ToSWIGWrapper(Event *event); // These prototypes are the Pythonic implementations of the required // callbacks. Although these are scripting-language specific, their definition // depends on the public API. @@ -147,21 +148,6 @@ public: const char *session_dictionary_name, lldb::DebuggerSP debugger_sp); - static python::PythonObject LLDBSwigPythonCreateScriptedThreadPlan( - const char *python_class_name, const char *session_dictionary_name, - const StructuredDataImpl &args_data, std::string &error_string, - const lldb::ThreadPlanSP &thread_plan_sp); - - static bool LLDBSWIGPythonCallThreadPlan(void *implementor, - const char *method_name, - lldb_private::Event *event_sp, - bool &got_error); - - static bool LLDBSWIGPythonCallThreadPlan(void *implementor, - const char *method_name, - lldb_private::Stream *stream, - bool &got_error); - static python::PythonObject LLDBSwigPythonCreateScriptedBreakpointResolver( const char *python_class_name, const char *session_dictionary_name, const StructuredDataImpl &args, const lldb::BreakpointSP &bkpt_sp); @@ -269,6 +255,8 @@ void *LLDBSWIGPython_CastPyObjectToSBBreakpoint(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBLaunchInfo(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBError(PyObject *data); +void *LLDBSWIGPython_CastPyObjectToSBEvent(PyObject *data); +void *LLDBSWIGPython_CastPyObjectToSBStream(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBValue(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyObject *data); } // namespace python diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 6e676de146b3..70c9f9475441 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -17,6 +17,7 @@ #include "Interfaces/OperatingSystemPythonInterface.h" #include "Interfaces/ScriptedPlatformPythonInterface.h" #include "Interfaces/ScriptedProcessPythonInterface.h" +#include "Interfaces/ScriptedThreadPlanPythonInterface.h" #include "Interfaces/ScriptedThreadPythonInterface.h" #include "PythonDataObjects.h" #include "PythonReadline.h" @@ -1537,6 +1538,11 @@ ScriptInterpreterPythonImpl::CreateScriptedThreadInterface() { return std::make_shared<ScriptedThreadPythonInterface>(*this); } +ScriptedThreadPlanInterfaceSP +ScriptInterpreterPythonImpl::CreateScriptedThreadPlanInterface() { + return std::make_shared<ScriptedThreadPlanPythonInterface>(*this); +} + OperatingSystemInterfaceSP ScriptInterpreterPythonImpl::CreateOperatingSystemInterface() { return std::make_shared<OperatingSystemPythonInterface>(*this); @@ -1553,122 +1559,6 @@ ScriptInterpreterPythonImpl::CreateStructuredDataFromScriptObject( return py_obj.CreateStructuredObject(); } -StructuredData::ObjectSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlan( - const char *class_name, const StructuredDataImpl &args_data, - std::string &error_str, lldb::ThreadPlanSP thread_plan_sp) { - if (class_name == nullptr || class_name[0] == '\0') - return StructuredData::ObjectSP(); - - if (!thread_plan_sp.get()) - return {}; - - Debugger &debugger = thread_plan_sp->GetTarget().GetDebugger(); - ScriptInterpreterPythonImpl *python_interpreter = - GetPythonInterpreter(debugger); - - if (!python_interpreter) - return {}; - - Locker py_lock(this, - Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); - PythonObject ret_val = SWIGBridge::LLDBSwigPythonCreateScriptedThreadPlan( - class_name, python_interpreter->m_dictionary_name.c_str(), args_data, - error_str, thread_plan_sp); - if (!ret_val) - return {}; - - return StructuredData::ObjectSP( - new StructuredPythonObject(std::move(ret_val))); -} - -bool ScriptInterpreterPythonImpl::ScriptedThreadPlanExplainsStop( - StructuredData::ObjectSP implementor_sp, Event *event, bool &script_error) { - bool explains_stop = true; - StructuredData::Generic *generic = nullptr; - if (implementor_sp) - generic = implementor_sp->GetAsGeneric(); - if (generic) { - Locker py_lock(this, - Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); - explains_stop = SWIGBridge::LLDBSWIGPythonCallThreadPlan( - generic->GetValue(), "explains_stop", event, script_error); - if (script_error) - return true; - } - return explains_stop; -} - -bool ScriptInterpreterPythonImpl::ScriptedThreadPlanShouldStop( - StructuredData::ObjectSP implementor_sp, Event *event, bool &script_error) { - bool should_stop = true; - StructuredData::Generic *generic = nullptr; - if (implementor_sp) - generic = implementor_sp->GetAsGeneric(); - if (generic) { - Locker py_lock(this, - Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); - should_stop = SWIGBridge::LLDBSWIGPythonCallThreadPlan( - generic->GetValue(), "should_stop", event, script_error); - if (script_error) - return true; - } - return should_stop; -} - -bool ScriptInterpreterPythonImpl::ScriptedThreadPlanIsStale( - StructuredData::ObjectSP implementor_sp, bool &script_error) { - bool is_stale = true; - StructuredData::Generic *generic = nullptr; - if (implementor_sp) - generic = implementor_sp->GetAsGeneric(); - if (generic) { - Locker py_lock(this, - Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); - is_stale = SWIGBridge::LLDBSWIGPythonCallThreadPlan( - generic->GetValue(), "is_stale", (Event *)nullptr, script_error); - if (script_error) - return true; - } - return is_stale; -} - -lldb::StateType ScriptInterpreterPythonImpl::ScriptedThreadPlanGetRunState( - StructuredData::ObjectSP implementor_sp, bool &script_error) { - bool should_step = false; - StructuredData::Generic *generic = nullptr; - if (implementor_sp) - generic = implementor_sp->GetAsGeneric(); - if (generic) { - Locker py_lock(this, - Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); - should_step = SWIGBridge::LLDBSWIGPythonCallThreadPlan( - generic->GetValue(), "should_step", (Event *)nullptr, script_error); - if (script_error) - should_step = true; - } - if (should_step) - return lldb::eStateStepping; - return lldb::eStateRunning; -} - -bool -ScriptInterpreterPythonImpl::ScriptedThreadPlanGetStopDescription( - StructuredData::ObjectSP implementor_sp, lldb_private::Stream *stream, - bool &script_error) { - StructuredData::Generic *generic = nullptr; - if (implementor_sp) - generic = implementor_sp->GetAsGeneric(); - if (!generic) { - script_error = true; - return false; - } - Locker py_lock(this, - Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN); - return SWIGBridge::LLDBSWIGPythonCallThreadPlan( - generic->GetValue(), "stop_description", stream, script_error); -} - - StructuredData::GenericSP ScriptInterpreterPythonImpl::CreateScriptedBreakpointResolver( const char *class_name, const StructuredDataImpl &args_data, diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index fcd21dff612b..fa2354053473 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -78,33 +78,8 @@ public: CreateScriptCommandObject(const char *class_name) override; StructuredData::ObjectSP - CreateScriptedThreadPlan(const char *class_name, - const StructuredDataImpl &args_data, - std::string &error_str, - lldb::ThreadPlanSP thread_plan) override; - - StructuredData::ObjectSP CreateStructuredDataFromScriptObject(ScriptObject obj) override; - bool ScriptedThreadPlanExplainsStop(StructuredData::ObjectSP implementor_sp, - Event *event, - bool &script_error) override; - - bool ScriptedThreadPlanShouldStop(StructuredData::ObjectSP implementor_sp, - Event *event, bool &script_error) override; - - bool ScriptedThreadPlanIsStale(StructuredData::ObjectSP implementor_sp, - bool &script_error) override; - - lldb::StateType - ScriptedThreadPlanGetRunState(StructuredData::ObjectSP implementor_sp, - bool &script_error) override; - - bool - ScriptedThreadPlanGetStopDescription(StructuredData::ObjectSP implementor_sp, - lldb_private::Stream *s, - bool &script_error) override; - StructuredData::GenericSP CreateScriptedBreakpointResolver(const char *class_name, const StructuredDataImpl &args_data, @@ -136,6 +111,9 @@ public: lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override; + lldb::ScriptedThreadPlanInterfaceSP + CreateScriptedThreadPlanInterface() override; + lldb::OperatingSystemInterfaceSP CreateOperatingSystemInterface() override; StructuredData::ObjectSP diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h index 66db396279e0..abaeb2502cbb 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParser.h @@ -58,7 +58,7 @@ public: virtual void EnsureAllDIEsInDeclContextHaveBeenParsed( CompilerDeclContext decl_context) = 0; - virtual ConstString GetDIEClassTemplateParams(const DWARFDIE &die) = 0; + virtual std::string GetDIEClassTemplateParams(const DWARFDIE &die) = 0; static std::optional<SymbolFile::ArrayInfo> ParseChildArrayInfo(const DWARFDIE &parent_die, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index 579a538af363..f36e2af9589b 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -23,6 +23,7 @@ #include "Plugins/ExpressionParser/Clang/ClangUtil.h" #include "Plugins/Language/ObjC/ObjCLanguage.h" #include "lldb/Core/Module.h" +#include "lldb/Core/Progress.h" #include "lldb/Core/Value.h" #include "lldb/Host/Host.h" #include "lldb/Symbol/CompileUnit.h" @@ -38,10 +39,12 @@ #include "lldb/Utility/StreamString.h" #include "clang/AST/CXXInheritance.h" +#include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Type.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Demangle/Demangle.h" #include <map> @@ -222,7 +225,6 @@ TypeSP DWARFASTParserClang::ParseTypeFromClangModule(const SymbolContext &sc, nullptr, LLDB_INVALID_UID, Type::eEncodingInvalid, &pcm_type_sp->GetDeclaration(), type, Type::ResolveState::Forward, TypePayloadClang(GetOwningClangModule(die))); - dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); clang::TagDecl *tag_decl = TypeSystemClang::GetAsTagDecl(type); if (tag_decl) { LinkDeclContextToDIE(tag_decl, die); @@ -457,90 +459,78 @@ TypeSP DWARFASTParserClang::ParseTypeFromDWARF(const SymbolContext &sc, DW_TAG_value_to_name(die.Tag()), die.Tag(), die.GetName()); } - Type *type_ptr = dwarf->GetDIEToType().lookup(die.GetDIE()); - if (type_ptr == DIE_IS_BEING_PARSED) - return nullptr; - if (type_ptr) - return type_ptr->shared_from_this(); // Set a bit that lets us know that we are currently parsing this - dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED; + if (auto [it, inserted] = + dwarf->GetDIEToType().try_emplace(die.GetDIE(), DIE_IS_BEING_PARSED); + !inserted) { + if (it->getSecond() == nullptr || it->getSecond() == DIE_IS_BEING_PARSED) + return nullptr; + return it->getSecond()->shared_from_this(); + } ParsedDWARFTypeAttributes attrs(die); + TypeSP type_sp; if (DWARFDIE signature_die = attrs.signature.Reference()) { - if (TypeSP type_sp = - ParseTypeFromDWARF(sc, signature_die, type_is_new_ptr)) { - dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); + type_sp = ParseTypeFromDWARF(sc, signature_die, type_is_new_ptr); + if (type_sp) { if (clang::DeclContext *decl_ctx = GetCachedClangDeclContextForDIE(signature_die)) LinkDeclContextToDIE(decl_ctx, die); - return type_sp; } - return nullptr; - } - - if (type_is_new_ptr) - *type_is_new_ptr = true; - - const dw_tag_t tag = die.Tag(); - - TypeSP type_sp; - - switch (tag) { - case DW_TAG_typedef: - case DW_TAG_base_type: - case DW_TAG_pointer_type: - case DW_TAG_reference_type: - case DW_TAG_rvalue_reference_type: - case DW_TAG_const_type: - case DW_TAG_restrict_type: - case DW_TAG_volatile_type: - case DW_TAG_LLVM_ptrauth_type: - case DW_TAG_atomic_type: - case DW_TAG_unspecified_type: { - type_sp = ParseTypeModifier(sc, die, attrs); - break; - } - - case DW_TAG_structure_type: - case DW_TAG_union_type: - case DW_TAG_class_type: { - type_sp = ParseStructureLikeDIE(sc, die, attrs); - break; - } + } else { + if (type_is_new_ptr) + *type_is_new_ptr = true; - case DW_TAG_enumeration_type: { - type_sp = ParseEnum(sc, die, attrs); - break; - } + const dw_tag_t tag = die.Tag(); - case DW_TAG_inlined_subroutine: - case DW_TAG_subprogram: - case DW_TAG_subroutine_type: { - type_sp = ParseSubroutine(die, attrs); - break; - } - case DW_TAG_array_type: { - type_sp = ParseArrayType(die, attrs); - break; - } - case DW_TAG_ptr_to_member_type: { - type_sp = ParsePointerToMemberType(die, attrs); - break; + switch (tag) { + case DW_TAG_typedef: + case DW_TAG_base_type: + case DW_TAG_pointer_type: + case DW_TAG_reference_type: + case DW_TAG_rvalue_reference_type: + case DW_TAG_const_type: + case DW_TAG_restrict_type: + case DW_TAG_volatile_type: + case DW_TAG_LLVM_ptrauth_type: + case DW_TAG_atomic_type: + case DW_TAG_unspecified_type: + type_sp = ParseTypeModifier(sc, die, attrs); + break; + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + type_sp = ParseStructureLikeDIE(sc, die, attrs); + break; + case DW_TAG_enumeration_type: + type_sp = ParseEnum(sc, die, attrs); + break; + case DW_TAG_inlined_subroutine: + case DW_TAG_subprogram: + case DW_TAG_subroutine_type: + type_sp = ParseSubroutine(die, attrs); + break; + case DW_TAG_array_type: + type_sp = ParseArrayType(die, attrs); + break; + case DW_TAG_ptr_to_member_type: + type_sp = ParsePointerToMemberType(die, attrs); + break; + default: + dwarf->GetObjectFile()->GetModule()->ReportError( + "[{0:x16}]: unhandled type tag {1:x4} ({2}), " + "please file a bug and " + "attach the file at the start of this error message", + die.GetOffset(), tag, DW_TAG_value_to_name(tag)); + break; + } + UpdateSymbolContextScopeForType(sc, die, type_sp); } - default: - dwarf->GetObjectFile()->GetModule()->ReportError( - "[{0:x16}]: unhandled type tag {1:x4} ({2}), " - "please file a bug and " - "attach the file at the start of this error message", - die.GetOffset(), tag, DW_TAG_value_to_name(tag)); - break; + if (type_sp) { + dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); } - - // TODO: We should consider making the switch above exhaustive to simplify - // control flow in ParseTypeFromDWARF. Then, we could simply replace this - // return statement with a call to llvm_unreachable. - return UpdateSymbolContextScopeForType(sc, die, type_sp); + return type_sp; } static std::optional<uint32_t> @@ -829,78 +819,68 @@ DWARFASTParserClang::ParseTypeModifier(const SymbolContext &sc, } } - type_sp = dwarf->MakeType(die.GetID(), attrs.name, attrs.byte_size, nullptr, - attrs.type.Reference().GetID(), encoding_data_type, - &attrs.decl, clang_type, resolve_state, payload); - - dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); - return type_sp; + return dwarf->MakeType(die.GetID(), attrs.name, attrs.byte_size, nullptr, + attrs.type.Reference().GetID(), encoding_data_type, + &attrs.decl, clang_type, resolve_state, payload); } -ConstString +std::string DWARFASTParserClang::GetDIEClassTemplateParams(const DWARFDIE &die) { if (llvm::StringRef(die.GetName()).contains("<")) - return ConstString(); + return {}; TypeSystemClang::TemplateParameterInfos template_param_infos; - if (ParseTemplateParameterInfos(die, template_param_infos)) { - return ConstString(m_ast.PrintTemplateParams(template_param_infos)); - } - return ConstString(); + if (ParseTemplateParameterInfos(die, template_param_infos)) + return m_ast.PrintTemplateParams(template_param_infos); + + return {}; } TypeSP DWARFASTParserClang::ParseEnum(const SymbolContext &sc, - const DWARFDIE &die, + const DWARFDIE &decl_die, ParsedDWARFTypeAttributes &attrs) { Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); - SymbolFileDWARF *dwarf = die.GetDWARF(); - const dw_tag_t tag = die.Tag(); - TypeSP type_sp; + SymbolFileDWARF *dwarf = decl_die.GetDWARF(); + const dw_tag_t tag = decl_die.Tag(); + DWARFDIE def_die; if (attrs.is_forward_declaration) { - type_sp = ParseTypeFromClangModule(sc, die, log); - if (type_sp) + if (TypeSP type_sp = ParseTypeFromClangModule(sc, decl_die, log)) return type_sp; - type_sp = dwarf->FindDefinitionTypeForDWARFDeclContext(die); + def_die = dwarf->FindDefinitionDIE(decl_die); - if (!type_sp) { + if (!def_die) { SymbolFileDWARFDebugMap *debug_map_symfile = dwarf->GetDebugMapSymfile(); if (debug_map_symfile) { // We weren't able to find a full declaration in this DWARF, // see if we have a declaration anywhere else... - type_sp = debug_map_symfile->FindDefinitionTypeForDWARFDeclContext(die); + def_die = debug_map_symfile->FindDefinitionDIE(decl_die); } } - if (type_sp) { - if (log) { - dwarf->GetObjectFile()->GetModule()->LogMessage( - log, - "SymbolFileDWARF({0:p}) - {1:x16}}: {2} ({3}) type \"{4}\" is a " - "forward declaration, complete type is {5:x8}", - static_cast<void *>(this), die.GetOffset(), - DW_TAG_value_to_name(tag), tag, attrs.name.GetCString(), - type_sp->GetID()); - } - - // We found a real definition for this type elsewhere so lets use - // it and cache the fact that we found a complete type for this - // die - dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); - clang::DeclContext *defn_decl_ctx = - GetCachedClangDeclContextForDIE(dwarf->GetDIE(type_sp->GetID())); - if (defn_decl_ctx) - LinkDeclContextToDIE(defn_decl_ctx, die); - return type_sp; + if (log) { + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF({0:p}) - {1:x16}}: {2} ({3}) type \"{4}\" is a " + "forward declaration, complete DIE is {5}", + static_cast<void *>(this), decl_die.GetID(), DW_TAG_value_to_name(tag), + tag, attrs.name.GetCString(), + def_die ? llvm::utohexstr(def_die.GetID()) : "not found"); } } - DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(), - DW_TAG_value_to_name(tag), type_name_cstr); + if (def_die) { + attrs = ParsedDWARFTypeAttributes(def_die); + } else { + // No definition found. Proceed with the declaration die. We can use it to + // create a forward-declared type. + def_die = decl_die; + } CompilerType enumerator_clang_type; if (attrs.type.IsValid()) { - Type *enumerator_type = dwarf->ResolveTypeUID(attrs.type.Reference(), true); + Type *enumerator_type = + dwarf->ResolveTypeUID(attrs.type.Reference(), true); if (enumerator_type) enumerator_clang_type = enumerator_type->GetFullCompilerType(); } @@ -915,24 +895,31 @@ TypeSP DWARFASTParserClang::ParseEnum(const SymbolContext &sc, } CompilerType clang_type = m_ast.CreateEnumerationType( - attrs.name.GetStringRef(), GetClangDeclContextContainingDIE(die, nullptr), - GetOwningClangModule(die), attrs.decl, enumerator_clang_type, + attrs.name.GetStringRef(), GetClangDeclContextContainingDIE(def_die, nullptr), + GetOwningClangModule(def_die), attrs.decl, enumerator_clang_type, attrs.is_scoped_enum); - - LinkDeclContextToDIE(TypeSystemClang::GetDeclContextForType(clang_type), die); - - type_sp = - dwarf->MakeType(die.GetID(), attrs.name, attrs.byte_size, nullptr, + TypeSP type_sp = + dwarf->MakeType(def_die.GetID(), attrs.name, attrs.byte_size, nullptr, attrs.type.Reference().GetID(), Type::eEncodingIsUID, &attrs.decl, clang_type, Type::ResolveState::Forward, - TypePayloadClang(GetOwningClangModule(die))); + TypePayloadClang(GetOwningClangModule(def_die))); + + clang::DeclContext *type_decl_ctx = + TypeSystemClang::GetDeclContextForType(clang_type); + LinkDeclContextToDIE(type_decl_ctx, decl_die); + if (decl_die != def_die) { + LinkDeclContextToDIE(type_decl_ctx, def_die); + dwarf->GetDIEToType()[def_die.GetDIE()] = type_sp.get(); + // Declaration DIE is inserted into the type map in ParseTypeFromDWARF + } + if (TypeSystemClang::StartTagDeclarationDefinition(clang_type)) { - if (die.HasChildren()) { + if (def_die.HasChildren()) { bool is_signed = false; enumerator_clang_type.IsIntegerType(is_signed); ParseChildEnumerators(clang_type, is_signed, - type_sp->GetByteSize(nullptr).value_or(0), die); + type_sp->GetByteSize(nullptr).value_or(0), def_die); } TypeSystemClang::CompleteTagDeclarationDefinition(clang_type); } else { @@ -940,7 +927,7 @@ TypeSP DWARFASTParserClang::ParseEnum(const SymbolContext &sc, "DWARF DIE at {0:x16} named \"{1}\" was not able to start its " "definition.\nPlease file a bug and attach the file at the " "start of this error message", - die.GetOffset(), attrs.name.GetCString()); + def_die.GetOffset(), attrs.name.GetCString()); } return type_sp; } @@ -975,6 +962,216 @@ ConvertDWARFCallingConventionToClang(const ParsedDWARFTypeAttributes &attrs) { return clang::CC_C; } +bool DWARFASTParserClang::ParseObjCMethod( + const ObjCLanguage::MethodName &objc_method, const DWARFDIE &die, + CompilerType clang_type, const ParsedDWARFTypeAttributes &attrs, + bool is_variadic) { + SymbolFileDWARF *dwarf = die.GetDWARF(); + assert(dwarf); + + const auto tag = die.Tag(); + ConstString class_name(objc_method.GetClassName()); + if (!class_name) + return false; + + TypeSP complete_objc_class_type_sp = + dwarf->FindCompleteObjCDefinitionTypeForDIE(DWARFDIE(), class_name, + false); + + if (!complete_objc_class_type_sp) + return false; + + CompilerType type_clang_forward_type = + complete_objc_class_type_sp->GetForwardCompilerType(); + + if (!type_clang_forward_type) + return false; + + if (!TypeSystemClang::IsObjCObjectOrInterfaceType(type_clang_forward_type)) + return false; + + clang::ObjCMethodDecl *objc_method_decl = m_ast.AddMethodToObjCObjectType( + type_clang_forward_type, attrs.name.GetCString(), clang_type, + attrs.is_artificial, is_variadic, attrs.is_objc_direct_call); + + if (!objc_method_decl) { + dwarf->GetObjectFile()->GetModule()->ReportError( + "[{0:x16}]: invalid Objective-C method {1:x4} ({2}), " + "please file a bug and attach the file at the start of " + "this error message", + die.GetOffset(), tag, DW_TAG_value_to_name(tag)); + return false; + } + + LinkDeclContextToDIE(objc_method_decl, die); + m_ast.SetMetadataAsUserID(objc_method_decl, die.GetID()); + + return true; +} + +std::pair<bool, TypeSP> DWARFASTParserClang::ParseCXXMethod( + const DWARFDIE &die, CompilerType clang_type, + const ParsedDWARFTypeAttributes &attrs, const DWARFDIE &decl_ctx_die, + bool is_static, bool &ignore_containing_context) { + Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); + SymbolFileDWARF *dwarf = die.GetDWARF(); + assert(dwarf); + + Type *class_type = dwarf->ResolveType(decl_ctx_die); + if (!class_type) + return {}; + + if (class_type->GetID() != decl_ctx_die.GetID() || + IsClangModuleFwdDecl(decl_ctx_die)) { + + // We uniqued the parent class of this function to another + // class so we now need to associate all dies under + // "decl_ctx_die" to DIEs in the DIE for "class_type"... + if (DWARFDIE class_type_die = dwarf->GetDIE(class_type->GetID())) { + std::vector<DWARFDIE> failures; + + CopyUniqueClassMethodTypes(decl_ctx_die, class_type_die, class_type, + failures); + + // FIXME do something with these failures that's + // smarter than just dropping them on the ground. + // Unfortunately classes don't like having stuff added + // to them after their definitions are complete... + + Type *type_ptr = dwarf->GetDIEToType().lookup(die.GetDIE()); + if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) + return {true, type_ptr->shared_from_this()}; + } + } + + if (attrs.specification.IsValid()) { + // We have a specification which we are going to base our + // function prototype off of, so we need this type to be + // completed so that the m_die_to_decl_ctx for the method in + // the specification has a valid clang decl context. + class_type->GetForwardCompilerType(); + // If we have a specification, then the function type should + // have been made with the specification and not with this + // die. + DWARFDIE spec_die = attrs.specification.Reference(); + clang::DeclContext *spec_clang_decl_ctx = + GetClangDeclContextForDIE(spec_die); + if (spec_clang_decl_ctx) + LinkDeclContextToDIE(spec_clang_decl_ctx, die); + else + dwarf->GetObjectFile()->GetModule()->ReportWarning( + "{0:x8}: DW_AT_specification({1:x16}" + ") has no decl\n", + die.GetID(), spec_die.GetOffset()); + + return {true, nullptr}; + } + + if (attrs.abstract_origin.IsValid()) { + // We have a specification which we are going to base our + // function prototype off of, so we need this type to be + // completed so that the m_die_to_decl_ctx for the method in + // the abstract origin has a valid clang decl context. + class_type->GetForwardCompilerType(); + + DWARFDIE abs_die = attrs.abstract_origin.Reference(); + clang::DeclContext *abs_clang_decl_ctx = GetClangDeclContextForDIE(abs_die); + if (abs_clang_decl_ctx) + LinkDeclContextToDIE(abs_clang_decl_ctx, die); + else + dwarf->GetObjectFile()->GetModule()->ReportWarning( + "{0:x8}: DW_AT_abstract_origin({1:x16}" + ") has no decl\n", + die.GetID(), abs_die.GetOffset()); + + return {true, nullptr}; + } + + CompilerType class_opaque_type = class_type->GetForwardCompilerType(); + if (!TypeSystemClang::IsCXXClassType(class_opaque_type)) + return {}; + + if (class_opaque_type.IsBeingDefined()) { + // We have a C++ member function with no children (this + // pointer!) and clang will get mad if we try and make + // a function that isn't well formed in the DWARF, so + // we will just skip it... + if (!is_static && !die.HasChildren()) + return {true, nullptr}; + + const bool is_attr_used = false; + // Neither GCC 4.2 nor clang++ currently set a valid + // accessibility in the DWARF for C++ methods... + // Default to public for now... + const auto accessibility = attrs.accessibility == eAccessNone + ? eAccessPublic + : attrs.accessibility; + + clang::CXXMethodDecl *cxx_method_decl = m_ast.AddMethodToCXXRecordType( + class_opaque_type.GetOpaqueQualType(), attrs.name.GetCString(), + attrs.mangled_name, clang_type, accessibility, attrs.is_virtual, + is_static, attrs.is_inline, attrs.is_explicit, is_attr_used, + attrs.is_artificial); + + if (cxx_method_decl) { + LinkDeclContextToDIE(cxx_method_decl, die); + + ClangASTMetadata metadata; + metadata.SetUserID(die.GetID()); + + char const *object_pointer_name = + attrs.object_pointer ? attrs.object_pointer.GetName() : nullptr; + if (object_pointer_name) { + metadata.SetObjectPtrName(object_pointer_name); + LLDB_LOGF(log, + "Setting object pointer name: %s on method " + "object %p.\n", + object_pointer_name, static_cast<void *>(cxx_method_decl)); + } + m_ast.SetMetadata(cxx_method_decl, metadata); + } else { + ignore_containing_context = true; + } + + // Artificial methods are always handled even when we + // don't create a new declaration for them. + const bool type_handled = cxx_method_decl != nullptr || attrs.is_artificial; + + return {type_handled, nullptr}; + } + + // We were asked to parse the type for a method in a + // class, yet the class hasn't been asked to complete + // itself through the clang::ExternalASTSource protocol, + // so we need to just have the class complete itself and + // do things the right way, then our + // DIE should then have an entry in the + // dwarf->GetDIEToType() map. First + // we need to modify the dwarf->GetDIEToType() so it + // doesn't think we are trying to parse this DIE + // anymore... + dwarf->GetDIEToType().erase(die.GetDIE()); + + // Now we get the full type to force our class type to + // complete itself using the clang::ExternalASTSource + // protocol which will parse all base classes and all + // methods (including the method for this DIE). + class_type->GetFullCompilerType(); + + // The type for this DIE should have been filled in the + // function call above. + Type *type_ptr = dwarf->GetDIEToType().lookup(die.GetDIE()); + if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) + return {true, type_ptr->shared_from_this()}; + + // The previous comment isn't actually true if the class wasn't + // resolved using the current method's parent DIE as source + // data. We need to ensure that we look up the method correctly + // in the class and then link the method's DIE to the unique + // CXXMethodDecl appropriately. + return {true, nullptr}; +} + TypeSP DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, const ParsedDWARFTypeAttributes &attrs) { @@ -989,13 +1186,6 @@ DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, unsigned type_quals = 0; - std::string object_pointer_name; - if (attrs.object_pointer) { - const char *object_pointer_name_cstr = attrs.object_pointer.GetName(); - if (object_pointer_name_cstr) - object_pointer_name = object_pointer_name_cstr; - } - DEBUG_PRINTF("0x%8.8" PRIx64 ": %s (\"%s\")\n", die.GetID(), DW_TAG_value_to_name(tag), type_name_cstr); @@ -1066,208 +1256,19 @@ DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, if (attrs.name) { bool type_handled = false; if (tag == DW_TAG_subprogram || tag == DW_TAG_inlined_subroutine) { - std::optional<const ObjCLanguage::MethodName> objc_method = - ObjCLanguage::MethodName::Create(attrs.name.GetStringRef(), true); - if (objc_method) { - CompilerType class_opaque_type; - ConstString class_name(objc_method->GetClassName()); - if (class_name) { - TypeSP complete_objc_class_type_sp( - dwarf->FindCompleteObjCDefinitionTypeForDIE(DWARFDIE(), - class_name, false)); - - if (complete_objc_class_type_sp) { - CompilerType type_clang_forward_type = - complete_objc_class_type_sp->GetForwardCompilerType(); - if (TypeSystemClang::IsObjCObjectOrInterfaceType( - type_clang_forward_type)) - class_opaque_type = type_clang_forward_type; - } - } - - if (class_opaque_type) { - clang::ObjCMethodDecl *objc_method_decl = - m_ast.AddMethodToObjCObjectType( - class_opaque_type, attrs.name.GetCString(), clang_type, - attrs.is_artificial, is_variadic, attrs.is_objc_direct_call); - type_handled = objc_method_decl != nullptr; - if (type_handled) { - LinkDeclContextToDIE(objc_method_decl, die); - m_ast.SetMetadataAsUserID(objc_method_decl, die.GetID()); - } else { - dwarf->GetObjectFile()->GetModule()->ReportError( - "[{0:x16}]: invalid Objective-C method {1:x4} ({2}), " - "please file a bug and attach the file at the start of " - "this error message", - die.GetOffset(), tag, DW_TAG_value_to_name(tag)); - } - } + if (std::optional<const ObjCLanguage::MethodName> objc_method = + ObjCLanguage::MethodName::Create(attrs.name.GetStringRef(), + true)) { + type_handled = + ParseObjCMethod(*objc_method, die, clang_type, attrs, is_variadic); } else if (is_cxx_method) { - // Look at the parent of this DIE and see if it is a class or - // struct and see if this is actually a C++ method - Type *class_type = dwarf->ResolveType(decl_ctx_die); - if (class_type) { - if (class_type->GetID() != decl_ctx_die.GetID() || - IsClangModuleFwdDecl(decl_ctx_die)) { - - // We uniqued the parent class of this function to another - // class so we now need to associate all dies under - // "decl_ctx_die" to DIEs in the DIE for "class_type"... - DWARFDIE class_type_die = dwarf->GetDIE(class_type->GetID()); - - if (class_type_die) { - std::vector<DWARFDIE> failures; - - CopyUniqueClassMethodTypes(decl_ctx_die, class_type_die, - class_type, failures); - - // FIXME do something with these failures that's - // smarter than just dropping them on the ground. - // Unfortunately classes don't like having stuff added - // to them after their definitions are complete... - - Type *type_ptr = dwarf->GetDIEToType()[die.GetDIE()]; - if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) { - return type_ptr->shared_from_this(); - } - } - } + auto [handled, type_sp] = + ParseCXXMethod(die, clang_type, attrs, decl_ctx_die, is_static, + ignore_containing_context); + if (type_sp) + return type_sp; - if (attrs.specification.IsValid()) { - // We have a specification which we are going to base our - // function prototype off of, so we need this type to be - // completed so that the m_die_to_decl_ctx for the method in - // the specification has a valid clang decl context. - class_type->GetForwardCompilerType(); - // If we have a specification, then the function type should - // have been made with the specification and not with this - // die. - DWARFDIE spec_die = attrs.specification.Reference(); - clang::DeclContext *spec_clang_decl_ctx = - GetClangDeclContextForDIE(spec_die); - if (spec_clang_decl_ctx) { - LinkDeclContextToDIE(spec_clang_decl_ctx, die); - } else { - dwarf->GetObjectFile()->GetModule()->ReportWarning( - "{0:x8}: DW_AT_specification({1:x16}" - ") has no decl\n", - die.GetID(), spec_die.GetOffset()); - } - type_handled = true; - } else if (attrs.abstract_origin.IsValid()) { - // We have a specification which we are going to base our - // function prototype off of, so we need this type to be - // completed so that the m_die_to_decl_ctx for the method in - // the abstract origin has a valid clang decl context. - class_type->GetForwardCompilerType(); - - DWARFDIE abs_die = attrs.abstract_origin.Reference(); - clang::DeclContext *abs_clang_decl_ctx = - GetClangDeclContextForDIE(abs_die); - if (abs_clang_decl_ctx) { - LinkDeclContextToDIE(abs_clang_decl_ctx, die); - } else { - dwarf->GetObjectFile()->GetModule()->ReportWarning( - "{0:x8}: DW_AT_abstract_origin({1:x16}" - ") has no decl\n", - die.GetID(), abs_die.GetOffset()); - } - type_handled = true; - } else { - CompilerType class_opaque_type = - class_type->GetForwardCompilerType(); - if (TypeSystemClang::IsCXXClassType(class_opaque_type)) { - if (class_opaque_type.IsBeingDefined()) { - if (!is_static && !die.HasChildren()) { - // We have a C++ member function with no children (this - // pointer!) and clang will get mad if we try and make - // a function that isn't well formed in the DWARF, so - // we will just skip it... - type_handled = true; - } else { - llvm::PrettyStackTraceFormat stack_trace( - "SymbolFileDWARF::ParseType() is adding a method " - "%s to class %s in DIE 0x%8.8" PRIx64 " from %s", - attrs.name.GetCString(), - class_type->GetName().GetCString(), die.GetID(), - dwarf->GetObjectFile()->GetFileSpec().GetPath().c_str()); - - const bool is_attr_used = false; - // Neither GCC 4.2 nor clang++ currently set a valid - // accessibility in the DWARF for C++ methods... - // Default to public for now... - const auto accessibility = attrs.accessibility == eAccessNone - ? eAccessPublic - : attrs.accessibility; - - clang::CXXMethodDecl *cxx_method_decl = - m_ast.AddMethodToCXXRecordType( - class_opaque_type.GetOpaqueQualType(), - attrs.name.GetCString(), attrs.mangled_name, - clang_type, accessibility, attrs.is_virtual, - is_static, attrs.is_inline, attrs.is_explicit, - is_attr_used, attrs.is_artificial); - - type_handled = cxx_method_decl != nullptr; - // Artificial methods are always handled even when we - // don't create a new declaration for them. - type_handled |= attrs.is_artificial; - - if (cxx_method_decl) { - LinkDeclContextToDIE(cxx_method_decl, die); - - ClangASTMetadata metadata; - metadata.SetUserID(die.GetID()); - - if (!object_pointer_name.empty()) { - metadata.SetObjectPtrName(object_pointer_name.c_str()); - LLDB_LOGF(log, - "Setting object pointer name: %s on method " - "object %p.\n", - object_pointer_name.c_str(), - static_cast<void *>(cxx_method_decl)); - } - m_ast.SetMetadata(cxx_method_decl, metadata); - } else { - ignore_containing_context = true; - } - } - } else { - // We were asked to parse the type for a method in a - // class, yet the class hasn't been asked to complete - // itself through the clang::ExternalASTSource protocol, - // so we need to just have the class complete itself and - // do things the right way, then our - // DIE should then have an entry in the - // dwarf->GetDIEToType() map. First - // we need to modify the dwarf->GetDIEToType() so it - // doesn't think we are trying to parse this DIE - // anymore... - dwarf->GetDIEToType()[die.GetDIE()] = NULL; - - // Now we get the full type to force our class type to - // complete itself using the clang::ExternalASTSource - // protocol which will parse all base classes and all - // methods (including the method for this DIE). - class_type->GetFullCompilerType(); - - // The type for this DIE should have been filled in the - // function call above. - Type *type_ptr = dwarf->GetDIEToType()[die.GetDIE()]; - if (type_ptr && type_ptr != DIE_IS_BEING_PARSED) { - return type_ptr->shared_from_this(); - } - - // The previous comment isn't actually true if the class wasn't - // resolved using the current method's parent DIE as source - // data. We need to ensure that we look up the method correctly - // in the class and then link the method's DIE to the unique - // CXXMethodDecl appropriately. - type_handled = true; - } - } - } - } + type_handled = handled; } } @@ -1356,13 +1357,14 @@ DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, ClangASTMetadata metadata; metadata.SetUserID(die.GetID()); - if (!object_pointer_name.empty()) { - metadata.SetObjectPtrName(object_pointer_name.c_str()); + char const *object_pointer_name = + attrs.object_pointer ? attrs.object_pointer.GetName() : nullptr; + if (object_pointer_name) { + metadata.SetObjectPtrName(object_pointer_name); LLDB_LOGF(log, "Setting object pointer name: %s on function " "object %p.", - object_pointer_name.c_str(), - static_cast<void *>(function_decl)); + object_pointer_name, static_cast<void *>(function_decl)); } m_ast.SetMetadata(function_decl, metadata); } @@ -1560,7 +1562,6 @@ TypeSP DWARFASTParserClang::UpdateSymbolContextScopeForType( if (!type_sp) return type_sp; - SymbolFileDWARF *dwarf = die.GetDWARF(); DWARFDIE sc_parent_die = SymbolFileDWARF::GetParentSymbolContextDIE(die); dw_tag_t sc_parent_tag = sc_parent_die.Tag(); @@ -1579,8 +1580,6 @@ TypeSP DWARFASTParserClang::UpdateSymbolContextScopeForType( if (symbol_context_scope != nullptr) type_sp->SetSymbolContextScope(symbol_context_scope); - - dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); return type_sp; } @@ -1616,7 +1615,7 @@ DWARFASTParserClang::GetCPlusPlusQualifiedName(const DWARFDIE &die) { case DW_TAG_union_type: { if (const char *class_union_struct_name = parent_decl_ctx_die.GetName()) { qualified_name.insert( - 0, GetDIEClassTemplateParams(parent_decl_ctx_die).AsCString("")); + 0, GetDIEClassTemplateParams(parent_decl_ctx_die)); qualified_name.insert(0, "::"); qualified_name.insert(0, class_union_struct_name); } @@ -1634,20 +1633,19 @@ DWARFASTParserClang::GetCPlusPlusQualifiedName(const DWARFDIE &die) { qualified_name.append("::"); qualified_name.append(name); - qualified_name.append(GetDIEClassTemplateParams(die).AsCString("")); + qualified_name.append(GetDIEClassTemplateParams(die)); return qualified_name; } TypeSP DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, - const DWARFDIE &die, + const DWARFDIE &decl_die, ParsedDWARFTypeAttributes &attrs) { - TypeSP type_sp; CompilerType clang_type; - const dw_tag_t tag = die.Tag(); - SymbolFileDWARF *dwarf = die.GetDWARF(); - LanguageType cu_language = SymbolFileDWARF::GetLanguage(*die.GetCU()); + const dw_tag_t tag = decl_die.Tag(); + SymbolFileDWARF *dwarf = decl_die.GetDWARF(); + LanguageType cu_language = SymbolFileDWARF::GetLanguage(*decl_die.GetCU()); Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); // UniqueDWARFASTType is large, so don't create a local variables on the @@ -1664,20 +1662,19 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, // 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. - std::string qualified_name = GetCPlusPlusQualifiedName(die); + std::string qualified_name = GetCPlusPlusQualifiedName(decl_die); if (!qualified_name.empty()) unique_typename = ConstString(qualified_name); unique_decl.Clear(); } if (dwarf->GetUniqueDWARFASTTypeMap().Find( - unique_typename, die, unique_decl, attrs.byte_size.value_or(-1), - *unique_ast_entry_up)) { - type_sp = unique_ast_entry_up->m_type_sp; - if (type_sp) { - dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); + unique_typename, decl_die, unique_decl, + attrs.byte_size.value_or(-1), *unique_ast_entry_up)) { + if (TypeSP type_sp = unique_ast_entry_up->m_type_sp) { LinkDeclContextToDIE( - GetCachedClangDeclContextForDIE(unique_ast_entry_up->m_die), die); + GetCachedClangDeclContextForDIE(unique_ast_entry_up->m_die), + decl_die); return type_sp; } } @@ -1700,7 +1697,7 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, } if (attrs.byte_size && *attrs.byte_size == 0 && attrs.name && - !die.HasChildren() && cu_language == eLanguageTypeObjC) { + !decl_die.HasChildren() && cu_language == eLanguageTypeObjC) { // Work around an issue with clang at the moment where forward // declarations for objective C classes are emitted as: // DW_TAG_structure_type [2] @@ -1717,14 +1714,14 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, if (attrs.class_language == eLanguageTypeObjC || attrs.class_language == eLanguageTypeObjC_plus_plus) { if (!attrs.is_complete_objc_class && - die.Supports_DW_AT_APPLE_objc_complete_type()) { + decl_die.Supports_DW_AT_APPLE_objc_complete_type()) { // We have a valid eSymbolTypeObjCClass class symbol whose name // matches the current objective C class that we are trying to find // and this DIE isn't the complete definition (we checked // is_complete_objc_class above and know it is false), so the real // definition is in here somewhere - type_sp = - dwarf->FindCompleteObjCDefinitionTypeForDIE(die, attrs.name, true); + TypeSP type_sp = + dwarf->FindCompleteObjCDefinitionTypeForDIE(decl_die, attrs.name, true); if (!type_sp) { SymbolFileDWARFDebugMap *debug_map_symfile = @@ -1733,7 +1730,7 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, // We weren't able to find a full declaration in this DWARF, // see if we have a declaration anywhere else... type_sp = debug_map_symfile->FindCompleteObjCDefinitionTypeForDIE( - die, attrs.name, true); + decl_die, attrs.name, true); } } @@ -1743,21 +1740,22 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, log, "SymbolFileDWARF({0:p}) - {1:x16}: {2} ({3}) type \"{4}\" is an " "incomplete objc type, complete type is {5:x8}", - static_cast<void *>(this), die.GetOffset(), + static_cast<void *>(this), decl_die.GetOffset(), DW_TAG_value_to_name(tag), tag, attrs.name.GetCString(), type_sp->GetID()); } - - // We found a real definition for this type elsewhere so lets use - // it and cache the fact that we found a complete type for this - // die - dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); return type_sp; } } } + DWARFDIE def_die; if (attrs.is_forward_declaration) { + Progress progress(llvm::formatv( + "Parsing type in {0}: '{1}'", + dwarf->GetObjectFile()->GetFileSpec().GetFilename().GetString(), + attrs.name.GetString())); + // We have a forward declaration to a type and we need to try and // find a full declaration. We look in the current type index just in // case we have a forward declaration followed by an actual @@ -1768,83 +1766,80 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, log, "SymbolFileDWARF({0:p}) - {1:x16}: {2} ({3}) type \"{4}\" is a " "forward declaration, trying to find complete type", - static_cast<void *>(this), die.GetOffset(), DW_TAG_value_to_name(tag), - tag, attrs.name.GetCString()); + static_cast<void *>(this), decl_die.GetID(), + DW_TAG_value_to_name(tag), tag, attrs.name.GetCString()); } // See if the type comes from a Clang module and if so, track down // that type. - type_sp = ParseTypeFromClangModule(sc, die, log); - if (type_sp) + if (TypeSP type_sp = ParseTypeFromClangModule(sc, decl_die, log)) return type_sp; - // type_sp = FindDefinitionTypeForDIE (dwarf_cu, die, - // type_name_const_str); - type_sp = dwarf->FindDefinitionTypeForDWARFDeclContext(die); + def_die = dwarf->FindDefinitionDIE(decl_die); - if (!type_sp) { + if (!def_die) { SymbolFileDWARFDebugMap *debug_map_symfile = dwarf->GetDebugMapSymfile(); if (debug_map_symfile) { // We weren't able to find a full declaration in this DWARF, see // if we have a declaration anywhere else... - type_sp = debug_map_symfile->FindDefinitionTypeForDWARFDeclContext(die); + def_die = debug_map_symfile->FindDefinitionDIE(decl_die); } } - if (type_sp) { - if (log) { - dwarf->GetObjectFile()->GetModule()->LogMessage( - log, - "SymbolFileDWARF({0:p}) - {1:x16}: {2} ({3}) type \"{4}\" is a " - "forward declaration, complete type is {5:x8}", - static_cast<void *>(this), die.GetOffset(), - DW_TAG_value_to_name(tag), tag, attrs.name.GetCString(), - type_sp->GetID()); - } - - // We found a real definition for this type elsewhere so lets use - // it and cache the fact that we found a complete type for this die - dwarf->GetDIEToType()[die.GetDIE()] = type_sp.get(); - clang::DeclContext *defn_decl_ctx = - GetCachedClangDeclContextForDIE(dwarf->GetDIE(type_sp->GetID())); - if (defn_decl_ctx) - LinkDeclContextToDIE(defn_decl_ctx, die); - return type_sp; + if (log) { + dwarf->GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF({0:p}) - {1:x16}: {2} ({3}) type \"{4}\" is a " + "forward declaration, complete type is {5}", + static_cast<void *>(this), def_die.GetID(), DW_TAG_value_to_name(tag), + tag, attrs.name.GetCString(), + def_die ? llvm::utohexstr(def_die.GetID()) : "not found"); } } + + if (def_die) { + attrs = ParsedDWARFTypeAttributes(def_die); + } else { + // No definition found. Proceed with the declaration die. We can use it to + // create a forward-declared type. + def_die = decl_die; + } assert(tag_decl_kind != -1); UNUSED_IF_ASSERT_DISABLED(tag_decl_kind); bool clang_type_was_created = false; - clang::DeclContext *decl_ctx = GetClangDeclContextContainingDIE(die, nullptr); + clang::DeclContext *containing_decl_ctx = GetClangDeclContextContainingDIE(def_die, nullptr); - PrepareContextToReceiveMembers(m_ast, GetClangASTImporter(), decl_ctx, die, + PrepareContextToReceiveMembers(m_ast, GetClangASTImporter(), + containing_decl_ctx, def_die, attrs.name.GetCString()); - if (attrs.accessibility == eAccessNone && decl_ctx) { + if (attrs.accessibility == eAccessNone && containing_decl_ctx) { // Check the decl context that contains this class/struct/union. If // it is a class we must give it an accessibility. - const clang::Decl::Kind containing_decl_kind = decl_ctx->getDeclKind(); + const clang::Decl::Kind containing_decl_kind = + containing_decl_ctx->getDeclKind(); if (DeclKindIsCXXClass(containing_decl_kind)) attrs.accessibility = default_accessibility; } ClangASTMetadata metadata; - metadata.SetUserID(die.GetID()); - metadata.SetIsDynamicCXXType(dwarf->ClassOrStructIsVirtual(die)); + metadata.SetUserID(def_die.GetID()); + metadata.SetIsDynamicCXXType(dwarf->ClassOrStructIsVirtual(def_die)); TypeSystemClang::TemplateParameterInfos template_param_infos; - if (ParseTemplateParameterInfos(die, template_param_infos)) { + if (ParseTemplateParameterInfos(def_die, template_param_infos)) { clang::ClassTemplateDecl *class_template_decl = m_ast.ParseClassTemplateDecl( - decl_ctx, GetOwningClangModule(die), attrs.accessibility, - attrs.name.GetCString(), tag_decl_kind, template_param_infos); + containing_decl_ctx, GetOwningClangModule(def_die), + attrs.accessibility, attrs.name.GetCString(), tag_decl_kind, + template_param_infos); if (!class_template_decl) { if (log) { dwarf->GetObjectFile()->GetModule()->LogMessage( log, "SymbolFileDWARF({0:p}) - {1:x16}: {2} ({3}) type \"{4}\" " "clang::ClassTemplateDecl failed to return a decl.", - static_cast<void *>(this), die.GetOffset(), + static_cast<void *>(this), def_die.GetID(), DW_TAG_value_to_name(tag), tag, attrs.name.GetCString()); } return TypeSP(); @@ -1852,8 +1847,8 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, clang::ClassTemplateSpecializationDecl *class_specialization_decl = m_ast.CreateClassTemplateSpecializationDecl( - decl_ctx, GetOwningClangModule(die), class_template_decl, - tag_decl_kind, template_param_infos); + containing_decl_ctx, GetOwningClangModule(def_die), + class_template_decl, tag_decl_kind, template_param_infos); clang_type = m_ast.CreateClassTemplateSpecializationType(class_specialization_decl); clang_type_was_created = true; @@ -1865,26 +1860,34 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, if (!clang_type_was_created) { clang_type_was_created = true; clang_type = m_ast.CreateRecordType( - decl_ctx, GetOwningClangModule(die), attrs.accessibility, + containing_decl_ctx, GetOwningClangModule(def_die), attrs.accessibility, attrs.name.GetCString(), tag_decl_kind, attrs.class_language, &metadata, attrs.exports_symbols); } - // Store a forward declaration to this class type in case any - // parameters in any class methods need it for the clang types for - // function prototypes. - LinkDeclContextToDIE(m_ast.GetDeclContextForType(clang_type), die); - type_sp = dwarf->MakeType( - die.GetID(), attrs.name, attrs.byte_size, nullptr, LLDB_INVALID_UID, + TypeSP type_sp = dwarf->MakeType( + def_die.GetID(), attrs.name, attrs.byte_size, nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, &attrs.decl, clang_type, Type::ResolveState::Forward, TypePayloadClang(OptionalClangModuleID(), attrs.is_complete_objc_class)); + // Store a forward declaration to this class type in case any + // parameters in any class methods need it for the clang types for + // function prototypes. + clang::DeclContext *type_decl_ctx = + TypeSystemClang::GetDeclContextForType(clang_type); + LinkDeclContextToDIE(type_decl_ctx, decl_die); + if (decl_die != def_die) { + LinkDeclContextToDIE(type_decl_ctx, def_die); + dwarf->GetDIEToType()[def_die.GetDIE()] = type_sp.get(); + // Declaration DIE is inserted into the type map in ParseTypeFromDWARF + } + // Add our type to the unique type map so we don't end up creating many // copies of the same type over and over in the ASTContext for our // module unique_ast_entry_up->m_type_sp = type_sp; - unique_ast_entry_up->m_die = die; + unique_ast_entry_up->m_die = def_die; unique_ast_entry_up->m_declaration = unique_decl; unique_ast_entry_up->m_byte_size = attrs.byte_size.value_or(0); dwarf->GetUniqueDWARFASTTypeMap().Insert(unique_typename, @@ -1895,18 +1898,17 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, // has child classes or types that require the class to be created // for use as their decl contexts the class will be ready to accept // these child definitions. - if (!die.HasChildren()) { + if (!def_die.HasChildren()) { // No children for this struct/union/class, lets finish it if (TypeSystemClang::StartTagDeclarationDefinition(clang_type)) { TypeSystemClang::CompleteTagDeclarationDefinition(clang_type); } else { dwarf->GetObjectFile()->GetModule()->ReportError( - "DWARF DIE at {0:x16} named \"{1}\" was not able to start " - "its " + "DWARF DIE {0:x16} named \"{1}\" was not able to start its " "definition.\nPlease file a bug and attach the file at the " "start of this error message", - die.GetOffset(), attrs.name.GetCString()); + def_die.GetID(), attrs.name.GetCString()); } // Setting authority byte size and alignment for empty structures. @@ -1954,7 +1956,7 @@ DWARFASTParserClang::ParseStructureLikeDIE(const SymbolContext &sc, // binaries. dwarf->GetForwardDeclCompilerTypeToDIE().try_emplace( ClangUtil::RemoveFastQualifiers(clang_type).GetOpaqueQualType(), - *die.GetDIERef()); + *def_die.GetDIERef()); m_ast.SetHasExternalStorage(clang_type.GetOpaqueQualType(), true); } } @@ -2440,7 +2442,7 @@ DWARFASTParserClang::ConstructDemangledNameFromDWARF(const DWARFDIE &die) { std::vector<clang::ParmVarDecl *> param_decls; StreamString sstr; - DWARFDeclContext decl_ctx = SymbolFileDWARF::GetDWARFDeclContext(die); + DWARFDeclContext decl_ctx = die.GetDWARFDeclContext(); sstr << decl_ctx.GetQualifiedName(); clang::DeclContext *containing_decl_ctx = @@ -3788,7 +3790,7 @@ bool DWARFASTParserClang::CopyUniqueClassMethodTypes( if (dst_decl_ctx) src_dwarf_ast_parser->LinkDeclContextToDIE(dst_decl_ctx, src); - if (Type *src_child_type = die_to_type[src.GetDIE()]) + if (Type *src_child_type = die_to_type.lookup(src.GetDIE())) die_to_type[dst.GetDIE()] = src_child_type; }; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h index 8d4af203bb28..7b5ddbaa2a6b 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h @@ -23,6 +23,7 @@ #include "lldb/Core/PluginInterface.h" #include "Plugins/ExpressionParser/Clang/ClangASTImporter.h" +#include "Plugins/Language/ObjC/ObjCLanguage.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include <optional> @@ -103,9 +104,9 @@ public: /// /// \param die The struct/class DWARFDIE containing template parameters. /// \return A string, including surrounding '<>', of the template parameters. - /// If the DIE's name already has '<>', returns an empty ConstString because + /// If the DIE's name already has '<>', returns an empty string because /// it's assumed that the caller is using the DIE name anyway. - lldb_private::ConstString GetDIEClassTemplateParams( + std::string GetDIEClassTemplateParams( const lldb_private::plugin::dwarf::DWARFDIE &die) override; protected: @@ -370,6 +371,56 @@ private: ParsedDWARFTypeAttributes &attrs); lldb::TypeSP ParseSubroutine(const lldb_private::plugin::dwarf::DWARFDIE &die, const ParsedDWARFTypeAttributes &attrs); + + /// Helper function called by \ref ParseSubroutine when parsing ObjC-methods. + /// + /// \param[in] objc_method Name of the ObjC method being parsed. + /// + /// \param[in] die The DIE that represents the ObjC method being parsed. + /// + /// \param[in] clang_type The CompilerType representing the function prototype + /// of the ObjC method being parsed. + /// + /// \param[in] attrs DWARF attributes for \ref die. + /// + /// \param[in] is_variadic Is true iff we're parsing a variadic method. + /// + /// \returns true on success + bool + ParseObjCMethod(const lldb_private::ObjCLanguage::MethodName &objc_method, + const lldb_private::plugin::dwarf::DWARFDIE &die, + lldb_private::CompilerType clang_type, + const ParsedDWARFTypeAttributes &attrs, bool is_variadic); + + /// Helper function called by \ref ParseSubroutine when parsing C++ methods. + /// + /// \param[in] die The DIE that represents the C++ method being parsed. + /// + /// \param[in] clang_type The CompilerType representing the function prototype + /// of the C++ method being parsed. + /// + /// \param[in] attrs DWARF attributes for \ref die. + /// + /// \param[in] decl_ctx_die The DIE representing the DeclContext of the C++ + /// method being parsed. + /// + /// \param[in] is_static Is true iff we're parsing a static method. + /// + /// \param[out] ignore_containing_context Will get set to true if the caller + /// should treat this C++ method as-if it was not a C++ method. + /// Currently used as a hack to work around templated C++ methods + /// causing class definitions to mismatch between CUs. + /// + /// \returns A pair of <bool, TypeSP>. The first element is 'true' on success. + /// The second element is non-null if we have previously parsed this + /// method (a null TypeSP does not indicate failure). + std::pair<bool, lldb::TypeSP> + ParseCXXMethod(const lldb_private::plugin::dwarf::DWARFDIE &die, + lldb_private::CompilerType clang_type, + const ParsedDWARFTypeAttributes &attrs, + const lldb_private::plugin::dwarf::DWARFDIE &decl_ctx_die, + bool is_static, bool &ignore_containing_context); + lldb::TypeSP ParseArrayType(const lldb_private::plugin::dwarf::DWARFDIE &die, const ParsedDWARFTypeAttributes &attrs); lldb::TypeSP diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp index 7cf92adc6ef5..fb32e2adeb3f 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp @@ -367,20 +367,6 @@ lldb_private::Type *DWARFDIE::ResolveTypeUID(const DWARFDIE &die) const { return nullptr; } -std::vector<DWARFDIE> DWARFDIE::GetDeclContextDIEs() const { - if (!IsValid()) - return {}; - - std::vector<DWARFDIE> result; - DWARFDIE parent = GetParentDeclContextDIE(); - while (parent.IsValid() && parent.GetDIE() != GetDIE()) { - result.push_back(std::move(parent)); - parent = parent.GetParentDeclContextDIE(); - } - - return result; -} - static void GetDeclContextImpl(DWARFDIE die, llvm::SmallSet<lldb::user_id_t, 4> &seen, std::vector<CompilerContext> &context) { @@ -408,15 +394,13 @@ static void GetDeclContextImpl(DWARFDIE die, case DW_TAG_namespace: push_ctx(CompilerContextKind::Namespace, die.GetName()); break; + case DW_TAG_class_type: case DW_TAG_structure_type: - push_ctx(CompilerContextKind::Struct, die.GetName()); + push_ctx(CompilerContextKind::ClassOrStruct, die.GetName()); break; case DW_TAG_union_type: push_ctx(CompilerContextKind::Union, die.GetName()); break; - case DW_TAG_class_type: - push_ctx(CompilerContextKind::Class, die.GetName()); - break; case DW_TAG_enumeration_type: push_ctx(CompilerContextKind::Enum, die.GetName()); break; @@ -470,15 +454,13 @@ static void GetTypeLookupContextImpl(DWARFDIE die, case DW_TAG_namespace: push_ctx(CompilerContextKind::Namespace, die.GetName()); break; + case DW_TAG_class_type: case DW_TAG_structure_type: - push_ctx(CompilerContextKind::Struct, die.GetName()); + push_ctx(CompilerContextKind::ClassOrStruct, die.GetName()); break; case DW_TAG_union_type: push_ctx(CompilerContextKind::Union, die.GetName()); break; - case DW_TAG_class_type: - push_ctx(CompilerContextKind::Class, die.GetName()); - break; case DW_TAG_enumeration_type: push_ctx(CompilerContextKind::Enum, die.GetName()); break; @@ -491,6 +473,18 @@ static void GetTypeLookupContextImpl(DWARFDIE die, case DW_TAG_base_type: push_ctx(CompilerContextKind::Builtin, name); break; + // If any of the tags below appear in the parent chain, stop the decl + // context and return. Prior to these being in here, if a type existed in a + // namespace "a" like "a::my_struct", but we also have a function in that + // same namespace "a" which contained a type named "my_struct", both would + // return "a::my_struct" as the declaration context since the + // DW_TAG_subprogram would be skipped and its parent would be found. + case DW_TAG_compile_unit: + case DW_TAG_type_unit: + case DW_TAG_subprogram: + case DW_TAG_lexical_block: + case DW_TAG_inlined_subroutine: + return; default: break; } @@ -507,12 +501,64 @@ std::vector<CompilerContext> DWARFDIE::GetTypeLookupContext() const { return context; } +static DWARFDeclContext GetDWARFDeclContextImpl(DWARFDIE die) { + DWARFDeclContext dwarf_decl_ctx; + while (die) { + const dw_tag_t tag = die.Tag(); + if (tag == DW_TAG_compile_unit || tag == DW_TAG_partial_unit) + break; + dwarf_decl_ctx.AppendDeclContext(tag, die.GetName()); + DWARFDIE parent_decl_ctx_die = die.GetParentDeclContextDIE(); + if (parent_decl_ctx_die == die) + break; + die = parent_decl_ctx_die; + } + return dwarf_decl_ctx; +} + +DWARFDeclContext DWARFDIE::GetDWARFDeclContext() const { + return GetDWARFDeclContextImpl(*this); +} + +static DWARFDIE GetParentDeclContextDIEImpl(DWARFDIE die) { + DWARFDIE orig_die = die; + while (die) { + // If this is the original DIE that we are searching for a declaration for, + // then don't look in the cache as we don't want our own decl context to be + // our decl context... + if (die != orig_die) { + switch (die.Tag()) { + case DW_TAG_compile_unit: + case DW_TAG_partial_unit: + case DW_TAG_namespace: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_class_type: + return die; + + default: + break; + } + } + + if (DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification)) { + if (DWARFDIE decl_ctx_die = spec_die.GetParentDeclContextDIE()) + return decl_ctx_die; + } + + if (DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin)) { + if (DWARFDIE decl_ctx_die = abs_die.GetParentDeclContextDIE()) + return decl_ctx_die; + } + + die = die.GetParent(); + } + return DWARFDIE(); +} + DWARFDIE DWARFDIE::GetParentDeclContextDIE() const { - if (IsValid()) - return m_die->GetParentDeclContextDIE(m_cu); - else - return DWARFDIE(); + return GetParentDeclContextDIEImpl(*this); } bool DWARFDIE::IsStructUnionOrClass() const { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h index 511ca62d0197..e1318953a384 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.h @@ -69,9 +69,6 @@ public: DWARFDIE GetParentDeclContextDIE() const; - // DeclContext related functions - std::vector<DWARFDIE> GetDeclContextDIEs() const; - /// Return this DIE's decl context as it is needed to look up types /// in Clang modules. This context will include any modules or functions that /// the type is declared in so an exact module match can be efficiently made. @@ -89,6 +86,8 @@ public: /// using a full or partial CompilerContext array. std::vector<CompilerContext> GetTypeLookupContext() const; + DWARFDeclContext GetDWARFDeclContext() const; + // Getting attribute values from the DIE. // // GetAttributeValueAsXXX() functions should only be used if you are diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp index da73891f6665..f383261e8a5f 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp @@ -89,8 +89,7 @@ void DWARFDebugAranges::AppendRange(dw_offset_t offset, dw_addr_t low_pc, } void DWARFDebugAranges::Sort(bool minimize) { - LLDB_SCOPED_TIMERF("%s this = %p", LLVM_PRETTY_FUNCTION, - static_cast<void *>(this)); + LLDB_SCOPED_TIMER(); m_aranges.Sort(); m_aranges.CombineConsecutiveEntriesWithEqualData(); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp index c37cc91e08ed..f7df38d24019 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp @@ -232,6 +232,10 @@ DWARFDebugInfo::GetUnitContainingDIEOffset(DIERef::Section section, return result; } +const std::shared_ptr<SymbolFileDWARFDwo> &DWARFDebugInfo::GetDwpSymbolFile() { + return m_dwarf.GetDwpSymbolFile(); +} + DWARFTypeUnit *DWARFDebugInfo::GetTypeUnitForHash(uint64_t hash) { auto pos = llvm::lower_bound(m_type_hash_to_unit_index, std::make_pair(hash, 0u), llvm::less_first()); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h index 4706b55d38ea..598739bf3cb9 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h @@ -52,6 +52,8 @@ public: const DWARFDebugAranges &GetCompileUnitAranges(); + const std::shared_ptr<SymbolFileDWARFDwo> &GetDwpSymbolFile(); + protected: typedef std::vector<DWARFUnitSP> UnitColl; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp index 688a287a0650..e2660735ea7d 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp @@ -610,79 +610,6 @@ void DWARFDebugInfoEntry::BuildFunctionAddressRangeTable( } } -DWARFDeclContext -DWARFDebugInfoEntry::GetDWARFDeclContextStatic(const DWARFDebugInfoEntry *die, - DWARFUnit *cu) { - DWARFDeclContext dwarf_decl_ctx; - for (;;) { - const dw_tag_t tag = die->Tag(); - if (tag == DW_TAG_compile_unit || tag == DW_TAG_partial_unit) - return dwarf_decl_ctx; - dwarf_decl_ctx.AppendDeclContext(tag, die->GetName(cu)); - DWARFDIE parent_decl_ctx_die = die->GetParentDeclContextDIE(cu); - if (!parent_decl_ctx_die || parent_decl_ctx_die.GetDIE() == die) - return dwarf_decl_ctx; - if (parent_decl_ctx_die.Tag() == DW_TAG_compile_unit || - parent_decl_ctx_die.Tag() == DW_TAG_partial_unit) - return dwarf_decl_ctx; - die = parent_decl_ctx_die.GetDIE(); - cu = parent_decl_ctx_die.GetCU(); - } -} - -DWARFDeclContext DWARFDebugInfoEntry::GetDWARFDeclContext(DWARFUnit *cu) const { - return GetDWARFDeclContextStatic(this, cu); -} - -DWARFDIE -DWARFDebugInfoEntry::GetParentDeclContextDIE(DWARFUnit *cu) const { - DWARFAttributes attributes = GetAttributes(cu, Recurse::yes); - return GetParentDeclContextDIE(cu, attributes); -} - -DWARFDIE -DWARFDebugInfoEntry::GetParentDeclContextDIE( - DWARFUnit *cu, const DWARFAttributes &attributes) const { - DWARFDIE die(cu, const_cast<DWARFDebugInfoEntry *>(this)); - - while (die) { - // If this is the original DIE that we are searching for a declaration for, - // then don't look in the cache as we don't want our own decl context to be - // our decl context... - if (die.GetDIE() != this) { - switch (die.Tag()) { - case DW_TAG_compile_unit: - case DW_TAG_partial_unit: - case DW_TAG_namespace: - case DW_TAG_structure_type: - case DW_TAG_union_type: - case DW_TAG_class_type: - return die; - - default: - break; - } - } - - DWARFDIE spec_die = attributes.FormValueAsReference(DW_AT_specification); - if (spec_die) { - DWARFDIE decl_ctx_die = spec_die.GetParentDeclContextDIE(); - if (decl_ctx_die) - return decl_ctx_die; - } - - DWARFDIE abs_die = attributes.FormValueAsReference(DW_AT_abstract_origin); - if (abs_die) { - DWARFDIE decl_ctx_die = abs_die.GetParentDeclContextDIE(); - if (decl_ctx_die) - return decl_ctx_die; - } - - die = die.GetParent(); - } - return DWARFDIE(); -} - lldb::offset_t DWARFDebugInfoEntry::GetFirstAttributeOffset() const { return GetOffset() + llvm::getULEB128Size(m_abbr_idx); } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h index 6773b00e8206..3816c6500717 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h @@ -157,12 +157,6 @@ public: return HasChildren() ? this + 1 : nullptr; } - DWARFDeclContext GetDWARFDeclContext(DWARFUnit *cu) const; - - DWARFDIE GetParentDeclContextDIE(DWARFUnit *cu) const; - DWARFDIE GetParentDeclContextDIE(DWARFUnit *cu, - const DWARFAttributes &attributes) const; - void SetSiblingIndex(uint32_t idx) { m_sibling_idx = idx; } void SetParentIndex(uint32_t idx) { m_parent_idx = idx; } @@ -172,9 +166,6 @@ public: bool IsGlobalOrStaticScopeVariable() const; protected: - static DWARFDeclContext - GetDWARFDeclContextStatic(const DWARFDebugInfoEntry *die, DWARFUnit *cu); - // Up to 2TB offset within the .debug_info/.debug_types dw_offset_t m_offset : DW_DIE_OFFSET_MAX_BITSIZE; // How many to subtract from "this" to get the parent. If zero this die has no diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp index 44421c0eda3e..f759cb8fae61 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp @@ -12,18 +12,16 @@ using namespace lldb_private::dwarf; using namespace lldb_private::plugin::dwarf; -/// Returns the name of `entry` if it has one, or the appropriate "anonymous -/// {namespace, class, struct, union}". -static const char *GetName(DWARFDeclContext::Entry entry) { - if (entry.name != nullptr) - return entry.name; - if (entry.tag == DW_TAG_namespace) +const char *DWARFDeclContext::Entry::GetName() const { + if (name != nullptr) + return name; + if (tag == DW_TAG_namespace) return "(anonymous namespace)"; - if (entry.tag == DW_TAG_class_type) + if (tag == DW_TAG_class_type) return "(anonymous class)"; - if (entry.tag == DW_TAG_structure_type) + if (tag == DW_TAG_structure_type) return "(anonymous struct)"; - if (entry.tag == DW_TAG_union_type) + if (tag == DW_TAG_union_type) return "(anonymous union)"; return "(anonymous)"; } @@ -46,7 +44,7 @@ const char *DWARFDeclContext::GetQualifiedName() const { llvm::raw_string_ostream string_stream(m_qualified_name); llvm::interleave( llvm::reverse(m_entries), string_stream, - [&](auto entry) { string_stream << GetName(entry); }, "::"); + [&](auto entry) { string_stream << entry.GetName(); }, "::"); } } } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h index 7e6c5f51f4be..b563d1c4417b 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h @@ -9,8 +9,9 @@ #ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDECLCONTEXT_H #define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_DWARFDECLCONTEXT_H -#include "lldb/Utility/ConstString.h" #include "DWARFDefines.h" +#include "lldb/Utility/ConstString.h" +#include "llvm/ADT/StringExtras.h" #include <cassert> #include <string> @@ -38,6 +39,10 @@ public: return false; } + /// Returns the name of this entry if it has one, or the appropriate + /// "anonymous {namespace, class, struct, union}". + const char *GetName() const; + // Test operator explicit operator bool() const { return tag != 0; } @@ -83,6 +88,17 @@ public: m_qualified_name.clear(); } + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const DWARFDeclContext &ctx) { + OS << "DWARFDeclContext{"; + llvm::ListSeparator LS; + for (const Entry &e : ctx.m_entries) { + OS << LS << "{" << DW_TAG_value_to_name(e.tag) << ", " << e.GetName() + << "}"; + } + return OS << "}"; + } + protected: typedef std::vector<Entry> collection; collection m_entries; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp index 30fb5d5ebdb0..eafddbad03f5 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp @@ -121,8 +121,7 @@ void DWARFIndex::GetFullyQualifiedType( bool DWARFIndex::GetFullyQualifiedTypeImpl( const DWARFDeclContext &context, DWARFDIE die, llvm::function_ref<bool(DWARFDIE die)> callback) { - DWARFDeclContext dwarf_decl_ctx = - die.GetDIE()->GetDWARFDeclContext(die.GetCU()); + DWARFDeclContext dwarf_decl_ctx = die.GetDWARFDeclContext(); if (dwarf_decl_ctx == context) return callback(die); return true; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp index 90e42be7202d..7e66b3dccf97 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp @@ -10,6 +10,7 @@ #include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h" #include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h" #include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h" +#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h" #include "lldb/Core/Module.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Stream.h" @@ -34,6 +35,17 @@ DebugNamesDWARFIndex::Create(Module &module, DWARFDataExtractor debug_names, module, std::move(index_up), debug_names, debug_str, dwarf)); } +llvm::DenseSet<uint64_t> +DebugNamesDWARFIndex::GetTypeUnitSignatures(const DebugNames &debug_names) { + llvm::DenseSet<uint64_t> result; + for (const DebugNames::NameIndex &ni : debug_names) { + const uint32_t num_tus = ni.getForeignTUCount(); + for (uint32_t tu = 0; tu < num_tus; ++tu) + result.insert(ni.getForeignTUSignature(tu)); + } + return result; +} + llvm::DenseSet<dw_offset_t> DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) { llvm::DenseSet<dw_offset_t> result; @@ -48,20 +60,80 @@ DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) { return result; } +std::optional<DWARFTypeUnit *> +DebugNamesDWARFIndex::GetForeignTypeUnit(const DebugNames::Entry &entry) const { + std::optional<uint64_t> type_sig = entry.getForeignTUTypeSignature(); + if (!type_sig.has_value()) + return std::nullopt; + + // Ask the entry for the skeleton compile unit offset and fetch the .dwo + // file from it and get the type unit by signature from there. If we find + // the type unit in the .dwo file, we don't need to check that the + // DW_AT_dwo_name matches because each .dwo file can have its own type unit. + std::optional<uint64_t> cu_offset = entry.getRelatedCUOffset(); + if (!cu_offset) + return nullptr; // Return NULL, this is a type unit, but couldn't find it. + + DWARFUnit *cu = + m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *cu_offset); + if (!cu) + return nullptr; // Return NULL, this is a type unit, but couldn't find it. + + auto dwp_sp = m_debug_info.GetDwpSymbolFile(); + if (!dwp_sp) { + // No .dwp file, we need to load the .dwo file. + DWARFUnit &dwo_cu = cu->GetNonSkeletonUnit(); + // We don't need the check if the type unit matches the .dwo file if we have + // a .dwo file (not a .dwp), so we can just return the value here. + if (!dwo_cu.IsDWOUnit()) + return nullptr; // We weren't able to load the .dwo file. + return dwo_cu.GetSymbolFileDWARF().DebugInfo().GetTypeUnitForHash( + *type_sig); + } + // We have a .dwp file, just get the type unit from there. We need to verify + // that the type unit that ended up in the final .dwp file is the right type + // unit. Type units have signatures which are the same across multiple .dwo + // files, but only one of those type units will end up in the .dwp file. The + // contents of type units for the same type can be different in different .dwo + // files, which means the DIE offsets might not be the same between two + // different type units. So we need to determine if this accelerator table + // matches the type unit that ended up in the .dwp file. If it doesn't match, + // then we need to ignore this accelerator table entry as the type unit that + // is in the .dwp file will have its own index. In order to determine if the + // type unit that ended up in a .dwp file matches this DebugNames::Entry, we + // need to find the skeleton compile unit for this entry. + DWARFTypeUnit *foreign_tu = dwp_sp->DebugInfo().GetTypeUnitForHash(*type_sig); + if (!foreign_tu) + return nullptr; // Return NULL, this is a type unit, but couldn't find it. + + DWARFBaseDIE cu_die = cu->GetUnitDIEOnly(); + DWARFBaseDIE tu_die = foreign_tu->GetUnitDIEOnly(); + llvm::StringRef cu_dwo_name = + cu_die.GetAttributeValueAsString(DW_AT_dwo_name, nullptr); + llvm::StringRef tu_dwo_name = + tu_die.GetAttributeValueAsString(DW_AT_dwo_name, nullptr); + if (cu_dwo_name == tu_dwo_name) + return foreign_tu; // We found a match! + return nullptr; // Return NULL, this is a type unit, but couldn't find it. +} + DWARFUnit * DebugNamesDWARFIndex::GetNonSkeletonUnit(const DebugNames::Entry &entry) const { + + if (std::optional<DWARFTypeUnit *> foreign_tu = GetForeignTypeUnit(entry)) + return foreign_tu.value(); + // Look for a DWARF unit offset (CU offset or local TU offset) as they are // both offsets into the .debug_info section. std::optional<uint64_t> unit_offset = entry.getCUOffset(); - if (!unit_offset) { + if (!unit_offset) unit_offset = entry.getLocalTUOffset(); - if (!unit_offset) - return nullptr; + if (unit_offset) { + if (DWARFUnit *cu = m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, + *unit_offset)) + return &cu->GetNonSkeletonUnit(); } - - DWARFUnit *cu = - m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *unit_offset); - return cu ? &cu->GetNonSkeletonUnit() : nullptr; + return nullptr; } DWARFDIE DebugNamesDWARFIndex::GetDIE(const DebugNames::Entry &entry) const { @@ -85,6 +157,11 @@ bool DebugNamesDWARFIndex::ProcessEntry( DWARFDIE die = GetDIE(entry); if (!die) return true; + // Clang used to erroneously emit index entries for declaration DIEs in case + // when the definition is in a type unit (llvm.org/pr77696). + if (die.IsStructUnionOrClass() && + die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0)) + return true; return callback(die); } @@ -269,6 +346,13 @@ void DebugNamesDWARFIndex::GetFullyQualifiedType( if (!isType(entry.tag())) continue; + // If we get a NULL foreign_tu back, the entry doesn't match the type unit + // in the .dwp file, or we were not able to load the .dwo file or the DWO ID + // didn't match. + std::optional<DWARFTypeUnit *> foreign_tu = GetForeignTypeUnit(entry); + if (foreign_tu && foreign_tu.value() == nullptr) + continue; + // Grab at most one extra parent, subsequent parents are not necessary to // test equality. std::optional<llvm::SmallVector<Entry, 4>> parent_chain = diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h index a27a414ecdd1..cb15c1d4f994 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h @@ -70,7 +70,8 @@ private: : DWARFIndex(module), m_debug_info(dwarf.DebugInfo()), m_debug_names_data(debug_names_data), m_debug_str_data(debug_str_data), m_debug_names_up(std::move(debug_names_up)), - m_fallback(module, dwarf, GetUnits(*m_debug_names_up)) {} + m_fallback(module, dwarf, GetUnits(*m_debug_names_up), + GetTypeUnitSignatures(*m_debug_names_up)) {} DWARFDebugInfo &m_debug_info; @@ -85,6 +86,31 @@ private: DWARFUnit *GetNonSkeletonUnit(const DebugNames::Entry &entry) const; DWARFDIE GetDIE(const DebugNames::Entry &entry) const; + + /// Checks if an entry is a foreign TU and fetch the type unit. + /// + /// This function checks if the DebugNames::Entry refers to a foreign TU and + /// returns an optional with a value of the \a entry is a foreign type unit + /// entry. A valid pointer will be returned if this entry is from a .dwo file + /// or if it is from a .dwp file and it matches the type unit's originating + /// .dwo file by verifying that the DW_TAG_type_unit DIE has a DW_AT_dwo_name + /// that matches the DWO name from the originating skeleton compile unit. + /// + /// \param[in] entry + /// The accelerator table entry to check. + /// + /// \returns + /// A std::optional that has a value if this entry represents a foreign type + /// unit. If the pointer is valid, then we were able to find and match the + /// entry to the type unit in the .dwo or .dwp file. The returned value can + /// have a valid, yet contain NULL in the following cases: + /// - we were not able to load the .dwo file (missing or DWO ID mismatch) + /// - we were able to load the .dwp file, but the type units DWO name + /// doesn't match the originating skeleton compile unit's entry + /// Returns std::nullopt if this entry is not a foreign type unit entry. + std::optional<DWARFTypeUnit *> + GetForeignTypeUnit(const DebugNames::Entry &entry) const; + bool ProcessEntry(const DebugNames::Entry &entry, llvm::function_ref<bool(DWARFDIE die)> callback); @@ -97,6 +123,8 @@ private: llvm::StringRef name); static llvm::DenseSet<dw_offset_t> GetUnits(const DebugNames &debug_names); + static llvm::DenseSet<uint64_t> + GetTypeUnitSignatures(const DebugNames &debug_names); }; } // namespace dwarf diff --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp index 92275600f99c..d581d3773ab2 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp @@ -60,8 +60,11 @@ void ManualDWARFIndex::Index() { } if (dwp_info && dwp_info->ContainsTypeUnits()) { for (size_t U = 0; U < dwp_info->GetNumUnits(); ++U) { - if (auto *tu = llvm::dyn_cast<DWARFTypeUnit>(dwp_info->GetUnitAtIndex(U))) - units_to_index.push_back(tu); + if (auto *tu = + llvm::dyn_cast<DWARFTypeUnit>(dwp_info->GetUnitAtIndex(U))) { + if (!m_type_sigs_to_avoid.contains(tu->GetTypeHash())) + units_to_index.push_back(tu); + } } } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h index 0126e587e52d..d8c4a22ab21f 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h @@ -21,9 +21,11 @@ class SymbolFileDWARFDwo; class ManualDWARFIndex : public DWARFIndex { public: ManualDWARFIndex(Module &module, SymbolFileDWARF &dwarf, - llvm::DenseSet<dw_offset_t> units_to_avoid = {}) + llvm::DenseSet<dw_offset_t> units_to_avoid = {}, + llvm::DenseSet<uint64_t> type_sigs_to_avoid = {}) : DWARFIndex(module), m_dwarf(&dwarf), - m_units_to_avoid(std::move(units_to_avoid)) {} + m_units_to_avoid(std::move(units_to_avoid)), + m_type_sigs_to_avoid(std::move(type_sigs_to_avoid)) {} void Preload() override { Index(); } @@ -170,6 +172,7 @@ private: SymbolFileDWARF *m_dwarf; /// Which dwarf units should we skip while building the index. llvm::DenseSet<dw_offset_t> m_units_to_avoid; + llvm::DenseSet<uint64_t> m_type_sigs_to_avoid; IndexSet m_set; bool m_indexed = false; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index a52a7d676737..f2ff3a8b259f 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -710,8 +710,8 @@ llvm::DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() { DWARFDebugInfo &SymbolFileDWARF::DebugInfo() { llvm::call_once(m_info_once_flag, [&] { - LLDB_SCOPED_TIMERF("%s this = %p", LLVM_PRETTY_FUNCTION, - static_cast<void *>(this)); + LLDB_SCOPED_TIMER(); + m_info = std::make_unique<DWARFDebugInfo>(*this, m_context); }); return *m_info; @@ -732,8 +732,7 @@ DWARFCompileUnit *SymbolFileDWARF::GetDWARFCompileUnit(CompileUnit *comp_unit) { DWARFDebugRanges *SymbolFileDWARF::GetDebugRanges() { if (!m_ranges) { - LLDB_SCOPED_TIMERF("%s this = %p", LLVM_PRETTY_FUNCTION, - static_cast<void *>(this)); + LLDB_SCOPED_TIMER(); if (m_context.getOrLoadRangesData().GetByteSize() > 0) m_ranges = std::make_unique<DWARFDebugRanges>(); @@ -1728,14 +1727,7 @@ lldb::ModuleSP SymbolFileDWARF::GetExternalModule(ConstString name) { return pos->second; } -DWARFDIE -SymbolFileDWARF::GetDIE(const DIERef &die_ref) { - // This method can be called without going through the symbol vendor so we - // need to lock the module. - std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); - - SymbolFileDWARF *symbol_file = nullptr; - +SymbolFileDWARF *SymbolFileDWARF::GetDIERefSymbolFile(const DIERef &die_ref) { // Anytime we get a "lldb::user_id_t" from an lldb_private::SymbolFile API we // must make sure we use the correct DWARF file when resolving things. On // MacOSX, when using SymbolFileDWARFDebugMap, we will use multiple @@ -1743,30 +1735,44 @@ SymbolFileDWARF::GetDIE(const DIERef &die_ref) { // references to other DWARF objects and we must be ready to receive a // "lldb::user_id_t" that specifies a DIE from another SymbolFileDWARF // instance. + std::optional<uint32_t> file_index = die_ref.file_index(); - if (file_index) { - if (SymbolFileDWARFDebugMap *debug_map = GetDebugMapSymfile()) { - symbol_file = debug_map->GetSymbolFileByOSOIndex(*file_index); // OSO case - if (symbol_file) - return symbol_file->DebugInfo().GetDIE(die_ref.section(), - die_ref.die_offset()); - return DWARFDIE(); - } + // If the file index matches, then we have the right SymbolFileDWARF already. + // This will work for both .dwo file and DWARF in .o files for mac. Also if + // both the file indexes are invalid, then we have a match. + if (GetFileIndex() == file_index) + return this; + + if (file_index) { + // We have a SymbolFileDWARFDebugMap, so let it find the right file + if (SymbolFileDWARFDebugMap *debug_map = GetDebugMapSymfile()) + return debug_map->GetSymbolFileByOSOIndex(*file_index); + + // Handle the .dwp file case correctly if (*file_index == DIERef::k_file_index_mask) - symbol_file = GetDwpSymbolFile().get(); // DWP case - else - symbol_file = this->DebugInfo() - .GetUnitAtIndex(*die_ref.file_index()) - ->GetDwoSymbolFile(); // DWO case - } else if (die_ref.die_offset() == DW_INVALID_OFFSET) { - return DWARFDIE(); + return GetDwpSymbolFile().get(); // DWP case + + // Handle the .dwo file case correctly + return DebugInfo().GetUnitAtIndex(*die_ref.file_index()) + ->GetDwoSymbolFile(); // DWO case } + return this; +} - if (symbol_file) - return symbol_file->GetDIE(die_ref); +DWARFDIE +SymbolFileDWARF::GetDIE(const DIERef &die_ref) { + if (die_ref.die_offset() == DW_INVALID_OFFSET) + return DWARFDIE(); - return DebugInfo().GetDIE(die_ref.section(), die_ref.die_offset()); + // This method can be called without going through the symbol vendor so we + // need to lock the module. + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + SymbolFileDWARF *symbol_file = GetDIERefSymbolFile(die_ref); + if (symbol_file) + return symbol_file->DebugInfo().GetDIE(die_ref.section(), + die_ref.die_offset()); + return DWARFDIE(); } /// Return the DW_AT_(GNU_)dwo_id. @@ -3039,205 +3045,113 @@ TypeSP SymbolFileDWARF::FindCompleteObjCDefinitionTypeForDIE( return type_sp; } -// This function helps to ensure that the declaration contexts match for two -// different DIEs. Often times debug information will refer to a forward -// declaration of a type (the equivalent of "struct my_struct;". There will -// often be a declaration of that type elsewhere that has the full definition. -// When we go looking for the full type "my_struct", we will find one or more -// matches in the accelerator tables and we will then need to make sure the -// type was in the same declaration context as the original DIE. This function -// can efficiently compare two DIEs and will return true when the declaration -// context matches, and false when they don't. -bool SymbolFileDWARF::DIEDeclContextsMatch(const DWARFDIE &die1, - const DWARFDIE &die2) { - if (die1 == die2) - return true; +DWARFDIE +SymbolFileDWARF::FindDefinitionDIE(const DWARFDIE &die) { + if (!die.GetName()) + return {}; - std::vector<DWARFDIE> decl_ctx_1; - std::vector<DWARFDIE> decl_ctx_2; - // The declaration DIE stack is a stack of the declaration context DIEs all - // the way back to the compile unit. If a type "T" is declared inside a class - // "B", and class "B" is declared inside a class "A" and class "A" is in a - // namespace "lldb", and the namespace is in a compile unit, there will be a - // stack of DIEs: - // - // [0] DW_TAG_class_type for "B" - // [1] DW_TAG_class_type for "A" - // [2] DW_TAG_namespace for "lldb" - // [3] DW_TAG_compile_unit or DW_TAG_partial_unit for the source file. - // - // We grab both contexts and make sure that everything matches all the way - // back to the compiler unit. - - // First lets grab the decl contexts for both DIEs - decl_ctx_1 = die1.GetDeclContextDIEs(); - decl_ctx_2 = die2.GetDeclContextDIEs(); - // Make sure the context arrays have the same size, otherwise we are done - const size_t count1 = decl_ctx_1.size(); - const size_t count2 = decl_ctx_2.size(); - if (count1 != count2) - return false; + const dw_tag_t tag = die.Tag(); - // Make sure the DW_TAG values match all the way back up the compile unit. If - // they don't, then we are done. - DWARFDIE decl_ctx_die1; - DWARFDIE decl_ctx_die2; - size_t i; - for (i = 0; i < count1; i++) { - decl_ctx_die1 = decl_ctx_1[i]; - decl_ctx_die2 = decl_ctx_2[i]; - if (decl_ctx_die1.Tag() != decl_ctx_die2.Tag()) - return false; + Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); + if (log) { + GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::FindDefinitionDIE(tag={0} " + "({1}), name='{2}')", + DW_TAG_value_to_name(tag), tag, die.GetName()); } -#ifndef NDEBUG - - // Make sure the top item in the decl context die array is always - // DW_TAG_compile_unit or DW_TAG_partial_unit. If it isn't then - // something went wrong in the DWARFDIE::GetDeclContextDIEs() - // function. - dw_tag_t cu_tag = decl_ctx_1[count1 - 1].Tag(); - UNUSED_IF_ASSERT_DISABLED(cu_tag); - assert(cu_tag == DW_TAG_compile_unit || cu_tag == DW_TAG_partial_unit); - -#endif - // Always skip the compile unit when comparing by only iterating up to "count - // - 1". Here we compare the names as we go. - for (i = 0; i < count1 - 1; i++) { - decl_ctx_die1 = decl_ctx_1[i]; - decl_ctx_die2 = decl_ctx_2[i]; - const char *name1 = decl_ctx_die1.GetName(); - const char *name2 = decl_ctx_die2.GetName(); - // If the string was from a DW_FORM_strp, then the pointer will often be - // the same! - if (name1 == name2) - continue; - // Name pointers are not equal, so only compare the strings if both are not - // NULL. - if (name1 && name2) { - // If the strings don't compare, we are done... - if (strcmp(name1, name2) != 0) - return false; + // Get the type system that we are looking to find a type for. We will + // use this to ensure any matches we find are in a language that this + // type system supports + const LanguageType language = GetLanguage(*die.GetCU()); + TypeSystemSP type_system = nullptr; + if (language != eLanguageTypeUnknown) { + auto type_system_or_err = GetTypeSystemForLanguage(language); + if (auto err = type_system_or_err.takeError()) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), + "Cannot get TypeSystem for language {1}: {0}", + Language::GetNameForLanguageType(language)); } else { - // One name was NULL while the other wasn't - return false; + type_system = *type_system_or_err; } } - // We made it through all of the checks and the declaration contexts are - // equal. - return true; -} - -TypeSP -SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(const DWARFDIE &die) { - TypeSP type_sp; - if (die.GetName()) { - const dw_tag_t tag = die.Tag(); - - Log *log = GetLog(DWARFLog::TypeCompletion | DWARFLog::Lookups); - if (log) { - GetObjectFile()->GetModule()->LogMessage( - log, - "SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(tag={0} " - "({1}), name='{2}')", - DW_TAG_value_to_name(tag), tag, die.GetName()); - } - - // Get the type system that we are looking to find a type for. We will - // use this to ensure any matches we find are in a language that this - // type system supports - const LanguageType language = GetLanguage(*die.GetCU()); - TypeSystemSP type_system = nullptr; - if (language != eLanguageTypeUnknown) { - auto type_system_or_err = GetTypeSystemForLanguage(language); - if (auto err = type_system_or_err.takeError()) { - LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err), - "Cannot get TypeSystem for language {1}: {0}", - Language::GetNameForLanguageType(language)); - } else { - type_system = *type_system_or_err; + // See comments below about -gsimple-template-names for why we attempt to + // compute missing template parameter names. + std::vector<std::string> template_params; + DWARFDeclContext die_dwarf_decl_ctx; + DWARFASTParser *dwarf_ast = + type_system ? type_system->GetDWARFParser() : nullptr; + for (DWARFDIE ctx_die = die; ctx_die && !isUnitType(ctx_die.Tag()); + ctx_die = ctx_die.GetParentDeclContextDIE()) { + die_dwarf_decl_ctx.AppendDeclContext(ctx_die.Tag(), ctx_die.GetName()); + template_params.push_back( + (ctx_die.IsStructUnionOrClass() && dwarf_ast) + ? dwarf_ast->GetDIEClassTemplateParams(ctx_die) + : ""); + } + const bool any_template_params = llvm::any_of( + template_params, [](llvm::StringRef p) { return !p.empty(); }); + + auto die_matches = [&](DWARFDIE type_die) { + // Resolve the type if both have the same tag or {class, struct} tags. + const bool tag_matches = + type_die.Tag() == tag || + (IsStructOrClassTag(type_die.Tag()) && IsStructOrClassTag(tag)); + if (!tag_matches) + return false; + if (any_template_params) { + size_t pos = 0; + for (DWARFDIE ctx_die = type_die; ctx_die && !isUnitType(ctx_die.Tag()) && + pos < template_params.size(); + ctx_die = ctx_die.GetParentDeclContextDIE(), ++pos) { + if (template_params[pos].empty()) + continue; + if (template_params[pos] != + dwarf_ast->GetDIEClassTemplateParams(ctx_die)) + return false; } + if (pos != template_params.size()) + return false; } + return true; + }; + DWARFDIE result; + m_index->GetFullyQualifiedType(die_dwarf_decl_ctx, [&](DWARFDIE type_die) { + // Make sure type_die's language matches the type system we are + // looking for. We don't want to find a "Foo" type from Java if we + // are looking for a "Foo" type for C, C++, ObjC, or ObjC++. + if (type_system && + !type_system->SupportsLanguage(GetLanguage(*type_die.GetCU()))) + return true; - // See comments below about -gsimple-template-names for why we attempt to - // compute missing template parameter names. - ConstString template_params; - if (type_system) { - DWARFASTParser *dwarf_ast = type_system->GetDWARFParser(); - if (dwarf_ast) - template_params = dwarf_ast->GetDIEClassTemplateParams(die); - } - - const DWARFDeclContext die_dwarf_decl_ctx = GetDWARFDeclContext(die); - m_index->GetFullyQualifiedType(die_dwarf_decl_ctx, [&](DWARFDIE type_die) { - // Make sure type_die's language matches the type system we are - // looking for. We don't want to find a "Foo" type from Java if we - // are looking for a "Foo" type for C, C++, ObjC, or ObjC++. - if (type_system && - !type_system->SupportsLanguage(GetLanguage(*type_die.GetCU()))) - return true; - - const dw_tag_t type_tag = type_die.Tag(); - // Resolve the type if both have the same tag or {class, struct} tags. - const bool try_resolving_type = - type_tag == tag || - (IsStructOrClassTag(type_tag) && IsStructOrClassTag(tag)); - - if (!try_resolving_type) { - if (log) { - GetObjectFile()->GetModule()->LogMessage( - log, - "SymbolFileDWARF::" - "FindDefinitionTypeForDWARFDeclContext(tag={0} ({1}), " - "name='{2}') ignoring die={3:x16} ({4})", - DW_TAG_value_to_name(tag), tag, die.GetName(), - type_die.GetOffset(), type_die.GetName()); - } - return true; - } - + if (!die_matches(type_die)) { if (log) { - DWARFDeclContext type_dwarf_decl_ctx = GetDWARFDeclContext(type_die); GetObjectFile()->GetModule()->LogMessage( log, - "SymbolFileDWARF::" - "FindDefinitionTypeForDWARFDeclContext(tag={0} ({1}), name='{2}') " - "trying die={3:x16} ({4})", + "SymbolFileDWARF::FindDefinitionDIE(tag={0} ({1}), " + "name='{2}') ignoring die={3:x16} ({4})", DW_TAG_value_to_name(tag), tag, die.GetName(), type_die.GetOffset(), - type_dwarf_decl_ctx.GetQualifiedName()); + type_die.GetName()); } + return true; + } - Type *resolved_type = ResolveType(type_die, false); - if (!resolved_type || resolved_type == DIE_IS_BEING_PARSED) - return true; - - // With -gsimple-template-names, the DIE name may not contain the template - // parameters. If the declaration has template parameters but doesn't - // contain '<', check that the child template parameters match. - if (template_params) { - llvm::StringRef test_base_name = - GetTypeForDIE(type_die)->GetBaseName().GetStringRef(); - auto i = test_base_name.find('<'); - - // Full name from clang AST doesn't contain '<' so this type_die isn't - // a template parameter, but we're expecting template parameters, so - // bail. - if (i == llvm::StringRef::npos) - return true; - - llvm::StringRef test_template_params = - test_base_name.slice(i, test_base_name.size()); - // Bail if template parameters don't match. - if (test_template_params != template_params.GetStringRef()) - return true; - } + if (log) { + DWARFDeclContext type_dwarf_decl_ctx = type_die.GetDWARFDeclContext(); + GetObjectFile()->GetModule()->LogMessage( + log, + "SymbolFileDWARF::FindDefinitionTypeDIE(tag={0} ({1}), name='{2}') " + "trying die={3:x16} ({4})", + DW_TAG_value_to_name(tag), tag, die.GetName(), type_die.GetOffset(), + type_dwarf_decl_ctx.GetQualifiedName()); + } - type_sp = resolved_type->shared_from_this(); - return false; - }); - } - return type_sp; + result = type_die; + return false; + }); + return result; } TypeSP SymbolFileDWARF::ParseType(const SymbolContext &sc, const DWARFDIE &die, @@ -3624,8 +3538,9 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, if ((parent_tag == DW_TAG_compile_unit || parent_tag == DW_TAG_partial_unit) && Language::LanguageIsCPlusPlus(GetLanguage(*die.GetCU()))) - mangled = - GetDWARFDeclContext(die).GetQualifiedNameAsConstString().GetCString(); + mangled = die.GetDWARFDeclContext() + .GetQualifiedNameAsConstString() + .GetCString(); } if (tag == DW_TAG_formal_parameter) @@ -4450,14 +4365,6 @@ SymbolFileDWARF::GetContainingDeclContext(const DWARFDIE &die) { return CompilerDeclContext(); } -DWARFDeclContext SymbolFileDWARF::GetDWARFDeclContext(const DWARFDIE &die) { - if (!die.IsValid()) - return {}; - DWARFDeclContext dwarf_decl_ctx = - die.GetDIE()->GetDWARFDeclContext(die.GetCU()); - return dwarf_decl_ctx; -} - LanguageType SymbolFileDWARF::LanguageTypeFromDWARF(uint64_t val) { // Note: user languages between lo_user and hi_user must be handled // explicitly here. diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 7282c08c6857..8469248872a4 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -241,6 +241,15 @@ public: return m_external_type_modules; } + /// Given a DIERef, find the correct SymbolFileDWARF. + /// + /// A DIERef contains a file index that can uniquely identify a N_OSO file for + /// DWARF in .o files on mac, or a .dwo or .dwp file index for split DWARF. + /// Calling this function will find the correct symbol file to use so that + /// further lookups can be done on the correct symbol file so that the DIE + /// offset makes sense in the DIERef. + virtual SymbolFileDWARF *GetDIERefSymbolFile(const DIERef &die_ref); + virtual DWARFDIE GetDIE(const DIERef &die_ref); DWARFDIE GetDIE(lldb::user_id_t uid); @@ -296,8 +305,6 @@ public: static CompilerDeclContext GetContainingDeclContext(const DWARFDIE &die); - static DWARFDeclContext GetDWARFDeclContext(const DWARFDIE &die); - static lldb::LanguageType LanguageTypeFromDWARF(uint64_t val); static lldb::LanguageType GetLanguage(DWARFUnit &unit); @@ -353,8 +360,7 @@ public: SymbolFileDWARFDebugMap *GetDebugMapSymfile(); - virtual lldb::TypeSP - FindDefinitionTypeForDWARFDeclContext(const DWARFDIE &die); + virtual DWARFDIE FindDefinitionDIE(const DWARFDIE &die); virtual lldb::TypeSP FindCompleteObjCDefinitionTypeForDIE( const DWARFDIE &die, ConstString type_name, bool must_be_implementation); @@ -461,8 +467,6 @@ protected: FindBlockContainingSpecification(const DWARFDIE &die, dw_offset_t spec_block_die_offset); - bool DIEDeclContextsMatch(const DWARFDIE &die1, const DWARFDIE &die2); - bool ClassContainsSelector(const DWARFDIE &class_die, ConstString selector); /// Parse call site entries (DW_TAG_call_site), including any nested call site diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp index f066f13d51c5..64cde16433ef 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -1147,14 +1147,13 @@ SymbolFileDWARFDebugMap::ParseCallEdgesInFunction( return {}; } -TypeSP SymbolFileDWARFDebugMap::FindDefinitionTypeForDWARFDeclContext( - const DWARFDIE &die) { - TypeSP type_sp; +DWARFDIE SymbolFileDWARFDebugMap::FindDefinitionDIE(const DWARFDIE &die) { + DWARFDIE result; ForEachSymbolFile([&](SymbolFileDWARF *oso_dwarf) { - type_sp = oso_dwarf->FindDefinitionTypeForDWARFDeclContext(die); - return type_sp ? IterationAction::Stop : IterationAction::Continue; + result = oso_dwarf->FindDefinitionDIE(die); + return result ? IterationAction::Stop : IterationAction::Continue; }); - return type_sp; + return result; } bool SymbolFileDWARFDebugMap::Supports_DW_AT_APPLE_objc_complete_type( diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h index de22dd676eef..7d5516b92737 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -277,7 +277,7 @@ protected: CompileUnitInfo *GetCompileUnitInfo(SymbolFileDWARF *oso_dwarf); - lldb::TypeSP FindDefinitionTypeForDWARFDeclContext(const DWARFDIE &die); + DWARFDIE FindDefinitionDIE(const DWARFDIE &die); bool Supports_DW_AT_APPLE_objc_complete_type(SymbolFileDWARF *skip_dwarf_oso); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp index 71c9997e4c82..4a8c532a0d2a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp @@ -125,9 +125,8 @@ UniqueDWARFASTTypeMap &SymbolFileDWARFDwo::GetUniqueDWARFASTTypeMap() { return GetBaseSymbolFile().GetUniqueDWARFASTTypeMap(); } -lldb::TypeSP -SymbolFileDWARFDwo::FindDefinitionTypeForDWARFDeclContext(const DWARFDIE &die) { - return GetBaseSymbolFile().FindDefinitionTypeForDWARFDeclContext(die); +DWARFDIE SymbolFileDWARFDwo::FindDefinitionDIE(const DWARFDIE &die) { + return GetBaseSymbolFile().FindDefinitionDIE(die); } lldb::TypeSP SymbolFileDWARFDwo::FindCompleteObjCDefinitionTypeForDIE( @@ -174,3 +173,8 @@ bool SymbolFileDWARFDwo::GetDebugInfoHadFrameVariableErrors() const { void SymbolFileDWARFDwo::SetDebugInfoHadFrameVariableErrors() { return GetBaseSymbolFile().SetDebugInfoHadFrameVariableErrors(); } + +SymbolFileDWARF * +SymbolFileDWARFDwo::GetDIERefSymbolFile(const DIERef &die_ref) { + return GetBaseSymbolFile().GetDIERefSymbolFile(die_ref); +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h index 1500540424b5..3bd0a2d25a5a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h @@ -67,6 +67,8 @@ public: bool GetDebugInfoHadFrameVariableErrors() const override; void SetDebugInfoHadFrameVariableErrors() override; + SymbolFileDWARF *GetDIERefSymbolFile(const DIERef &die_ref) override; + protected: DIEToTypePtr &GetDIEToType() override; @@ -76,8 +78,7 @@ protected: UniqueDWARFASTTypeMap &GetUniqueDWARFASTTypeMap() override; - lldb::TypeSP - FindDefinitionTypeForDWARFDeclContext(const DWARFDIE &die) override; + DWARFDIE FindDefinitionDIE(const DWARFDIE &die) override; lldb::TypeSP FindCompleteObjCDefinitionTypeForDIE(const DWARFDIE &die, diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 369ae46cf264..cd1c500d9aa2 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -2574,6 +2574,128 @@ TypeSystemClang::GetDeclContextForType(clang::QualType type) { return nullptr; } +/// Returns the clang::RecordType of the specified \ref qual_type. This +/// function will try to complete the type if necessary (and allowed +/// by the specified \ref allow_completion). If we fail to return a *complete* +/// type, returns nullptr. +static const clang::RecordType *GetCompleteRecordType(clang::ASTContext *ast, + clang::QualType qual_type, + bool allow_completion) { + assert(qual_type->isRecordType()); + + const auto *tag_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr()); + + clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); + + // RecordType with no way of completing it, return the plain + // TagType. + if (!cxx_record_decl || !cxx_record_decl->hasExternalLexicalStorage()) + return tag_type; + + const bool is_complete = cxx_record_decl->isCompleteDefinition(); + const bool fields_loaded = + cxx_record_decl->hasLoadedFieldsFromExternalStorage(); + + // Already completed this type, nothing to be done. + if (is_complete && fields_loaded) + return tag_type; + + if (!allow_completion) + return nullptr; + + // Call the field_begin() accessor to for it to use the external source + // to load the fields... + // + // TODO: if we need to complete the type but have no external source, + // shouldn't we error out instead? + clang::ExternalASTSource *external_ast_source = ast->getExternalSource(); + if (external_ast_source) { + external_ast_source->CompleteType(cxx_record_decl); + if (cxx_record_decl->isCompleteDefinition()) { + cxx_record_decl->field_begin(); + cxx_record_decl->setHasLoadedFieldsFromExternalStorage(true); + } + } + + return tag_type; +} + +/// Returns the clang::EnumType of the specified \ref qual_type. This +/// function will try to complete the type if necessary (and allowed +/// by the specified \ref allow_completion). If we fail to return a *complete* +/// type, returns nullptr. +static const clang::EnumType *GetCompleteEnumType(clang::ASTContext *ast, + clang::QualType qual_type, + bool allow_completion) { + assert(qual_type->isEnumeralType()); + assert(ast); + + const clang::EnumType *enum_type = + llvm::cast<clang::EnumType>(qual_type.getTypePtr()); + + auto *tag_decl = enum_type->getAsTagDecl(); + assert(tag_decl); + + // Already completed, nothing to be done. + if (tag_decl->getDefinition()) + return enum_type; + + if (!allow_completion) + return nullptr; + + // No definition but can't complete it, error out. + if (!tag_decl->hasExternalLexicalStorage()) + return nullptr; + + // We can't complete the type without an external source. + clang::ExternalASTSource *external_ast_source = ast->getExternalSource(); + if (!external_ast_source) + return nullptr; + + external_ast_source->CompleteType(tag_decl); + return enum_type; +} + +/// Returns the clang::ObjCObjectType of the specified \ref qual_type. This +/// function will try to complete the type if necessary (and allowed +/// by the specified \ref allow_completion). If we fail to return a *complete* +/// type, returns nullptr. +static const clang::ObjCObjectType * +GetCompleteObjCObjectType(clang::ASTContext *ast, QualType qual_type, + bool allow_completion) { + assert(qual_type->isObjCObjectType()); + assert(ast); + + const clang::ObjCObjectType *objc_class_type = + llvm::cast<clang::ObjCObjectType>(qual_type); + + clang::ObjCInterfaceDecl *class_interface_decl = + objc_class_type->getInterface(); + // We currently can't complete objective C types through the newly added + // ASTContext because it only supports TagDecl objects right now... + if (!class_interface_decl) + return objc_class_type; + + // Already complete, nothing to be done. + if (class_interface_decl->getDefinition()) + return objc_class_type; + + if (!allow_completion) + return nullptr; + + // No definition but can't complete it, error out. + if (!class_interface_decl->hasExternalLexicalStorage()) + return nullptr; + + // We can't complete the type without an external source. + clang::ExternalASTSource *external_ast_source = ast->getExternalSource(); + if (!external_ast_source) + return nullptr; + + external_ast_source->CompleteType(class_interface_decl); + return objc_class_type; +} + static bool GetCompleteQualType(clang::ASTContext *ast, clang::QualType qual_type, bool allow_completion = true) { @@ -2591,92 +2713,26 @@ static bool GetCompleteQualType(clang::ASTContext *ast, allow_completion); } break; case clang::Type::Record: { - clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); - if (cxx_record_decl) { - if (cxx_record_decl->hasExternalLexicalStorage()) { - const bool is_complete = cxx_record_decl->isCompleteDefinition(); - const bool fields_loaded = - cxx_record_decl->hasLoadedFieldsFromExternalStorage(); - if (is_complete && fields_loaded) - return true; + if (const auto *RT = + GetCompleteRecordType(ast, qual_type, allow_completion)) + return !RT->isIncompleteType(); - if (!allow_completion) - return false; - - // Call the field_begin() accessor to for it to use the external source - // to load the fields... - clang::ExternalASTSource *external_ast_source = - ast->getExternalSource(); - if (external_ast_source) { - external_ast_source->CompleteType(cxx_record_decl); - if (cxx_record_decl->isCompleteDefinition()) { - cxx_record_decl->field_begin(); - cxx_record_decl->setHasLoadedFieldsFromExternalStorage(true); - } - } - } - } - const clang::TagType *tag_type = - llvm::cast<clang::TagType>(qual_type.getTypePtr()); - return !tag_type->isIncompleteType(); + return false; } break; case clang::Type::Enum: { - const clang::TagType *tag_type = - llvm::dyn_cast<clang::TagType>(qual_type.getTypePtr()); - if (tag_type) { - clang::TagDecl *tag_decl = tag_type->getDecl(); - if (tag_decl) { - if (tag_decl->getDefinition()) - return true; - - if (!allow_completion) - return false; - - if (tag_decl->hasExternalLexicalStorage()) { - if (ast) { - clang::ExternalASTSource *external_ast_source = - ast->getExternalSource(); - if (external_ast_source) { - external_ast_source->CompleteType(tag_decl); - return !tag_type->isIncompleteType(); - } - } - } - return false; - } - } + if (const auto *ET = GetCompleteEnumType(ast, qual_type, allow_completion)) + return !ET->isIncompleteType(); + return false; } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: { - const clang::ObjCObjectType *objc_class_type = - llvm::dyn_cast<clang::ObjCObjectType>(qual_type); - if (objc_class_type) { - clang::ObjCInterfaceDecl *class_interface_decl = - objc_class_type->getInterface(); - // We currently can't complete objective C types through the newly added - // ASTContext because it only supports TagDecl objects right now... - if (class_interface_decl) { - if (class_interface_decl->getDefinition()) - return true; + if (const auto *OT = + GetCompleteObjCObjectType(ast, qual_type, allow_completion)) + return !OT->isIncompleteType(); - if (!allow_completion) - return false; - - if (class_interface_decl->hasExternalLexicalStorage()) { - if (ast) { - clang::ExternalASTSource *external_ast_source = - ast->getExternalSource(); - if (external_ast_source) { - external_ast_source->CompleteType(class_interface_decl); - return !objc_class_type->isIncompleteType(); - } - } - } - return false; - } - } + return false; } break; case clang::Type::Attributed: @@ -4688,11 +4744,11 @@ TypeSystemClang::GetBitSize(lldb::opaque_compiler_type_t type, ExecutionContext exe_ctx(exe_scope); Process *process = exe_ctx.GetProcessPtr(); if (process) { - ObjCLanguageRuntime *objc_runtime = ObjCLanguageRuntime::Get(*process); - if (objc_runtime) { - uint64_t bit_size = 0; - if (objc_runtime->GetTypeBitSize(GetType(qual_type), bit_size)) - return bit_size; + if (ObjCLanguageRuntime *objc_runtime = + ObjCLanguageRuntime::Get(*process)) { + if (std::optional<uint64_t> bit_size = + objc_runtime->GetTypeBitSize(GetType(qual_type))) + return *bit_size; } } else { static bool g_printed = false; @@ -5003,6 +5059,11 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type, case clang::BuiltinType::UnresolvedTemplate: break; + + // AMD GPU builtin types. +#define AMDGPU_TYPE(Name, Id, SingletonId) case clang::BuiltinType::Id: +#include "clang/Basic/AMDGPUTypes.def" + break; } break; // All pointer types are represented as unsigned integer encodings. We may @@ -9112,10 +9173,8 @@ static CompilerContextKind GetCompilerKind(clang::Decl::Kind clang_kind, if (decl_ctx) { if (decl_ctx->isFunctionOrMethod()) return CompilerContextKind::Function; - else if (decl_ctx->isRecord()) - return (CompilerContextKind)((uint16_t)CompilerContextKind::Class | - (uint16_t)CompilerContextKind::Struct | - (uint16_t)CompilerContextKind::Union); + if (decl_ctx->isRecord()) + return CompilerContextKind::ClassOrStruct | CompilerContextKind::Union; } break; } diff --git a/lldb/source/Symbol/DWARFCallFrameInfo.cpp b/lldb/source/Symbol/DWARFCallFrameInfo.cpp index dc54d13ae23c..f3df8a2c27f5 100644 --- a/lldb/source/Symbol/DWARFCallFrameInfo.cpp +++ b/lldb/source/Symbol/DWARFCallFrameInfo.cpp @@ -423,8 +423,7 @@ void DWARFCallFrameInfo::GetFDEIndex() { if (m_fde_index_initialized) // if two threads hit the locker return; - LLDB_SCOPED_TIMERF("%s - %s", LLVM_PRETTY_FUNCTION, - m_objfile.GetFileSpec().GetFilename().AsCString("")); + LLDB_SCOPED_TIMERF("%s", m_objfile.GetFileSpec().GetFilename().AsCString("")); bool clear_address_zeroth_bit = false; if (ArchSpec arch = m_objfile.GetArchitecture()) { diff --git a/lldb/source/Symbol/LineEntry.cpp b/lldb/source/Symbol/LineEntry.cpp index 461399e0326e..19e9bb561375 100644 --- a/lldb/source/Symbol/LineEntry.cpp +++ b/lldb/source/Symbol/LineEntry.cpp @@ -244,7 +244,9 @@ void LineEntry::ApplyFileMappings(lldb::TargetSP target_sp) { if (target_sp) { // Apply any file remappings to our file. if (auto new_file_spec = target_sp->GetSourcePathMap().FindFile( - original_file_sp->GetSpecOnly())) - file_sp->Update(*new_file_spec); + original_file_sp->GetSpecOnly())) { + file_sp = std::make_shared<SupportFile>(*new_file_spec, + original_file_sp->GetChecksum()); + } } } diff --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp index 585808ace15c..e76574795733 100644 --- a/lldb/source/Symbol/Type.cpp +++ b/lldb/source/Symbol/Type.cpp @@ -75,20 +75,18 @@ bool lldb_private::contextMatches(llvm::ArrayRef<CompilerContext> context_chain, static CompilerContextKind ConvertTypeClass(lldb::TypeClass type_class) { if (type_class == eTypeClassAny) return CompilerContextKind::AnyType; - uint16_t result = 0; - if (type_class & lldb::eTypeClassClass) - result |= (uint16_t)CompilerContextKind::Class; - if (type_class & lldb::eTypeClassStruct) - result |= (uint16_t)CompilerContextKind::Struct; + CompilerContextKind result = {}; + if (type_class & (lldb::eTypeClassClass | lldb::eTypeClassStruct)) + result |= CompilerContextKind::ClassOrStruct; if (type_class & lldb::eTypeClassUnion) - result |= (uint16_t)CompilerContextKind::Union; + result |= CompilerContextKind::Union; if (type_class & lldb::eTypeClassEnumeration) - result |= (uint16_t)CompilerContextKind::Enum; + result |= CompilerContextKind::Enum; if (type_class & lldb::eTypeClassFunction) - result |= (uint16_t)CompilerContextKind::Function; + result |= CompilerContextKind::Function; if (type_class & lldb::eTypeClassTypedef) - result |= (uint16_t)CompilerContextKind::Typedef; - return (CompilerContextKind)result; + result |= CompilerContextKind::Typedef; + return result; } TypeQuery::TypeQuery(llvm::StringRef name, TypeQueryOptions options) @@ -207,11 +205,8 @@ void CompilerContext::Dump(Stream &s) const { case CompilerContextKind::Namespace: s << "Namespace"; break; - case CompilerContextKind::Class: - s << "Class"; - break; - case CompilerContextKind::Struct: - s << "Structure"; + case CompilerContextKind::ClassOrStruct: + s << "ClassOrStruct"; break; case CompilerContextKind::Union: s << "Union"; diff --git a/lldb/source/Symbol/TypeSystem.cpp b/lldb/source/Symbol/TypeSystem.cpp index 4956f10a0b0a..931ce1b0203a 100644 --- a/lldb/source/Symbol/TypeSystem.cpp +++ b/lldb/source/Symbol/TypeSystem.cpp @@ -335,3 +335,14 @@ TypeSystemMap::GetTypeSystemForLanguage(lldb::LanguageType language, } return GetTypeSystemForLanguage(language); } + +bool TypeSystem::SupportsLanguageStatic(lldb::LanguageType language) { + if (language == eLanguageTypeUnknown || language >= eNumLanguageTypes) + return false; + + LanguageSet languages = + PluginManager::GetAllTypeSystemSupportedLanguagesForTypes(); + if (languages.Empty()) + return false; + return languages[language]; +} diff --git a/lldb/source/Target/InstrumentationRuntime.cpp b/lldb/source/Target/InstrumentationRuntime.cpp index 9f22a1be20cc..9da06e8e155a 100644 --- a/lldb/source/Target/InstrumentationRuntime.cpp +++ b/lldb/source/Target/InstrumentationRuntime.cpp @@ -60,6 +60,8 @@ void InstrumentationRuntime::ModulesDidLoad( if (CheckIfRuntimeIsValid(module_sp)) { SetRuntimeModuleSP(module_sp); Activate(); + if (!IsActive()) + SetRuntimeModuleSP({}); // Don't cache module if activation failed. return false; // Stop iterating, we're done. } } diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp index ee1f92470e16..b3116545b91d 100644 --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -768,41 +768,6 @@ Platform::ResolveExecutable(const ModuleSpec &module_spec, const FileSpecList *module_search_paths_ptr) { Status error; - if (FileSystem::Instance().Exists(module_spec.GetFileSpec())) { - if (module_spec.GetArchitecture().IsValid()) { - error = ModuleList::GetSharedModule(module_spec, exe_module_sp, - module_search_paths_ptr, nullptr, - nullptr); - } else { - // No valid architecture was specified, ask the platform for the - // architectures that we should be using (in the correct order) and see - // if we can find a match that way - ModuleSpec arch_module_spec(module_spec); - ArchSpec process_host_arch; - for (const ArchSpec &arch : - GetSupportedArchitectures(process_host_arch)) { - arch_module_spec.GetArchitecture() = arch; - error = ModuleList::GetSharedModule(arch_module_spec, exe_module_sp, - module_search_paths_ptr, nullptr, - nullptr); - // Did we find an executable using one of the - if (error.Success() && exe_module_sp) - break; - } - } - } else { - error.SetErrorStringWithFormat( - "'%s' does not exist", module_spec.GetFileSpec().GetPath().c_str()); - } - return error; -} - -Status -Platform::ResolveRemoteExecutable(const ModuleSpec &module_spec, - lldb::ModuleSP &exe_module_sp, - const FileSpecList *module_search_paths_ptr) { - Status error; - // We may connect to a process and use the provided executable (Don't use // local $PATH). ModuleSpec resolved_module_spec(module_spec); @@ -822,9 +787,9 @@ Platform::ResolveRemoteExecutable(const ModuleSpec &module_spec, return error; exe_module_sp.reset(); } - // No valid architecture was specified or the exact arch wasn't found so - // ask the platform for the architectures that we should be using (in the - // correct order) and see if we can find a match that way + // No valid architecture was specified or the exact arch wasn't found. + // Ask the platform for the architectures that we should be using (in the + // correct order) and see if we can find a match that way. StreamString arch_names; llvm::ListSeparator LS; ArchSpec process_host_arch; @@ -833,12 +798,10 @@ Platform::ResolveRemoteExecutable(const ModuleSpec &module_spec, error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, module_search_paths_ptr, nullptr, nullptr); - // Did we find an executable using one of the if (error.Success()) { if (exe_module_sp && exe_module_sp->GetObjectFile()) break; - else - error.SetErrorToGenericError(); + error.SetErrorToGenericError(); } arch_names << LS << arch.GetArchitectureName(); @@ -1516,8 +1479,7 @@ Platform::GetCachedExecutable(ModuleSpec &module_spec, Status error = GetRemoteSharedModule( module_spec, nullptr, module_sp, [&](const ModuleSpec &spec) { - return ResolveRemoteExecutable(spec, module_sp, - module_search_paths_ptr); + return ResolveExecutable(spec, module_sp, module_search_paths_ptr); }, nullptr); if (error.Success()) { diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 1e321f8bde39..6fac0df1d7a6 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -2007,6 +2007,129 @@ size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Status &error) { } } +void Process::DoFindInMemory(lldb::addr_t start_addr, lldb::addr_t end_addr, + const uint8_t *buf, size_t size, + AddressRanges &matches, size_t alignment, + size_t max_matches) { + // Inputs are already validated in FindInMemory() functions. + assert(buf != nullptr); + assert(size > 0); + assert(alignment > 0); + assert(max_matches > 0); + assert(start_addr != LLDB_INVALID_ADDRESS); + assert(end_addr != LLDB_INVALID_ADDRESS); + assert(start_addr < end_addr); + + lldb::addr_t start = llvm::alignTo(start_addr, alignment); + while (matches.size() < max_matches && (start + size) < end_addr) { + const lldb::addr_t found_addr = FindInMemory(start, end_addr, buf, size); + if (found_addr == LLDB_INVALID_ADDRESS) + break; + + if (found_addr % alignment) { + // We need to check the alignment because the FindInMemory uses a special + // algorithm to efficiently search mememory but doesn't support alignment. + start = llvm::alignTo(start + 1, alignment); + continue; + } + + matches.emplace_back(found_addr, size); + start = found_addr + alignment; + } +} + +AddressRanges Process::FindRangesInMemory(const uint8_t *buf, uint64_t size, + const AddressRanges &ranges, + size_t alignment, size_t max_matches, + Status &error) { + AddressRanges matches; + if (buf == nullptr) { + error.SetErrorString("buffer is null"); + return matches; + } + if (size == 0) { + error.SetErrorString("buffer size is zero"); + return matches; + } + if (ranges.empty()) { + error.SetErrorString("empty ranges"); + return matches; + } + if (alignment == 0) { + error.SetErrorString("alignment must be greater than zero"); + return matches; + } + if (max_matches == 0) { + error.SetErrorString("max_matches must be greater than zero"); + return matches; + } + + int resolved_ranges = 0; + Target &target = GetTarget(); + for (size_t i = 0; i < ranges.size(); ++i) { + if (matches.size() >= max_matches) + break; + const AddressRange &range = ranges[i]; + if (range.IsValid() == false) + continue; + + const lldb::addr_t start_addr = + range.GetBaseAddress().GetLoadAddress(&target); + if (start_addr == LLDB_INVALID_ADDRESS) + continue; + + ++resolved_ranges; + const lldb::addr_t end_addr = start_addr + range.GetByteSize(); + DoFindInMemory(start_addr, end_addr, buf, size, matches, alignment, + max_matches); + } + + if (resolved_ranges > 0) + error.Clear(); + else + error.SetErrorString("unable to resolve any ranges"); + + return matches; +} + +lldb::addr_t Process::FindInMemory(const uint8_t *buf, uint64_t size, + const AddressRange &range, size_t alignment, + Status &error) { + if (buf == nullptr) { + error.SetErrorString("buffer is null"); + return LLDB_INVALID_ADDRESS; + } + if (size == 0) { + error.SetErrorString("buffer size is zero"); + return LLDB_INVALID_ADDRESS; + } + if (!range.IsValid()) { + error.SetErrorString("range is invalid"); + return LLDB_INVALID_ADDRESS; + } + if (alignment == 0) { + error.SetErrorString("alignment must be greater than zero"); + return LLDB_INVALID_ADDRESS; + } + + Target &target = GetTarget(); + const lldb::addr_t start_addr = + range.GetBaseAddress().GetLoadAddress(&target); + if (start_addr == LLDB_INVALID_ADDRESS) { + error.SetErrorString("range load address is invalid"); + return LLDB_INVALID_ADDRESS; + } + const lldb::addr_t end_addr = start_addr + range.GetByteSize(); + + AddressRanges matches; + DoFindInMemory(start_addr, end_addr, buf, size, matches, alignment, 1); + if (matches.empty()) + return LLDB_INVALID_ADDRESS; + + error.Clear(); + return matches[0].GetBaseAddress().GetLoadAddress(&target); +} + size_t Process::ReadCStringFromMemory(addr_t addr, std::string &out_str, Status &error) { char buf[256]; @@ -5924,7 +6047,9 @@ void Process::PrintWarningUnsupportedLanguage(const SymbolContext &sc) { if (!sc.module_sp) return; LanguageType language = sc.GetLanguage(); - if (language == eLanguageTypeUnknown) + if (language == eLanguageTypeUnknown || + language == lldb::eLanguageTypeAssembly || + language == lldb::eLanguageTypeMipsAssembler) return; LanguageSet plugins = PluginManager::GetAllTypeSystemSupportedLanguagesForTypes(); diff --git a/lldb/source/Target/RegisterFlags.cpp b/lldb/source/Target/RegisterFlags.cpp index 5274960587bf..976e03870ad9 100644 --- a/lldb/source/Target/RegisterFlags.cpp +++ b/lldb/source/Target/RegisterFlags.cpp @@ -12,17 +12,42 @@ #include "llvm/ADT/StringExtras.h" +#include <limits> #include <numeric> #include <optional> using namespace lldb_private; RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end) - : m_name(std::move(name)), m_start(start), m_end(end) { + : m_name(std::move(name)), m_start(start), m_end(end), + m_enum_type(nullptr) { assert(m_start <= m_end && "Start bit must be <= end bit."); } -void RegisterFlags::Field::log(Log *log) const { +RegisterFlags::Field::Field(std::string name, unsigned bit_position) + : m_name(std::move(name)), m_start(bit_position), m_end(bit_position), + m_enum_type(nullptr) {} + +RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end, + const FieldEnum *enum_type) + : m_name(std::move(name)), m_start(start), m_end(end), + m_enum_type(enum_type) { + if (m_enum_type) { + // Check that all values fit into this field. The XML parser will also + // do this check so at runtime nothing should fail this check. + // We can also make enums in C++ at compile time, which might fail this + // check, so we catch them before it makes it into a release. + uint64_t max_value = GetMaxValue(); + UNUSED_IF_ASSERT_DISABLED(max_value); + for (const auto &enumerator : m_enum_type->GetEnumerators()) { + UNUSED_IF_ASSERT_DISABLED(enumerator); + assert(enumerator.m_value <= max_value && + "Enumerator value exceeds maximum value for this field"); + } + } +} + +void RegisterFlags::Field::DumpToLog(Log *log) const { LLDB_LOG(log, " Name: \"{0}\" Start: {1} End: {2}", m_name.c_str(), m_start, m_end); } @@ -53,11 +78,36 @@ unsigned RegisterFlags::Field::PaddingDistance(const Field &other) const { return lhs_start - rhs_end - 1; } -void RegisterFlags::SetFields(const std::vector<Field> &fields) { - // We expect that the XML processor will discard anything describing flags but - // with no fields. - assert(fields.size() && "Some fields must be provided."); +unsigned RegisterFlags::Field::GetSizeInBits(unsigned start, unsigned end) { + return end - start + 1; +} + +unsigned RegisterFlags::Field::GetSizeInBits() const { + return GetSizeInBits(m_start, m_end); +} + +uint64_t RegisterFlags::Field::GetMaxValue(unsigned start, unsigned end) { + uint64_t max = std::numeric_limits<uint64_t>::max(); + unsigned bits = GetSizeInBits(start, end); + // If the field is >= 64 bits the shift below would be undefined. + // We assume the GDB client has discarded any field that would fail this + // assert, it's only to check information we define directly in C++. + assert(bits <= 64 && "Cannot handle field with size > 64 bits"); + if (bits < 64) { + max = ((uint64_t)1 << bits) - 1; + } + return max; +} + +uint64_t RegisterFlags::Field::GetMaxValue() const { + return GetMaxValue(m_start, m_end); +} + +uint64_t RegisterFlags::Field::GetMask() const { + return GetMaxValue() << m_start; +} +void RegisterFlags::SetFields(const std::vector<Field> &fields) { // We expect that these are unsorted but do not overlap. // They could fill the register but may have gaps. std::vector<Field> provided_fields = fields; @@ -102,10 +152,10 @@ RegisterFlags::RegisterFlags(std::string id, unsigned size, SetFields(fields); } -void RegisterFlags::log(Log *log) const { +void RegisterFlags::DumpToLog(Log *log) const { LLDB_LOG(log, "ID: \"{0}\" Size: {1}", m_id.c_str(), m_size); for (const Field &field : m_fields) - field.log(log); + field.DumpToLog(log); } static StreamString FormatCell(const StreamString &content, @@ -190,6 +240,142 @@ std::string RegisterFlags::AsTable(uint32_t max_width) const { return table; } +// Print enums as: +// value = name, value2 = name2 +// Subject to the limits of the terminal width. +static void DumpEnumerators(StreamString &strm, size_t indent, + size_t current_width, uint32_t max_width, + const FieldEnum::Enumerators &enumerators) { + for (auto it = enumerators.cbegin(); it != enumerators.cend(); ++it) { + StreamString enumerator_strm; + // The first enumerator of a line doesn't need to be separated. + if (current_width != indent) + enumerator_strm << ' '; + + enumerator_strm.Printf("%" PRIu64 " = %s", it->m_value, it->m_name.c_str()); + + // Don't put "," after the last enumerator. + if (std::next(it) != enumerators.cend()) + enumerator_strm << ","; + + llvm::StringRef enumerator_string = enumerator_strm.GetString(); + // If printing the next enumerator would take us over the width, start + // a new line. However, if we're printing the first enumerator of this + // line, don't start a new one. Resulting in there being at least one per + // line. + // + // This means for very small widths we get: + // A: 0 = foo, + // 1 = bar + // Instead of: + // A: + // 0 = foo, + // 1 = bar + if ((current_width + enumerator_string.size() > max_width) && + current_width != indent) { + current_width = indent; + strm << '\n' << std::string(indent, ' '); + // We're going to a new line so we don't need a space before the + // name of the enumerator. + enumerator_string = enumerator_string.drop_front(); + } + + current_width += enumerator_string.size(); + strm << enumerator_string; + } +} + +std::string RegisterFlags::DumpEnums(uint32_t max_width) const { + StreamString strm; + bool printed_enumerators_once = false; + + for (const auto &field : m_fields) { + const FieldEnum *enum_type = field.GetEnum(); + if (!enum_type) + continue; + + const FieldEnum::Enumerators &enumerators = enum_type->GetEnumerators(); + if (enumerators.empty()) + continue; + + // Break between enumerators of different fields. + if (printed_enumerators_once) + strm << "\n\n"; + else + printed_enumerators_once = true; + + std::string name_string = field.GetName() + ": "; + size_t indent = name_string.size(); + size_t current_width = indent; + + strm << name_string; + + DumpEnumerators(strm, indent, current_width, max_width, enumerators); + } + + return strm.GetString().str(); +} + +void RegisterFlags::EnumsToXML(Stream &strm, llvm::StringSet<> &seen) const { + for (const Field &field : m_fields) + if (const FieldEnum *enum_type = field.GetEnum()) { + const std::string &id = enum_type->GetID(); + if (!seen.contains(id)) { + enum_type->ToXML(strm, GetSize()); + seen.insert(id); + } + } +} + +void FieldEnum::ToXML(Stream &strm, unsigned size) const { + // Example XML: + // <enum id="foo" size="4"> + // <evalue name="bar" value="1"/> + // </enum> + // Note that "size" is only emitted for GDB compatibility, LLDB does not need + // it. + + strm.Indent(); + strm << "<enum id=\"" << GetID() << "\" "; + // This is the size of the underlying enum type if this were a C type. + // In other words, the size of the register in bytes. + strm.Printf("size=\"%d\"", size); + + const Enumerators &enumerators = GetEnumerators(); + if (enumerators.empty()) { + strm << "/>\n"; + return; + } + + strm << ">\n"; + strm.IndentMore(); + for (const auto &enumerator : enumerators) { + strm.Indent(); + enumerator.ToXML(strm); + strm.PutChar('\n'); + } + strm.IndentLess(); + strm.Indent("</enum>\n"); +} + +void FieldEnum::Enumerator::ToXML(Stream &strm) const { + std::string escaped_name; + llvm::raw_string_ostream escape_strm(escaped_name); + llvm::printHTMLEscaped(m_name, escape_strm); + strm.Printf("<evalue name=\"%s\" value=\"%" PRIu64 "\"/>", + escaped_name.c_str(), m_value); +} + +void FieldEnum::Enumerator::DumpToLog(Log *log) const { + LLDB_LOG(log, " Name: \"{0}\" Value: {1}", m_name.c_str(), m_value); +} + +void FieldEnum::DumpToLog(Log *log) const { + LLDB_LOG(log, "ID: \"{0}\"", m_id.c_str()); + for (const auto &enumerator : GetEnumerators()) + enumerator.DumpToLog(log); +} + void RegisterFlags::ToXML(Stream &strm) const { // Example XML: // <flags id="cpsr_flags" size="4"> @@ -214,7 +400,9 @@ void RegisterFlags::ToXML(Stream &strm) const { } void RegisterFlags::Field::ToXML(Stream &strm) const { - // Example XML: + // Example XML with an enum: + // <field name="correct" start="0" end="0" type="some_enum"> + // Without: // <field name="correct" start="0" end="0"/> strm.Indent(); strm << "<field name=\""; @@ -225,5 +413,17 @@ void RegisterFlags::Field::ToXML(Stream &strm) const { strm << escaped_name << "\" "; strm.Printf("start=\"%d\" end=\"%d\"", GetStart(), GetEnd()); + + if (const FieldEnum *enum_type = GetEnum()) + strm << " type=\"" << enum_type->GetID() << "\""; + strm << "/>"; } + +FieldEnum::FieldEnum(std::string id, const Enumerators &enumerators) + : m_id(id), m_enumerators(enumerators) { + for (const auto &enumerator : m_enumerators) { + UNUSED_IF_ASSERT_DISABLED(enumerator); + assert(enumerator.m_name.size() && "Enumerator name cannot be empty"); + } +}
\ No newline at end of file diff --git a/lldb/source/Target/RemoteAwarePlatform.cpp b/lldb/source/Target/RemoteAwarePlatform.cpp index 9a41a423cadd..5fc2d63876b9 100644 --- a/lldb/source/Target/RemoteAwarePlatform.cpp +++ b/lldb/source/Target/RemoteAwarePlatform.cpp @@ -30,142 +30,26 @@ bool RemoteAwarePlatform::GetModuleSpec(const FileSpec &module_file_spec, } Status RemoteAwarePlatform::ResolveExecutable( - const ModuleSpec &module_spec, ModuleSP &exe_module_sp, + const ModuleSpec &module_spec, lldb::ModuleSP &exe_module_sp, const FileSpecList *module_search_paths_ptr) { - Status error; - // Nothing special to do here, just use the actual file and architecture - - char exe_path[PATH_MAX]; ModuleSpec resolved_module_spec(module_spec); + // The host platform can resolve the path more aggressively. if (IsHost()) { - // If we have "ls" as the exe_file, resolve the executable location based - // on the current path variables - if (!FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec())) { - resolved_module_spec.GetFileSpec().GetPath(exe_path, sizeof(exe_path)); - resolved_module_spec.GetFileSpec().SetFile(exe_path, - FileSpec::Style::native); - FileSystem::Instance().Resolve(resolved_module_spec.GetFileSpec()); - } + FileSpec &resolved_file_spec = resolved_module_spec.GetFileSpec(); - if (!FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec())) - FileSystem::Instance().ResolveExecutableLocation( - resolved_module_spec.GetFileSpec()); - - // Resolve any executable within a bundle on MacOSX - Host::ResolveExecutableInBundle(resolved_module_spec.GetFileSpec()); - - if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec())) - error.Clear(); - else { - const uint32_t permissions = FileSystem::Instance().GetPermissions( - resolved_module_spec.GetFileSpec()); - if (permissions && (permissions & eFilePermissionsEveryoneR) == 0) - error.SetErrorStringWithFormat( - "executable '%s' is not readable", - resolved_module_spec.GetFileSpec().GetPath().c_str()); - else - error.SetErrorStringWithFormat( - "unable to find executable for '%s'", - resolved_module_spec.GetFileSpec().GetPath().c_str()); - } - } else { - if (m_remote_platform_sp) { - return GetCachedExecutable(resolved_module_spec, exe_module_sp, - module_search_paths_ptr); + if (!FileSystem::Instance().Exists(resolved_file_spec)) { + resolved_module_spec.GetFileSpec().SetFile(resolved_file_spec.GetPath(), + FileSpec::Style::native); + FileSystem::Instance().Resolve(resolved_file_spec); } - // We may connect to a process and use the provided executable (Don't use - // local $PATH). - - // Resolve any executable within a bundle on MacOSX - Host::ResolveExecutableInBundle(resolved_module_spec.GetFileSpec()); - - if (FileSystem::Instance().Exists(resolved_module_spec.GetFileSpec())) - error.Clear(); - else - error.SetErrorStringWithFormat("the platform is not currently " - "connected, and '%s' doesn't exist in " - "the system root.", - exe_path); - } - - if (error.Success()) { - if (resolved_module_spec.GetArchitecture().IsValid()) { - error = ModuleList::GetSharedModule(resolved_module_spec, exe_module_sp, - module_search_paths_ptr, nullptr, nullptr); - if (error.Fail()) { - // If we failed, it may be because the vendor and os aren't known. If - // that is the case, try setting them to the host architecture and give - // it another try. - llvm::Triple &module_triple = - resolved_module_spec.GetArchitecture().GetTriple(); - bool is_vendor_specified = - (module_triple.getVendor() != llvm::Triple::UnknownVendor); - bool is_os_specified = - (module_triple.getOS() != llvm::Triple::UnknownOS); - if (!is_vendor_specified || !is_os_specified) { - const llvm::Triple &host_triple = - HostInfo::GetArchitecture(HostInfo::eArchKindDefault).GetTriple(); - - if (!is_vendor_specified) - module_triple.setVendorName(host_triple.getVendorName()); - if (!is_os_specified) - module_triple.setOSName(host_triple.getOSName()); - - error = ModuleList::GetSharedModule(resolved_module_spec, - exe_module_sp, module_search_paths_ptr, nullptr, nullptr); - } - } - - // TODO find out why exe_module_sp might be NULL - if (error.Fail() || !exe_module_sp || !exe_module_sp->GetObjectFile()) { - exe_module_sp.reset(); - error.SetErrorStringWithFormat( - "'%s' doesn't contain the architecture %s", - resolved_module_spec.GetFileSpec().GetPath().c_str(), - resolved_module_spec.GetArchitecture().GetArchitectureName()); - } - } else { - // No valid architecture was specified, ask the platform for the - // architectures that we should be using (in the correct order) and see - // if we can find a match that way - StreamString arch_names; - llvm::ListSeparator LS; - ArchSpec process_host_arch; - 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); - // Did we find an executable using one of the - if (error.Success()) { - if (exe_module_sp && exe_module_sp->GetObjectFile()) - break; - else - error.SetErrorToGenericError(); - } - - arch_names << LS << arch.GetArchitectureName(); - } - - if (error.Fail() || !exe_module_sp) { - if (FileSystem::Instance().Readable( - resolved_module_spec.GetFileSpec())) { - error.SetErrorStringWithFormatv( - "'{0}' doesn't contain any '{1}' platform architectures: {2}", - resolved_module_spec.GetFileSpec(), GetPluginName(), - arch_names.GetData()); - } else { - error.SetErrorStringWithFormat( - "'%s' is not readable", - resolved_module_spec.GetFileSpec().GetPath().c_str()); - } - } - } + if (!FileSystem::Instance().Exists(resolved_file_spec)) + FileSystem::Instance().ResolveExecutableLocation(resolved_file_spec); } - return error; + return Platform::ResolveExecutable(resolved_module_spec, exe_module_sp, + module_search_paths_ptr); } Status RemoteAwarePlatform::RunShellCommand( diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp index 2a5300012511..583d1524881f 100644 --- a/lldb/source/Target/Statistics.cpp +++ b/lldb/source/Target/Statistics.cpp @@ -107,7 +107,8 @@ TargetStats::ToJSON(Target &target, const lldb_private::StatisticsOptions &options) { json::Object target_metrics_json; ProcessSP process_sp = target.GetProcessSP(); - const bool summary_only = options.summary_only; + const bool summary_only = options.GetSummaryOnly(); + const bool include_modules = options.GetIncludeModules(); if (!summary_only) { CollectStats(target); @@ -117,8 +118,9 @@ TargetStats::ToJSON(Target &target, target_metrics_json.try_emplace(m_expr_eval.name, m_expr_eval.ToJSON()); target_metrics_json.try_emplace(m_frame_var.name, m_frame_var.ToJSON()); - target_metrics_json.try_emplace("moduleIdentifiers", - std::move(json_module_uuid_array)); + if (include_modules) + target_metrics_json.try_emplace("moduleIdentifiers", + std::move(json_module_uuid_array)); if (m_launch_or_attach_time && m_first_private_stop_time) { double elapsed_time = @@ -224,9 +226,11 @@ llvm::json::Value DebuggerStats::ReportStatistics( Debugger &debugger, Target *target, const lldb_private::StatisticsOptions &options) { - const bool summary_only = options.summary_only; - const bool load_all_debug_info = options.load_all_debug_info; - const bool include_transcript = options.include_transcript; + const bool summary_only = options.GetSummaryOnly(); + const bool load_all_debug_info = options.GetLoadAllDebugInfo(); + const bool include_targets = options.GetIncludeTargets(); + const bool include_modules = options.GetIncludeModules(); + const bool include_transcript = options.GetIncludeTranscript(); json::Array json_targets; json::Array json_modules; @@ -314,7 +318,7 @@ llvm::json::Value DebuggerStats::ReportStatistics( if (module_stat.debug_info_had_incomplete_types) ++num_modules_with_incomplete_types; - if (!summary_only) { + if (include_modules) { module_stat.identifier = (intptr_t)module; module_stat.path = module->GetFileSpec().GetPath(); if (ConstString object_name = module->GetObjectName()) { @@ -347,13 +351,15 @@ llvm::json::Value DebuggerStats::ReportStatistics( {"totalSymbolTableStripped", num_stripped_modules}, }; - if (target) { - json_targets.emplace_back(target->ReportStatistics(options)); - } else { - for (const auto &target : debugger.GetTargetList().Targets()) + if (include_targets) { + if (target) { json_targets.emplace_back(target->ReportStatistics(options)); + } else { + for (const auto &target : debugger.GetTargetList().Targets()) + json_targets.emplace_back(target->ReportStatistics(options)); + } + global_stats.try_emplace("targets", std::move(json_targets)); } - global_stats.try_emplace("targets", std::move(json_targets)); ConstStringStats const_string_stats; json::Object json_memory{ @@ -362,10 +368,13 @@ llvm::json::Value DebuggerStats::ReportStatistics( global_stats.try_emplace("memory", std::move(json_memory)); if (!summary_only) { json::Value cmd_stats = debugger.GetCommandInterpreter().GetStatistics(); - global_stats.try_emplace("modules", std::move(json_modules)); global_stats.try_emplace("commands", std::move(cmd_stats)); } + if (include_modules) { + global_stats.try_emplace("modules", std::move(json_modules)); + } + if (include_transcript) { // When transcript is available, add it to the to-be-returned statistics. // diff --git a/lldb/source/Target/ThreadPlanPython.cpp b/lldb/source/Target/ThreadPlanPython.cpp index d6de6b3c3cf0..373555324ba6 100644 --- a/lldb/source/Target/ThreadPlanPython.cpp +++ b/lldb/source/Target/ThreadPlanPython.cpp @@ -10,6 +10,7 @@ #include "lldb/Core/Debugger.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/Interfaces/ScriptedThreadPlanInterface.h" #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" @@ -32,6 +33,26 @@ ThreadPlanPython::ThreadPlanPython(Thread &thread, const char *class_name, eVoteNoOpinion, eVoteNoOpinion), m_class_name(class_name), m_args_data(args_data), m_did_push(false), m_stop_others(false) { + ScriptInterpreter *interpreter = GetScriptInterpreter(); + if (!interpreter) { + SetPlanComplete(false); + // FIXME: error handling + // error.SetErrorStringWithFormat( + // "ThreadPlanPython::%s () - ERROR: %s", __FUNCTION__, + // "Couldn't get script interpreter"); + return; + } + + m_interface = interpreter->CreateScriptedThreadPlanInterface(); + if (!m_interface) { + SetPlanComplete(false); + // FIXME: error handling + // error.SetErrorStringWithFormat( + // "ThreadPlanPython::%s () - ERROR: %s", __FUNCTION__, + // "Script interpreter couldn't create Scripted Thread Plan Interface"); + return; + } + SetIsControllingPlan(true); SetOkayToDiscard(true); SetPrivate(false); @@ -60,13 +81,14 @@ void ThreadPlanPython::DidPush() { // We set up the script side in DidPush, so that it can push other plans in // the constructor, and doesn't have to care about the details of DidPush. m_did_push = true; - if (!m_class_name.empty()) { - ScriptInterpreter *script_interp = GetScriptInterpreter(); - if (script_interp) { - m_implementation_sp = script_interp->CreateScriptedThreadPlan( - m_class_name.c_str(), m_args_data, m_error_str, - this->shared_from_this()); - } + if (m_interface) { + auto obj_or_err = m_interface->CreatePluginObject( + m_class_name, this->shared_from_this(), m_args_data); + if (!obj_or_err) { + m_error_str = llvm::toString(obj_or_err.takeError()); + SetPlanComplete(false); + } else + m_implementation_sp = *obj_or_err; } } @@ -77,14 +99,13 @@ bool ThreadPlanPython::ShouldStop(Event *event_ptr) { bool should_stop = true; if (m_implementation_sp) { - ScriptInterpreter *script_interp = GetScriptInterpreter(); - if (script_interp) { - bool script_error; - should_stop = script_interp->ScriptedThreadPlanShouldStop( - m_implementation_sp, event_ptr, script_error); - if (script_error) - SetPlanComplete(false); - } + auto should_stop_or_err = m_interface->ShouldStop(event_ptr); + if (!should_stop_or_err) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Thread), should_stop_or_err.takeError(), + "Can't call ScriptedThreadPlan::ShouldStop."); + SetPlanComplete(false); + } else + should_stop = *should_stop_or_err; } return should_stop; } @@ -96,14 +117,13 @@ bool ThreadPlanPython::IsPlanStale() { bool is_stale = true; if (m_implementation_sp) { - ScriptInterpreter *script_interp = GetScriptInterpreter(); - if (script_interp) { - bool script_error; - is_stale = script_interp->ScriptedThreadPlanIsStale(m_implementation_sp, - script_error); - if (script_error) - SetPlanComplete(false); - } + auto is_stale_or_err = m_interface->IsStale(); + if (!is_stale_or_err) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Thread), is_stale_or_err.takeError(), + "Can't call ScriptedThreadPlan::IsStale."); + SetPlanComplete(false); + } else + is_stale = *is_stale_or_err; } return is_stale; } @@ -115,14 +135,14 @@ bool ThreadPlanPython::DoPlanExplainsStop(Event *event_ptr) { bool explains_stop = true; if (m_implementation_sp) { - ScriptInterpreter *script_interp = GetScriptInterpreter(); - if (script_interp) { - bool script_error; - explains_stop = script_interp->ScriptedThreadPlanExplainsStop( - m_implementation_sp, event_ptr, script_error); - if (script_error) - SetPlanComplete(false); - } + auto explains_stop_or_error = m_interface->ExplainsStop(event_ptr); + if (!explains_stop_or_error) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Thread), + explains_stop_or_error.takeError(), + "Can't call ScriptedThreadPlan::ExplainsStop."); + SetPlanComplete(false); + } else + explains_stop = *explains_stop_or_error; } return explains_stop; } @@ -150,14 +170,8 @@ lldb::StateType ThreadPlanPython::GetPlanRunState() { LLDB_LOGF(log, "%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION, m_class_name.c_str()); lldb::StateType run_state = eStateRunning; - if (m_implementation_sp) { - ScriptInterpreter *script_interp = GetScriptInterpreter(); - if (script_interp) { - bool script_error; - run_state = script_interp->ScriptedThreadPlanGetRunState( - m_implementation_sp, script_error); - } - } + if (m_implementation_sp) + run_state = m_interface->GetRunState(); return run_state; } @@ -168,12 +182,13 @@ void ThreadPlanPython::GetDescription(Stream *s, lldb::DescriptionLevel level) { if (m_implementation_sp) { ScriptInterpreter *script_interp = GetScriptInterpreter(); if (script_interp) { - bool script_error; - bool added_desc = script_interp->ScriptedThreadPlanGetStopDescription( - m_implementation_sp, s, script_error); - if (script_error || !added_desc) + auto desc_or_err = m_interface->GetStopDescription(s); + if (!desc_or_err || !*desc_or_err) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Thread), desc_or_err.takeError(), + "Can't call ScriptedThreadPlan::GetStopDescription."); s->Printf("Python thread plan implemented by class %s.", m_class_name.c_str()); + } } return; } diff --git a/lldb/source/Utility/CMakeLists.txt b/lldb/source/Utility/CMakeLists.txt index a3b0a405b413..e9954d66cd1a 100644 --- a/lldb/source/Utility/CMakeLists.txt +++ b/lldb/source/Utility/CMakeLists.txt @@ -39,6 +39,7 @@ add_lldb_library(lldbUtility NO_INTERNAL_DEPENDENCIES DataExtractor.cpp Diagnostics.cpp Environment.cpp + ErrorMessages.cpp Event.cpp FileSpec.cpp FileSpecList.cpp diff --git a/lldb/source/Utility/ErrorMessages.cpp b/lldb/source/Utility/ErrorMessages.cpp new file mode 100644 index 000000000000..aea5cb5f47c1 --- /dev/null +++ b/lldb/source/Utility/ErrorMessages.cpp @@ -0,0 +1,40 @@ +//===-- ErrorMessages.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/Utility/ErrorMessages.h" +#include "llvm/Support/ErrorHandling.h" + +namespace lldb_private { + +std::string toString(lldb::ExpressionResults e) { + switch (e) { + case lldb::eExpressionSetupError: + return "expression setup error"; + case lldb::eExpressionParseError: + return "expression parse error"; + case lldb::eExpressionResultUnavailable: + return "expression error"; + case lldb::eExpressionCompleted: + return "expression completed successfully"; + case lldb::eExpressionDiscarded: + return "expression discarded"; + case lldb::eExpressionInterrupted: + return "expression interrupted"; + case lldb::eExpressionHitBreakpoint: + return "expression hit breakpoint"; + case lldb::eExpressionTimedOut: + return "expression timed out"; + case lldb::eExpressionStoppedForDebug: + return "expression stop at entry point for debugging"; + case lldb::eExpressionThreadVanished: + return "expression thread vanished"; + } + llvm_unreachable("unhandled enumerator"); +} + +} // namespace lldb_private diff --git a/lldb/source/Utility/ProcessInfo.cpp b/lldb/source/Utility/ProcessInfo.cpp index 6b2a7114dfb4..845b337e246f 100644 --- a/lldb/source/Utility/ProcessInfo.cpp +++ b/lldb/source/Utility/ProcessInfo.cpp @@ -121,8 +121,8 @@ void ProcessInstanceInfo::Dump(Stream &s, UserIDResolver &resolver) const { if (m_pid != LLDB_INVALID_PROCESS_ID) s.Printf(" pid = %" PRIu64 "\n", m_pid); - if (m_parent_pid != LLDB_INVALID_PROCESS_ID) - s.Printf(" parent = %" PRIu64 "\n", m_parent_pid); + if (ParentProcessIDIsValid()) + s.Printf(" parent = %" PRIu64 "\n", GetParentProcessID()); if (m_executable) { s.Printf(" name = %s\n", m_executable.GetFilename().GetCString()); @@ -193,7 +193,8 @@ void ProcessInstanceInfo::DumpTableHeader(Stream &s, bool show_args, void ProcessInstanceInfo::DumpAsTableRow(Stream &s, UserIDResolver &resolver, bool show_args, bool verbose) const { if (m_pid != LLDB_INVALID_PROCESS_ID) { - s.Printf("%-6" PRIu64 " %-6" PRIu64 " ", m_pid, m_parent_pid); + s.Printf("%-6" PRIu64 " %-6" PRIu64 " ", m_pid, + (ParentProcessIDIsValid()) ? GetParentProcessID() : 0); StreamString arch_strm; if (m_arch.IsValid()) diff --git a/lldb/source/Utility/RegularExpression.cpp b/lldb/source/Utility/RegularExpression.cpp index 20bebbfe15f2..026793462221 100644 --- a/lldb/source/Utility/RegularExpression.cpp +++ b/lldb/source/Utility/RegularExpression.cpp @@ -12,10 +12,11 @@ using namespace lldb_private; -RegularExpression::RegularExpression(llvm::StringRef str) +RegularExpression::RegularExpression(llvm::StringRef str, + llvm::Regex::RegexFlags flags) : m_regex_text(std::string(str)), // m_regex does not reference str anymore after it is constructed. - m_regex(llvm::Regex(str)) {} + m_regex(llvm::Regex(str, flags)) {} RegularExpression::RegularExpression(const RegularExpression &rhs) : RegularExpression(rhs.GetText()) {} diff --git a/lldb/source/Utility/Scalar.cpp b/lldb/source/Utility/Scalar.cpp index c70c5e107991..c680101aa9ef 100644 --- a/lldb/source/Utility/Scalar.cpp +++ b/lldb/source/Utility/Scalar.cpp @@ -753,9 +753,7 @@ bool Scalar::SignExtend(uint32_t sign_bit_pos) { return false; case Scalar::e_int: - if (max_bit_pos == sign_bit_pos) - return true; - else if (sign_bit_pos < (max_bit_pos - 1)) { + if (sign_bit_pos < (max_bit_pos - 1)) { llvm::APInt sign_bit = llvm::APInt::getSignMask(sign_bit_pos + 1); llvm::APInt bitwize_and = m_integer & sign_bit; if (bitwize_and.getBoolValue()) { |
